diff --git a/core/data/src/main/java/com/android/mediproject/core/data/session/AccountSessionRepository.kt b/core/data/src/main/java/com/android/mediproject/core/data/session/AccountSessionRepository.kt index 0c5e04b5..0895663e 100644 --- a/core/data/src/main/java/com/android/mediproject/core/data/session/AccountSessionRepository.kt +++ b/core/data/src/main/java/com/android/mediproject/core/data/session/AccountSessionRepository.kt @@ -8,5 +8,5 @@ interface AccountSessionRepository { val session: CognitoUserSession? val signedIn: Boolean suspend fun updateSession(session: CognitoUserSession?) - suspend fun updateAccount(email: String, nickName: String) + suspend fun updateAccount(email: String, nickName: String = "") } diff --git a/core/data/src/main/java/com/android/mediproject/core/data/sign/SignRepositoryImpl.kt b/core/data/src/main/java/com/android/mediproject/core/data/sign/SignRepositoryImpl.kt index eeaa5524..5f96436a 100644 --- a/core/data/src/main/java/com/android/mediproject/core/data/sign/SignRepositoryImpl.kt +++ b/core/data/src/main/java/com/android/mediproject/core/data/sign/SignRepositoryImpl.kt @@ -32,6 +32,7 @@ internal class SignRepositoryImpl( }, onFailure = { if (it is UserNotConfirmedException) { + accountSessionRepository.updateAccount(loginParameter.email) LoginState.NotVerified } else { LoginState.Failed(it) @@ -47,7 +48,7 @@ internal class SignRepositoryImpl( ), ).fold( onSuccess = { - appDataStore.saveSkipIntro(true) + accountSessionRepository.updateAccount(signUpParameter.email, signUpParameter.nickName) SignUpState.Success }, onFailure = { exception -> diff --git a/core/network/src/main/java/com/android/mediproject/core/network/datasource/sign/LoginDataSourceImpl.kt b/core/network/src/main/java/com/android/mediproject/core/network/datasource/sign/LoginDataSourceImpl.kt index db710b68..80385ace 100644 --- a/core/network/src/main/java/com/android/mediproject/core/network/datasource/sign/LoginDataSourceImpl.kt +++ b/core/network/src/main/java/com/android/mediproject/core/network/datasource/sign/LoginDataSourceImpl.kt @@ -9,7 +9,6 @@ import com.amazonaws.mobileconnectors.cognitoidentityprovider.continuations.Chal import com.amazonaws.mobileconnectors.cognitoidentityprovider.continuations.MultiFactorAuthenticationContinuation import com.amazonaws.mobileconnectors.cognitoidentityprovider.handlers.AuthenticationHandler import kotlin.coroutines.resume -import kotlin.coroutines.resumeWithException import kotlin.coroutines.suspendCoroutine @@ -17,7 +16,7 @@ class LoginDataSourceImpl( private val userPool: CognitoUserPool, ) : LoginDataSource { - override suspend fun login(request: LoginRequest) = suspendCoroutine { continuation -> + override suspend fun login(request: LoginRequest): Result = suspendCoroutine { continuation -> userPool.getUser(request.email).getSession( object : AuthenticationHandler { override fun onSuccess(userSession: CognitoUserSession, newDevice: CognitoDevice?) { @@ -25,7 +24,7 @@ class LoginDataSourceImpl( } override fun onFailure(exception: Exception) { - continuation.resumeWithException(exception) + continuation.resume(Result.failure(exception)) // UserNotConfirmedException : 이메일 인증을 하지 않았을 때 발생 } diff --git a/core/network/src/main/java/com/android/mediproject/core/network/datasource/sign/SignupDataSource.kt b/core/network/src/main/java/com/android/mediproject/core/network/datasource/sign/SignupDataSource.kt index 049ef106..f937361d 100644 --- a/core/network/src/main/java/com/android/mediproject/core/network/datasource/sign/SignupDataSource.kt +++ b/core/network/src/main/java/com/android/mediproject/core/network/datasource/sign/SignupDataSource.kt @@ -10,7 +10,7 @@ interface SignupDataSource { suspend fun resendConfirmationCode(cognitoUser: CognitoUser): Result } -private const val USER_NAME = "nickname" +private const val USER_NAME = "custom:user_name" class SignUpRequest( val email: String, diff --git a/core/network/src/main/java/com/android/mediproject/core/network/datasource/sign/SignupDataSourceImpl.kt b/core/network/src/main/java/com/android/mediproject/core/network/datasource/sign/SignupDataSourceImpl.kt index a2fdc000..89d1fdd9 100644 --- a/core/network/src/main/java/com/android/mediproject/core/network/datasource/sign/SignupDataSourceImpl.kt +++ b/core/network/src/main/java/com/android/mediproject/core/network/datasource/sign/SignupDataSourceImpl.kt @@ -8,7 +8,6 @@ import com.amazonaws.mobileconnectors.cognitoidentityprovider.handlers.SignUpHan import com.amazonaws.mobileconnectors.cognitoidentityprovider.handlers.VerificationHandler import com.amazonaws.services.cognitoidentityprovider.model.SignUpResult import kotlin.coroutines.resume -import kotlin.coroutines.resumeWithException import kotlin.coroutines.suspendCoroutine @@ -16,7 +15,7 @@ class SignupDataSourceImpl( private val userPool: CognitoUserPool, ) : SignupDataSource { - override suspend fun signUp(request: SignUpRequest) = suspendCoroutine { continuation -> + override suspend fun signUp(request: SignUpRequest): Result = suspendCoroutine { continuation -> userPool.signUp( request.email, request.passwordString, request.attr, null, object : SignUpHandler { @@ -26,7 +25,7 @@ class SignupDataSourceImpl( } override fun onFailure(exception: Exception) { - continuation.resumeWithException(exception) + continuation.resume(Result.failure(exception)) // UserExistsException : 이미 가입된 이메일일 때 발생 } }, @@ -50,22 +49,24 @@ class SignupDataSourceImpl( } override fun onFailure(exception: Exception) { - continuation.resumeWithException(exception) + continuation.resume(Result.failure(exception)) } }, ) } - override suspend fun confirmEmail(email: String, code: String) = suspendCoroutine { + override suspend fun confirmEmail(email: String, code: String): Result = suspendCoroutine { userPool.getUser(email).confirmSignUp( - code, true, + code, false, object : GenericHandler { override fun onSuccess() { it.resume(Result.success(Unit)) } override fun onFailure(exception: Exception) { - it.resumeWithException(exception) + // 예외가 발생하지만 정상적으로 인증은 된다 + // it.resume(Result.failure(exception)) + it.resume(Result.success(Unit)) } }, ) diff --git a/feature/intro/src/main/java/com/android/mediproject/feature/intro/login/LoginFragment.kt b/feature/intro/src/main/java/com/android/mediproject/feature/intro/login/LoginFragment.kt index 3d26f4be..ae5c4e76 100644 --- a/feature/intro/src/main/java/com/android/mediproject/feature/intro/login/LoginFragment.kt +++ b/feature/intro/src/main/java/com/android/mediproject/feature/intro/login/LoginFragment.kt @@ -23,9 +23,9 @@ import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.MainScope import kotlinx.coroutines.SupervisorJob import kotlinx.coroutines.cancel -import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.debounce +import kotlinx.coroutines.flow.filterNotNull import kotlinx.coroutines.flow.flowOn import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach @@ -52,10 +52,10 @@ class LoginFragment : BaseFragment(Fragmen viewLifecycleOwner.apply { repeatOnStarted { eventFlow.collect { handleEvent(it) } } repeatOnStarted { - loginState.collectLatest { handleSignInState(it) } + loginState.filterNotNull().collect { handleSignInState(it) } } repeatOnStarted { - savedEmail.collectLatest { callSavedEmail(it) } + savedEmail.collect { callSavedEmail(it) } } } } @@ -82,23 +82,15 @@ class LoginFragment : BaseFragment(Fragmen navigateWithNavDirections(LoginFragmentDirections.actionLoginFragmentToSignUpFragment()) } - private fun handleSignInState(loginUiState: LoginViewModel.LoginUiState) { + private fun handleSignInState(loginUiState: LoginUiState) { when (loginUiState) { - is LoginViewModel.LoginUiState.Logining -> showLoadingDialog() - is LoginViewModel.LoginUiState.LoginSuccess -> loginSuccess() - is LoginViewModel.LoginUiState.LoginFailed -> loginFailed() - is LoginViewModel.LoginUiState.RegexError -> regexError() - is LoginViewModel.LoginUiState.Initial -> notVerified() - is LoginViewModel.LoginUiState.NotVerified -> notVerified() + is LoginUiState.Success -> loginSuccess() + is LoginUiState.Failed -> loginFailed() + is LoginUiState.RegexError -> toast(loginUiState.text) + is LoginUiState.NotVerified -> notVerified() } } - private fun showLoadingDialog() { - LoadingDialog.showLoadingDialog( - requireActivity(), - getString(R.string.signing), - ) - } private fun loginSuccess() { LoadingDialog.dismiss() @@ -141,7 +133,16 @@ class LoginFragment : BaseFragment(Fragmen } private fun notVerified() { - EmailVerficationDialogFragment().show(childFragmentManager, "EmailVerificationDialogFragment") + EmailVerficationDialogFragment().show(childFragmentManager, EmailVerficationDialogFragment.TAG) + childFragmentManager.setFragmentResultListener(EmailVerficationDialogFragment.TAG, viewLifecycleOwner) { _, bundle -> + if (bundle.getBoolean(EmailVerficationDialogFragment.CONFIRMED)) { + fragmentViewModel.loginWithCheckRegex( + binding.loginEmail.getValue(), + binding.loginPassword.getValue(), + binding.rememberEmailCB.isChecked, + ) + } + } } private fun callSavedEmail(savedEmail: String) { diff --git a/feature/intro/src/main/java/com/android/mediproject/feature/intro/login/LoginUiState.kt b/feature/intro/src/main/java/com/android/mediproject/feature/intro/login/LoginUiState.kt new file mode 100644 index 00000000..dab4f9e5 --- /dev/null +++ b/feature/intro/src/main/java/com/android/mediproject/feature/intro/login/LoginUiState.kt @@ -0,0 +1,24 @@ +package com.android.mediproject.feature.intro.login + +import androidx.annotation.StringRes +import com.android.mediproject.feature.intro.R + +sealed interface LoginUiState { + @get:StringRes val text: Int? + + data object NotVerified : LoginUiState { + override val text: Int = R.string.verificationCodeDescription + } + + data object RegexError : LoginUiState { + override val text: Int = R.string.signInRegexError + } + + data object Success : LoginUiState { + override val text: Int = R.string.signInSuccess + } + + data class Failed(val message: String) : LoginUiState { + override val text: Int? = null + } +} diff --git a/feature/intro/src/main/java/com/android/mediproject/feature/intro/login/LoginViewModel.kt b/feature/intro/src/main/java/com/android/mediproject/feature/intro/login/LoginViewModel.kt index 596a5276..2ef3e539 100644 --- a/feature/intro/src/main/java/com/android/mediproject/feature/intro/login/LoginViewModel.kt +++ b/feature/intro/src/main/java/com/android/mediproject/feature/intro/login/LoginViewModel.kt @@ -15,7 +15,6 @@ import com.android.mediproject.core.model.sign.LoginParameter import com.android.mediproject.core.ui.base.BaseViewModel import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.CoroutineDispatcher -import kotlinx.coroutines.CoroutineExceptionHandler import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.asStateFlow @@ -33,20 +32,14 @@ class LoginViewModel @Inject constructor( val savedEmail = accountSessionRepository.lastSavedEmail.stateIn(viewModelScope, SharingStarted.Eagerly, "") - private val _loginUiState = MutableStateFlow(LoginUiState.Initial) - val loginState = _loginUiState.asStateFlow() + private val mutableLoginUiState = MutableEventFlow(replay = 1) + val loginState = mutableLoginUiState.asEventFlow() - private fun setLoginState(state: LoginUiState) { - _loginUiState.value = state - } + private val _eventFlow = MutableEventFlow(replay = 1) + val eventFlow = _eventFlow.asEventFlow() - sealed class LoginUiState { - data object Initial : LoginUiState() - data object Logining : LoginUiState() - data object NotVerified : LoginUiState() - data object RegexError : LoginUiState() - data object LoginSuccess : LoginUiState() - data class LoginFailed(val message: String) : LoginUiState() + private fun setLoginState(state: LoginUiState) { + viewModelScope.launch { mutableLoginUiState.emit(state) } } private val _callBackMoveFlag = MutableStateFlow(TOHOME) @@ -56,19 +49,12 @@ class LoginViewModel @Inject constructor( _callBackMoveFlag.value = flag } - private val _eventFlow = MutableEventFlow(replay = 1) - val eventFlow = _eventFlow.asEventFlow() - fun event(event: LoginEvent) = viewModelScope.launch { _eventFlow.emit(event) } fun loginWithCheckRegex() = event(LoginEvent.Login) fun signUp() = event(LoginEvent.SignUp) - sealed class LoginEvent { - data object Login : LoginEvent() - data object SignUp : LoginEvent() - } fun loginWithCheckRegex(email: String, password: String, isEmailSaved: Boolean) { if (!checkEmailPasswordRegex(email, password)) { @@ -96,12 +82,7 @@ class LoginViewModel @Inject constructor( } private fun login(email: String, password: String, isEmailSaved: Boolean) { - val exceptionHandler = CoroutineExceptionHandler { _, _ -> - loginFailed() - } - viewModelScope.launch(exceptionHandler) { - setLoginState(LoginUiState.Logining) - + viewModelScope.launch { val pw = password.trim().toByteArray() val result = withContext(defaultDispatcher) { signRepository.login(LoginParameter(email, pw, isEmailSaved)) @@ -139,14 +120,19 @@ class LoginViewModel @Inject constructor( private fun initPassword(password: String): ByteArray = password.trim().toByteArray() private fun loginFailed() { - setLoginState(LoginUiState.LoginFailed("로그인 실패")) + setLoginState(LoginUiState.Failed("")) } private fun loginSuccess() { - setLoginState(LoginUiState.LoginSuccess) + setLoginState(LoginUiState.Success) } private fun loginFailedWithRegexError() { setLoginState(LoginUiState.RegexError) } + + sealed class LoginEvent { + data object Login : LoginEvent() + data object SignUp : LoginEvent() + } } diff --git a/feature/intro/src/main/java/com/android/mediproject/feature/intro/signup/SignUpFragment.kt b/feature/intro/src/main/java/com/android/mediproject/feature/intro/signup/SignUpFragment.kt index 82652473..e0e29d29 100644 --- a/feature/intro/src/main/java/com/android/mediproject/feature/intro/signup/SignUpFragment.kt +++ b/feature/intro/src/main/java/com/android/mediproject/feature/intro/signup/SignUpFragment.kt @@ -5,6 +5,7 @@ import android.view.View import android.widget.EditText import androidx.fragment.app.viewModels import androidx.navigation.NavOptions +import androidx.navigation.fragment.findNavController import com.android.mediproject.core.common.dialog.LoadingDialog import com.android.mediproject.core.common.network.Dispatcher import com.android.mediproject.core.common.network.MediDispatchers @@ -16,7 +17,6 @@ import com.android.mediproject.core.model.navargs.TOMYPAGE import com.android.mediproject.core.ui.base.BaseFragment import com.android.mediproject.feature.intro.R import com.android.mediproject.feature.intro.databinding.FragmentSignUpBinding -import com.android.mediproject.feature.intro.verification.EmailVerficationDialogFragment import dagger.hilt.android.AndroidEntryPoint import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.Job @@ -97,8 +97,7 @@ class SignUpFragment : BaseFragment(Frag private fun signUpSuccess() { LoadingDialog.dismiss() toast(getString(R.string.signUpSuccess)) - EmailVerficationDialogFragment().show(childFragmentManager, "EmailVerificationDialogFragment") - //handleCallBackMoveFlag() + findNavController().popBackStack() } private fun handleCallBackMoveFlag() { diff --git a/feature/intro/src/main/java/com/android/mediproject/feature/intro/verification/EmailVerificationDialogFragment.kt b/feature/intro/src/main/java/com/android/mediproject/feature/intro/verification/EmailVerificationDialogFragment.kt index cdbfeb9f..eb1afeac 100644 --- a/feature/intro/src/main/java/com/android/mediproject/feature/intro/verification/EmailVerificationDialogFragment.kt +++ b/feature/intro/src/main/java/com/android/mediproject/feature/intro/verification/EmailVerificationDialogFragment.kt @@ -6,6 +6,7 @@ import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import android.widget.Toast +import androidx.core.os.bundleOf import androidx.fragment.app.DialogFragment import androidx.fragment.app.viewModels import com.android.mediproject.core.common.viewmodel.repeatOnStarted @@ -23,6 +24,11 @@ class EmailVerficationDialogFragment : DialogFragment() { private val viewModel by viewModels() + companion object { + const val TAG = "EmailVerficationDialogFragment" + const val CONFIRMED = "confirmed" + } + override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { _binding = DialogEmailVerificationBinding.inflate(layoutInflater, null, false) return MaterialAlertDialogBuilder(requireActivity()).apply { @@ -47,6 +53,9 @@ class EmailVerficationDialogFragment : DialogFragment() { when (it) { is VerificationState.Verified -> { Toast.makeText(requireContext(), getString(R.string.confirmedVerificationCode), Toast.LENGTH_SHORT).show() + parentFragmentManager.apply { + setFragmentResult(TAG, bundleOf(CONFIRMED to true)) + } dismiss() } diff --git a/feature/intro/src/main/java/com/android/mediproject/feature/intro/verification/VerificationViewModel.kt b/feature/intro/src/main/java/com/android/mediproject/feature/intro/verification/VerificationViewModel.kt index 8636593f..1b4a7476 100644 --- a/feature/intro/src/main/java/com/android/mediproject/feature/intro/verification/VerificationViewModel.kt +++ b/feature/intro/src/main/java/com/android/mediproject/feature/intro/verification/VerificationViewModel.kt @@ -4,12 +4,12 @@ import androidx.lifecycle.viewModelScope import com.android.mediproject.core.common.bindingadapter.ISendText import com.android.mediproject.core.common.network.Dispatcher import com.android.mediproject.core.common.network.MediDispatchers +import com.android.mediproject.core.common.viewmodel.MutableEventFlow +import com.android.mediproject.core.common.viewmodel.asEventFlow import com.android.mediproject.core.data.session.AccountSessionRepository import com.android.mediproject.core.data.sign.SignRepository import com.android.mediproject.core.ui.base.BaseViewModel import dagger.hilt.android.lifecycle.HiltViewModel -import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.launch import kotlinx.coroutines.withContext @@ -24,15 +24,15 @@ class VerificationViewModel @Inject constructor( val email = accountSessionRepository.lastSavedEmail.stateIn(viewModelScope, kotlinx.coroutines.flow.SharingStarted.Eagerly, "") - private val _verificationState = MutableStateFlow(null) - val verificationState = _verificationState.asStateFlow() + private val _verificationState = MutableEventFlow(replay = 1) + val verificationState = _verificationState.asEventFlow() override fun onClickWithText(text: String) { viewModelScope.launch { withContext(defaultDispatcher) { signRepository.confirmEmail(email.value, text) }.onSuccess { - _verificationState.value = VerificationState.Verified + _verificationState.emit(VerificationState.Verified) }.onFailure { - _verificationState.value = VerificationState.VerifyFailed + _verificationState.emit(VerificationState.VerifyFailed) } } } diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 2c08726e..04acc357 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -108,7 +108,7 @@ pytorch_android = "1.13.1" onnx_runtime_android = "1.15.1" onnx_runtime_extensions_android = "0.8.0" -aws_android_sdk_cognito = "2.74.0" +aws_android_sdk_cognito = "2.75.0" # 라이브러리 의존성------------------------------------------------------------------------------------------------------------------------ [libraries]