diff --git a/core/network/src/main/java/com/easyhz/noffice/core/network/api/auth/AuthService.kt b/core/network/src/main/java/com/easyhz/noffice/core/network/api/auth/AuthService.kt index fbe75a38..4c4003ab 100644 --- a/core/network/src/main/java/com/easyhz/noffice/core/network/api/auth/AuthService.kt +++ b/core/network/src/main/java/com/easyhz/noffice/core/network/api/auth/AuthService.kt @@ -4,6 +4,7 @@ import com.easyhz.noffice.core.network.model.request.sign.LoginRequest import com.easyhz.noffice.core.network.model.response.auth.UserResponse import com.easyhz.noffice.core.network.util.NofficeResult import retrofit2.http.Body +import retrofit2.http.DELETE import retrofit2.http.Header import retrofit2.http.POST @@ -15,7 +16,7 @@ interface AuthService { @Body body: LoginRequest ): NofficeResult - /* 로그아웃 */ + /* 로그 아웃 */ @POST("/api/v1/member/logout") suspend fun logout( @Header("notification-token") notificationToken: String @@ -26,4 +27,8 @@ interface AuthService { suspend fun registerMessagingToken( @Body fcmToken: String ): NofficeResult + + /* 회원 탈퇴 */ + @DELETE("/api/v1/member/withdrawal") + suspend fun withdrawal(): NofficeResult } \ No newline at end of file diff --git a/data/auth/src/main/java/com/easyhz/noffice/data/auth/repository/auth/AuthRepository.kt b/data/auth/src/main/java/com/easyhz/noffice/data/auth/repository/auth/AuthRepository.kt index 5a4b6f07..165941dc 100644 --- a/data/auth/src/main/java/com/easyhz/noffice/data/auth/repository/auth/AuthRepository.kt +++ b/data/auth/src/main/java/com/easyhz/noffice/data/auth/repository/auth/AuthRepository.kt @@ -5,4 +5,5 @@ import android.content.Context interface AuthRepository { suspend fun login(context: Context, provider: String): Result suspend fun logout(context: Context): Result + suspend fun withdraw(context: Context): Result } \ No newline at end of file diff --git a/data/auth/src/main/java/com/easyhz/noffice/data/auth/repository/auth/AuthRepositoryIml.kt b/data/auth/src/main/java/com/easyhz/noffice/data/auth/repository/auth/AuthRepositoryIml.kt index 71217b81..d1d139ee 100644 --- a/data/auth/src/main/java/com/easyhz/noffice/data/auth/repository/auth/AuthRepositoryIml.kt +++ b/data/auth/src/main/java/com/easyhz/noffice/data/auth/repository/auth/AuthRepositoryIml.kt @@ -40,12 +40,15 @@ class AuthRepositoryIml @Inject constructor( override suspend fun logout(context: Context): Result = withContext(dispatcher) { runCatching { logoutFromServer() - val provider = getAuthProvider().getOrThrow() - authStrategyContext.setStrategy(provider) - val logoutJob = async { authStrategyContext.logout(context) } - val deleteJob = async { deleteLocalUserInfo() } + localLogout(context) + Unit + } + } - awaitAll(logoutJob, deleteJob) + override suspend fun withdraw(context: Context): Result = withContext(dispatcher) { + runCatching { + authService.withdrawal().toResult() + localLogout(context) Unit } } @@ -62,6 +65,20 @@ class AuthRepositoryIml @Inject constructor( awaitAll(authJob, userJob) } + /** + * 로컬에서 로그아웃을 처리합니다. + */ + private suspend fun localLogout(context: Context) = coroutineScope { + val provider = getAuthProvider().getOrThrow() + authStrategyContext.setStrategy(provider) + val logoutJob = async { authStrategyContext.logout(context) } + val deleteJob = async { deleteLocalUserInfo() } + awaitAll(logoutJob, deleteJob) + } + + /** + * 로컬에 저장된 유저 정보를 삭제합니다. + */ private suspend fun deleteLocalUserInfo() = coroutineScope { val authJob = async { authLocalDataSource.deleteToken() @@ -73,12 +90,18 @@ class AuthRepositoryIml @Inject constructor( awaitAll(authJob, userJob) } + /** + * 로컬에 저장된 인증 방식을 가져옵니다. + */ private suspend fun getAuthProvider(): Result { return authLocalDataSource.getAuthProvider() } + /** + * 서버에서 로그아웃을 처리합니다. + */ private suspend fun logoutFromServer() { val token = cloudMessagingRepository.getToken().getOrThrow() - authService.logout(token) + authService.logout(token).toResult() } } \ No newline at end of file diff --git a/domain/my-page/src/main/java/com/easyhz/noffice/domain/my_page/usecase/WithdrawUserCase.kt b/domain/my-page/src/main/java/com/easyhz/noffice/domain/my_page/usecase/WithdrawUserCase.kt new file mode 100644 index 00000000..35a6e044 --- /dev/null +++ b/domain/my-page/src/main/java/com/easyhz/noffice/domain/my_page/usecase/WithdrawUserCase.kt @@ -0,0 +1,14 @@ +package com.easyhz.noffice.domain.my_page.usecase + +import android.content.Context +import com.easyhz.noffice.core.common.base.BaseUseCase +import com.easyhz.noffice.data.auth.repository.auth.AuthRepository +import javax.inject.Inject + +class WithdrawUserCase @Inject constructor( + private val authRepository: AuthRepository +): BaseUseCase() { + override suspend fun invoke(param: Context): Result { + return authRepository.withdraw(param) + } +} \ No newline at end of file diff --git a/feature/my-page/src/main/java/com/easyhz/noffice/feature/my_page/contract/detail/withdrawal/WithdrawalIntent.kt b/feature/my-page/src/main/java/com/easyhz/noffice/feature/my_page/contract/detail/withdrawal/WithdrawalIntent.kt index c10a65a5..c7fe8e81 100644 --- a/feature/my-page/src/main/java/com/easyhz/noffice/feature/my_page/contract/detail/withdrawal/WithdrawalIntent.kt +++ b/feature/my-page/src/main/java/com/easyhz/noffice/feature/my_page/contract/detail/withdrawal/WithdrawalIntent.kt @@ -1,9 +1,10 @@ package com.easyhz.noffice.feature.my_page.contract.detail.withdrawal +import android.content.Context import com.easyhz.noffice.core.common.base.UiIntent sealed class WithdrawalIntent: UiIntent() { data object ClickBackButton: WithdrawalIntent() data object ClickConsentButton: WithdrawalIntent() - data object ClickWithdrawalButton: WithdrawalIntent() + data class ClickWithdrawalButton(val context: Context): WithdrawalIntent() } \ No newline at end of file diff --git a/feature/my-page/src/main/java/com/easyhz/noffice/feature/my_page/contract/detail/withdrawal/WithdrawalSideEffect.kt b/feature/my-page/src/main/java/com/easyhz/noffice/feature/my_page/contract/detail/withdrawal/WithdrawalSideEffect.kt index cd1be31d..6799bc01 100644 --- a/feature/my-page/src/main/java/com/easyhz/noffice/feature/my_page/contract/detail/withdrawal/WithdrawalSideEffect.kt +++ b/feature/my-page/src/main/java/com/easyhz/noffice/feature/my_page/contract/detail/withdrawal/WithdrawalSideEffect.kt @@ -4,5 +4,5 @@ import com.easyhz.noffice.core.common.base.UiSideEffect sealed class WithdrawalSideEffect: UiSideEffect() { data object NavigateToUp: WithdrawalSideEffect() - data object NavigateToLogIn: WithdrawalSideEffect() + data object NavigateToLogin: WithdrawalSideEffect() } \ No newline at end of file diff --git a/feature/my-page/src/main/java/com/easyhz/noffice/feature/my_page/contract/detail/withdrawal/WithdrawalState.kt b/feature/my-page/src/main/java/com/easyhz/noffice/feature/my_page/contract/detail/withdrawal/WithdrawalState.kt index 3fa6e0ee..60d6f261 100644 --- a/feature/my-page/src/main/java/com/easyhz/noffice/feature/my_page/contract/detail/withdrawal/WithdrawalState.kt +++ b/feature/my-page/src/main/java/com/easyhz/noffice/feature/my_page/contract/detail/withdrawal/WithdrawalState.kt @@ -3,11 +3,13 @@ package com.easyhz.noffice.feature.my_page.contract.detail.withdrawal import com.easyhz.noffice.core.common.base.UiState data class WithdrawalState( - val isChecked: Boolean + val isChecked: Boolean, + val isLoading: Boolean, ): UiState() { companion object { fun init() = WithdrawalState( - isChecked = false + isChecked = false, + isLoading = false ) } } diff --git a/feature/my-page/src/main/java/com/easyhz/noffice/feature/my_page/screen/MyPageScreen.kt b/feature/my-page/src/main/java/com/easyhz/noffice/feature/my_page/screen/MyPageScreen.kt index 636f6d6d..728b49ee 100644 --- a/feature/my-page/src/main/java/com/easyhz/noffice/feature/my_page/screen/MyPageScreen.kt +++ b/feature/my-page/src/main/java/com/easyhz/noffice/feature/my_page/screen/MyPageScreen.kt @@ -99,6 +99,8 @@ fun MyPageScreen( ) { NofficeBasicScaffold( containerColor = Grey50, + statusBarColor = Grey50, + navigationBarColor = Grey50, topBar = { DetailTopBar( modifier = Modifier.background(Grey50), diff --git a/feature/my-page/src/main/java/com/easyhz/noffice/feature/my_page/screen/detail/withdrawal/WithdrawalScreen.kt b/feature/my-page/src/main/java/com/easyhz/noffice/feature/my_page/screen/detail/withdrawal/WithdrawalScreen.kt index 0eb50f85..20661d9d 100644 --- a/feature/my-page/src/main/java/com/easyhz/noffice/feature/my_page/screen/detail/withdrawal/WithdrawalScreen.kt +++ b/feature/my-page/src/main/java/com/easyhz/noffice/feature/my_page/screen/detail/withdrawal/WithdrawalScreen.kt @@ -19,6 +19,7 @@ import androidx.compose.runtime.getValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip +import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp @@ -29,6 +30,7 @@ import com.easyhz.noffice.core.common.util.collectInSideEffectWithLifecycle import com.easyhz.noffice.core.design_system.R import com.easyhz.noffice.core.design_system.component.button.CircleCheck import com.easyhz.noffice.core.design_system.component.button.MediumButton +import com.easyhz.noffice.core.design_system.component.loading.LoadingScreenProvider import com.easyhz.noffice.core.design_system.component.scaffold.NofficeBasicScaffold import com.easyhz.noffice.core.design_system.component.topBar.DetailTopBar import com.easyhz.noffice.core.design_system.extension.screenHorizonPadding @@ -54,112 +56,119 @@ fun WithdrawalScreen( navigateToLogin: () -> Unit ) { val uiState by viewModel.uiState.collectAsStateWithLifecycle() + val context = LocalContext.current - NofficeBasicScaffold( - containerColor = Grey50, + LoadingScreenProvider( + isLoading = uiState.isLoading, statusBarColor = Grey50, - navigationBarColor = Grey50, - topBar = { - DetailTopBar( - modifier = Modifier.background(Grey50), - leadingItem = DetailTopBarMenu( - content = { - Icon( - modifier = Modifier.size(24.dp), - painter = painterResource(id = R.drawable.ic_chevron_left), - contentDescription = "left", - tint = Grey400 - ) - }, - onClick = { viewModel.postIntent(WithdrawalIntent.ClickBackButton) } - ), - ) - }, - bottomBar = { - MediumButton( - modifier = Modifier - .screenHorizonPadding() - .fillMaxWidth() - .padding(bottom = 16.dp), - text = stringResource(id = R.string.my_page_menu_withdrawal_button), - enabled = uiState.isChecked - ) { viewModel.postIntent(WithdrawalIntent.ClickWithdrawalButton) } - } - ) { paddingValues -> - Column( - modifier = modifier - .padding(paddingValues) - .padding(horizontal = 20.dp, vertical = 16.dp), - verticalArrangement = Arrangement.spacedBy(16.dp) - ) { - Column( - modifier = Modifier.padding(horizontal = 16.dp), - verticalArrangement = Arrangement.spacedBy(4.dp) - ) { - Text(text = stringResource(id = R.string.my_page_menu_withdrawal), style = SemiBold16, color = Green500) - Text( - text = stringResource(id = R.string.my_page_menu_withdrawal_title), - style = Title3, - lineHeight = (22 * 1.4).sp, - color = Grey800 - ) - } - Spacer( - modifier = Modifier - .weight(0.1f) - ) - Box(modifier = Modifier.fillMaxWidth(), contentAlignment = Alignment.Center) { - Image( - painter = painterResource(id = R.drawable.ic_withdrawal), - contentDescription = "withdrawal" + navigationBarColor = Grey50 + ) { + NofficeBasicScaffold( + containerColor = Grey50, + statusBarColor = Grey50, + navigationBarColor = Grey50, + topBar = { + DetailTopBar( + modifier = Modifier.background(Grey50), + leadingItem = DetailTopBarMenu( + content = { + Icon( + modifier = Modifier.size(24.dp), + painter = painterResource(id = R.drawable.ic_chevron_left), + contentDescription = "left", + tint = Grey400 + ) + }, + onClick = { viewModel.postIntent(WithdrawalIntent.ClickBackButton) } + ), ) + }, + bottomBar = { + MediumButton( + modifier = Modifier + .screenHorizonPadding() + .fillMaxWidth() + .padding(bottom = 16.dp), + text = stringResource(id = R.string.my_page_menu_withdrawal_button), + enabled = uiState.isChecked + ) { viewModel.postIntent(WithdrawalIntent.ClickWithdrawalButton(context)) } } - + ) { paddingValues -> Column( - modifier = Modifier - .fillMaxWidth() - .clip(RoundedCornerShape(10.dp)) - .background(White) - .padding(16.dp), + modifier = modifier + .padding(paddingValues) + .padding(horizontal = 20.dp, vertical = 16.dp), verticalArrangement = Arrangement.spacedBy(16.dp) ) { - Text(text = stringResource(id = R.string.my_page_menu_withdrawal_type_title), style = SemiBold16) Column( + modifier = Modifier.padding(horizontal = 16.dp), verticalArrangement = Arrangement.spacedBy(4.dp) ) { - WithdrawalType.entries.forEach { - WithdrawalTypeItem(type = it) + Text(text = stringResource(id = R.string.my_page_menu_withdrawal), style = SemiBold16, color = Green500) + Text( + text = stringResource(id = R.string.my_page_menu_withdrawal_title), + style = Title3, + lineHeight = (22 * 1.4).sp, + color = Grey800 + ) + } + Spacer( + modifier = Modifier + .weight(0.1f) + ) + Box(modifier = Modifier.fillMaxWidth(), contentAlignment = Alignment.Center) { + Image( + painter = painterResource(id = R.drawable.ic_withdrawal), + contentDescription = "withdrawal" + ) + } + + Column( + modifier = Modifier + .fillMaxWidth() + .clip(RoundedCornerShape(10.dp)) + .background(White) + .padding(16.dp), + verticalArrangement = Arrangement.spacedBy(16.dp) + ) { + Text(text = stringResource(id = R.string.my_page_menu_withdrawal_type_title), style = SemiBold16) + Column( + verticalArrangement = Arrangement.spacedBy(4.dp) + ) { + WithdrawalType.entries.forEach { + WithdrawalTypeItem(type = it) + } } } - } - Row( - modifier = Modifier - .fillMaxWidth() - .clip(RoundedCornerShape(10.dp)) - .background(White) - .clickable { viewModel.postIntent(WithdrawalIntent.ClickConsentButton) } - .padding(16.dp), - horizontalArrangement = Arrangement.spacedBy(8.dp), - verticalAlignment = Alignment.CenterVertically - ) { - CircleCheck( - modifier = Modifier.size(18.dp), - isChecked = uiState.isChecked, - enabled = false + Row( + modifier = Modifier + .fillMaxWidth() + .clip(RoundedCornerShape(10.dp)) + .background(White) + .clickable { viewModel.postIntent(WithdrawalIntent.ClickConsentButton) } + .padding(16.dp), + horizontalArrangement = Arrangement.spacedBy(8.dp), + verticalAlignment = Alignment.CenterVertically + ) { + CircleCheck( + modifier = Modifier.size(18.dp), + isChecked = uiState.isChecked, + enabled = false + ) + Text(text = stringResource(id = R.string.my_page_menu_withdrawal_consent), style = SubBody14) + } + Spacer( + modifier = Modifier + .weight(0.3f) ) - Text(text = stringResource(id = R.string.my_page_menu_withdrawal_consent), style = SubBody14) } - Spacer( - modifier = Modifier - .weight(0.3f) - ) } } viewModel.sideEffect.collectInSideEffectWithLifecycle { sideEffect -> when(sideEffect) { is WithdrawalSideEffect.NavigateToUp -> { navigateToUp() } - is WithdrawalSideEffect.NavigateToLogIn -> { navigateToLogin() } + is WithdrawalSideEffect.NavigateToLogin -> { navigateToLogin() } } } } \ No newline at end of file diff --git a/feature/my-page/src/main/java/com/easyhz/noffice/feature/my_page/screen/detail/withdrawal/WithdrawalViewModel.kt b/feature/my-page/src/main/java/com/easyhz/noffice/feature/my_page/screen/detail/withdrawal/WithdrawalViewModel.kt index d99245d1..432dc4de 100644 --- a/feature/my-page/src/main/java/com/easyhz/noffice/feature/my_page/screen/detail/withdrawal/WithdrawalViewModel.kt +++ b/feature/my-page/src/main/java/com/easyhz/noffice/feature/my_page/screen/detail/withdrawal/WithdrawalViewModel.kt @@ -1,18 +1,29 @@ package com.easyhz.noffice.feature.my_page.screen.detail.withdrawal +import android.content.Context +import androidx.lifecycle.viewModelScope import com.easyhz.noffice.core.common.base.BaseViewModel +import com.easyhz.noffice.core.common.error.NofficeError +import com.easyhz.noffice.core.common.util.errorLogging +import com.easyhz.noffice.domain.my_page.usecase.WithdrawUserCase import com.easyhz.noffice.feature.my_page.contract.detail.withdrawal.WithdrawalIntent import com.easyhz.noffice.feature.my_page.contract.detail.withdrawal.WithdrawalSideEffect import com.easyhz.noffice.feature.my_page.contract.detail.withdrawal.WithdrawalState +import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.coroutines.launch +import javax.inject.Inject -class WithdrawalViewModel: BaseViewModel( +@HiltViewModel +class WithdrawalViewModel @Inject constructor( + private val withdrawUserCase: WithdrawUserCase +): BaseViewModel( initialState = WithdrawalState.init() ) { override fun handleIntent(intent: WithdrawalIntent) { when(intent) { is WithdrawalIntent.ClickBackButton -> { onClickBackButton() } is WithdrawalIntent.ClickConsentButton -> { onClickConsentButton() } - is WithdrawalIntent.ClickWithdrawalButton -> { onClickWithdrawalButton() } + is WithdrawalIntent.ClickWithdrawalButton -> { onClickWithdrawalButton(intent.context) } } } @@ -20,9 +31,23 @@ class WithdrawalViewModel: BaseViewModel