diff --git a/android/app/src/main/java/com/happy/friendogly/data/error/DataErrorHandler.kt b/android/app/src/main/java/com/happy/friendogly/data/error/DataErrorHandler.kt new file mode 100644 index 000000000..ea7b72cf3 --- /dev/null +++ b/android/app/src/main/java/com/happy/friendogly/data/error/DataErrorHandler.kt @@ -0,0 +1,15 @@ +package com.happy.friendogly.data.error + +import com.happy.friendogly.data.mapper.toDomain +import com.happy.friendogly.domain.DomainResult +import com.happy.friendogly.domain.error.DataError +import java.net.ConnectException +import java.net.UnknownHostException + +fun Throwable.toDomainError(): DomainResult.Error { + return when (this) { + is ApiExceptionDto -> DomainResult.Error(this.error.data.errorCode.toDomain()) + is ConnectException, is UnknownHostException -> DomainResult.Error(DataError.Network.NO_INTERNET) + else -> DomainResult.Error(DataError.Network.SERVER_ERROR) + } +} diff --git a/android/app/src/main/java/com/happy/friendogly/data/repository/ClubRepositoryImpl.kt b/android/app/src/main/java/com/happy/friendogly/data/repository/ClubRepositoryImpl.kt index 9faa66872..017ed9d4e 100644 --- a/android/app/src/main/java/com/happy/friendogly/data/repository/ClubRepositoryImpl.kt +++ b/android/app/src/main/java/com/happy/friendogly/data/repository/ClubRepositoryImpl.kt @@ -1,8 +1,11 @@ package com.happy.friendogly.data.repository +import com.happy.friendogly.data.error.toDomainError import com.happy.friendogly.data.mapper.toData import com.happy.friendogly.data.mapper.toDomain import com.happy.friendogly.data.source.ClubDataSource +import com.happy.friendogly.domain.DomainResult +import com.happy.friendogly.domain.error.DataError import com.happy.friendogly.domain.model.Club import com.happy.friendogly.domain.model.ClubAddress import com.happy.friendogly.domain.model.ClubDetail @@ -29,8 +32,8 @@ class ClubRepositoryImpl memberCapacity: Int, file: MultipartBody.Part?, petIds: List, - ): Result = - source.postClub( + ): DomainResult { + return source.postClub( title = title, content = content, address = address.toData(), @@ -39,44 +42,94 @@ class ClubRepositoryImpl memberCapacity = memberCapacity, file = file, petIds = petIds, + ).fold( + onSuccess = { result -> + DomainResult.Success(result) + }, + onFailure = { error -> + error.toDomainError() + }, ) + } override suspend fun getSearchingClubs( filterCondition: ClubFilterCondition, address: ClubAddress, genderParams: List, sizeParams: List, - ): Result> = - source.getSearchingClubs( + ): DomainResult, DataError.Network> { + return source.getSearchingClubs( filterCondition = filterCondition.toData(), address = address.toData(), genderParams = genderParams.map { it.toData() }, sizeParams = sizeParams.map { it.toData() }, - ).mapCatching { it.toDomain() } + ).fold( + onSuccess = { clubs -> + DomainResult.Success(clubs.toDomain()) + }, + onFailure = { error -> + error.toDomainError() + }, + ) + } - override suspend fun getClub(clubId: Long): Result = source.getClub(clubId).mapCatching { it.toDomain() } + override suspend fun getClub(clubId: Long): DomainResult { + return source.getClub(clubId).fold( + onSuccess = { clubDetail -> + DomainResult.Success(clubDetail.toDomain()) + }, + onFailure = { error -> + error.toDomainError() + }, + ) + } override suspend fun postClubMember( clubId: Long, participatingPetsId: List, - ): Result = - source.postClubMember( + ): DomainResult { + return source.postClubMember( clubId = clubId, participatingPetsId = participatingPetsId, - ).mapCatching { it.toDomain() } + ).fold( + onSuccess = { clubParticipation -> + DomainResult.Success(clubParticipation.toDomain()) + }, + onFailure = { error -> + error.toDomainError() + }, + ) + } - override suspend fun deleteClubMember(clubId: Long): Result = source.deleteClubMember(clubId) + override suspend fun deleteClubMember(clubId: Long): DomainResult { + return source.deleteClubMember(clubId).fold( + onSuccess = { result -> + DomainResult.Success(result) + }, + onFailure = { error -> + error.toDomainError() + }, + ) + } override suspend fun patchClub( clubId: Long, title: String, content: String, state: ClubState, - ): Result = - source.patchClub( + ): DomainResult { + return source.patchClub( clubId = clubId, title = title, content = content, state = state.toData(), + ).fold( + onSuccess = { result -> + DomainResult.Success(result) + }, + onFailure = { error -> + error.toDomainError() + }, ) + } } diff --git a/android/app/src/main/java/com/happy/friendogly/data/repository/MyClubRepositoryImpl.kt b/android/app/src/main/java/com/happy/friendogly/data/repository/MyClubRepositoryImpl.kt index ae19f7b01..dbc732bb6 100644 --- a/android/app/src/main/java/com/happy/friendogly/data/repository/MyClubRepositoryImpl.kt +++ b/android/app/src/main/java/com/happy/friendogly/data/repository/MyClubRepositoryImpl.kt @@ -1,7 +1,10 @@ package com.happy.friendogly.data.repository +import com.happy.friendogly.data.error.toDomainError import com.happy.friendogly.data.mapper.toDomain import com.happy.friendogly.data.source.MyClubDataSource +import com.happy.friendogly.domain.DomainResult +import com.happy.friendogly.domain.error.DataError import com.happy.friendogly.domain.model.Club import com.happy.friendogly.domain.repository.MyClubRepository import javax.inject.Inject @@ -11,7 +14,25 @@ class MyClubRepositoryImpl constructor( private val source: MyClubDataSource, ) : MyClubRepository { - override suspend fun getMyClubs(): Result> = source.getParticipatingClubs().mapCatching { it.toDomain() } + override suspend fun getMyClubs(): DomainResult, DataError.Network> { + return source.getParticipatingClubs().fold( + onSuccess = { clubs -> + DomainResult.Success(clubs.toDomain()) + }, + onFailure = { error -> + error.toDomainError() + }, + ) + } - override suspend fun getMyHeadClubs(): Result> = source.getMyOwningClubs().mapCatching { it.toDomain() } + override suspend fun getMyHeadClubs(): DomainResult, DataError.Network> { + return source.getMyOwningClubs().fold( + onSuccess = { clubs -> + DomainResult.Success(clubs.toDomain()) + }, + onFailure = { error -> + error.toDomainError() + }, + ) + } } diff --git a/android/app/src/main/java/com/happy/friendogly/domain/repository/ClubRepository.kt b/android/app/src/main/java/com/happy/friendogly/domain/repository/ClubRepository.kt index 456297e6f..58ab8af1d 100644 --- a/android/app/src/main/java/com/happy/friendogly/domain/repository/ClubRepository.kt +++ b/android/app/src/main/java/com/happy/friendogly/domain/repository/ClubRepository.kt @@ -1,5 +1,7 @@ package com.happy.friendogly.domain.repository +import com.happy.friendogly.domain.DomainResult +import com.happy.friendogly.domain.error.DataError import com.happy.friendogly.domain.model.Club import com.happy.friendogly.domain.model.ClubAddress import com.happy.friendogly.domain.model.ClubDetail @@ -20,28 +22,28 @@ interface ClubRepository { memberCapacity: Int, file: MultipartBody.Part?, petIds: List, - ): Result + ): DomainResult suspend fun getSearchingClubs( filterCondition: ClubFilterCondition, address: ClubAddress, genderParams: List, sizeParams: List, - ): Result> + ): DomainResult, DataError.Network> - suspend fun getClub(clubId: Long): Result + suspend fun getClub(clubId: Long): DomainResult suspend fun postClubMember( clubId: Long, participatingPetsId: List, - ): Result + ): DomainResult - suspend fun deleteClubMember(clubId: Long): Result + suspend fun deleteClubMember(clubId: Long): DomainResult suspend fun patchClub( clubId: Long, title: String, content: String, state: ClubState, - ): Result + ): DomainResult } diff --git a/android/app/src/main/java/com/happy/friendogly/domain/repository/MyClubRepository.kt b/android/app/src/main/java/com/happy/friendogly/domain/repository/MyClubRepository.kt index 02bc951dd..37e17035b 100644 --- a/android/app/src/main/java/com/happy/friendogly/domain/repository/MyClubRepository.kt +++ b/android/app/src/main/java/com/happy/friendogly/domain/repository/MyClubRepository.kt @@ -1,9 +1,11 @@ package com.happy.friendogly.domain.repository +import com.happy.friendogly.domain.DomainResult +import com.happy.friendogly.domain.error.DataError import com.happy.friendogly.domain.model.Club interface MyClubRepository { - suspend fun getMyClubs(): Result> + suspend fun getMyClubs(): DomainResult, DataError.Network> - suspend fun getMyHeadClubs(): Result> + suspend fun getMyHeadClubs(): DomainResult, DataError.Network> } diff --git a/android/app/src/main/java/com/happy/friendogly/domain/usecase/DeleteClubMemberUseCase.kt b/android/app/src/main/java/com/happy/friendogly/domain/usecase/DeleteClubMemberUseCase.kt index 8082957bb..55c2fe281 100644 --- a/android/app/src/main/java/com/happy/friendogly/domain/usecase/DeleteClubMemberUseCase.kt +++ b/android/app/src/main/java/com/happy/friendogly/domain/usecase/DeleteClubMemberUseCase.kt @@ -1,5 +1,7 @@ package com.happy.friendogly.domain.usecase +import com.happy.friendogly.domain.DomainResult +import com.happy.friendogly.domain.error.DataError import com.happy.friendogly.domain.repository.ClubRepository import javax.inject.Inject @@ -8,5 +10,5 @@ class DeleteClubMemberUseCase constructor( private val repository: ClubRepository, ) { - suspend operator fun invoke(id: Long): Result = repository.deleteClubMember(clubId = id) + suspend operator fun invoke(id: Long): DomainResult = repository.deleteClubMember(clubId = id) } diff --git a/android/app/src/main/java/com/happy/friendogly/domain/usecase/GetClubUseCase.kt b/android/app/src/main/java/com/happy/friendogly/domain/usecase/GetClubUseCase.kt index ea7155cd5..44284ac9b 100644 --- a/android/app/src/main/java/com/happy/friendogly/domain/usecase/GetClubUseCase.kt +++ b/android/app/src/main/java/com/happy/friendogly/domain/usecase/GetClubUseCase.kt @@ -1,5 +1,7 @@ package com.happy.friendogly.domain.usecase +import com.happy.friendogly.domain.DomainResult +import com.happy.friendogly.domain.error.DataError import com.happy.friendogly.domain.model.ClubDetail import com.happy.friendogly.domain.repository.ClubRepository import javax.inject.Inject @@ -9,5 +11,5 @@ class GetClubUseCase constructor( private val repository: ClubRepository, ) { - suspend operator fun invoke(id: Long): Result = repository.getClub(clubId = id) + suspend operator fun invoke(id: Long): DomainResult = repository.getClub(clubId = id) } diff --git a/android/app/src/main/java/com/happy/friendogly/domain/usecase/GetMyClubUseCase.kt b/android/app/src/main/java/com/happy/friendogly/domain/usecase/GetMyClubUseCase.kt index f2574c508..0174a744f 100644 --- a/android/app/src/main/java/com/happy/friendogly/domain/usecase/GetMyClubUseCase.kt +++ b/android/app/src/main/java/com/happy/friendogly/domain/usecase/GetMyClubUseCase.kt @@ -1,5 +1,7 @@ package com.happy.friendogly.domain.usecase +import com.happy.friendogly.domain.DomainResult +import com.happy.friendogly.domain.error.DataError import com.happy.friendogly.domain.model.Club import com.happy.friendogly.domain.repository.MyClubRepository import javax.inject.Inject @@ -9,5 +11,5 @@ class GetMyClubUseCase constructor( private val repository: MyClubRepository, ) { - suspend operator fun invoke(): Result> = repository.getMyClubs() + suspend operator fun invoke(): DomainResult, DataError.Network> = repository.getMyClubs() } diff --git a/android/app/src/main/java/com/happy/friendogly/domain/usecase/GetMyHeadClubUseCase.kt b/android/app/src/main/java/com/happy/friendogly/domain/usecase/GetMyHeadClubUseCase.kt index b0bfaabe2..bacd8504e 100644 --- a/android/app/src/main/java/com/happy/friendogly/domain/usecase/GetMyHeadClubUseCase.kt +++ b/android/app/src/main/java/com/happy/friendogly/domain/usecase/GetMyHeadClubUseCase.kt @@ -1,5 +1,7 @@ package com.happy.friendogly.domain.usecase +import com.happy.friendogly.domain.DomainResult +import com.happy.friendogly.domain.error.DataError import com.happy.friendogly.domain.model.Club import com.happy.friendogly.domain.repository.MyClubRepository import javax.inject.Inject @@ -9,5 +11,5 @@ class GetMyHeadClubUseCase constructor( private val repository: MyClubRepository, ) { - suspend operator fun invoke(): Result> = repository.getMyHeadClubs() + suspend operator fun invoke(): DomainResult, DataError.Network> = repository.getMyHeadClubs() } diff --git a/android/app/src/main/java/com/happy/friendogly/domain/usecase/GetSearchingClubsUseCase.kt b/android/app/src/main/java/com/happy/friendogly/domain/usecase/GetSearchingClubsUseCase.kt index fa5184031..312543c36 100644 --- a/android/app/src/main/java/com/happy/friendogly/domain/usecase/GetSearchingClubsUseCase.kt +++ b/android/app/src/main/java/com/happy/friendogly/domain/usecase/GetSearchingClubsUseCase.kt @@ -1,5 +1,7 @@ package com.happy.friendogly.domain.usecase +import com.happy.friendogly.domain.DomainResult +import com.happy.friendogly.domain.error.DataError import com.happy.friendogly.domain.model.Club import com.happy.friendogly.domain.model.ClubAddress import com.happy.friendogly.domain.model.ClubFilterCondition @@ -18,7 +20,7 @@ class GetSearchingClubsUseCase address: ClubAddress, genderParams: List, sizeParams: List, - ): Result> = + ): DomainResult, DataError.Network> = repository.getSearchingClubs( filterCondition = filterCondition, address = address, diff --git a/android/app/src/main/java/com/happy/friendogly/domain/usecase/PatchClubUseCase.kt b/android/app/src/main/java/com/happy/friendogly/domain/usecase/PatchClubUseCase.kt index 037c18bdc..84f8744d2 100644 --- a/android/app/src/main/java/com/happy/friendogly/domain/usecase/PatchClubUseCase.kt +++ b/android/app/src/main/java/com/happy/friendogly/domain/usecase/PatchClubUseCase.kt @@ -1,5 +1,7 @@ package com.happy.friendogly.domain.usecase +import com.happy.friendogly.domain.DomainResult +import com.happy.friendogly.domain.error.DataError import com.happy.friendogly.domain.model.ClubState import com.happy.friendogly.domain.repository.ClubRepository import javax.inject.Inject @@ -12,7 +14,7 @@ class PatchClubUseCase title: String, content: String, state: ClubState, - ): Result = + ): DomainResult = repository.patchClub( clubId = clubId, title = title, diff --git a/android/app/src/main/java/com/happy/friendogly/domain/usecase/PostClubUseCase.kt b/android/app/src/main/java/com/happy/friendogly/domain/usecase/PostClubUseCase.kt index fb3e09cfb..4cba1683f 100644 --- a/android/app/src/main/java/com/happy/friendogly/domain/usecase/PostClubUseCase.kt +++ b/android/app/src/main/java/com/happy/friendogly/domain/usecase/PostClubUseCase.kt @@ -1,5 +1,7 @@ package com.happy.friendogly.domain.usecase +import com.happy.friendogly.domain.DomainResult +import com.happy.friendogly.domain.error.DataError import com.happy.friendogly.domain.model.ClubAddress import com.happy.friendogly.domain.model.Gender import com.happy.friendogly.domain.model.SizeType @@ -21,7 +23,7 @@ class PostClubUseCase memberCapacity: Int, file: MultipartBody.Part?, petIds: List, - ): Result = + ): DomainResult = repository.postClub( title = title, content = content, diff --git a/android/app/src/main/java/com/happy/friendogly/presentation/ui/club/add/ClubAddActivity.kt b/android/app/src/main/java/com/happy/friendogly/presentation/ui/club/add/ClubAddActivity.kt index f0bccc243..16990e5a6 100644 --- a/android/app/src/main/java/com/happy/friendogly/presentation/ui/club/add/ClubAddActivity.kt +++ b/android/app/src/main/java/com/happy/friendogly/presentation/ui/club/add/ClubAddActivity.kt @@ -18,6 +18,8 @@ import com.happy.friendogly.presentation.base.BaseActivity import com.happy.friendogly.presentation.base.observeEvent import com.happy.friendogly.presentation.ui.club.add.adapter.ClubAddAdapter import com.happy.friendogly.presentation.ui.club.common.ClubChangeStateIntent +import com.happy.friendogly.presentation.ui.club.common.MessageHandler +import com.happy.friendogly.presentation.ui.club.common.handleError import com.happy.friendogly.presentation.ui.club.common.model.clubfilter.ClubFilter import com.happy.friendogly.presentation.ui.club.select.PetSelectBottomSheet import com.happy.friendogly.presentation.ui.profilesetting.bottom.EditProfileImageBottomSheet @@ -99,13 +101,28 @@ class ClubAddActivity : BaseActivity(R.layout.activity_c } ClubAddEvent.FailLoadAddress -> showSnackbar(getString(R.string.club_add_information_fail_address)) - ClubAddEvent.FailAddClub -> showSnackbar(getString(R.string.club_add_fail)) ClubAddEvent.Navigation.NavigateToHomeWithAdded -> { putLoadState() finish() } } } + + viewModel.clubErrorHandler.error.observeEvent(this@ClubAddActivity) { + it.handleError { message -> + when (message) { + is MessageHandler.SendSnackBar -> { + showSnackbar(getString(message.messageId)) { + setAction(resources.getString(R.string.club_detail_fail_button)) { + finish() + } + } + } + + is MessageHandler.SendToast -> showToastMessage(getString(message.messageId)) + } + } + } } private fun putLoadState() { diff --git a/android/app/src/main/java/com/happy/friendogly/presentation/ui/club/add/ClubAddEvent.kt b/android/app/src/main/java/com/happy/friendogly/presentation/ui/club/add/ClubAddEvent.kt index b0e8584b8..a76d7b155 100644 --- a/android/app/src/main/java/com/happy/friendogly/presentation/ui/club/add/ClubAddEvent.kt +++ b/android/app/src/main/java/com/happy/friendogly/presentation/ui/club/add/ClubAddEvent.kt @@ -16,6 +16,4 @@ sealed interface ClubAddEvent { data class ChangePage(val page: Int) : ClubAddEvent data object FailLoadAddress : ClubAddEvent - - data object FailAddClub : ClubAddEvent } diff --git a/android/app/src/main/java/com/happy/friendogly/presentation/ui/club/add/ClubAddViewModel.kt b/android/app/src/main/java/com/happy/friendogly/presentation/ui/club/add/ClubAddViewModel.kt index 53570f882..b617a0e08 100644 --- a/android/app/src/main/java/com/happy/friendogly/presentation/ui/club/add/ClubAddViewModel.kt +++ b/android/app/src/main/java/com/happy/friendogly/presentation/ui/club/add/ClubAddViewModel.kt @@ -5,6 +5,7 @@ import androidx.lifecycle.LiveData import androidx.lifecycle.MediatorLiveData import androidx.lifecycle.MutableLiveData import androidx.lifecycle.viewModelScope +import com.happy.friendogly.domain.fold import com.happy.friendogly.domain.model.UserAddress import com.happy.friendogly.domain.usecase.GetAddressUseCase import com.happy.friendogly.domain.usecase.PostClubUseCase @@ -15,6 +16,7 @@ import com.happy.friendogly.presentation.base.emit import com.happy.friendogly.presentation.ui.club.add.adapter.ClubAddAdapter.Companion.MAX_PAGE_SIZE import com.happy.friendogly.presentation.ui.club.add.adapter.ClubAddAdapter.Companion.MIN_PAGE import com.happy.friendogly.presentation.ui.club.add.model.ClubCounter +import com.happy.friendogly.presentation.ui.club.common.ClubErrorHandler import com.happy.friendogly.presentation.ui.club.common.mapper.toDomain import com.happy.friendogly.presentation.ui.club.common.mapper.toGenders import com.happy.friendogly.presentation.ui.club.common.mapper.toSizeTypes @@ -38,6 +40,10 @@ class ClubAddViewModel private val getAddressUseCase: GetAddressUseCase, private val postClubUseCase: PostClubUseCase, ) : BaseViewModel(), ClubAddActionHandler, ClubFilterItemActionHandler { + val clubErrorHandler = ClubErrorHandler() + + private val clubFilterSelector = ClubFilterSelector() + private val _myAddress: MutableLiveData = MutableLiveData() val myAddress: LiveData get() = _myAddress @@ -48,8 +54,6 @@ class ClubAddViewModel private val _currentPage: MutableLiveData = MutableLiveData(MIN_PAGE) val currentPage: LiveData get() = _currentPage - private val clubFilterSelector = ClubFilterSelector() - private val _clubCounter: MutableLiveData = MutableLiveData(ClubCounter()) val clubCounter: LiveData get() = _clubCounter @@ -181,13 +185,14 @@ class ClubAddViewModel memberCapacity = clubCounter.value?.count ?: return@launch, file = file, petIds = dogs, - ) - .onSuccess { + ).fold( + onSuccess = { _clubAddEvent.emit(ClubAddEvent.Navigation.NavigateToHomeWithAdded) - } - .onFailure { - _clubAddEvent.emit(ClubAddEvent.FailAddClub) - } + }, + onError = { error -> + clubErrorHandler.handle(error) + }, + ) } override fun navigatePrevPage() { diff --git a/android/app/src/main/java/com/happy/friendogly/presentation/ui/club/common/ClubErrorHandler.kt b/android/app/src/main/java/com/happy/friendogly/presentation/ui/club/common/ClubErrorHandler.kt new file mode 100644 index 000000000..077aede5f --- /dev/null +++ b/android/app/src/main/java/com/happy/friendogly/presentation/ui/club/common/ClubErrorHandler.kt @@ -0,0 +1,42 @@ +package com.happy.friendogly.presentation.ui.club.common + +import androidx.lifecycle.LiveData +import androidx.lifecycle.MutableLiveData +import com.happy.friendogly.domain.error.DataError +import com.happy.friendogly.presentation.base.Event +import com.happy.friendogly.presentation.base.emit + +sealed interface ClubErrorEvent { + data object InternetError : ClubErrorEvent + + data object FileSizeError : ClubErrorEvent + + data object ServerError : ClubErrorEvent + + data object UnKnownError : ClubErrorEvent +} + +class ClubErrorHandler { + private val _error: MutableLiveData> = MutableLiveData(null) + val error: LiveData> get() = _error + + fun handle(error: DataError) { + _error.emit( + when (error) { + is DataError.Network -> { + when (error) { + DataError.Network.NO_INTERNET -> ClubErrorEvent.InternetError + DataError.Network.FILE_SIZE_EXCEED -> ClubErrorEvent.FileSizeError + DataError.Network.UNKNOWN -> ClubErrorEvent.UnKnownError + else -> ClubErrorEvent.ServerError + } + } + + is DataError.Local -> { + // TODO : Local Exception + ClubErrorEvent.UnKnownError + } + }, + ) + } +} diff --git a/android/app/src/main/java/com/happy/friendogly/presentation/ui/club/common/ClubMessageHandler.kt b/android/app/src/main/java/com/happy/friendogly/presentation/ui/club/common/ClubMessageHandler.kt new file mode 100644 index 000000000..62ce3e5a5 --- /dev/null +++ b/android/app/src/main/java/com/happy/friendogly/presentation/ui/club/common/ClubMessageHandler.kt @@ -0,0 +1,18 @@ +package com.happy.friendogly.presentation.ui.club.common + +import com.happy.friendogly.R + +sealed interface MessageHandler { + data class SendToast(val messageId: Int) : MessageHandler + + data class SendSnackBar(val messageId: Int) : MessageHandler +} + +fun ClubErrorEvent.handleError(sendMessage: (MessageHandler) -> Unit) { + when (this) { + ClubErrorEvent.FileSizeError -> sendMessage(MessageHandler.SendToast(R.string.file_size_exceed_message)) + ClubErrorEvent.ServerError -> sendMessage(MessageHandler.SendToast(R.string.server_error_message)) + ClubErrorEvent.UnKnownError -> sendMessage(MessageHandler.SendToast(R.string.default_error_message)) + ClubErrorEvent.InternetError -> sendMessage(MessageHandler.SendSnackBar(R.string.no_internet_message)) + } +} diff --git a/android/app/src/main/java/com/happy/friendogly/presentation/ui/club/detail/ClubDetailActivity.kt b/android/app/src/main/java/com/happy/friendogly/presentation/ui/club/detail/ClubDetailActivity.kt index cef8334bb..d8189d808 100644 --- a/android/app/src/main/java/com/happy/friendogly/presentation/ui/club/detail/ClubDetailActivity.kt +++ b/android/app/src/main/java/com/happy/friendogly/presentation/ui/club/detail/ClubDetailActivity.kt @@ -14,6 +14,8 @@ import com.happy.friendogly.presentation.base.observeEvent import com.happy.friendogly.presentation.dialog.PetAddAlertDialog import com.happy.friendogly.presentation.ui.chatlist.chat.ChatActivity import com.happy.friendogly.presentation.ui.club.common.ClubChangeStateIntent +import com.happy.friendogly.presentation.ui.club.common.MessageHandler +import com.happy.friendogly.presentation.ui.club.common.handleError import com.happy.friendogly.presentation.ui.club.common.model.clubfilter.ClubFilter import com.happy.friendogly.presentation.ui.club.detail.adapter.DetailProfileAdapter import com.happy.friendogly.presentation.ui.club.detail.model.ClubDetailProfileUiModel @@ -133,15 +135,25 @@ class ClubDetailActivity : bottomSheet.show(supportFragmentManager, "TAG") } - ClubDetailEvent.FailLoadDetail -> openFailClubDetailLoad() - ClubDetailEvent.FailParticipation -> - showSnackbar( - getString(R.string.club_detail_participate_fail), - ) - ClubDetailEvent.Navigation.NavigateToRegisterPet -> openRegisterPetDialog() } } + + viewModel.clubErrorHandler.error.observeEvent(this@ClubDetailActivity) { + it.handleError { message -> + when (message) { + is MessageHandler.SendSnackBar -> { + showSnackbar(getString(message.messageId)) { + setAction(resources.getString(R.string.club_detail_fail_button)) { + finish() + } + } + } + + is MessageHandler.SendToast -> showToastMessage(getString(message.messageId)) + } + } + } } override fun navigateToModify() { @@ -158,7 +170,6 @@ class ClubDetailActivity : } private fun openChatRoom(chatRoomId: Long) { - // TODO : memberId 지우기 startActivity( ChatActivity.getIntent( context = this@ClubDetailActivity, diff --git a/android/app/src/main/java/com/happy/friendogly/presentation/ui/club/detail/ClubDetailEvent.kt b/android/app/src/main/java/com/happy/friendogly/presentation/ui/club/detail/ClubDetailEvent.kt index bfc60207e..d9d0e6897 100644 --- a/android/app/src/main/java/com/happy/friendogly/presentation/ui/club/detail/ClubDetailEvent.kt +++ b/android/app/src/main/java/com/happy/friendogly/presentation/ui/club/detail/ClubDetailEvent.kt @@ -15,8 +15,4 @@ sealed interface ClubDetailEvent { data object NavigateToRegisterPet : Navigation } - - data object FailLoadDetail : ClubDetailEvent - - data object FailParticipation : ClubDetailEvent } diff --git a/android/app/src/main/java/com/happy/friendogly/presentation/ui/club/detail/ClubDetailViewModel.kt b/android/app/src/main/java/com/happy/friendogly/presentation/ui/club/detail/ClubDetailViewModel.kt index 66efd9bc5..0400ccbc7 100644 --- a/android/app/src/main/java/com/happy/friendogly/presentation/ui/club/detail/ClubDetailViewModel.kt +++ b/android/app/src/main/java/com/happy/friendogly/presentation/ui/club/detail/ClubDetailViewModel.kt @@ -3,12 +3,14 @@ package com.happy.friendogly.presentation.ui.club.detail import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData import androidx.lifecycle.viewModelScope +import com.happy.friendogly.domain.fold import com.happy.friendogly.domain.usecase.GetClubUseCase import com.happy.friendogly.domain.usecase.PostClubMemberUseCase import com.happy.friendogly.firebase.analytics.AnalyticsHelper import com.happy.friendogly.presentation.base.BaseViewModel import com.happy.friendogly.presentation.base.Event import com.happy.friendogly.presentation.base.emit +import com.happy.friendogly.presentation.ui.club.common.ClubErrorHandler import com.happy.friendogly.presentation.ui.club.common.mapper.toPresentation import com.happy.friendogly.presentation.ui.club.detail.model.ClubDetailViewType import com.happy.friendogly.presentation.ui.club.modify.ClubModifyUiModel @@ -25,6 +27,8 @@ class ClubDetailViewModel private val getClubUseCase: GetClubUseCase, private val postClubMemberUseCase: PostClubMemberUseCase, ) : BaseViewModel(), ClubDetailActionHandler { + val clubErrorHandler = ClubErrorHandler() + private val _club: MutableLiveData = MutableLiveData() val club: LiveData get() = _club @@ -33,13 +37,14 @@ class ClubDetailViewModel fun loadClub(clubId: Long) = viewModelScope.launch { - getClubUseCase(clubId) - .onSuccess { - _club.value = it.toPresentation() - } - .onFailure { - _clubDetailEvent.emit(ClubDetailEvent.FailLoadDetail) - } + getClubUseCase(clubId).fold( + onSuccess = { club -> + _club.value = club.toPresentation() + }, + onError = { error -> + clubErrorHandler.handle(error) + }, + ) } override fun confirmParticipation() { @@ -77,17 +82,18 @@ class ClubDetailViewModel postClubMemberUseCase( id = clubDetailId, participatingPetsId = dogs, - ) - .onSuccess { clubParticipation -> + ).fold( + onSuccess = { clubParticipation -> _clubDetailEvent.emit( ClubDetailEvent.Navigation.NavigateToChat( clubParticipation.chatRoomId, ), ) - } - .onFailure { - _clubDetailEvent.emit(ClubDetailEvent.FailParticipation) - } + }, + onError = { error -> + clubErrorHandler.handle(error) + }, + ) } private fun requireRegisterUserPet() { diff --git a/android/app/src/main/java/com/happy/friendogly/presentation/ui/club/list/ClubListFragment.kt b/android/app/src/main/java/com/happy/friendogly/presentation/ui/club/list/ClubListFragment.kt index 84f6cf285..4d4a47a16 100644 --- a/android/app/src/main/java/com/happy/friendogly/presentation/ui/club/list/ClubListFragment.kt +++ b/android/app/src/main/java/com/happy/friendogly/presentation/ui/club/list/ClubListFragment.kt @@ -14,8 +14,11 @@ import com.happy.friendogly.presentation.base.observeEvent import com.happy.friendogly.presentation.dialog.PetAddAlertDialog import com.happy.friendogly.presentation.ui.MainActivityActionHandler import com.happy.friendogly.presentation.ui.club.common.ClubChangeStateIntent +import com.happy.friendogly.presentation.ui.club.common.ClubErrorEvent import com.happy.friendogly.presentation.ui.club.common.ClubItemActionHandler +import com.happy.friendogly.presentation.ui.club.common.MessageHandler import com.happy.friendogly.presentation.ui.club.common.adapter.club.ClubListAdapter +import com.happy.friendogly.presentation.ui.club.common.handleError import com.happy.friendogly.presentation.ui.club.filter.bottom.ClubFilterBottomSheet import com.happy.friendogly.presentation.ui.club.filter.bottom.ParticipationFilterBottomSheet import com.happy.friendogly.presentation.ui.club.list.adapter.selectfilter.SelectFilterAdapter @@ -134,6 +137,17 @@ class ClubListFragment : BaseFragment(R.layout.fragment ClubListEvent.OpenAddPet -> openRegisterPetDialog() } } + + viewModel.clubErrorHandler.error.observeEvent(viewLifecycleOwner) { + it.handleError( + sendMessage = { message -> + when (message) { + is MessageHandler.SendSnackBar -> showSnackbar(getString(message.messageId)) + is MessageHandler.SendToast -> showToastMessage(getString(message.messageId)) + } + }, + ) + } } private fun openRegisterPetDialog() { @@ -170,4 +184,13 @@ class ClubListFragment : BaseFragment(R.layout.fragment companion object { const val TAG = "ClubListFragment" } + + fun ClubErrorEvent.handleError(messageHandler: (MessageHandler) -> Unit) { + when (this) { + ClubErrorEvent.FileSizeError -> messageHandler(MessageHandler.SendToast(R.string.file_size_exceed_message)) + ClubErrorEvent.ServerError -> MessageHandler.SendToast(R.string.server_error_message) + ClubErrorEvent.UnKnownError -> MessageHandler.SendToast(R.string.default_error_message) + ClubErrorEvent.InternetError -> MessageHandler.SendSnackBar(R.string.no_internet_message) + } + } } diff --git a/android/app/src/main/java/com/happy/friendogly/presentation/ui/club/list/ClubListViewModel.kt b/android/app/src/main/java/com/happy/friendogly/presentation/ui/club/list/ClubListViewModel.kt index 68e45d9b4..d52ba447a 100644 --- a/android/app/src/main/java/com/happy/friendogly/presentation/ui/club/list/ClubListViewModel.kt +++ b/android/app/src/main/java/com/happy/friendogly/presentation/ui/club/list/ClubListViewModel.kt @@ -13,6 +13,7 @@ import com.happy.friendogly.firebase.analytics.AnalyticsHelper import com.happy.friendogly.presentation.base.BaseViewModel import com.happy.friendogly.presentation.base.Event import com.happy.friendogly.presentation.base.emit +import com.happy.friendogly.presentation.ui.club.common.ClubErrorHandler import com.happy.friendogly.presentation.ui.club.common.ClubItemActionHandler import com.happy.friendogly.presentation.ui.club.common.mapper.toDomain import com.happy.friendogly.presentation.ui.club.common.mapper.toGenders @@ -38,6 +39,8 @@ class ClubListViewModel private val getAddressUseCase: GetAddressUseCase, private val searchingClubsUseCase: GetSearchingClubsUseCase, ) : BaseViewModel(), ClubListActionHandler, ClubItemActionHandler { + val clubErrorHandler = ClubErrorHandler() + private val _uiState: MutableLiveData = MutableLiveData(ClubListUiState.Init) val uiState: LiveData get() = _uiState @@ -84,8 +87,8 @@ class ClubListViewModel _clubListEvent.emit(ClubListEvent.OpenAddPet) } }, - onError = { - // TODO 예외처리 + onError = { error -> + clubErrorHandler.handle(error) }, ) } @@ -97,18 +100,20 @@ class ClubListViewModel address = myAddress.value?.toDomain() ?: return@launch, genderParams = clubFilterSelector.selectGenderFilters().toGenders(), sizeParams = clubFilterSelector.selectSizeFilters().toSizeTypes(), - ) - .onSuccess { clubs -> + ).fold( + onSuccess = { clubs -> if (clubs.isEmpty()) { _uiState.value = ClubListUiState.NotData } else { _uiState.value = ClubListUiState.Init } _clubs.value = clubs.toPresentation() - } - .onFailure { + }, + onError = { error -> + clubErrorHandler.handle(error) _uiState.value = ClubListUiState.Error - } + }, + ) } fun updateClubFilter(filters: List) { @@ -143,8 +148,8 @@ class ClubListViewModel _clubListEvent.emit(ClubListEvent.Navigation.NavigateToAddClub) } }, - onError = { - // TODO 예외처리 + onError = { error -> + clubErrorHandler.handle(error) }, ) } diff --git a/android/app/src/main/java/com/happy/friendogly/presentation/ui/club/menu/ClubMenuBottomSheet.kt b/android/app/src/main/java/com/happy/friendogly/presentation/ui/club/menu/ClubMenuBottomSheet.kt index d24fdbabc..9d8d946d2 100644 --- a/android/app/src/main/java/com/happy/friendogly/presentation/ui/club/menu/ClubMenuBottomSheet.kt +++ b/android/app/src/main/java/com/happy/friendogly/presentation/ui/club/menu/ClubMenuBottomSheet.kt @@ -14,6 +14,8 @@ import com.happy.friendogly.databinding.BottomSheetClubMenuBinding import com.happy.friendogly.presentation.base.observeEvent import com.happy.friendogly.presentation.dialog.AlertDialogModel import com.happy.friendogly.presentation.dialog.DefaultCoralAlertDialog +import com.happy.friendogly.presentation.ui.club.common.MessageHandler +import com.happy.friendogly.presentation.ui.club.common.handleError import com.happy.friendogly.presentation.ui.club.detail.ClubDetailNavigation import com.happy.friendogly.presentation.ui.club.detail.model.ClubDetailViewType import dagger.hilt.android.AndroidEntryPoint @@ -79,15 +81,28 @@ class ClubMenuBottomSheet( (activity as ClubDetailNavigation).navigateToPrevWithReload() dismissNow() } - - ClubMenuEvent.FailDelete -> { - makeToast( - requireContext().getString(R.string.club_detail_delete_fail), - ) - dismissNow() - } } } + + viewModel.clubErrorHandler.error.observeEvent(viewLifecycleOwner) { + it.handleError( + sendMessage = { message -> + when (message) { + is MessageHandler.SendSnackBar -> { + makeToast( + requireContext().getString(message.messageId), + ) + dismissNow() + } + + is MessageHandler.SendToast -> { + makeToast(getString(message.messageId)) + dismissNow() + } + } + }, + ) + } } private fun makeToast(message: String) { diff --git a/android/app/src/main/java/com/happy/friendogly/presentation/ui/club/menu/ClubMenuEvent.kt b/android/app/src/main/java/com/happy/friendogly/presentation/ui/club/menu/ClubMenuEvent.kt index d45e07470..f439fd1d3 100644 --- a/android/app/src/main/java/com/happy/friendogly/presentation/ui/club/menu/ClubMenuEvent.kt +++ b/android/app/src/main/java/com/happy/friendogly/presentation/ui/club/menu/ClubMenuEvent.kt @@ -14,6 +14,4 @@ sealed interface ClubMenuEvent { sealed interface Navigation : ClubMenuEvent { data object NavigateToPrev : Navigation } - - data object FailDelete : ClubMenuEvent } diff --git a/android/app/src/main/java/com/happy/friendogly/presentation/ui/club/menu/ClubMenuViewModel.kt b/android/app/src/main/java/com/happy/friendogly/presentation/ui/club/menu/ClubMenuViewModel.kt index 1d8de7cde..2ebc65ee9 100644 --- a/android/app/src/main/java/com/happy/friendogly/presentation/ui/club/menu/ClubMenuViewModel.kt +++ b/android/app/src/main/java/com/happy/friendogly/presentation/ui/club/menu/ClubMenuViewModel.kt @@ -3,11 +3,13 @@ package com.happy.friendogly.presentation.ui.club.menu import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData import androidx.lifecycle.viewModelScope +import com.happy.friendogly.domain.fold import com.happy.friendogly.domain.usecase.DeleteClubMemberUseCase import com.happy.friendogly.firebase.analytics.AnalyticsHelper import com.happy.friendogly.presentation.base.BaseViewModel import com.happy.friendogly.presentation.base.Event import com.happy.friendogly.presentation.base.emit +import com.happy.friendogly.presentation.ui.club.common.ClubErrorHandler import com.happy.friendogly.presentation.ui.club.detail.model.ClubDetailViewType import com.happy.friendogly.presentation.utils.logDeleteMemberClick import com.happy.friendogly.presentation.utils.logUpdateClubClick @@ -22,6 +24,8 @@ class ClubMenuViewModel private val analyticsHelper: AnalyticsHelper, private val deleteClubMemberUseCase: DeleteClubMemberUseCase, ) : BaseViewModel(), ClubMenuActionHandler { + val clubErrorHandler = ClubErrorHandler() + private val _clubMenuEvent: MutableLiveData> = MutableLiveData() val clubMenuEvent: LiveData> get() = _clubMenuEvent @@ -36,12 +40,14 @@ class ClubMenuViewModel fun withdrawClub(clubId: Long) = viewModelScope.launch { deleteClubMemberUseCase(clubId) - .onSuccess { - _clubMenuEvent.emit(ClubMenuEvent.Navigation.NavigateToPrev) - } - .onFailure { - _clubMenuEvent.emit(ClubMenuEvent.FailDelete) - } + .fold( + onSuccess = { + _clubMenuEvent.emit(ClubMenuEvent.Navigation.NavigateToPrev) + }, + onError = { error -> + clubErrorHandler.handle(error) + }, + ) } override fun close() { diff --git a/android/app/src/main/java/com/happy/friendogly/presentation/ui/club/modify/ClubModifyActivity.kt b/android/app/src/main/java/com/happy/friendogly/presentation/ui/club/modify/ClubModifyActivity.kt index e2c82d985..51ee2c12c 100644 --- a/android/app/src/main/java/com/happy/friendogly/presentation/ui/club/modify/ClubModifyActivity.kt +++ b/android/app/src/main/java/com/happy/friendogly/presentation/ui/club/modify/ClubModifyActivity.kt @@ -8,6 +8,8 @@ import com.happy.friendogly.R import com.happy.friendogly.databinding.ActivityClubModifyBinding import com.happy.friendogly.presentation.base.BaseActivity import com.happy.friendogly.presentation.base.observeEvent +import com.happy.friendogly.presentation.ui.club.common.MessageHandler +import com.happy.friendogly.presentation.ui.club.common.handleError import com.happy.friendogly.presentation.ui.club.modify.bottom.ClubRecruitmentBottomSheet import com.happy.friendogly.presentation.utils.customOnFocusChangeListener import com.happy.friendogly.presentation.utils.hideKeyboard @@ -65,7 +67,15 @@ class ClubModifyActivity : } ClubModifyEvent.Navigation.NavigateSelectState -> openSelectState() - ClubModifyEvent.FailModify -> showSnackbar(getString(R.string.club_modify_fail)) + } + } + + viewModel.clubErrorHandler.error.observeEvent(this@ClubModifyActivity) { + it.handleError { message -> + when (message) { + is MessageHandler.SendSnackBar -> showSnackbar(getString(message.messageId)) + is MessageHandler.SendToast -> showToastMessage(getString(message.messageId)) + } } } } diff --git a/android/app/src/main/java/com/happy/friendogly/presentation/ui/club/modify/ClubModifyEvent.kt b/android/app/src/main/java/com/happy/friendogly/presentation/ui/club/modify/ClubModifyEvent.kt index 4b30ca9a0..f4beb5c1f 100644 --- a/android/app/src/main/java/com/happy/friendogly/presentation/ui/club/modify/ClubModifyEvent.kt +++ b/android/app/src/main/java/com/happy/friendogly/presentation/ui/club/modify/ClubModifyEvent.kt @@ -8,6 +8,4 @@ sealed interface ClubModifyEvent { data object NavigateSelectState : Navigation } - - data object FailModify : ClubModifyEvent } diff --git a/android/app/src/main/java/com/happy/friendogly/presentation/ui/club/modify/ClubModifyViewModel.kt b/android/app/src/main/java/com/happy/friendogly/presentation/ui/club/modify/ClubModifyViewModel.kt index 10d48a4e9..0071136af 100644 --- a/android/app/src/main/java/com/happy/friendogly/presentation/ui/club/modify/ClubModifyViewModel.kt +++ b/android/app/src/main/java/com/happy/friendogly/presentation/ui/club/modify/ClubModifyViewModel.kt @@ -4,11 +4,13 @@ import androidx.lifecycle.LiveData import androidx.lifecycle.MediatorLiveData import androidx.lifecycle.MutableLiveData import androidx.lifecycle.viewModelScope +import com.happy.friendogly.domain.fold import com.happy.friendogly.domain.model.ClubState import com.happy.friendogly.domain.usecase.PatchClubUseCase import com.happy.friendogly.presentation.base.BaseViewModel import com.happy.friendogly.presentation.base.Event import com.happy.friendogly.presentation.base.emit +import com.happy.friendogly.presentation.ui.club.common.ClubErrorHandler import com.happy.friendogly.presentation.utils.addSourceList import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.launch @@ -20,6 +22,8 @@ class ClubModifyViewModel constructor( private val patchClubUseCase: PatchClubUseCase, ) : BaseViewModel(), ClubModifyActionHandler { + val clubErrorHandler = ClubErrorHandler() + private val _modifyEvent: MutableLiveData> = MutableLiveData() val modifyEvent: LiveData> get() = _modifyEvent @@ -82,13 +86,14 @@ class ClubModifyViewModel title = clubTitle.value ?: return@launch, content = clubContent.value ?: return@launch, state = clubState.value ?: return@launch, - ) - .onSuccess { + ).fold( + onSuccess = { _modifyEvent.emit(ClubModifyEvent.Navigation.NavigateSubmit) - } - .onFailure { - _modifyEvent.emit(ClubModifyEvent.FailModify) - } + }, + onError = { error -> + clubErrorHandler.handle(error) + }, + ) } override fun openSelectState() { diff --git a/android/app/src/main/java/com/happy/friendogly/presentation/ui/club/my/MyClubActivity.kt b/android/app/src/main/java/com/happy/friendogly/presentation/ui/club/my/MyClubActivity.kt index 843b14ad3..2266183b9 100644 --- a/android/app/src/main/java/com/happy/friendogly/presentation/ui/club/my/MyClubActivity.kt +++ b/android/app/src/main/java/com/happy/friendogly/presentation/ui/club/my/MyClubActivity.kt @@ -11,6 +11,8 @@ import com.happy.friendogly.presentation.base.BaseActivity import com.happy.friendogly.presentation.base.observeEvent import com.happy.friendogly.presentation.dialog.PetAddAlertDialog import com.happy.friendogly.presentation.ui.club.add.ClubAddActivity +import com.happy.friendogly.presentation.ui.club.common.MessageHandler +import com.happy.friendogly.presentation.ui.club.common.handleError import com.happy.friendogly.presentation.ui.club.detail.ClubDetailActivity import com.happy.friendogly.presentation.ui.club.my.adapter.MyClubAdapter import com.happy.friendogly.presentation.ui.registerpet.RegisterPetActivity @@ -66,6 +68,15 @@ class MyClubActivity : MyClubEvent.AddPet.OpenAddPet -> openRegisterPetDialog() } } + + viewModel.clubErrorHandler.error.observeEvent(this@MyClubActivity) { + it.handleError { message -> + when (message) { + is MessageHandler.SendSnackBar -> showSnackbar(getString(message.messageId)) + is MessageHandler.SendToast -> showToastMessage(getString(message.messageId)) + } + } + } } private fun openRegisterPetDialog() { diff --git a/android/app/src/main/java/com/happy/friendogly/presentation/ui/club/my/MyClubViewModel.kt b/android/app/src/main/java/com/happy/friendogly/presentation/ui/club/my/MyClubViewModel.kt index 2a56da03e..fc4036b9e 100644 --- a/android/app/src/main/java/com/happy/friendogly/presentation/ui/club/my/MyClubViewModel.kt +++ b/android/app/src/main/java/com/happy/friendogly/presentation/ui/club/my/MyClubViewModel.kt @@ -8,6 +8,7 @@ import com.happy.friendogly.domain.usecase.GetPetsMineUseCase import com.happy.friendogly.presentation.base.BaseViewModel import com.happy.friendogly.presentation.base.Event import com.happy.friendogly.presentation.base.emit +import com.happy.friendogly.presentation.ui.club.common.ClubErrorHandler import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.launch import javax.inject.Inject @@ -18,6 +19,8 @@ class MyClubViewModel constructor( private val getPetsMineUseCase: GetPetsMineUseCase, ) : BaseViewModel() { + val clubErrorHandler = ClubErrorHandler() + private val _myClubEvent: MutableLiveData> = MutableLiveData() val myClubEvent: LiveData> get() = _myClubEvent @@ -32,8 +35,8 @@ class MyClubViewModel _myClubEvent.emit(MyClubEvent.AddPet.OpenAddClub) } }, - onError = { - // TODO 예외처리 + onError = { error -> + clubErrorHandler.handle(error) }, ) } diff --git a/android/app/src/main/java/com/happy/friendogly/presentation/ui/club/my/head/MyHeadClubFragment.kt b/android/app/src/main/java/com/happy/friendogly/presentation/ui/club/my/head/MyHeadClubFragment.kt index 316e04070..fe8a486ab 100644 --- a/android/app/src/main/java/com/happy/friendogly/presentation/ui/club/my/head/MyHeadClubFragment.kt +++ b/android/app/src/main/java/com/happy/friendogly/presentation/ui/club/my/head/MyHeadClubFragment.kt @@ -7,7 +7,9 @@ import com.happy.friendogly.databinding.FragmentMyClubBinding import com.happy.friendogly.presentation.base.BaseFragment import com.happy.friendogly.presentation.base.observeEvent import com.happy.friendogly.presentation.ui.club.common.ClubItemActionHandler +import com.happy.friendogly.presentation.ui.club.common.MessageHandler import com.happy.friendogly.presentation.ui.club.common.adapter.club.ClubListAdapter +import com.happy.friendogly.presentation.ui.club.common.handleError import com.happy.friendogly.presentation.ui.club.my.MyClubActivity import com.happy.friendogly.presentation.ui.club.my.MyClubEvent import com.happy.friendogly.presentation.ui.club.my.MyClubUiState @@ -58,6 +60,15 @@ class MyHeadClubFragment : BaseFragment(R.layout.fragment MyClubUiState.NotData -> applyViewState(binding.includeClubData.linearLayoutClubNotData) } } + + viewModel.clubErrorHandler.error.observeEvent(viewLifecycleOwner) { + it.handleError { message -> + when (message) { + is MessageHandler.SendSnackBar -> showSnackbar(getString(message.messageId)) + is MessageHandler.SendToast -> showToastMessage(getString(message.messageId)) + } + } + } } private fun applyViewState(currentView: View) { diff --git a/android/app/src/main/java/com/happy/friendogly/presentation/ui/club/my/head/MyHeadClubViewModel.kt b/android/app/src/main/java/com/happy/friendogly/presentation/ui/club/my/head/MyHeadClubViewModel.kt index 8dded3e9d..df00067ec 100644 --- a/android/app/src/main/java/com/happy/friendogly/presentation/ui/club/my/head/MyHeadClubViewModel.kt +++ b/android/app/src/main/java/com/happy/friendogly/presentation/ui/club/my/head/MyHeadClubViewModel.kt @@ -3,10 +3,12 @@ package com.happy.friendogly.presentation.ui.club.my.head import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData import androidx.lifecycle.viewModelScope +import com.happy.friendogly.domain.fold import com.happy.friendogly.domain.usecase.GetMyHeadClubUseCase import com.happy.friendogly.presentation.base.BaseViewModel import com.happy.friendogly.presentation.base.Event import com.happy.friendogly.presentation.base.emit +import com.happy.friendogly.presentation.ui.club.common.ClubErrorHandler import com.happy.friendogly.presentation.ui.club.common.ClubItemActionHandler import com.happy.friendogly.presentation.ui.club.common.mapper.toPresentation import com.happy.friendogly.presentation.ui.club.common.model.ClubItemUiModel @@ -22,6 +24,8 @@ class MyHeadClubViewModel constructor( private val getMyHeadClubUseCase: GetMyHeadClubUseCase, ) : BaseViewModel(), ClubItemActionHandler { + val clubErrorHandler = ClubErrorHandler() + private val _myHeadClubs: MutableLiveData> = MutableLiveData() val myHeadClubs: LiveData> get() = _myHeadClubs @@ -38,17 +42,20 @@ class MyHeadClubViewModel fun loadMyHeadClubs() = viewModelScope.launch { getMyHeadClubUseCase() - .onSuccess { clubs -> - if (clubs.isEmpty()) { - _myHeadClubUiState.value = MyClubUiState.NotData - } else { - _myHeadClubUiState.value = MyClubUiState.Init - } - _myHeadClubs.value = clubs.toPresentation() - } - .onFailure { - _myHeadClubUiState.value = MyClubUiState.Error - } + .fold( + onSuccess = { clubs -> + if (clubs.isEmpty()) { + _myHeadClubUiState.value = MyClubUiState.NotData + } else { + _myHeadClubUiState.value = MyClubUiState.Init + } + _myHeadClubs.value = clubs.toPresentation() + }, + onError = { error -> + _myHeadClubUiState.value = MyClubUiState.Error + clubErrorHandler.handle(error) + }, + ) } override fun loadClub(clubId: Long) { diff --git a/android/app/src/main/java/com/happy/friendogly/presentation/ui/club/my/participating/MyParticipatingClubViewModel.kt b/android/app/src/main/java/com/happy/friendogly/presentation/ui/club/my/participating/MyParticipatingClubViewModel.kt index a381f71c5..a7dcd860e 100644 --- a/android/app/src/main/java/com/happy/friendogly/presentation/ui/club/my/participating/MyParticipatingClubViewModel.kt +++ b/android/app/src/main/java/com/happy/friendogly/presentation/ui/club/my/participating/MyParticipatingClubViewModel.kt @@ -3,10 +3,12 @@ package com.happy.friendogly.presentation.ui.club.my.participating import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData import androidx.lifecycle.viewModelScope +import com.happy.friendogly.domain.fold import com.happy.friendogly.domain.usecase.GetMyClubUseCase import com.happy.friendogly.presentation.base.BaseViewModel import com.happy.friendogly.presentation.base.Event import com.happy.friendogly.presentation.base.emit +import com.happy.friendogly.presentation.ui.club.common.ClubErrorHandler import com.happy.friendogly.presentation.ui.club.common.ClubItemActionHandler import com.happy.friendogly.presentation.ui.club.common.mapper.toPresentation import com.happy.friendogly.presentation.ui.club.common.model.ClubItemUiModel @@ -22,6 +24,8 @@ class MyParticipatingClubViewModel constructor( private val getMyClubUseCase: GetMyClubUseCase, ) : BaseViewModel(), ClubItemActionHandler { + val clubErrorHandler = ClubErrorHandler() + private val _myClubs: MutableLiveData> = MutableLiveData() val myClubs: LiveData> get() = _myClubs @@ -38,17 +42,20 @@ class MyParticipatingClubViewModel fun loadMyClubs() = viewModelScope.launch { getMyClubUseCase() - .onSuccess { clubs -> - if (clubs.isEmpty()) { - _myClubUiState.value = MyClubUiState.NotData - } else { - _myClubUiState.value = MyClubUiState.Init - } - _myClubs.value = clubs.toPresentation() - } - .onFailure { - _myClubUiState.value = MyClubUiState.Error - } + .fold( + onSuccess = { clubs -> + if (clubs.isEmpty()) { + _myClubUiState.value = MyClubUiState.NotData + } else { + _myClubUiState.value = MyClubUiState.Init + } + _myClubs.value = clubs.toPresentation() + }, + onError = { error -> + _myClubUiState.value = MyClubUiState.Error + clubErrorHandler.handle(error) + }, + ) } override fun loadClub(clubId: Long) { diff --git a/android/app/src/main/java/com/happy/friendogly/presentation/ui/club/my/participating/MyParticipatingFragment.kt b/android/app/src/main/java/com/happy/friendogly/presentation/ui/club/my/participating/MyParticipatingFragment.kt index 9a7b13e3f..1a21c4af4 100644 --- a/android/app/src/main/java/com/happy/friendogly/presentation/ui/club/my/participating/MyParticipatingFragment.kt +++ b/android/app/src/main/java/com/happy/friendogly/presentation/ui/club/my/participating/MyParticipatingFragment.kt @@ -7,7 +7,9 @@ import com.happy.friendogly.databinding.FragmentMyClubBinding import com.happy.friendogly.presentation.base.BaseFragment import com.happy.friendogly.presentation.base.observeEvent import com.happy.friendogly.presentation.ui.club.common.ClubItemActionHandler +import com.happy.friendogly.presentation.ui.club.common.MessageHandler import com.happy.friendogly.presentation.ui.club.common.adapter.club.ClubListAdapter +import com.happy.friendogly.presentation.ui.club.common.handleError import com.happy.friendogly.presentation.ui.club.my.MyClubActivity import com.happy.friendogly.presentation.ui.club.my.MyClubEvent import com.happy.friendogly.presentation.ui.club.my.MyClubUiState @@ -44,7 +46,10 @@ class MyParticipatingFragment : BaseFragment(R.layout.fra viewModel.myClubEvent.observeEvent(viewLifecycleOwner) { event -> when (event) { MyClubEvent.Navigation.NavigateToAddClub -> (activity as MyClubActivity).addClub() - is MyClubEvent.Navigation.NavigateToClub -> (activity as MyClubActivity).openClub(event.clubId) + is MyClubEvent.Navigation.NavigateToClub -> + (activity as MyClubActivity).openClub( + event.clubId, + ) } } @@ -55,6 +60,15 @@ class MyParticipatingFragment : BaseFragment(R.layout.fra MyClubUiState.NotData -> applyViewState(binding.includeClubData.linearLayoutClubNotData) } } + + viewModel.clubErrorHandler.error.observeEvent(viewLifecycleOwner) { + it.handleError { message -> + when (message) { + is MessageHandler.SendSnackBar -> showSnackbar(getString(message.messageId)) + is MessageHandler.SendToast -> showToastMessage(getString(message.messageId)) + } + } + } } private fun applyViewState(currentView: View) { diff --git a/android/app/src/main/java/com/happy/friendogly/presentation/ui/club/select/PetSelectBottomSheet.kt b/android/app/src/main/java/com/happy/friendogly/presentation/ui/club/select/PetSelectBottomSheet.kt index c1d6b8501..2e63f2953 100644 --- a/android/app/src/main/java/com/happy/friendogly/presentation/ui/club/select/PetSelectBottomSheet.kt +++ b/android/app/src/main/java/com/happy/friendogly/presentation/ui/club/select/PetSelectBottomSheet.kt @@ -17,6 +17,8 @@ import com.google.android.material.bottomsheet.BottomSheetDialogFragment import com.happy.friendogly.R import com.happy.friendogly.databinding.BottomSheetDogSelectorBinding import com.happy.friendogly.presentation.base.observeEvent +import com.happy.friendogly.presentation.ui.club.common.MessageHandler +import com.happy.friendogly.presentation.ui.club.common.handleError import com.happy.friendogly.presentation.ui.club.common.model.clubfilter.ClubFilter import com.happy.friendogly.presentation.ui.club.select.adapter.PetSelectAdapter import dagger.hilt.android.AndroidEntryPoint @@ -106,7 +108,7 @@ class PetSelectBottomSheet( submit(event.pets) } - PetSelectEvent.FailLoadPet -> + PetSelectEvent.EmptyPet -> makeToast( requireContext().getString( R.string.dog_select_load_fail, @@ -121,6 +123,15 @@ class PetSelectBottomSheet( ) } } + + viewModel.clubErrorHandler.error.observeEvent(viewLifecycleOwner) { + it.handleError { message -> + when (message) { + is MessageHandler.SendSnackBar -> makeToast(getString(message.messageId)) + is MessageHandler.SendToast -> makeToast(getString(message.messageId)) + } + } + } } private fun makeToast(message: String) { diff --git a/android/app/src/main/java/com/happy/friendogly/presentation/ui/club/select/PetSelectEvent.kt b/android/app/src/main/java/com/happy/friendogly/presentation/ui/club/select/PetSelectEvent.kt index b651ad45c..ebab206c8 100644 --- a/android/app/src/main/java/com/happy/friendogly/presentation/ui/club/select/PetSelectEvent.kt +++ b/android/app/src/main/java/com/happy/friendogly/presentation/ui/club/select/PetSelectEvent.kt @@ -9,7 +9,7 @@ sealed interface PetSelectEvent { data class SelectPets(val pets: List) : PetSelectEvent - data object FailLoadPet : PetSelectEvent + data object EmptyPet : PetSelectEvent data object PreventCommit : PetSelectEvent } diff --git a/android/app/src/main/java/com/happy/friendogly/presentation/ui/club/select/PetSelectViewModel.kt b/android/app/src/main/java/com/happy/friendogly/presentation/ui/club/select/PetSelectViewModel.kt index db487f34c..38e82d7bb 100644 --- a/android/app/src/main/java/com/happy/friendogly/presentation/ui/club/select/PetSelectViewModel.kt +++ b/android/app/src/main/java/com/happy/friendogly/presentation/ui/club/select/PetSelectViewModel.kt @@ -7,6 +7,7 @@ import com.happy.friendogly.domain.usecase.GetPetsMineUseCase import com.happy.friendogly.presentation.base.BaseViewModel import com.happy.friendogly.presentation.base.Event import com.happy.friendogly.presentation.base.emit +import com.happy.friendogly.presentation.ui.club.common.ClubErrorHandler import com.happy.friendogly.presentation.ui.club.common.model.clubfilter.ClubFilter import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.launch @@ -18,6 +19,8 @@ class PetSelectViewModel constructor( private val getPetsMineUseCase: GetPetsMineUseCase, ) : BaseViewModel(), PetSelectActionHandler { + val clubErrorHandler = ClubErrorHandler() + private val _pets: MutableLiveData> = MutableLiveData() val pets: LiveData> get() = _pets @@ -43,15 +46,19 @@ class PetSelectViewModel launch { getPetsMineUseCase().fold( onSuccess = { pets -> - _pets.value = - pets.map { pet -> - pet.toPetSelectUiModel().apply { - initSelectableState(filters) + if (pets.isEmpty()) { + _petSelectEvent.emit(PetSelectEvent.EmptyPet) + } else { + _pets.value = + pets.map { pet -> + pet.toPetSelectUiModel().apply { + initSelectableState(filters) + } } - } + } }, - onError = { - _petSelectEvent.emit(PetSelectEvent.FailLoadPet) + onError = { error -> + clubErrorHandler.handle(error) }, ) } diff --git a/android/app/src/main/java/com/happy/friendogly/remote/source/ClubDataSourceImpl.kt b/android/app/src/main/java/com/happy/friendogly/remote/source/ClubDataSourceImpl.kt index b5774d8d9..25359e808 100644 --- a/android/app/src/main/java/com/happy/friendogly/remote/source/ClubDataSourceImpl.kt +++ b/android/app/src/main/java/com/happy/friendogly/remote/source/ClubDataSourceImpl.kt @@ -1,5 +1,6 @@ package com.happy.friendogly.remote.source +import com.happy.friendogly.data.error.ApiExceptionDto.Companion.FileSizeExceedExceptionDto import com.happy.friendogly.data.model.ClubAddressDto import com.happy.friendogly.data.model.ClubDetailDto import com.happy.friendogly.data.model.ClubDto @@ -10,6 +11,7 @@ import com.happy.friendogly.data.model.GenderDto import com.happy.friendogly.data.model.SizeTypeDto import com.happy.friendogly.data.source.ClubDataSource import com.happy.friendogly.remote.api.ClubService +import com.happy.friendogly.remote.error.ApiExceptionResponse import com.happy.friendogly.remote.mapper.toData import com.happy.friendogly.remote.mapper.toRemote import com.happy.friendogly.remote.model.request.ClubModifyRequest @@ -30,25 +32,33 @@ class ClubDataSourceImpl memberCapacity: Int, file: MultipartBody.Part?, petIds: List, - ): Result = - runCatching { - val request = - PostClubRequest( - title = title, - content = content, - province = address.province, - city = address.city, - village = address.village, - allowedGenders = allowedGender.map { it.toRemote().name }, - allowedSizes = allowedSize.map { it.toRemote().name }, - memberCapacity = memberCapacity, - participatingPetsId = petIds, - ) - service.postClub( - body = request, - file = file, - ).data + ): Result { + val result = + runCatching { + val request = + PostClubRequest( + title = title, + content = content, + province = address.province, + city = address.city, + village = address.village, + allowedGenders = allowedGender.map { it.toRemote().name }, + allowedSizes = allowedSize.map { it.toRemote().name }, + memberCapacity = memberCapacity, + participatingPetsId = petIds, + ) + service.postClub( + body = request, + file = file, + ).data + } + return when (val exception = result.exceptionOrNull()) { + null -> result + is ApiExceptionResponse -> Result.failure(exception.toData()) + is IllegalStateException -> Result.failure(FileSizeExceedExceptionDto) + else -> Result.failure(exception) } + } override suspend fun getSearchingClubs( filterCondition: ClubFilterConditionDto, diff --git a/android/app/src/test/java/com/happy/friendogly/presentation/ui/club/detail/ClubDetailViewModelTest.kt b/android/app/src/test/java/com/happy/friendogly/presentation/ui/club/detail/ClubDetailViewModelTest.kt index ef607bc74..2a9dc58bd 100644 --- a/android/app/src/test/java/com/happy/friendogly/presentation/ui/club/detail/ClubDetailViewModelTest.kt +++ b/android/app/src/test/java/com/happy/friendogly/presentation/ui/club/detail/ClubDetailViewModelTest.kt @@ -1,9 +1,12 @@ package com.happy.friendogly.presentation.ui.club.detail +import com.happy.friendogly.domain.DomainResult +import com.happy.friendogly.domain.error.DataError import com.happy.friendogly.domain.model.ClubState import com.happy.friendogly.domain.usecase.GetClubUseCase import com.happy.friendogly.domain.usecase.PostClubMemberUseCase import com.happy.friendogly.firebase.analytics.AnalyticsHelper +import com.happy.friendogly.presentation.ui.club.common.ClubErrorEvent import com.happy.friendogly.utils.CoroutinesTestExtension import com.happy.friendogly.utils.InstantTaskExecutorExtension import com.happy.friendogly.utils.TestFixture @@ -55,7 +58,7 @@ class ClubDetailViewModelTest { coEvery { getClubUseCase(0L) - } returns Result.success(clubDetail) + } returns DomainResult.Success(clubDetail) viewModel = ClubDetailViewModel( @@ -78,7 +81,7 @@ class ClubDetailViewModelTest { // given coEvery { getClubUseCase(0L) - } returns Result.failure(Throwable()) + } returns DomainResult.Error(DataError.Network.SERVER_ERROR) viewModel = ClubDetailViewModel( @@ -89,10 +92,10 @@ class ClubDetailViewModelTest { // when viewModel.loadClub(0L) - val actualClubEvent = viewModel.clubDetailEvent.getOrAwaitValue() + val actualClubEvent = viewModel.clubErrorHandler.error.getOrAwaitValue() // then - Assertions.assertThat(actualClubEvent.equals(ClubDetailEvent.FailLoadDetail)) + Assertions.assertThat(actualClubEvent.equals(ClubErrorEvent.ServerError)) } @Test @@ -109,14 +112,14 @@ class ClubDetailViewModelTest { coEvery { getClubUseCase(0L) - } returns Result.success(clubDetail) + } returns DomainResult.Success(clubDetail) coEvery { postClubMemberUseCase( 0L, listOf(), ) - } returns Result.success(clubParticipation) + } returns DomainResult.Success(clubParticipation) viewModel = ClubDetailViewModel( @@ -146,14 +149,14 @@ class ClubDetailViewModelTest { coEvery { getClubUseCase(0L) - } returns Result.success(clubDetail) + } returns DomainResult.Success(clubDetail) coEvery { postClubMemberUseCase( 0L, listOf(), ) - } returns Result.failure(Throwable()) + } returns DomainResult.Error(DataError.Network.SERVER_ERROR) viewModel = ClubDetailViewModel( @@ -165,9 +168,9 @@ class ClubDetailViewModelTest { // when viewModel.loadClub(0L) viewModel.joinClub(listOf()) - val clubDetailEvent = viewModel.clubDetailEvent.getOrAwaitValue() + val clubDetailEvent = viewModel.clubErrorHandler.error.getOrAwaitValue() // then - Assertions.assertThat(clubDetailEvent.equals(ClubDetailEvent.FailParticipation)) + Assertions.assertThat(clubDetailEvent.equals(ClubErrorEvent.ServerError)) } } diff --git a/android/app/src/test/java/com/happy/friendogly/presentation/ui/club/list/ClubListViewModelTest.kt b/android/app/src/test/java/com/happy/friendogly/presentation/ui/club/list/ClubListViewModelTest.kt index 654ce5ae2..f0a9e4bc0 100644 --- a/android/app/src/test/java/com/happy/friendogly/presentation/ui/club/list/ClubListViewModelTest.kt +++ b/android/app/src/test/java/com/happy/friendogly/presentation/ui/club/list/ClubListViewModelTest.kt @@ -1,6 +1,7 @@ package com.happy.friendogly.presentation.ui.club.list import com.happy.friendogly.domain.DomainResult +import com.happy.friendogly.domain.error.DataError import com.happy.friendogly.domain.model.Club import com.happy.friendogly.domain.model.ClubAddress import com.happy.friendogly.domain.model.ClubFilterCondition @@ -74,7 +75,7 @@ class ClubListViewModelTest { genderParams = ClubFilter.makeGenderFilterEntry().mapNotNull { it.toGender() }, sizeParams = ClubFilter.makeSizeFilterEntry().mapNotNull { it.toSizeType() }, ) - } returns Result.success(clubs) + } returns DomainResult.Success(clubs) viewModel = ClubListViewModel( @@ -142,7 +143,7 @@ class ClubListViewModelTest { genderParams = ClubFilter.makeGenderFilterEntry().mapNotNull { it.toGender() }, sizeParams = ClubFilter.makeSizeFilterEntry().mapNotNull { it.toSizeType() }, ) - } returns Result.success(emptyList()) + } returns DomainResult.Success(emptyList()) viewModel = ClubListViewModel( @@ -183,7 +184,7 @@ class ClubListViewModelTest { genderParams = ClubFilter.makeGenderFilterEntry().mapNotNull { it.toGender() }, sizeParams = ClubFilter.makeSizeFilterEntry().mapNotNull { it.toSizeType() }, ) - } returns Result.failure(Throwable()) + } returns DomainResult.Error(DataError.Network.UNKNOWN) viewModel = ClubListViewModel(