From 422abee8286e176a8e5de456a0cd56b2e05880bb Mon Sep 17 00:00:00 2001 From: chws0508 Date: Wed, 2 Aug 2023 19:13:25 +0900 Subject: [PATCH 1/7] =?UTF-8?q?feat=20:=20UidRepository=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/emmsale/data/uid/UidRepository.kt | 5 +++++ .../com/emmsale/data/uid/UidRepositoryImpl.kt | 17 +++++++++++++++++ .../java/com/emmsale/di/RepositoryContainer.kt | 5 +++++ 3 files changed, 27 insertions(+) create mode 100644 android/2023-emmsale/app/src/main/java/com/emmsale/data/uid/UidRepository.kt create mode 100644 android/2023-emmsale/app/src/main/java/com/emmsale/data/uid/UidRepositoryImpl.kt diff --git a/android/2023-emmsale/app/src/main/java/com/emmsale/data/uid/UidRepository.kt b/android/2023-emmsale/app/src/main/java/com/emmsale/data/uid/UidRepository.kt new file mode 100644 index 000000000..5f7d1ee34 --- /dev/null +++ b/android/2023-emmsale/app/src/main/java/com/emmsale/data/uid/UidRepository.kt @@ -0,0 +1,5 @@ +package com.emmsale.data.uid + +interface UidRepository { + fun getCurrentUid(): Long +} diff --git a/android/2023-emmsale/app/src/main/java/com/emmsale/data/uid/UidRepositoryImpl.kt b/android/2023-emmsale/app/src/main/java/com/emmsale/data/uid/UidRepositoryImpl.kt new file mode 100644 index 000000000..60e0c7e56 --- /dev/null +++ b/android/2023-emmsale/app/src/main/java/com/emmsale/data/uid/UidRepositoryImpl.kt @@ -0,0 +1,17 @@ +package com.emmsale.data.uid + +import android.content.SharedPreferences + +class UidRepositoryImpl( + private val sharedPreferences: SharedPreferences, +) : UidRepository { + + override fun getCurrentUid(): Long { + return sharedPreferences.getLong(UID_KEY, DEFAULT_UID) + } + + companion object { + private const val UID_KEY = "uid_key" + private const val DEFAULT_UID = 0L + } +} diff --git a/android/2023-emmsale/app/src/main/java/com/emmsale/di/RepositoryContainer.kt b/android/2023-emmsale/app/src/main/java/com/emmsale/di/RepositoryContainer.kt index 791e20622..8c8752e1e 100644 --- a/android/2023-emmsale/app/src/main/java/com/emmsale/di/RepositoryContainer.kt +++ b/android/2023-emmsale/app/src/main/java/com/emmsale/di/RepositoryContainer.kt @@ -14,6 +14,8 @@ import com.emmsale.data.member.MemberRepository import com.emmsale.data.member.MemberRepositoryImpl import com.emmsale.data.token.TokenRepository import com.emmsale.data.token.TokenRepositoryImpl +import com.emmsale.data.uid.UidRepository +import com.emmsale.data.uid.UidRepositoryImpl class RepositoryContainer( serviceContainer: ServiceContainer, @@ -40,4 +42,7 @@ class RepositoryContainer( val eventDetailRepository: EventDetailRepository by lazy { EventDetailRepositoryImpl(eventDetailService = serviceContainer.eventDetailService) } + val uidRepository: UidRepository by lazy { + UidRepositoryImpl(preferenceContainer.preference) + } } From 1d86baf07967440a7c68cbc87722f88bb808eb9a Mon Sep 17 00:00:00 2001 From: chws0508 Date: Wed, 2 Aug 2023 19:14:30 +0900 Subject: [PATCH 2/7] =?UTF-8?q?feat=20:=20Participant=20Repository=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../emmsale/data/participant/Participant.kt | 9 +++ .../data/participant/ParticipantRepository.kt | 21 +++++++ .../participant/ParticipantRepositoryImpl.kt | 57 +++++++++++++++++++ .../data/participant/ParticipantService.kt | 36 ++++++++++++ .../participant/dto/CompanionRequestBody.kt | 16 ++++++ .../participant/dto/ParticipantApiModel.kt | 29 ++++++++++ .../participant/dto/ParticipantRequestBody.kt | 10 ++++ .../participant/dto/ParticipationStatus.kt | 8 +++ 8 files changed, 186 insertions(+) create mode 100644 android/2023-emmsale/app/src/main/java/com/emmsale/data/participant/Participant.kt create mode 100644 android/2023-emmsale/app/src/main/java/com/emmsale/data/participant/ParticipantRepository.kt create mode 100644 android/2023-emmsale/app/src/main/java/com/emmsale/data/participant/ParticipantRepositoryImpl.kt create mode 100644 android/2023-emmsale/app/src/main/java/com/emmsale/data/participant/ParticipantService.kt create mode 100644 android/2023-emmsale/app/src/main/java/com/emmsale/data/participant/dto/CompanionRequestBody.kt create mode 100644 android/2023-emmsale/app/src/main/java/com/emmsale/data/participant/dto/ParticipantApiModel.kt create mode 100644 android/2023-emmsale/app/src/main/java/com/emmsale/data/participant/dto/ParticipantRequestBody.kt create mode 100644 android/2023-emmsale/app/src/main/java/com/emmsale/data/participant/dto/ParticipationStatus.kt diff --git a/android/2023-emmsale/app/src/main/java/com/emmsale/data/participant/Participant.kt b/android/2023-emmsale/app/src/main/java/com/emmsale/data/participant/Participant.kt new file mode 100644 index 000000000..0f9dec285 --- /dev/null +++ b/android/2023-emmsale/app/src/main/java/com/emmsale/data/participant/Participant.kt @@ -0,0 +1,9 @@ +package com.emmsale.data.participant + +data class Participant( + val id: Long, + val memberId: Long, + val name: String, + val imageUrl: String, + val description: String?, +) diff --git a/android/2023-emmsale/app/src/main/java/com/emmsale/data/participant/ParticipantRepository.kt b/android/2023-emmsale/app/src/main/java/com/emmsale/data/participant/ParticipantRepository.kt new file mode 100644 index 000000000..f3c14a53d --- /dev/null +++ b/android/2023-emmsale/app/src/main/java/com/emmsale/data/participant/ParticipantRepository.kt @@ -0,0 +1,21 @@ +package com.emmsale.data.participant + +import com.emmsale.data.common.ApiResult + +interface ParticipantRepository { + + suspend fun fetchEventParticipants( + eventId: Long, + ): ApiResult> + + suspend fun saveParticipant(eventId: Long): ApiResult + suspend fun deleteParticipant(eventId: Long): ApiResult + + suspend fun requestCompanion( + eventId: Long, + memberId: Long, + message: String, + ): ApiResult + + suspend fun checkParticipationStatus(eventId: Long): ApiResult +} diff --git a/android/2023-emmsale/app/src/main/java/com/emmsale/data/participant/ParticipantRepositoryImpl.kt b/android/2023-emmsale/app/src/main/java/com/emmsale/data/participant/ParticipantRepositoryImpl.kt new file mode 100644 index 000000000..b38ad38af --- /dev/null +++ b/android/2023-emmsale/app/src/main/java/com/emmsale/data/participant/ParticipantRepositoryImpl.kt @@ -0,0 +1,57 @@ +package com.emmsale.data.participant + +import com.emmsale.data.common.ApiResult +import com.emmsale.data.common.ApiSuccess +import com.emmsale.data.common.handleApi +import com.emmsale.data.participant.dto.CompanionRequestBody +import com.emmsale.data.participant.dto.ParticipantApiModel +import com.emmsale.data.participant.dto.ParticipantRequestBody +import com.emmsale.data.participant.dto.toData +import com.emmsale.data.uid.UidRepository + +class ParticipantRepositoryImpl( + private val participantService: ParticipantService, + uidRepository: UidRepository, +) : ParticipantRepository { + + private val currentUid = uidRepository.getCurrentUid() + + override suspend fun fetchEventParticipants(eventId: Long): ApiResult> { + val response = participantService.getParticipants(eventId) + return handleApi( + response = response, + mapToDomain = List::toData, + ) + } + + override suspend fun saveParticipant(eventId: Long): ApiResult { + val requestBody = ParticipantRequestBody(currentUid) + val response = participantService.saveParticipant(eventId, requestBody) + return handleApi(response, mapToDomain = { }) + } + + override suspend fun deleteParticipant(eventId: Long): ApiResult { + val requestBody = ParticipantRequestBody(currentUid) + val response = participantService.deleteParticipant(eventId, requestBody) + return handleApi(response, mapToDomain = {}) + } + + override suspend fun requestCompanion( + eventId: Long, + memberId: Long, + message: String, + ): ApiResult { + val requestBody = CompanionRequestBody( + senderId = currentUid, + receiverId = memberId, + eventId = eventId, + message = message, + ) + val response = participantService.postCompanion(requestBody) + return handleApi(response, mapToDomain = {}) + } + + override suspend fun checkParticipationStatus(eventId: Long): ApiResult { + return ApiSuccess(true) + } +} diff --git a/android/2023-emmsale/app/src/main/java/com/emmsale/data/participant/ParticipantService.kt b/android/2023-emmsale/app/src/main/java/com/emmsale/data/participant/ParticipantService.kt new file mode 100644 index 000000000..cbf6a00e3 --- /dev/null +++ b/android/2023-emmsale/app/src/main/java/com/emmsale/data/participant/ParticipantService.kt @@ -0,0 +1,36 @@ +package com.emmsale.data.participant + +import com.emmsale.data.participant.dto.CompanionRequestBody +import com.emmsale.data.participant.dto.ParticipantApiModel +import com.emmsale.data.participant.dto.ParticipantRequestBody +import retrofit2.Response +import retrofit2.http.Body +import retrofit2.http.DELETE +import retrofit2.http.GET +import retrofit2.http.POST +import retrofit2.http.Path + +interface ParticipantService { + + @GET("events/{eventId}/participants") + suspend fun getParticipants( + @Path("eventId") eventId: Long, + ): Response> + + @POST("events/{eventId}/participants") + suspend fun saveParticipant( + @Path("eventId") eventId: Long, + @Body participantRequestBody: ParticipantRequestBody, + ): Response + + @DELETE("events/{eventId}/participants") + suspend fun deleteParticipant( + @Path("eventId") eventId: Long, + @Body participantRequestBody: ParticipantRequestBody, + ): Response + + @POST("notifications") + suspend fun postCompanion( + @Body companionRequestBody: CompanionRequestBody, + ): Response +} diff --git a/android/2023-emmsale/app/src/main/java/com/emmsale/data/participant/dto/CompanionRequestBody.kt b/android/2023-emmsale/app/src/main/java/com/emmsale/data/participant/dto/CompanionRequestBody.kt new file mode 100644 index 000000000..daf073bb3 --- /dev/null +++ b/android/2023-emmsale/app/src/main/java/com/emmsale/data/participant/dto/CompanionRequestBody.kt @@ -0,0 +1,16 @@ +package com.emmsale.data.participant.dto + +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable + +@Serializable +data class CompanionRequestBody( + @SerialName("senderId") + val senderId: Long, + @SerialName("receiverId") + val receiverId: Long, + @SerialName("message") + val message: String, + @SerialName("eventId") + val eventId: Long, +) diff --git a/android/2023-emmsale/app/src/main/java/com/emmsale/data/participant/dto/ParticipantApiModel.kt b/android/2023-emmsale/app/src/main/java/com/emmsale/data/participant/dto/ParticipantApiModel.kt new file mode 100644 index 000000000..913dada18 --- /dev/null +++ b/android/2023-emmsale/app/src/main/java/com/emmsale/data/participant/dto/ParticipantApiModel.kt @@ -0,0 +1,29 @@ +package com.emmsale.data.participant.dto + +import com.emmsale.data.participant.Participant +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable + +@Serializable +data class ParticipantApiModel( + @SerialName("id") + val id: Long, + @SerialName("memberId") + val memberId: Long, + @SerialName("name") + val name: String, + @SerialName("imageUrl") + val imageUrl: String, + @SerialName("description") + val description: String?, +) + +fun List.toData(): List = map { + Participant( + id = it.id, + memberId = it.memberId, + name = it.name, + imageUrl = it.imageUrl, + description = it.description, + ) +} diff --git a/android/2023-emmsale/app/src/main/java/com/emmsale/data/participant/dto/ParticipantRequestBody.kt b/android/2023-emmsale/app/src/main/java/com/emmsale/data/participant/dto/ParticipantRequestBody.kt new file mode 100644 index 000000000..b342a2527 --- /dev/null +++ b/android/2023-emmsale/app/src/main/java/com/emmsale/data/participant/dto/ParticipantRequestBody.kt @@ -0,0 +1,10 @@ +package com.emmsale.data.participant.dto + +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable + +@Serializable +data class ParticipantRequestBody( + @SerialName("memberId") + val memberId: Long, +) diff --git a/android/2023-emmsale/app/src/main/java/com/emmsale/data/participant/dto/ParticipationStatus.kt b/android/2023-emmsale/app/src/main/java/com/emmsale/data/participant/dto/ParticipationStatus.kt new file mode 100644 index 000000000..f408b412f --- /dev/null +++ b/android/2023-emmsale/app/src/main/java/com/emmsale/data/participant/dto/ParticipationStatus.kt @@ -0,0 +1,8 @@ +package com.emmsale.data.participant.dto + +import kotlinx.serialization.Serializable + +@Serializable +data class ParticipationStatus( + val isParticipated: Boolean, +) From 1dcba3b60affc638dc1b57fc08e439582a8806dc Mon Sep 17 00:00:00 2001 From: chws0508 Date: Wed, 2 Aug 2023 21:51:30 +0900 Subject: [PATCH 3/7] =?UTF-8?q?feat=20:=20=EC=B0=B8=EA=B0=80=EC=9E=90=20?= =?UTF-8?q?=EB=AA=A9=EB=A1=9D=20=EB=B6=88=EB=9F=AC=EC=98=A4=EA=B8=B0,=20?= =?UTF-8?q?=EC=B0=B8=EA=B0=80=ED=95=98=EA=B8=B0,=20=EA=B0=99=EC=9D=B4?= =?UTF-8?q?=EA=B0=80=EC=9A=94=20=EC=9A=94=EC=B2=AD=20=EA=B8=B0=EB=8A=A5=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/emmsale/di/RepositoryContainer.kt | 8 ++ .../java/com/emmsale/di/ServiceContainer.kt | 2 + .../participant/EventParticipantAdapter.kt | 75 +++++++++++ .../participant/EventParticipantFragment.kt | 117 ++++++++++++++++++ .../participant/EventParticipantViewModel.kt | 100 +++++++++++++++ .../participant/uistate/ParticipantUiState.kt | 21 ++++ .../uistate/ParticipantsUiState.kt | 16 +++ .../uistate/ParticipationStatusUiState.kt | 6 + .../res/layout/fragment_event_participant.xml | 34 ++++- .../src/main/res/layout/item_participant.xml | 86 +++++++++++++ 10 files changed, 460 insertions(+), 5 deletions(-) create mode 100644 android/2023-emmsale/app/src/main/java/com/emmsale/presentation/ui/eventdetail/participant/EventParticipantAdapter.kt create mode 100644 android/2023-emmsale/app/src/main/java/com/emmsale/presentation/ui/eventdetail/participant/EventParticipantViewModel.kt create mode 100644 android/2023-emmsale/app/src/main/java/com/emmsale/presentation/ui/eventdetail/participant/uistate/ParticipantUiState.kt create mode 100644 android/2023-emmsale/app/src/main/java/com/emmsale/presentation/ui/eventdetail/participant/uistate/ParticipantsUiState.kt create mode 100644 android/2023-emmsale/app/src/main/java/com/emmsale/presentation/ui/eventdetail/participant/uistate/ParticipationStatusUiState.kt create mode 100644 android/2023-emmsale/app/src/main/res/layout/item_participant.xml diff --git a/android/2023-emmsale/app/src/main/java/com/emmsale/di/RepositoryContainer.kt b/android/2023-emmsale/app/src/main/java/com/emmsale/di/RepositoryContainer.kt index 8c8752e1e..1b4ba74d9 100644 --- a/android/2023-emmsale/app/src/main/java/com/emmsale/di/RepositoryContainer.kt +++ b/android/2023-emmsale/app/src/main/java/com/emmsale/di/RepositoryContainer.kt @@ -12,6 +12,8 @@ import com.emmsale.data.login.LoginRepository import com.emmsale.data.login.LoginRepositoryImpl import com.emmsale.data.member.MemberRepository import com.emmsale.data.member.MemberRepositoryImpl +import com.emmsale.data.participant.ParticipantRepository +import com.emmsale.data.participant.ParticipantRepositoryImpl import com.emmsale.data.token.TokenRepository import com.emmsale.data.token.TokenRepositoryImpl import com.emmsale.data.uid.UidRepository @@ -45,4 +47,10 @@ class RepositoryContainer( val uidRepository: UidRepository by lazy { UidRepositoryImpl(preferenceContainer.preference) } + val participantRepository: ParticipantRepository by lazy { + ParticipantRepositoryImpl( + uidRepository = uidRepository, + participantService = serviceContainer.participantService, + ) + } } diff --git a/android/2023-emmsale/app/src/main/java/com/emmsale/di/ServiceContainer.kt b/android/2023-emmsale/app/src/main/java/com/emmsale/di/ServiceContainer.kt index 4d9759710..107aa3497 100644 --- a/android/2023-emmsale/app/src/main/java/com/emmsale/di/ServiceContainer.kt +++ b/android/2023-emmsale/app/src/main/java/com/emmsale/di/ServiceContainer.kt @@ -7,6 +7,7 @@ import com.emmsale.data.eventdetail.EventDetailService import com.emmsale.data.fcmToken.FcmTokenService import com.emmsale.data.login.LoginService import com.emmsale.data.member.MemberService +import com.emmsale.data.participant.ParticipantService class ServiceContainer(serviceFactory: ServiceFactory) { val loginService: LoginService by lazy { serviceFactory.create(LoginService::class.java) } @@ -15,4 +16,5 @@ class ServiceContainer(serviceFactory: ServiceFactory) { val eventService: EventService by lazy { serviceFactory.create(EventService::class.java) } val fcmTokenService: FcmTokenService by lazy { serviceFactory.create(FcmTokenService::class.java) } val eventDetailService: EventDetailService by lazy { serviceFactory.create(EventDetailService::class.java) } + val participantService: ParticipantService by lazy { serviceFactory.create(ParticipantService::class.java) } } diff --git a/android/2023-emmsale/app/src/main/java/com/emmsale/presentation/ui/eventdetail/participant/EventParticipantAdapter.kt b/android/2023-emmsale/app/src/main/java/com/emmsale/presentation/ui/eventdetail/participant/EventParticipantAdapter.kt new file mode 100644 index 000000000..a748ce4ad --- /dev/null +++ b/android/2023-emmsale/app/src/main/java/com/emmsale/presentation/ui/eventdetail/participant/EventParticipantAdapter.kt @@ -0,0 +1,75 @@ +package com.emmsale.presentation.ui.eventdetail.participant + +import android.view.LayoutInflater +import android.view.ViewGroup +import androidx.recyclerview.widget.DiffUtil +import androidx.recyclerview.widget.ListAdapter +import androidx.recyclerview.widget.RecyclerView +import com.emmsale.databinding.ItemParticipantBinding +import com.emmsale.presentation.eventdetail.participant.uistate.ParticipantUiState + +class EventParticipantAdapter( + private val requestCompanion: (Long) -> Unit, + private val showMemberProfile: (Long) -> Unit, +) : ListAdapter(diffUtil) { + + private lateinit var binding: ItemParticipantBinding + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ParticipantViewHolder { + binding = ItemParticipantBinding.inflate( + LayoutInflater.from(parent.context), + parent, + false, + ) + return ParticipantViewHolder(binding, requestCompanion, showMemberProfile) + } + + override fun onBindViewHolder(holder: ParticipantViewHolder, position: Int) { + holder.bind(getItem(position)) + } + + companion object { + val diffUtil = object : DiffUtil.ItemCallback() { + override fun areItemsTheSame( + oldItem: ParticipantUiState, + newItem: ParticipantUiState, + ): Boolean = oldItem == newItem + + override fun areContentsTheSame( + oldItem: ParticipantUiState, + newItem: ParticipantUiState, + ): Boolean = oldItem.id == newItem.id + } + } +} + +class ParticipantViewHolder( + private val binding: ItemParticipantBinding, + private val requestCompanion: (Long) -> Unit, + private val showMemberProfile: (Long) -> Unit, +) : RecyclerView.ViewHolder(binding.root) { + + init { + binding.buttonRequestCompanion.setOnClickListener { requestCompanion(binding.participant!!.id) } + binding.ivParticipantImage.setOnClickListener { showMemberProfile(binding.participant!!.id) } + } + + fun bind(participant: ParticipantUiState) { + binding.participant = participant + } + + companion object { + fun create( + parent: ViewGroup, + requestCompanion: (Long) -> Unit, + showMemberProfile: (Long) -> Unit, + ): ParticipantViewHolder { + val binding = ItemParticipantBinding.inflate( + LayoutInflater.from(parent.context), + parent, + false, + ) + + return ParticipantViewHolder(binding, requestCompanion, showMemberProfile) + } + } +} diff --git a/android/2023-emmsale/app/src/main/java/com/emmsale/presentation/ui/eventdetail/participant/EventParticipantFragment.kt b/android/2023-emmsale/app/src/main/java/com/emmsale/presentation/ui/eventdetail/participant/EventParticipantFragment.kt index 7087499cd..1144cab7f 100644 --- a/android/2023-emmsale/app/src/main/java/com/emmsale/presentation/ui/eventdetail/participant/EventParticipantFragment.kt +++ b/android/2023-emmsale/app/src/main/java/com/emmsale/presentation/ui/eventdetail/participant/EventParticipantFragment.kt @@ -1,11 +1,128 @@ import android.os.Bundle +import android.view.View +import android.widget.Toast +import androidx.appcompat.widget.AppCompatButton +import androidx.fragment.app.viewModels +import androidx.recyclerview.widget.LinearLayoutManager import com.emmsale.R import com.emmsale.databinding.FragmentEventParticipantBinding import com.emmsale.presentation.base.fragment.BaseFragment +import com.emmsale.presentation.eventdetail.participant.uistate.ParticipantsUiState +import com.emmsale.presentation.eventdetail.participant.uistate.ParticipationStatusUiState +import com.emmsale.presentation.ui.eventdetail.participant.EventParticipantAdapter +import com.emmsale.presentation.ui.eventdetail.participant.EventParticipantViewModel class EventParticipantFragment : BaseFragment() { override val layoutResId: Int = R.layout.fragment_event_participant + private val viewModel: EventParticipantViewModel by viewModels { EventParticipantViewModel.factory } + private val eventId: Long by lazy { + arguments?.getLong(EVENT_ID_KEY) ?: throw IllegalArgumentException("아이디못가져옴") + } + private val participantAdapter: EventParticipantAdapter by lazy { + EventParticipantAdapter(::requestCompanion, ::showMemberProfile) + } + private val participationButton: AppCompatButton by lazy { binding.btnEventparticipantParticipate } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + initRecyclerView() + setUpParticipants() + setUpRequestCompanion() + setUpParticipation() + participationButtonClick() + setUpParticipationStatus() + viewModel.fetchParticipants(eventId) + } + + private fun initRecyclerView() { + binding.rvParticipants.apply { + adapter = participantAdapter + layoutManager = LinearLayoutManager(requireContext()) + } + } + + private fun requestCompanion(memberId: Long) { + viewModel.requestCompanion(eventId, memberId) + } + + private fun showMemberProfile(memberId: Long) { + } + + private fun setUpParticipants() { + viewModel.participants.observe(viewLifecycleOwner) { + when (it) { + is ParticipantsUiState.Success -> { + participantAdapter.submitList(it.value) + viewModel.checkParticipationStatus(eventId) + } + + else -> showToastMessage("참가자들 불러오기 실패") + } + } + } + + private fun setUpRequestCompanion() { + viewModel.requestCompanion.observe(viewLifecycleOwner) { success -> + if (success) { + showToastMessage("요청 성공") + } else { + showToastMessage("요청 실패") + } + } + } + + private fun setUpParticipation() { + viewModel.participationSaving.observe(viewLifecycleOwner) { success -> + if (success) { + showToastMessage("참가 성공") + viewModel.checkParticipationStatus(eventId) + } else { + showToastMessage("참가 실패") + } + } + } + + private fun setUpParticipationStatus() { + viewModel.isParticipate.observe(viewLifecycleOwner) { state -> + when (state) { + is ParticipationStatusUiState.Success -> { + if (state.isParticipate) { + showToastMessage("참가 상태") + participationButton.isSelected = true + participationButton.text = "참가 취소" + } else { + showToastMessage("불참 상태") + participationButton.isSelected = false + participationButton.text = "참가 신청" + } + } + + else -> showToastMessage("참여 여부 확인 불가") + } + } + } + + private fun participationButtonClick() { + participationButton.setOnClickListener { + when (val state = viewModel.isParticipate.value) { + is ParticipationStatusUiState.Success -> { + if (state.isParticipate) { + viewModel.deleteParticipant(eventId) + } else { + viewModel.saveParticipant(eventId) + } + } + + else -> showToastMessage("서버 오류로 실행할 수 없어요") + } + } + } + + private fun showToastMessage(message: String) { + Toast.makeText(requireContext(), message, Toast.LENGTH_LONG).show() + } + companion object { private const val EVENT_ID_KEY = "EVENT_ID_KEY" diff --git a/android/2023-emmsale/app/src/main/java/com/emmsale/presentation/ui/eventdetail/participant/EventParticipantViewModel.kt b/android/2023-emmsale/app/src/main/java/com/emmsale/presentation/ui/eventdetail/participant/EventParticipantViewModel.kt new file mode 100644 index 000000000..672d7ecb2 --- /dev/null +++ b/android/2023-emmsale/app/src/main/java/com/emmsale/presentation/ui/eventdetail/participant/EventParticipantViewModel.kt @@ -0,0 +1,100 @@ +package com.emmsale.presentation.ui.eventdetail.participant + +import androidx.lifecycle.LiveData +import androidx.lifecycle.MutableLiveData +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import com.emmsale.data.common.ApiSuccess +import com.emmsale.data.participant.ParticipantRepository +import com.emmsale.presentation.KerdyApplication +import com.emmsale.presentation.common.ViewModelFactory +import com.emmsale.presentation.eventdetail.participant.uistate.ParticipantsUiState +import com.emmsale.presentation.eventdetail.participant.uistate.ParticipationStatusUiState +import kotlinx.coroutines.launch + +class EventParticipantViewModel( + private val participantRepository: ParticipantRepository, +) : ViewModel() { + + private val _participants: MutableLiveData = MutableLiveData() + val participants: LiveData + get() = _participants + + private val _requestCompanion: MutableLiveData = MutableLiveData() + val requestCompanion: LiveData + get() = _requestCompanion + + private val _participationSaving: MutableLiveData = MutableLiveData() + val participationSaving: LiveData + get() = _participationDeletion + + private val _participationDeletion: MutableLiveData = MutableLiveData() + val participationDeletion: LiveData + get() = _participationDeletion + + private val _isParticipate: MutableLiveData = MutableLiveData() + val isParticipate: LiveData + get() = _isParticipate + + fun fetchParticipants(eventId: Long) { + viewModelScope.launch { + when (val response = participantRepository.fetchEventParticipants(eventId)) { + is ApiSuccess -> _participants.postValue(ParticipantsUiState.from(response.data)) + else -> _participants.postValue(ParticipantsUiState.Error) + } + } + } + + fun saveParticipant(eventId: Long) { + viewModelScope.launch { + when (val response = participantRepository.saveParticipant(eventId)) { + is ApiSuccess -> _participationDeletion.postValue(true) + else -> _participationDeletion.postValue(false) + } + } + } + + fun deleteParticipant(eventId: Long) { + viewModelScope.launch { + when (participantRepository.deleteParticipant(eventId)) { + is ApiSuccess -> _participationDeletion.postValue(true) + else -> _participationDeletion.postValue(false) + } + } + } + + fun requestCompanion(eventId: Long, memberId: Long) { + viewModelScope.launch { + when (participantRepository.requestCompanion(eventId, memberId, "")) { + is ApiSuccess -> _requestCompanion.postValue(true) + else -> _requestCompanion.postValue(false) + } + } + } + + fun checkParticipationStatus(eventId: Long) { + viewModelScope.launch { + when (val response = participantRepository.checkParticipationStatus(eventId)) { + is ApiSuccess -> _isParticipate.postValue( + ParticipationStatusUiState.Success( + response.data, + ), + ) + + else -> ParticipationStatusUiState.Error + } + } + } + + private fun getRequestMessage(): String { + return "" + } + + companion object { + val factory = ViewModelFactory { + EventParticipantViewModel( + KerdyApplication.repositoryContainer.participantRepository, + ) + } + } +} diff --git a/android/2023-emmsale/app/src/main/java/com/emmsale/presentation/ui/eventdetail/participant/uistate/ParticipantUiState.kt b/android/2023-emmsale/app/src/main/java/com/emmsale/presentation/ui/eventdetail/participant/uistate/ParticipantUiState.kt new file mode 100644 index 000000000..a14aa400f --- /dev/null +++ b/android/2023-emmsale/app/src/main/java/com/emmsale/presentation/ui/eventdetail/participant/uistate/ParticipantUiState.kt @@ -0,0 +1,21 @@ +package com.emmsale.presentation.eventdetail.participant.uistate + +import com.emmsale.data.participant.Participant + +data class ParticipantUiState( + val id: Long, + val memberId: Long, + val name: String, + val imageUrl: String, + val description: String?, +) { + companion object { + fun from(participant: Participant): ParticipantUiState = ParticipantUiState( + id = participant.id, + memberId = participant.memberId, + name = participant.name, + imageUrl = participant.imageUrl, + description = participant.description, + ) + } +} diff --git a/android/2023-emmsale/app/src/main/java/com/emmsale/presentation/ui/eventdetail/participant/uistate/ParticipantsUiState.kt b/android/2023-emmsale/app/src/main/java/com/emmsale/presentation/ui/eventdetail/participant/uistate/ParticipantsUiState.kt new file mode 100644 index 000000000..e26c1558b --- /dev/null +++ b/android/2023-emmsale/app/src/main/java/com/emmsale/presentation/ui/eventdetail/participant/uistate/ParticipantsUiState.kt @@ -0,0 +1,16 @@ +package com.emmsale.presentation.eventdetail.participant.uistate + +import com.emmsale.data.participant.Participant + +sealed class ParticipantsUiState { + data class Success( + val value: List, + ) : ParticipantsUiState() + + object Error : ParticipantsUiState() + + companion object { + fun from(participants: List): ParticipantsUiState.Success = + ParticipantsUiState.Success(participants.map(ParticipantUiState::from)) + } +} diff --git a/android/2023-emmsale/app/src/main/java/com/emmsale/presentation/ui/eventdetail/participant/uistate/ParticipationStatusUiState.kt b/android/2023-emmsale/app/src/main/java/com/emmsale/presentation/ui/eventdetail/participant/uistate/ParticipationStatusUiState.kt new file mode 100644 index 000000000..7a907a6b2 --- /dev/null +++ b/android/2023-emmsale/app/src/main/java/com/emmsale/presentation/ui/eventdetail/participant/uistate/ParticipationStatusUiState.kt @@ -0,0 +1,6 @@ +package com.emmsale.presentation.eventdetail.participant.uistate + +sealed class ParticipationStatusUiState { + data class Success(val isParticipate: Boolean) : ParticipationStatusUiState() + object Error : ParticipationStatusUiState() +} diff --git a/android/2023-emmsale/app/src/main/res/layout/fragment_event_participant.xml b/android/2023-emmsale/app/src/main/res/layout/fragment_event_participant.xml index ff36eb5f5..6034c5821 100644 --- a/android/2023-emmsale/app/src/main/res/layout/fragment_event_participant.xml +++ b/android/2023-emmsale/app/src/main/res/layout/fragment_event_participant.xml @@ -1,5 +1,6 @@ - + @@ -9,9 +10,32 @@ android:layout_width="match_parent" android:layout_height="match_parent"> - + + + + diff --git a/android/2023-emmsale/app/src/main/res/layout/item_participant.xml b/android/2023-emmsale/app/src/main/res/layout/item_participant.xml new file mode 100644 index 000000000..b52e8bbd6 --- /dev/null +++ b/android/2023-emmsale/app/src/main/res/layout/item_participant.xml @@ -0,0 +1,86 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + From 56447895b00bec1e74c311109a1b4b89e2348cd9 Mon Sep 17 00:00:00 2001 From: chws0508 Date: Wed, 2 Aug 2023 22:25:38 +0900 Subject: [PATCH 4/7] =?UTF-8?q?refactor=20:=20=EC=B0=B8=EC=97=AC=20?= =?UTF-8?q?=EC=B7=A8=EC=86=8C=20=EA=B8=B0=EB=8A=A5,=20=EC=B0=B8=EC=97=AC?= =?UTF-8?q?=ED=96=88=EB=8A=94=EC=A7=80=20=ED=99=95=EC=9D=B8=20=EA=B8=B0?= =?UTF-8?q?=EB=8A=A5=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- android/2023-emmsale/app/build.gradle.kts | 3 ++- .../main/java/com/emmsale/data/common/ServiceFactory.kt | 6 +++++- .../data/participant/ParticipantRepositoryImpl.kt | 7 +++---- .../com/emmsale/data/participant/ParticipantService.kt | 9 ++++++++- .../eventdetail/participant/EventParticipantFragment.kt | 6 ++---- 5 files changed, 20 insertions(+), 11 deletions(-) diff --git a/android/2023-emmsale/app/build.gradle.kts b/android/2023-emmsale/app/build.gradle.kts index bee3cc310..0e64e3ef1 100644 --- a/android/2023-emmsale/app/build.gradle.kts +++ b/android/2023-emmsale/app/build.gradle.kts @@ -26,7 +26,7 @@ android { buildConfigField( "String", "GITHUB_CLIENT_ID", - getApiKey("GH_CLIENT_ID") + getApiKey("GH_CLIENT_ID"), ) } buildFeatures { @@ -87,6 +87,7 @@ dependencies { implementation("com.squareup.okhttp3:mockwebserver:4.11.0") implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.5.1") implementation("com.jakewharton.retrofit:retrofit2-kotlinx-serialization-converter:1.0.0") + implementation("com.squareup.okhttp3:logging-interceptor:4.8.0") implementation("com.github.bumptech.glide:glide:4.15.1") implementation("androidx.swiperefreshlayout:swiperefreshlayout:1.1.0") diff --git a/android/2023-emmsale/app/src/main/java/com/emmsale/data/common/ServiceFactory.kt b/android/2023-emmsale/app/src/main/java/com/emmsale/data/common/ServiceFactory.kt index 829645b16..b484c29b3 100644 --- a/android/2023-emmsale/app/src/main/java/com/emmsale/data/common/ServiceFactory.kt +++ b/android/2023-emmsale/app/src/main/java/com/emmsale/data/common/ServiceFactory.kt @@ -4,6 +4,7 @@ import com.jakewharton.retrofit2.converter.kotlinx.serialization.asConverterFact import kotlinx.serialization.json.Json import okhttp3.MediaType.Companion.toMediaType import okhttp3.OkHttpClient +import okhttp3.logging.HttpLoggingInterceptor import retrofit2.Retrofit import java.util.concurrent.TimeUnit @@ -15,8 +16,11 @@ class ServiceFactory { isLenient = true } private val jsonConverterFactory = json.asConverterFactory(jsonMediaType) - + private val httpLoggingInterceptor = HttpLoggingInterceptor().apply { + level = HttpLoggingInterceptor.Level.BODY + } private val okhttpClient = OkHttpClient.Builder() + .addInterceptor(httpLoggingInterceptor) .addInterceptor(AuthInterceptor()) .connectTimeout(120, TimeUnit.SECONDS) .readTimeout(120, TimeUnit.SECONDS) diff --git a/android/2023-emmsale/app/src/main/java/com/emmsale/data/participant/ParticipantRepositoryImpl.kt b/android/2023-emmsale/app/src/main/java/com/emmsale/data/participant/ParticipantRepositoryImpl.kt index b38ad38af..b482772b2 100644 --- a/android/2023-emmsale/app/src/main/java/com/emmsale/data/participant/ParticipantRepositoryImpl.kt +++ b/android/2023-emmsale/app/src/main/java/com/emmsale/data/participant/ParticipantRepositoryImpl.kt @@ -1,7 +1,6 @@ package com.emmsale.data.participant import com.emmsale.data.common.ApiResult -import com.emmsale.data.common.ApiSuccess import com.emmsale.data.common.handleApi import com.emmsale.data.participant.dto.CompanionRequestBody import com.emmsale.data.participant.dto.ParticipantApiModel @@ -31,8 +30,7 @@ class ParticipantRepositoryImpl( } override suspend fun deleteParticipant(eventId: Long): ApiResult { - val requestBody = ParticipantRequestBody(currentUid) - val response = participantService.deleteParticipant(eventId, requestBody) + val response = participantService.deleteParticipant(eventId, currentUid) return handleApi(response, mapToDomain = {}) } @@ -52,6 +50,7 @@ class ParticipantRepositoryImpl( } override suspend fun checkParticipationStatus(eventId: Long): ApiResult { - return ApiSuccess(true) + val response = participantService.checkIsParticipated(eventId, currentUid) + return handleApi(response, mapToDomain = { response.body()!! }) } } diff --git a/android/2023-emmsale/app/src/main/java/com/emmsale/data/participant/ParticipantService.kt b/android/2023-emmsale/app/src/main/java/com/emmsale/data/participant/ParticipantService.kt index cbf6a00e3..26ba2ce66 100644 --- a/android/2023-emmsale/app/src/main/java/com/emmsale/data/participant/ParticipantService.kt +++ b/android/2023-emmsale/app/src/main/java/com/emmsale/data/participant/ParticipantService.kt @@ -9,6 +9,7 @@ import retrofit2.http.DELETE import retrofit2.http.GET import retrofit2.http.POST import retrofit2.http.Path +import retrofit2.http.Query interface ParticipantService { @@ -26,9 +27,15 @@ interface ParticipantService { @DELETE("events/{eventId}/participants") suspend fun deleteParticipant( @Path("eventId") eventId: Long, - @Body participantRequestBody: ParticipantRequestBody, + @Query("member-id") memberId: Long, ): Response + @GET("events/{eventId}/participants/already-participate") + suspend fun checkIsParticipated( + @Path("eventId") eventId: Long, + @Query("member-id") memberId: Long, + ): Response + @POST("notifications") suspend fun postCompanion( @Body companionRequestBody: CompanionRequestBody, diff --git a/android/2023-emmsale/app/src/main/java/com/emmsale/presentation/ui/eventdetail/participant/EventParticipantFragment.kt b/android/2023-emmsale/app/src/main/java/com/emmsale/presentation/ui/eventdetail/participant/EventParticipantFragment.kt index 1144cab7f..7e4d4ab6f 100644 --- a/android/2023-emmsale/app/src/main/java/com/emmsale/presentation/ui/eventdetail/participant/EventParticipantFragment.kt +++ b/android/2023-emmsale/app/src/main/java/com/emmsale/presentation/ui/eventdetail/participant/EventParticipantFragment.kt @@ -30,7 +30,6 @@ class EventParticipantFragment : BaseFragment() setUpParticipants() setUpRequestCompanion() setUpParticipation() - participationButtonClick() setUpParticipationStatus() viewModel.fetchParticipants(eventId) } @@ -55,6 +54,7 @@ class EventParticipantFragment : BaseFragment() is ParticipantsUiState.Success -> { participantAdapter.submitList(it.value) viewModel.checkParticipationStatus(eventId) + participationButtonClick() } else -> showToastMessage("참가자들 불러오기 실패") @@ -88,11 +88,9 @@ class EventParticipantFragment : BaseFragment() when (state) { is ParticipationStatusUiState.Success -> { if (state.isParticipate) { - showToastMessage("참가 상태") participationButton.isSelected = true participationButton.text = "참가 취소" } else { - showToastMessage("불참 상태") participationButton.isSelected = false participationButton.text = "참가 신청" } @@ -120,7 +118,7 @@ class EventParticipantFragment : BaseFragment() } private fun showToastMessage(message: String) { - Toast.makeText(requireContext(), message, Toast.LENGTH_LONG).show() + Toast.makeText(requireContext(), message, Toast.LENGTH_SHORT).show() } companion object { From cb1f436cb4713257c8e055cba9ce2a537dde32e8 Mon Sep 17 00:00:00 2001 From: chws0508 Date: Thu, 3 Aug 2023 12:58:04 +0900 Subject: [PATCH 5/7] =?UTF-8?q?feat=20:=20=EB=8B=A4=EC=9D=B4=EC=96=BC?= =?UTF-8?q?=EB=A1=9C=EA=B7=B8=EB=A5=BC=20=ED=86=B5=ED=95=B4=20=EC=9A=94?= =?UTF-8?q?=EC=B2=AD=EB=A9=94=EC=84=B8=EC=A7=80=20=EB=9D=84=EC=9A=B0?= =?UTF-8?q?=EA=B8=B0=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../participant/dto/ParticipationStatus.kt | 8 - .../ui/eventdetail/EventDetailViewModel.kt | 3 + .../participant/EventParticipantAdapter.kt | 15 +- .../participant/EventParticipantFragment.kt | 44 ++--- .../participant/EventParticipantViewModel.kt | 39 ++--- .../participant/ParticipantFragmentDialog.kt | 49 ++++++ .../main/res/layout/activity_event_detail.xml | 8 +- .../res/layout/fragment_event_participant.xml | 23 ++- .../res/layout/fragmentdialog_participant.xml | 150 ++++++++++++++++++ .../src/main/res/layout/item_participant.xml | 12 +- .../app/src/main/res/values/colors.xml | 1 + .../app/src/main/res/values/strings.xml | 2 + .../app/src/main/res/values/styles.xml | 4 +- 13 files changed, 293 insertions(+), 65 deletions(-) delete mode 100644 android/2023-emmsale/app/src/main/java/com/emmsale/data/participant/dto/ParticipationStatus.kt create mode 100644 android/2023-emmsale/app/src/main/java/com/emmsale/presentation/ui/eventdetail/participant/ParticipantFragmentDialog.kt create mode 100644 android/2023-emmsale/app/src/main/res/layout/fragmentdialog_participant.xml diff --git a/android/2023-emmsale/app/src/main/java/com/emmsale/data/participant/dto/ParticipationStatus.kt b/android/2023-emmsale/app/src/main/java/com/emmsale/data/participant/dto/ParticipationStatus.kt deleted file mode 100644 index f408b412f..000000000 --- a/android/2023-emmsale/app/src/main/java/com/emmsale/data/participant/dto/ParticipationStatus.kt +++ /dev/null @@ -1,8 +0,0 @@ -package com.emmsale.data.participant.dto - -import kotlinx.serialization.Serializable - -@Serializable -data class ParticipationStatus( - val isParticipated: Boolean, -) diff --git a/android/2023-emmsale/app/src/main/java/com/emmsale/presentation/ui/eventdetail/EventDetailViewModel.kt b/android/2023-emmsale/app/src/main/java/com/emmsale/presentation/ui/eventdetail/EventDetailViewModel.kt index f30ec108d..88bbb14d7 100644 --- a/android/2023-emmsale/app/src/main/java/com/emmsale/presentation/ui/eventdetail/EventDetailViewModel.kt +++ b/android/2023-emmsale/app/src/main/java/com/emmsale/presentation/ui/eventdetail/EventDetailViewModel.kt @@ -8,6 +8,7 @@ import com.emmsale.data.common.ApiError import com.emmsale.data.common.ApiException import com.emmsale.data.common.ApiSuccess import com.emmsale.data.eventdetail.EventDetailRepository +import com.emmsale.data.member.MemberRepository import com.emmsale.presentation.KerdyApplication import com.emmsale.presentation.common.ViewModelFactory import com.emmsale.presentation.ui.eventdetail.uistate.EventDetailUiState @@ -15,6 +16,7 @@ import kotlinx.coroutines.launch class EventDetailViewModel( private val eventDetailRepository: EventDetailRepository, + private val memberRepository: MemberRepository, ) : ViewModel() { private val _eventDetail: MutableLiveData = @@ -39,6 +41,7 @@ class EventDetailViewModel( val factory = ViewModelFactory { EventDetailViewModel( eventDetailRepository = KerdyApplication.repositoryContainer.eventDetailRepository, + memberRepository = KerdyApplication.repositoryContainer.memberRepository, ) } } diff --git a/android/2023-emmsale/app/src/main/java/com/emmsale/presentation/ui/eventdetail/participant/EventParticipantAdapter.kt b/android/2023-emmsale/app/src/main/java/com/emmsale/presentation/ui/eventdetail/participant/EventParticipantAdapter.kt index a748ce4ad..faf2c41a0 100644 --- a/android/2023-emmsale/app/src/main/java/com/emmsale/presentation/ui/eventdetail/participant/EventParticipantAdapter.kt +++ b/android/2023-emmsale/app/src/main/java/com/emmsale/presentation/ui/eventdetail/participant/EventParticipantAdapter.kt @@ -9,7 +9,7 @@ import com.emmsale.databinding.ItemParticipantBinding import com.emmsale.presentation.eventdetail.participant.uistate.ParticipantUiState class EventParticipantAdapter( - private val requestCompanion: (Long) -> Unit, + private val requestCompanion: (Long, String) -> Unit, private val showMemberProfile: (Long) -> Unit, ) : ListAdapter(diffUtil) { @@ -44,13 +44,18 @@ class EventParticipantAdapter( class ParticipantViewHolder( private val binding: ItemParticipantBinding, - private val requestCompanion: (Long) -> Unit, + private val requestCompanion: (Long, String) -> Unit, private val showMemberProfile: (Long) -> Unit, ) : RecyclerView.ViewHolder(binding.root) { init { - binding.buttonRequestCompanion.setOnClickListener { requestCompanion(binding.participant!!.id) } - binding.ivParticipantImage.setOnClickListener { showMemberProfile(binding.participant!!.id) } + binding.buttonRequestCompanion.setOnClickListener { + requestCompanion( + binding.participant!!.memberId, + binding.participant!!.name, + ) + } + binding.ivParticipantImage.setOnClickListener { showMemberProfile(binding.participant!!.memberId) } } fun bind(participant: ParticipantUiState) { @@ -60,7 +65,7 @@ class ParticipantViewHolder( companion object { fun create( parent: ViewGroup, - requestCompanion: (Long) -> Unit, + requestCompanion: (Long, String) -> Unit, showMemberProfile: (Long) -> Unit, ): ParticipantViewHolder { val binding = ItemParticipantBinding.inflate( diff --git a/android/2023-emmsale/app/src/main/java/com/emmsale/presentation/ui/eventdetail/participant/EventParticipantFragment.kt b/android/2023-emmsale/app/src/main/java/com/emmsale/presentation/ui/eventdetail/participant/EventParticipantFragment.kt index 7e4d4ab6f..728f83f39 100644 --- a/android/2023-emmsale/app/src/main/java/com/emmsale/presentation/ui/eventdetail/participant/EventParticipantFragment.kt +++ b/android/2023-emmsale/app/src/main/java/com/emmsale/presentation/ui/eventdetail/participant/EventParticipantFragment.kt @@ -11,6 +11,7 @@ import com.emmsale.presentation.eventdetail.participant.uistate.ParticipantsUiSt import com.emmsale.presentation.eventdetail.participant.uistate.ParticipationStatusUiState import com.emmsale.presentation.ui.eventdetail.participant.EventParticipantAdapter import com.emmsale.presentation.ui.eventdetail.participant.EventParticipantViewModel +import com.emmsale.presentation.ui.eventdetail.participant.ParticipantFragmentDialog class EventParticipantFragment : BaseFragment() { override val layoutResId: Int = R.layout.fragment_event_participant @@ -20,7 +21,7 @@ class EventParticipantFragment : BaseFragment() arguments?.getLong(EVENT_ID_KEY) ?: throw IllegalArgumentException("아이디못가져옴") } private val participantAdapter: EventParticipantAdapter by lazy { - EventParticipantAdapter(::requestCompanion, ::showMemberProfile) + EventParticipantAdapter(::showRequestDialog, ::showMemberProfile) } private val participationButton: AppCompatButton by lazy { binding.btnEventparticipantParticipate } @@ -29,7 +30,6 @@ class EventParticipantFragment : BaseFragment() initRecyclerView() setUpParticipants() setUpRequestCompanion() - setUpParticipation() setUpParticipationStatus() viewModel.fetchParticipants(eventId) } @@ -41,8 +41,15 @@ class EventParticipantFragment : BaseFragment() } } - private fun requestCompanion(memberId: Long) { - viewModel.requestCompanion(eventId, memberId) + private fun showRequestDialog(memberId: Long, memberName: String) { + ParticipantFragmentDialog(memberName, memberId, ::requestCompanion).show( + parentFragmentManager, + "Participant", + ) + } + + private fun requestCompanion(memberId: Long, message: String) { + viewModel.requestCompanion(eventId, memberId, message) } private fun showMemberProfile(memberId: Long) { @@ -72,27 +79,16 @@ class EventParticipantFragment : BaseFragment() } } - private fun setUpParticipation() { - viewModel.participationSaving.observe(viewLifecycleOwner) { success -> - if (success) { - showToastMessage("참가 성공") - viewModel.checkParticipationStatus(eventId) - } else { - showToastMessage("참가 실패") - } - } - } - private fun setUpParticipationStatus() { viewModel.isParticipate.observe(viewLifecycleOwner) { state -> when (state) { is ParticipationStatusUiState.Success -> { if (state.isParticipate) { - participationButton.isSelected = true - participationButton.text = "참가 취소" + setButtonParticipationState() + showToastMessage("참가 상태입니다!") } else { - participationButton.isSelected = false - participationButton.text = "참가 신청" + setButtonAbsenceState() + showToastMessage("불참 상태입니다!") } } @@ -101,6 +97,16 @@ class EventParticipantFragment : BaseFragment() } } + private fun setButtonParticipationState() { + participationButton.isSelected = true + participationButton.text = "참가 취소" + } + + private fun setButtonAbsenceState() { + participationButton.isSelected = false + participationButton.text = "참가 신청" + } + private fun participationButtonClick() { participationButton.setOnClickListener { when (val state = viewModel.isParticipate.value) { diff --git a/android/2023-emmsale/app/src/main/java/com/emmsale/presentation/ui/eventdetail/participant/EventParticipantViewModel.kt b/android/2023-emmsale/app/src/main/java/com/emmsale/presentation/ui/eventdetail/participant/EventParticipantViewModel.kt index 672d7ecb2..7bafeb71b 100644 --- a/android/2023-emmsale/app/src/main/java/com/emmsale/presentation/ui/eventdetail/participant/EventParticipantViewModel.kt +++ b/android/2023-emmsale/app/src/main/java/com/emmsale/presentation/ui/eventdetail/participant/EventParticipantViewModel.kt @@ -16,6 +16,10 @@ class EventParticipantViewModel( private val participantRepository: ParticipantRepository, ) : ViewModel() { + private val _isLoading: MutableLiveData = MutableLiveData() + val isLoading: LiveData + get() = _isLoading + private val _participants: MutableLiveData = MutableLiveData() val participants: LiveData get() = _participants @@ -24,22 +28,19 @@ class EventParticipantViewModel( val requestCompanion: LiveData get() = _requestCompanion - private val _participationSaving: MutableLiveData = MutableLiveData() - val participationSaving: LiveData - get() = _participationDeletion - - private val _participationDeletion: MutableLiveData = MutableLiveData() - val participationDeletion: LiveData - get() = _participationDeletion - private val _isParticipate: MutableLiveData = MutableLiveData() val isParticipate: LiveData get() = _isParticipate fun fetchParticipants(eventId: Long) { + _isLoading.postValue(true) viewModelScope.launch { when (val response = participantRepository.fetchEventParticipants(eventId)) { - is ApiSuccess -> _participants.postValue(ParticipantsUiState.from(response.data)) + is ApiSuccess -> { + _participants.postValue(ParticipantsUiState.from(response.data)) + _isLoading.postValue(false) + } + else -> _participants.postValue(ParticipantsUiState.Error) } } @@ -48,8 +49,8 @@ class EventParticipantViewModel( fun saveParticipant(eventId: Long) { viewModelScope.launch { when (val response = participantRepository.saveParticipant(eventId)) { - is ApiSuccess -> _participationDeletion.postValue(true) - else -> _participationDeletion.postValue(false) + is ApiSuccess -> _isParticipate.postValue(ParticipationStatusUiState.Success(true)) + else -> _isParticipate.postValue(ParticipationStatusUiState.Error) } } } @@ -57,15 +58,19 @@ class EventParticipantViewModel( fun deleteParticipant(eventId: Long) { viewModelScope.launch { when (participantRepository.deleteParticipant(eventId)) { - is ApiSuccess -> _participationDeletion.postValue(true) - else -> _participationDeletion.postValue(false) + is ApiSuccess -> { + _isParticipate.postValue(ParticipationStatusUiState.Success(false)) + fetchParticipants(eventId) + } + + else -> _isParticipate.postValue(ParticipationStatusUiState.Error) } } } - fun requestCompanion(eventId: Long, memberId: Long) { + fun requestCompanion(eventId: Long, memberId: Long, message: String) { viewModelScope.launch { - when (participantRepository.requestCompanion(eventId, memberId, "")) { + when (participantRepository.requestCompanion(eventId, memberId, message)) { is ApiSuccess -> _requestCompanion.postValue(true) else -> _requestCompanion.postValue(false) } @@ -86,10 +91,6 @@ class EventParticipantViewModel( } } - private fun getRequestMessage(): String { - return "" - } - companion object { val factory = ViewModelFactory { EventParticipantViewModel( diff --git a/android/2023-emmsale/app/src/main/java/com/emmsale/presentation/ui/eventdetail/participant/ParticipantFragmentDialog.kt b/android/2023-emmsale/app/src/main/java/com/emmsale/presentation/ui/eventdetail/participant/ParticipantFragmentDialog.kt new file mode 100644 index 000000000..78972216e --- /dev/null +++ b/android/2023-emmsale/app/src/main/java/com/emmsale/presentation/ui/eventdetail/participant/ParticipantFragmentDialog.kt @@ -0,0 +1,49 @@ +package com.emmsale.presentation.ui.eventdetail.participant + +import android.graphics.Color +import android.graphics.drawable.ColorDrawable +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.fragment.app.DialogFragment +import com.emmsale.databinding.FragmentdialogParticipantBinding + +class ParticipantFragmentDialog( + private val memberName: String, + private val memberId: Long, + private val requestCompanion: (Long, String) -> Unit, +) : DialogFragment() { + private lateinit var binding: FragmentdialogParticipantBinding + + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle?, + ): View? { + binding = FragmentdialogParticipantBinding.inflate(inflater, container, false) + dialog?.window?.setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT)) + return binding.root + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + binding.name = memberName + positiveButtonClick() + negativeButtonClick() + } + + private fun positiveButtonClick() { + binding.btnFragmentdialogPositive.setOnClickListener { + val message = binding.edittextFragmentdialogParticipantMessage.text.toString() + requestCompanion(memberId, message) + dismiss() + } + } + + private fun negativeButtonClick() { + binding.btnFragmentdialogNegative.setOnClickListener { + dismiss() + } + } +} diff --git a/android/2023-emmsale/app/src/main/res/layout/activity_event_detail.xml b/android/2023-emmsale/app/src/main/res/layout/activity_event_detail.xml index 746d3daae..2f44b1d80 100644 --- a/android/2023-emmsale/app/src/main/res/layout/activity_event_detail.xml +++ b/android/2023-emmsale/app/src/main/res/layout/activity_event_detail.xml @@ -46,7 +46,7 @@ android:lineHeight="20dp" android:textColor="@color/text_color_black" android:fontFamily="@font/nanum_square_bold" - android:textSize="24sp" + android:textSize="22sp" android:textStyle="bold" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@+id/tv_eventdetail_eventstatus" @@ -78,7 +78,7 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:textColor="@color/text_color_green" - android:textSize="20sp" + android:textSize="18sp" android:fontFamily="@font/nanum_square_bold" tools:text="진행중" android:text="@{eventDetail.status}" @@ -93,7 +93,7 @@ android:layout_height="wrap_content" android:layout_marginStart="21dp" android:text="@{eventDetail.location}" - android:textSize="18sp" + android:textSize="16sp" android:drawableLeft="@drawable/ic_eventdetail_place" android:drawablePadding="12dp" android:fontFamily="@font/nanum_square_regular" @@ -112,7 +112,7 @@ android:layout_marginStart="21dp" android:layout_marginTop="17dp" android:text="@{eventDetail.startDate}" - android:textSize="18sp" + android:textSize="16sp" android:fontFamily="@font/nanum_square_regular" android:textColor="@color/text_color_black" app:layout_constraintStart_toStartOf="parent" diff --git a/android/2023-emmsale/app/src/main/res/layout/fragment_event_participant.xml b/android/2023-emmsale/app/src/main/res/layout/fragment_event_participant.xml index 6034c5821..04726ae1a 100644 --- a/android/2023-emmsale/app/src/main/res/layout/fragment_event_participant.xml +++ b/android/2023-emmsale/app/src/main/res/layout/fragment_event_participant.xml @@ -3,7 +3,11 @@ xmlns:app="http://schemas.android.com/apk/res-auto"> + + + app:layout_constraintBottom_toTopOf="@id/btn_eventparticipant_participate" /> + + + + diff --git a/android/2023-emmsale/app/src/main/res/layout/fragmentdialog_participant.xml b/android/2023-emmsale/app/src/main/res/layout/fragmentdialog_participant.xml new file mode 100644 index 000000000..734a1efaa --- /dev/null +++ b/android/2023-emmsale/app/src/main/res/layout/fragmentdialog_participant.xml @@ -0,0 +1,150 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +