diff --git a/core/ui/src/main/kotlin/com/bff/wespot/ui/base/BaseViewModel.kt b/core/ui/src/main/kotlin/com/bff/wespot/ui/base/BaseViewModel.kt index 0448d824..a12d3714 100644 --- a/core/ui/src/main/kotlin/com/bff/wespot/ui/base/BaseViewModel.kt +++ b/core/ui/src/main/kotlin/com/bff/wespot/ui/base/BaseViewModel.kt @@ -18,7 +18,7 @@ open class BaseViewModel : ViewModel() { val networkState get() = networkStateChecker.networkState - private val _sideEffect = Channel() + private val _sideEffect = Channel(Channel.BUFFERED) val sideEffect = _sideEffect.receiveAsFlow() protected suspend fun postSideEffect(sideEffect: SideEffect) = _sideEffect.send(sideEffect) diff --git a/core/ui/src/main/kotlin/com/bff/wespot/ui/util/KakaoLoginManager.kt b/core/ui/src/main/kotlin/com/bff/wespot/ui/util/KakaoLoginManager.kt index c84ebb92..492f867d 100644 --- a/core/ui/src/main/kotlin/com/bff/wespot/ui/util/KakaoLoginManager.kt +++ b/core/ui/src/main/kotlin/com/bff/wespot/ui/util/KakaoLoginManager.kt @@ -85,9 +85,17 @@ class KakaoLoginManager { companion object { fun logout() = UserApiClient.instance.logout { error -> if (error != null) { - Timber.d("로그아웃 실패. SDK에서 토큰 삭제됨") + Timber.e("Kakao 로그아웃 실패. SDK에서 토큰 삭제됨 : $error") } else { - Timber.d("로그아웃 성공. SDK에서 토큰 삭제됨") + Timber.d("Kakao 로그아웃 성공. SDK에서 토큰 삭제됨") + } + } + + fun revoke() = UserApiClient.instance.unlink { error -> + if (error != null) { + Timber.e("Kakao 탈퇴 실패 : $error") + } else { + Timber.d("Kakao 탈퇴 성공. SDK에서 토큰 삭제됨") } } } diff --git a/data-remote/src/main/kotlin/com/bff/wespot/data/remote/source/auth/AuthDataSource.kt b/data-remote/src/main/kotlin/com/bff/wespot/data/remote/source/auth/AuthDataSource.kt index ddbbea94..d0362f7e 100644 --- a/data-remote/src/main/kotlin/com/bff/wespot/data/remote/source/auth/AuthDataSource.kt +++ b/data-remote/src/main/kotlin/com/bff/wespot/data/remote/source/auth/AuthDataSource.kt @@ -11,4 +11,5 @@ interface AuthDataSource { suspend fun signIn(signIn: SignInDto): Result suspend fun signUp(signUp: SignUpDto): Result suspend fun revoke(revokeReasonList: RevokeReasonListDto): Result + suspend fun signOut(): Result } diff --git a/data-remote/src/main/kotlin/com/bff/wespot/data/remote/source/auth/AuthDataSourceImpl.kt b/data-remote/src/main/kotlin/com/bff/wespot/data/remote/source/auth/AuthDataSourceImpl.kt index 7accefbb..f5c4e7d5 100644 --- a/data-remote/src/main/kotlin/com/bff/wespot/data/remote/source/auth/AuthDataSourceImpl.kt +++ b/data-remote/src/main/kotlin/com/bff/wespot/data/remote/source/auth/AuthDataSourceImpl.kt @@ -60,4 +60,12 @@ class AuthDataSourceImpl @Inject constructor( } setBody(revokeReasonList) } -} \ No newline at end of file + + override suspend fun signOut(): Result = + httpClient.safeRequest { + url { + method = HttpMethod.Patch + path("api/v1/auth/logout") + } + } +} diff --git a/data/src/main/kotlin/com/bff/wespot/data/repository/auth/AuthRepositoryImpl.kt b/data/src/main/kotlin/com/bff/wespot/data/repository/auth/AuthRepositoryImpl.kt index ca87adf2..04460b61 100644 --- a/data/src/main/kotlin/com/bff/wespot/data/repository/auth/AuthRepositoryImpl.kt +++ b/data/src/main/kotlin/com/bff/wespot/data/repository/auth/AuthRepositoryImpl.kt @@ -61,4 +61,6 @@ class AuthRepositoryImpl @Inject constructor( RevokeReasonListDto(revokeReasons = revokeReasonList) ) } -} \ No newline at end of file + + override suspend fun signOut(): Result = authDataSource.signOut() +} diff --git a/domain/src/main/kotlin/com/bff/wespot/domain/repository/auth/AuthRepository.kt b/domain/src/main/kotlin/com/bff/wespot/domain/repository/auth/AuthRepository.kt index edfb1fe1..4d836d75 100644 --- a/domain/src/main/kotlin/com/bff/wespot/domain/repository/auth/AuthRepository.kt +++ b/domain/src/main/kotlin/com/bff/wespot/domain/repository/auth/AuthRepository.kt @@ -7,4 +7,5 @@ interface AuthRepository { suspend fun signIn(signIn: SignIn): Result suspend fun signUp(signUp: SignUp): Boolean suspend fun revoke(revokeReasonList: List): Result + suspend fun signOut(): Result } diff --git a/feature/entire/src/main/java/com/bff/wespot/entire/screen/setting/AccountSettingScreen.kt b/feature/entire/src/main/java/com/bff/wespot/entire/screen/setting/AccountSettingScreen.kt index df69c788..263b0669 100644 --- a/feature/entire/src/main/java/com/bff/wespot/entire/screen/setting/AccountSettingScreen.kt +++ b/feature/entire/src/main/java/com/bff/wespot/entire/screen/setting/AccountSettingScreen.kt @@ -27,7 +27,10 @@ import com.bff.wespot.entire.state.EntireAction import com.bff.wespot.entire.state.EntireSideEffect import com.bff.wespot.entire.viewmodel.EntireViewModel import com.bff.wespot.navigation.Navigator +import com.bff.wespot.ui.component.LoadingAnimation +import com.bff.wespot.ui.util.handleSideEffect import com.ramcosta.composedestinations.annotation.Destination +import org.orbitmvi.orbit.compose.collectAsState import org.orbitmvi.orbit.compose.collectSideEffect interface AccountSettingNavigator { @@ -47,6 +50,9 @@ fun AccountSettingScreen( var showDialog by remember { mutableStateOf(false) } val action = viewModel::onAction + val state by viewModel.collectAsState() + + handleSideEffect(viewModel.sideEffect) viewModel.collectSideEffect { when (it) { @@ -54,7 +60,10 @@ fun AccountSettingScreen( val intent = activityNavigator.navigateToAuth(context) context.startActivity(intent) } - else -> {} + is EntireSideEffect.CloseSignOutDialog -> { + showDialog = false + } + is EntireSideEffect.CloseRevokeDialog -> { } } } @@ -94,6 +103,10 @@ fun AccountSettingScreen( onDismissRequest = { }, ) } + + if (state.isLoading) { + LoadingAnimation() + } } @Composable diff --git a/feature/entire/src/main/java/com/bff/wespot/entire/screen/setting/RevokeConfirmScreen.kt b/feature/entire/src/main/java/com/bff/wespot/entire/screen/setting/RevokeConfirmScreen.kt index 3b0b99e3..e4a8ee09 100644 --- a/feature/entire/src/main/java/com/bff/wespot/entire/screen/setting/RevokeConfirmScreen.kt +++ b/feature/entire/src/main/java/com/bff/wespot/entire/screen/setting/RevokeConfirmScreen.kt @@ -37,6 +37,7 @@ import com.bff.wespot.entire.viewmodel.EntireViewModel import com.bff.wespot.navigation.Navigator import com.bff.wespot.navigation.util.EXTRA_TOAST_MESSAGE import com.bff.wespot.ui.component.BottomButtonLayout +import com.bff.wespot.ui.component.LoadingAnimation import com.bff.wespot.ui.component.WSBottomSheet import com.bff.wespot.ui.component.WSSelectionItem import com.bff.wespot.ui.util.handleSideEffect @@ -74,6 +75,12 @@ fun RevokeConfirmScreen( intent.putExtra(EXTRA_TOAST_MESSAGE, context.getString(R.string.revoke_done)) context.startActivity(intent) } + + is EntireSideEffect.CloseRevokeDialog -> { + showDialog = false + } + + is EntireSideEffect.CloseSignOutDialog -> { } } } @@ -170,6 +177,10 @@ fun RevokeConfirmScreen( onDismissRequest = { }, ) } + + if (state.isLoading) { + LoadingAnimation() + } } @Composable diff --git a/feature/entire/src/main/java/com/bff/wespot/entire/state/EntireSideEffect.kt b/feature/entire/src/main/java/com/bff/wespot/entire/state/EntireSideEffect.kt index 073562ce..f103b11d 100644 --- a/feature/entire/src/main/java/com/bff/wespot/entire/state/EntireSideEffect.kt +++ b/feature/entire/src/main/java/com/bff/wespot/entire/state/EntireSideEffect.kt @@ -2,4 +2,6 @@ package com.bff.wespot.entire.state sealed class EntireSideEffect { data object NavigateToAuth : EntireSideEffect() + data object CloseSignOutDialog : EntireSideEffect() + data object CloseRevokeDialog : EntireSideEffect() } diff --git a/feature/entire/src/main/java/com/bff/wespot/entire/viewmodel/EntireViewModel.kt b/feature/entire/src/main/java/com/bff/wespot/entire/viewmodel/EntireViewModel.kt index 4a8a2a13..0d6ac8aa 100644 --- a/feature/entire/src/main/java/com/bff/wespot/entire/viewmodel/EntireViewModel.kt +++ b/feature/entire/src/main/java/com/bff/wespot/entire/viewmodel/EntireViewModel.kt @@ -51,8 +51,8 @@ class EntireViewModel @Inject constructor( EntireAction.OnRevokeScreenEntered -> observeProfileDataFlow() EntireAction.OnBlockListScreenEntered -> getBlockedMessageList() EntireAction.OnRevokeConfirmed -> handleRevokeConfirmed() - EntireAction.OnRevokeButtonClicked -> revokeUser() - EntireAction.OnSignOutButtonClicked -> signOut() + EntireAction.OnRevokeButtonClicked -> handleRevoke() + EntireAction.OnSignOutButtonClicked -> handleSignOut() EntireAction.UnBlockMessage -> unblockMessage() EntireAction.OnInputRevokeReasonSelected -> handleInputRevokeReasonSelected() is EntireAction.OnUnBlockButtonClicked -> handleUnBlockButtonClicked(action.messageId) @@ -93,34 +93,52 @@ class EntireViewModel @Inject constructor( reduce { state.copy(webLinkMap = webLinkMap) } } - private fun revokeUser() = intent { + private fun handleRevoke() = intent { + postSideEffect(EntireSideEffect.CloseRevokeDialog) + reduce { state.copy(isLoading = true) } + val revokeReason = if (state.isInputRevokeReasonSelected) { state.revokeReasonList + state.inputRevokeReason } else { state.revokeReasonList } - viewModelScope.launch { - launch { - authRepository.revoke(revokeReason) - .onSuccess { - clearCachedData() - postSideEffect(EntireSideEffect.NavigateToAuth) - } - .onNetworkFailure { - postSideEffect(it.toSideEffect()) - } - .onFailure { - Timber.e(it) - } - } + viewModelScope.launch(coroutineDispatcher) { + authRepository.revoke(revokeReason) + .onSuccess { + KakaoLoginManager.revoke() + clearCachedData() + postSideEffect(EntireSideEffect.NavigateToAuth) + } + .onNetworkFailure { + postSideEffect(it.toSideEffect()) + } + .onFailure { + reduce { state.copy(isLoading = false) } + Timber.e(it) + } } } - private fun signOut() = intent { - clearCachedData() - KakaoLoginManager.logout() - postSideEffect(EntireSideEffect.NavigateToAuth) + private fun handleSignOut() = intent { + postSideEffect(EntireSideEffect.CloseSignOutDialog) + reduce { state.copy(isLoading = true) } + + viewModelScope.launch(coroutineDispatcher) { + authRepository.signOut() + .onSuccess { + KakaoLoginManager.logout() + clearCachedData() + postSideEffect(EntireSideEffect.NavigateToAuth) + } + .onNetworkFailure { + postSideEffect(it.toSideEffect()) + } + .onFailure { + reduce { state.copy(isLoading = false) } + Timber.e(it) + } + } } private fun clearCachedData() {