Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

#225 : 로그아웃 API 구현해요 #228

Merged
merged 9 commits into from
Jan 31, 2025
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ open class BaseViewModel : ViewModel() {
val networkState
get() = networkStateChecker.networkState

private val _sideEffect = Channel<SideEffect>()
private val _sideEffect = Channel<SideEffect>(Channel.BUFFERED)
val sideEffect = _sideEffect.receiveAsFlow()

protected suspend fun postSideEffect(sideEffect: SideEffect) = _sideEffect.send(sideEffect)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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에서 토큰 삭제됨")
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,5 @@ interface AuthDataSource {
suspend fun signIn(signIn: SignInDto): Result<Any>
suspend fun signUp(signUp: SignUpDto): Result<AuthTokenDto>
suspend fun revoke(revokeReasonList: RevokeReasonListDto): Result<Unit>
suspend fun signOut(): Result<Unit>
}
Original file line number Diff line number Diff line change
Expand Up @@ -60,4 +60,12 @@ class AuthDataSourceImpl @Inject constructor(
}
setBody(revokeReasonList)
}
}

override suspend fun signOut(): Result<Unit> =
httpClient.safeRequest {
url {
method = HttpMethod.Patch
path("api/v1/auth/logout")
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -61,4 +61,6 @@ class AuthRepositoryImpl @Inject constructor(
RevokeReasonListDto(revokeReasons = revokeReasonList)
)
}
}

override suspend fun signOut(): Result<Unit> = authDataSource.signOut()
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,5 @@ interface AuthRepository {
suspend fun signIn(signIn: SignIn): Result<Any>
suspend fun signUp(signUp: SignUp): Boolean
suspend fun revoke(revokeReasonList: List<String>): Result<Unit>
suspend fun signOut(): Result<Unit>
}
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -47,14 +50,20 @@ fun AccountSettingScreen(
var showDialog by remember { mutableStateOf(false) }

val action = viewModel::onAction
val state by viewModel.collectAsState()

handleSideEffect(viewModel.sideEffect)

viewModel.collectSideEffect {
when (it) {
is EntireSideEffect.NavigateToAuth -> {
val intent = activityNavigator.navigateToAuth(context)
context.startActivity(intent)
}
else -> {}
is EntireSideEffect.CloseSignOutDialog -> {
showDialog = false
}
is EntireSideEffect.CloseRevokeDialog -> { }
}
}

Expand Down Expand Up @@ -94,6 +103,10 @@ fun AccountSettingScreen(
onDismissRequest = { },
)
}

if (state.isLoading) {
LoadingAnimation()
}
}

@Composable
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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 -> { }
}
}

Expand Down Expand Up @@ -170,6 +177,10 @@ fun RevokeConfirmScreen(
onDismissRequest = { },
)
}

if (state.isLoading) {
LoadingAnimation()
}
}

@Composable
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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()
}
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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() {
Expand Down