diff --git a/core-ui/src/main/java/com/going/ui/extension/EnumUiState.kt b/core-ui/src/main/java/com/going/ui/extension/EnumUiState.kt new file mode 100644 index 00000000..d9700d4c --- /dev/null +++ b/core-ui/src/main/java/com/going/ui/extension/EnumUiState.kt @@ -0,0 +1,5 @@ +package com.going.ui.extension + +enum class EnumUiState { + LOADING, SUCCESS, FAILURE +} diff --git a/data/src/main/java/com/going/data/datasource/AuthDataSource.kt b/data/src/main/java/com/going/data/datasource/AuthDataSource.kt index e0da7f45..dd2dee76 100644 --- a/data/src/main/java/com/going/data/datasource/AuthDataSource.kt +++ b/data/src/main/java/com/going/data/datasource/AuthDataSource.kt @@ -4,12 +4,13 @@ import com.going.data.dto.BaseResponse import com.going.data.dto.request.SignInRequestDto import com.going.data.dto.request.SignUpRequestDto import com.going.data.dto.response.AuthResponseDto +import com.going.data.dto.response.SignInResponseDto interface AuthDataSource { suspend fun postSignIn( Authorization: String, platform: SignInRequestDto, - ): BaseResponse + ): BaseResponse suspend fun postSignUp( Authorization: String, diff --git a/data/src/main/java/com/going/data/datasource/SettingDataSource.kt b/data/src/main/java/com/going/data/datasource/SettingDataSource.kt index 0b7cab84..3a14bbe4 100644 --- a/data/src/main/java/com/going/data/datasource/SettingDataSource.kt +++ b/data/src/main/java/com/going/data/datasource/SettingDataSource.kt @@ -1,7 +1,10 @@ package com.going.data.datasource +import com.going.data.dto.NonDataBaseResponse import com.going.data.dto.response.SignOutResponseDto interface SettingDataSource { suspend fun patchSignOut(): SignOutResponseDto + + suspend fun deleteWithDraw(): NonDataBaseResponse } diff --git a/data/src/main/java/com/going/data/datasource/TodoDataSource.kt b/data/src/main/java/com/going/data/datasource/TodoDataSource.kt index f83c0642..06f70e39 100644 --- a/data/src/main/java/com/going/data/datasource/TodoDataSource.kt +++ b/data/src/main/java/com/going/data/datasource/TodoDataSource.kt @@ -8,7 +8,6 @@ interface TodoDataSource { suspend fun getTodoListData( tripId: Long, category: String, - progress: String + progress: String, ): BaseResponse> - -} \ No newline at end of file +} diff --git a/data/src/main/java/com/going/data/datasourceImpl/AuthDataSourceImpl.kt b/data/src/main/java/com/going/data/datasourceImpl/AuthDataSourceImpl.kt index 2c561ab7..1965f787 100644 --- a/data/src/main/java/com/going/data/datasourceImpl/AuthDataSourceImpl.kt +++ b/data/src/main/java/com/going/data/datasourceImpl/AuthDataSourceImpl.kt @@ -5,6 +5,7 @@ import com.going.data.dto.BaseResponse import com.going.data.dto.request.SignInRequestDto import com.going.data.dto.request.SignUpRequestDto import com.going.data.dto.response.AuthResponseDto +import com.going.data.dto.response.SignInResponseDto import com.going.data.service.AuthService import javax.inject.Inject @@ -14,7 +15,7 @@ class AuthDataSourceImpl @Inject constructor( override suspend fun postSignIn( Authorization: String, platform: SignInRequestDto, - ): BaseResponse = + ): BaseResponse = authService.postSignin(Authorization, platform) override suspend fun postSignUp( diff --git a/data/src/main/java/com/going/data/datasourceImpl/SettingDataSourceImpl.kt b/data/src/main/java/com/going/data/datasourceImpl/SettingDataSourceImpl.kt index 7e3444ca..d5f0beb0 100644 --- a/data/src/main/java/com/going/data/datasourceImpl/SettingDataSourceImpl.kt +++ b/data/src/main/java/com/going/data/datasourceImpl/SettingDataSourceImpl.kt @@ -1,6 +1,7 @@ package com.going.data.datasourceImpl import com.going.data.datasource.SettingDataSource +import com.going.data.dto.NonDataBaseResponse import com.going.data.dto.response.SignOutResponseDto import com.going.data.service.SettingService import javax.inject.Inject @@ -9,4 +10,5 @@ class SettingDataSourceImpl @Inject constructor( private val settingService: SettingService, ) : SettingDataSource { override suspend fun patchSignOut(): SignOutResponseDto = settingService.patchSignOut() + override suspend fun deleteWithDraw(): NonDataBaseResponse = settingService.deleteWithDraw() } diff --git a/data/src/main/java/com/going/data/dto/BaseResponse.kt b/data/src/main/java/com/going/data/dto/BaseResponse.kt index bf7834d1..f92cd87e 100644 --- a/data/src/main/java/com/going/data/dto/BaseResponse.kt +++ b/data/src/main/java/com/going/data/dto/BaseResponse.kt @@ -7,6 +7,8 @@ import kotlinx.serialization.Serializable data class BaseResponse( @SerialName("status") val status: Int, + @SerialName("code") + val code: String, @SerialName("message") val message: String, @SerialName("data") diff --git a/data/src/main/java/com/going/data/dto/NonDataBaseResponse.kt b/data/src/main/java/com/going/data/dto/NonDataBaseResponse.kt new file mode 100644 index 00000000..3138cd55 --- /dev/null +++ b/data/src/main/java/com/going/data/dto/NonDataBaseResponse.kt @@ -0,0 +1,14 @@ +package com.going.data.dto + +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable + +@Serializable +data class NonDataBaseResponse( + @SerialName("status") + val status: Int, + @SerialName("code") + val code: String, + @SerialName("message") + val message: String, +) diff --git a/data/src/main/java/com/going/data/dto/response/SignInResponseDto.kt b/data/src/main/java/com/going/data/dto/response/SignInResponseDto.kt new file mode 100644 index 00000000..5909913b --- /dev/null +++ b/data/src/main/java/com/going/data/dto/response/SignInResponseDto.kt @@ -0,0 +1,18 @@ +package com.going.data.dto.response + +import com.going.domain.entity.response.SignInModel +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable + +@Serializable +data class SignInResponseDto( + @SerialName("accessToken") + val accessToken: String, + @SerialName("refreshToken") + val refreshToken: String, + @SerialName("isResult") + val isResult: Boolean, +) { + fun toSignInModel() = + SignInModel(accessToken, refreshToken, isResult) +} diff --git a/data/src/main/java/com/going/data/repositoryImpl/AuthRepositoryImpl.kt b/data/src/main/java/com/going/data/repositoryImpl/AuthRepositoryImpl.kt index 4aeb29db..c0cf36f6 100644 --- a/data/src/main/java/com/going/data/repositoryImpl/AuthRepositoryImpl.kt +++ b/data/src/main/java/com/going/data/repositoryImpl/AuthRepositoryImpl.kt @@ -6,6 +6,7 @@ import com.going.data.dto.request.toSignUpRequestDto import com.going.domain.entity.request.RequestSignInModel import com.going.domain.entity.request.RequestSignUpModel import com.going.domain.entity.response.AuthTokenModel +import com.going.domain.entity.response.SignInModel import com.going.domain.repository.AuthRepository import javax.inject.Inject @@ -15,12 +16,12 @@ class AuthRepositoryImpl @Inject constructor( override suspend fun postSignIn( Authorization: String, requestSignIpModel: RequestSignInModel, - ): Result = + ): Result = runCatching { authDataSource.postSignIn( Authorization, requestSignIpModel.toSignInRequestDto(), - ).data.toAuthTokenModel() + ).data.toSignInModel() } override suspend fun postSignUp( diff --git a/data/src/main/java/com/going/data/repositoryImpl/SettingRepositoryImpl.kt b/data/src/main/java/com/going/data/repositoryImpl/SettingRepositoryImpl.kt index e1045509..ecc36a7b 100644 --- a/data/src/main/java/com/going/data/repositoryImpl/SettingRepositoryImpl.kt +++ b/data/src/main/java/com/going/data/repositoryImpl/SettingRepositoryImpl.kt @@ -11,4 +11,8 @@ class SettingRepositoryImpl @Inject constructor( override suspend fun patchSignOut(): Result = runCatching { settingDataSource.patchSignOut().toSignOutModel() } + + override suspend fun deleteWithDraw(): Result = runCatching { + settingDataSource.deleteWithDraw() + } } diff --git a/data/src/main/java/com/going/data/service/AuthService.kt b/data/src/main/java/com/going/data/service/AuthService.kt index 0102622a..00648096 100644 --- a/data/src/main/java/com/going/data/service/AuthService.kt +++ b/data/src/main/java/com/going/data/service/AuthService.kt @@ -4,6 +4,7 @@ import com.going.data.dto.BaseResponse import com.going.data.dto.request.SignInRequestDto import com.going.data.dto.request.SignUpRequestDto import com.going.data.dto.response.AuthResponseDto +import com.going.data.dto.response.SignInResponseDto import retrofit2.http.Body import retrofit2.http.Header import retrofit2.http.POST @@ -13,7 +14,7 @@ interface AuthService { suspend fun postSignin( @Header("Authorization") Authorization: String, @Body body: SignInRequestDto, - ): BaseResponse + ): BaseResponse @POST("api/users/signup") suspend fun postSignUp( diff --git a/data/src/main/java/com/going/data/service/SettingService.kt b/data/src/main/java/com/going/data/service/SettingService.kt index 6473cacd..27f7953d 100644 --- a/data/src/main/java/com/going/data/service/SettingService.kt +++ b/data/src/main/java/com/going/data/service/SettingService.kt @@ -1,9 +1,14 @@ package com.going.data.service +import com.going.data.dto.NonDataBaseResponse import com.going.data.dto.response.SignOutResponseDto +import retrofit2.http.DELETE import retrofit2.http.PATCH interface SettingService { @PATCH("api/users/signout") suspend fun patchSignOut(): SignOutResponseDto + + @DELETE("api/users/withdraw") + suspend fun deleteWithDraw(): NonDataBaseResponse } diff --git a/domain/src/main/kotlin/com/going/domain/entity/response/SignInModel.kt b/domain/src/main/kotlin/com/going/domain/entity/response/SignInModel.kt new file mode 100644 index 00000000..8953bf94 --- /dev/null +++ b/domain/src/main/kotlin/com/going/domain/entity/response/SignInModel.kt @@ -0,0 +1,7 @@ +package com.going.domain.entity.response + +data class SignInModel( + val accessToken: String, + val refreshToken: String, + val isResult: Boolean, +) diff --git a/domain/src/main/kotlin/com/going/domain/repository/AuthRepository.kt b/domain/src/main/kotlin/com/going/domain/repository/AuthRepository.kt index 1f4ebdd7..4caa0966 100644 --- a/domain/src/main/kotlin/com/going/domain/repository/AuthRepository.kt +++ b/domain/src/main/kotlin/com/going/domain/repository/AuthRepository.kt @@ -3,12 +3,13 @@ package com.going.domain.repository import com.going.domain.entity.request.RequestSignInModel import com.going.domain.entity.request.RequestSignUpModel import com.going.domain.entity.response.AuthTokenModel +import com.going.domain.entity.response.SignInModel interface AuthRepository { suspend fun postSignIn( Authorization: String, data: RequestSignInModel, - ): Result + ): Result suspend fun postSignUp( Authorization: String, diff --git a/domain/src/main/kotlin/com/going/domain/repository/SettingRepository.kt b/domain/src/main/kotlin/com/going/domain/repository/SettingRepository.kt index 456a23d0..6220d7e9 100644 --- a/domain/src/main/kotlin/com/going/domain/repository/SettingRepository.kt +++ b/domain/src/main/kotlin/com/going/domain/repository/SettingRepository.kt @@ -4,4 +4,6 @@ import com.going.domain.entity.response.SignOutModel interface SettingRepository { suspend fun patchSignOut(): Result + + suspend fun deleteWithDraw(): Result } diff --git a/presentation/src/main/java/com/going/presentation/onboarding/signin/SignInActivity.kt b/presentation/src/main/java/com/going/presentation/onboarding/signin/SignInActivity.kt index 1aa7b4be..48b75689 100644 --- a/presentation/src/main/java/com/going/presentation/onboarding/signin/SignInActivity.kt +++ b/presentation/src/main/java/com/going/presentation/onboarding/signin/SignInActivity.kt @@ -9,6 +9,7 @@ import androidx.lifecycle.lifecycleScope import com.going.presentation.R import com.going.presentation.databinding.ActivitySigninBinding import com.going.presentation.onboarding.signup.OnboardingProfileSettingActivity +import com.going.presentation.setting.SettingActivity import com.going.presentation.tendencytest.TendencyTestActivity import com.going.presentation.tendencytest.TendencyTestSplashActivity import com.going.ui.base.BaseActivity @@ -69,7 +70,7 @@ class SignInActivity : BaseActivity(R.layout.activity_sig private fun navigateToMainScreen() { // 추후 대시보드 연결시 연결 예정 - Intent(this, OnboardingProfileSettingActivity::class.java).apply { + Intent(this, SettingActivity::class.java).apply { addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP) startActivity(this) } diff --git a/presentation/src/main/java/com/going/presentation/onboarding/signin/SignInViewModel.kt b/presentation/src/main/java/com/going/presentation/onboarding/signin/SignInViewModel.kt index d4b80f38..88a20b99 100644 --- a/presentation/src/main/java/com/going/presentation/onboarding/signin/SignInViewModel.kt +++ b/presentation/src/main/java/com/going/presentation/onboarding/signin/SignInViewModel.kt @@ -74,13 +74,16 @@ class SignInViewModel @Inject constructor( authRepository.postSignIn(accessToken, RequestSignInModel(platform)).onSuccess { tokenRepository.setTokens(it.accessToken, it.refreshToken) - _postChangeTokenState.value = SignInState.SUCCESS + if (it.isResult) { + _postChangeTokenState.value = SignInState.SUCCESS + } else { + _postChangeTokenState.value = SignInState.TENDENCY + } }.onFailure { val errorCode = toErrorCode(it) _postChangeTokenState.value = when (errorCode) { SIGN_UP -> SignInState.SIGN_UP - TENDENCY -> SignInState.TENDENCY else -> SignInState.FAIL } } @@ -90,6 +93,5 @@ class SignInViewModel @Inject constructor( companion object { const val KAKAO = "kakao" const val SIGN_UP = "e4041" - const val TENDENCY = "e4045" } } diff --git a/presentation/src/main/java/com/going/presentation/setting/SettingActivity.kt b/presentation/src/main/java/com/going/presentation/setting/SettingActivity.kt index 2e4ca739..8bd913d7 100644 --- a/presentation/src/main/java/com/going/presentation/setting/SettingActivity.kt +++ b/presentation/src/main/java/com/going/presentation/setting/SettingActivity.kt @@ -5,7 +5,9 @@ import com.going.presentation.R import com.going.presentation.databinding.ActivitySettingBinding import com.going.ui.base.BaseActivity import com.going.ui.extension.setOnSingleClickListener +import dagger.hilt.android.AndroidEntryPoint +@AndroidEntryPoint class SettingActivity : BaseActivity(R.layout.activity_setting) { private var quitDialog: SettingQuitDialogFragment? = null @@ -21,30 +23,25 @@ class SettingActivity : BaseActivity(R.layout.activity_s initLogoutClickListener() initQuitClickListener() setVersionCode() - } private fun initProfileClickListener() { binding.layoutSettingProfile.setOnSingleClickListener { - } } private fun initInquireClickListener() { binding.layoutSettingInquire.setOnSingleClickListener { - } } private fun initPolicyClickListener() { binding.layoutSettingPolicy.setOnSingleClickListener { - } } private fun initAboutDooripClickListener() { binding.layoutSettingAboutDoorip.setOnSingleClickListener { - } } @@ -83,5 +80,4 @@ class SettingActivity : BaseActivity(R.layout.activity_s companion object { private const val VERSION_CODE = "v1.0" } - -} \ No newline at end of file +} diff --git a/presentation/src/main/java/com/going/presentation/setting/SettingLogoutDialogFragment.kt b/presentation/src/main/java/com/going/presentation/setting/SettingLogoutDialogFragment.kt index 7154636c..fc6fb3ec 100644 --- a/presentation/src/main/java/com/going/presentation/setting/SettingLogoutDialogFragment.kt +++ b/presentation/src/main/java/com/going/presentation/setting/SettingLogoutDialogFragment.kt @@ -1,22 +1,35 @@ package com.going.presentation.setting +import android.content.Context +import android.content.Intent import android.os.Bundle import android.view.View import android.view.WindowManager +import androidx.fragment.app.activityViewModels +import androidx.lifecycle.flowWithLifecycle +import androidx.lifecycle.lifecycleScope import com.going.presentation.R import com.going.presentation.databinding.FragmentSettingLogoutDialogBinding import com.going.ui.base.BaseDialog +import com.going.ui.extension.EnumUiState import com.going.ui.extension.setOnSingleClickListener +import com.going.ui.extension.toast +import dagger.hilt.android.AndroidEntryPoint +import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.onEach -class SettingLogoutDialogFragment : +@AndroidEntryPoint +class SettingLogoutDialogFragment() : BaseDialog(R.layout.fragment_setting_logout_dialog) { + private val viewModel by activityViewModels() + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) initNegativeClickListener() initPositiveClickListener() - + observeUserSignOutState() } override fun onStart() { @@ -31,14 +44,31 @@ class SettingLogoutDialogFragment : private fun initPositiveClickListener() { binding.tvDialogPositive.setOnSingleClickListener { - // 로그아웃 버튼 눌렀을 때의 로직 + viewModel.signOutKakao() } } + private fun observeUserSignOutState() { + viewModel.userSignOutState.flowWithLifecycle(lifecycle).onEach { state -> + when (state) { + EnumUiState.SUCCESS -> restartApp(requireContext()) + EnumUiState.FAILURE -> toast(getString(R.string.server_error)) + EnumUiState.LOADING -> {} + } + }.launchIn(lifecycleScope) + } + private fun initNegativeClickListener() { binding.btnDialogNegative.setOnSingleClickListener { dismiss() } } + private fun restartApp(context: Context) { + val packageManager = context.packageManager + val packageName = context.packageName + val componentName = packageManager.getLaunchIntentForPackage(packageName)?.component + context.startActivity(Intent.makeRestartActivityTask(componentName)) + Runtime.getRuntime().exit(0) + } } diff --git a/presentation/src/main/java/com/going/presentation/setting/SettingQuitDialogFragment.kt b/presentation/src/main/java/com/going/presentation/setting/SettingQuitDialogFragment.kt index c89b983a..b37b4c40 100644 --- a/presentation/src/main/java/com/going/presentation/setting/SettingQuitDialogFragment.kt +++ b/presentation/src/main/java/com/going/presentation/setting/SettingQuitDialogFragment.kt @@ -1,22 +1,35 @@ package com.going.presentation.setting +import android.content.Context +import android.content.Intent import android.os.Bundle import android.view.View import android.view.WindowManager +import androidx.fragment.app.activityViewModels +import androidx.lifecycle.flowWithLifecycle +import androidx.lifecycle.lifecycleScope import com.going.presentation.R import com.going.presentation.databinding.FragmentSettingQuitDialogBinding import com.going.ui.base.BaseDialog +import com.going.ui.extension.EnumUiState import com.going.ui.extension.setOnSingleClickListener +import com.going.ui.extension.toast +import dagger.hilt.android.AndroidEntryPoint +import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.onEach +@AndroidEntryPoint class SettingQuitDialogFragment : BaseDialog(R.layout.fragment_setting_quit_dialog) { + private val viewModel by activityViewModels() + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) initNegativeClickListener() initPositiveClickListener() - + observeUserWithDrawState() } override fun onStart() { @@ -36,9 +49,26 @@ class SettingQuitDialogFragment : } private fun initNegativeClickListener() { - // 탈퇴하기 버튼을 눌렀을 때의 로직 + binding.btnDialogNegative.setOnSingleClickListener { + viewModel.startWithDrawKakao() + } } -} - + private fun observeUserWithDrawState() { + viewModel.userWithDrawState.flowWithLifecycle(lifecycle).onEach { state -> + when (state) { + EnumUiState.SUCCESS -> restartApp(requireContext()) + EnumUiState.FAILURE -> toast(getString(R.string.server_error)) + EnumUiState.LOADING -> {} + } + }.launchIn(lifecycleScope) + } + private fun restartApp(context: Context) { + val packageManager = context.packageManager + val packageName = context.packageName + val componentName = packageManager.getLaunchIntentForPackage(packageName)?.component + context.startActivity(Intent.makeRestartActivityTask(componentName)) + Runtime.getRuntime().exit(0) + } +} diff --git a/presentation/src/main/java/com/going/presentation/setting/SettingViewModel.kt b/presentation/src/main/java/com/going/presentation/setting/SettingViewModel.kt new file mode 100644 index 00000000..d20da8dd --- /dev/null +++ b/presentation/src/main/java/com/going/presentation/setting/SettingViewModel.kt @@ -0,0 +1,73 @@ +package com.going.presentation.setting + +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import com.going.domain.repository.SettingRepository +import com.going.domain.repository.TokenRepository +import com.going.ui.extension.EnumUiState +import com.kakao.sdk.user.UserApiClient +import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.launch +import javax.inject.Inject + +@HiltViewModel +class SettingViewModel @Inject constructor( + private val settingRepository: SettingRepository, + private val tokenRepository: TokenRepository, +) : ViewModel() { + private val _userSignOutState = MutableStateFlow(EnumUiState.LOADING) + val userSignOutState: StateFlow = _userSignOutState + + private val _userWithDrawState = MutableStateFlow(EnumUiState.LOADING) + val userWithDrawState: StateFlow = _userWithDrawState + + fun signOutKakao() { + UserApiClient.instance.logout { error -> + _userSignOutState.value = EnumUiState.LOADING + + if (error == null) { + signOutJwt() + } else { + _userSignOutState.value = EnumUiState.FAILURE + } + } + } + + private fun signOutJwt() { + viewModelScope.launch { + settingRepository.patchSignOut().onSuccess { + tokenRepository.clearTokens() + + _userSignOutState.value = EnumUiState.SUCCESS + }.onFailure { + _userSignOutState.value = EnumUiState.FAILURE + } + } + } + + fun startWithDrawKakao() { + UserApiClient.instance.unlink { error -> + _userWithDrawState.value = EnumUiState.LOADING + + if (error == null) { + startWithDrawJwt() + } else { + _userWithDrawState.value = EnumUiState.FAILURE + } + } + } + + private fun startWithDrawJwt() { + viewModelScope.launch { + settingRepository.deleteWithDraw().onSuccess { + tokenRepository.clearTokens() + + _userWithDrawState.value = EnumUiState.SUCCESS + }.onFailure { + _userWithDrawState.value = EnumUiState.FAILURE + } + } + } +}