From 3c5bf49d21845c8db525fa123348978f1f2728af Mon Sep 17 00:00:00 2001 From: SYAAINN Date: Tue, 17 Dec 2024 21:22:19 +0900 Subject: [PATCH 1/6] =?UTF-8?q?[feat/#12]=20Base=20=ED=8C=8C=EC=9D=BC?= =?UTF-8?q?=EB=93=A4=EC=9D=84=20=EC=83=9D=EC=84=B1=ED=95=A9=EB=8B=88?= =?UTF-8?q?=EB=8B=A4.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../presentation/util/base/BaseViewModel.kt | 50 +++++++++++++++++++ .../and/presentation/util/base/UiEvent.kt | 3 ++ .../presentation/util/base/UiSideEffect.kt | 3 ++ .../and/presentation/util/base/UiState.kt | 3 ++ 4 files changed, 59 insertions(+) create mode 100644 app/src/main/java/org/sopt/and/presentation/util/base/BaseViewModel.kt create mode 100644 app/src/main/java/org/sopt/and/presentation/util/base/UiEvent.kt create mode 100644 app/src/main/java/org/sopt/and/presentation/util/base/UiSideEffect.kt create mode 100644 app/src/main/java/org/sopt/and/presentation/util/base/UiState.kt diff --git a/app/src/main/java/org/sopt/and/presentation/util/base/BaseViewModel.kt b/app/src/main/java/org/sopt/and/presentation/util/base/BaseViewModel.kt new file mode 100644 index 0000000..2d6f6a4 --- /dev/null +++ b/app/src/main/java/org/sopt/and/presentation/util/base/BaseViewModel.kt @@ -0,0 +1,50 @@ +package org.sopt.and.presentation.util.base + +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.MutableSharedFlow +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.SharedFlow +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.asSharedFlow +import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.launch + +abstract class BaseViewModel() : + ViewModel() { + private val initialState: State by lazy { createInitialState() } + abstract fun createInitialState(): State + + private val _uiState = MutableStateFlow(initialState) + val uiState: StateFlow + get() = _uiState.asStateFlow() + val currentState: State + get() = uiState.value + + private val _event: MutableSharedFlow = MutableSharedFlow() + val event: SharedFlow + get() = _event.asSharedFlow() + + private val _sideEffect: MutableSharedFlow = MutableSharedFlow() + val sideEffect: Flow + get() = _sideEffect.asSharedFlow() + + fun setState(reduce: State.() -> State) { + _uiState.value = currentState.reduce() + } + + open fun setEvent(event: Event) { + dispatchEvent(event) + } + + private fun dispatchEvent(event: Event) = viewModelScope.launch { + handleEvent(event) + } + + protected abstract suspend fun handleEvent(event: Event) + + fun setSideEffect(sideEffect: SideEffect) { + viewModelScope.launch { _sideEffect.emit(sideEffect) } + } +} \ No newline at end of file diff --git a/app/src/main/java/org/sopt/and/presentation/util/base/UiEvent.kt b/app/src/main/java/org/sopt/and/presentation/util/base/UiEvent.kt new file mode 100644 index 0000000..6d73367 --- /dev/null +++ b/app/src/main/java/org/sopt/and/presentation/util/base/UiEvent.kt @@ -0,0 +1,3 @@ +package org.sopt.and.presentation.util.base + +interface UiEvent \ No newline at end of file diff --git a/app/src/main/java/org/sopt/and/presentation/util/base/UiSideEffect.kt b/app/src/main/java/org/sopt/and/presentation/util/base/UiSideEffect.kt new file mode 100644 index 0000000..831a1b5 --- /dev/null +++ b/app/src/main/java/org/sopt/and/presentation/util/base/UiSideEffect.kt @@ -0,0 +1,3 @@ +package org.sopt.and.presentation.util.base + +interface UiSideEffect \ No newline at end of file diff --git a/app/src/main/java/org/sopt/and/presentation/util/base/UiState.kt b/app/src/main/java/org/sopt/and/presentation/util/base/UiState.kt new file mode 100644 index 0000000..c467772 --- /dev/null +++ b/app/src/main/java/org/sopt/and/presentation/util/base/UiState.kt @@ -0,0 +1,3 @@ +package org.sopt.and.presentation.util.base + +interface UiState \ No newline at end of file From 0113d15dc6219bc03c36ebefa75cdc7d82a01ad6 Mon Sep 17 00:00:00 2001 From: SYAAINN Date: Tue, 17 Dec 2024 23:56:20 +0900 Subject: [PATCH 2/6] =?UTF-8?q?[feat/#12]=20AuthContract=20=EB=A5=BC=20?= =?UTF-8?q?=EC=83=9D=EC=84=B1=ED=95=A9=EB=8B=88=EB=8B=A4.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ui/auth/screen/AuthContract.kt | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 app/src/main/java/org/sopt/and/presentation/ui/auth/screen/AuthContract.kt diff --git a/app/src/main/java/org/sopt/and/presentation/ui/auth/screen/AuthContract.kt b/app/src/main/java/org/sopt/and/presentation/ui/auth/screen/AuthContract.kt new file mode 100644 index 0000000..113be58 --- /dev/null +++ b/app/src/main/java/org/sopt/and/presentation/ui/auth/screen/AuthContract.kt @@ -0,0 +1,30 @@ +package org.sopt.and.presentation.ui.auth.screen + +import org.sopt.and.domain.model.Token +import org.sopt.and.domain.model.UserNo +import org.sopt.and.presentation.util.base.UiEvent +import org.sopt.and.presentation.util.base.UiSideEffect +import org.sopt.and.presentation.util.base.UiState + +class AuthContract { + data class AuthUiState( + val signInState: SignInState = SignInState.Idle, + val signInUsername: String = "", + val signInPassword: String = "", + val signUpState: SignUpState = SignUpState.Idle, + val signUpUsername: String = "", + val signUpPassword: String = "", + val signUpHobby: String = "", + ): UiState + + sealed interface AuthSideEffect: UiSideEffect { + data object NavigateToSignIn: AuthSideEffect + data object NavigateToSignUp: AuthSideEffect + data object NavigateToMain: AuthSideEffect + } + + sealed class AuthEvent: UiEvent { + data class OnSignInClicked(val signInState: SignInState): AuthEvent() + data class OnSignUpClicked(val signUpState: SignUpState): AuthEvent() + } +} \ No newline at end of file From 89f3a3c06122313abd5bf19ca9c93817b3443247 Mon Sep 17 00:00:00 2001 From: SYAAINN Date: Tue, 17 Dec 2024 23:56:40 +0900 Subject: [PATCH 3/6] =?UTF-8?q?[refactor/#12]=20AuthViewModel=20=EC=9D=84?= =?UTF-8?q?=20=EC=88=98=EC=A0=95=ED=95=A9=EB=8B=88=EB=8B=A4.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ui/auth/screen/AuthViewModel.kt | 75 ++++++++++--------- 1 file changed, 39 insertions(+), 36 deletions(-) diff --git a/app/src/main/java/org/sopt/and/presentation/ui/auth/screen/AuthViewModel.kt b/app/src/main/java/org/sopt/and/presentation/ui/auth/screen/AuthViewModel.kt index dd592fb..603e534 100644 --- a/app/src/main/java/org/sopt/and/presentation/ui/auth/screen/AuthViewModel.kt +++ b/app/src/main/java/org/sopt/and/presentation/ui/auth/screen/AuthViewModel.kt @@ -1,73 +1,76 @@ package org.sopt.and.presentation.ui.auth.screen -import android.util.Log -import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import dagger.hilt.android.lifecycle.HiltViewModel -import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.launch import org.sopt.and.domain.model.User import org.sopt.and.domain.repository.AuthRepository import org.sopt.and.domain.repository.TokenRepository +import org.sopt.and.presentation.util.base.BaseViewModel import javax.inject.Inject @HiltViewModel class AuthViewModel @Inject constructor( private val tokenRepository: TokenRepository, private val authRepository: AuthRepository, -) : ViewModel() { - private val _signInState = MutableStateFlow(SignInState.Idle) - val signInState: StateFlow = _signInState +) : BaseViewModel() { + override fun createInitialState(): AuthContract.AuthUiState = AuthContract.AuthUiState() - private val _token = MutableStateFlow("") - val token: StateFlow = _token - - private val _signUpState = MutableStateFlow(SignUpState.Idle) - val signUpState: StateFlow = _signUpState + override suspend fun handleEvent(event: AuthContract.AuthEvent) { + when (event) { + is AuthContract.AuthEvent.OnSignInClicked -> setState { + copy( + signInState = event.signInState, + ) + } + is AuthContract.AuthEvent.OnSignUpClicked -> setState { + copy( + signUpState = event.signUpState, + ) + } + } + } - fun validateSignIn(username: String, password: String) { + fun validateSignIn() { viewModelScope.launch { - _signInState.value = SignInState.Loading - val result = authRepository.login(username = username, password = password) - _signInState.value = result.fold( + setEvent(AuthContract.AuthEvent.OnSignInClicked(signInState = SignInState.Loading)) + authRepository.login(username = currentState.signInUsername, password = currentState.signInPassword).fold( onSuccess = { token -> tokenRepository.setToken(token.token) - SignInState.Success(token) + setEvent(AuthContract.AuthEvent.OnSignInClicked(signInState = SignInState.Success(result = token))) }, onFailure = { - SignInState.Failure(it.localizedMessage ?: "에러 발생") + setEvent(AuthContract.AuthEvent.OnSignInClicked(signInState = SignInState.Failure(errorMessage = it.localizedMessage?: ""))) } ) } } - fun validateSignUp(username: String, password: String, hobby: String) { + fun validateSignUp() { viewModelScope.launch { - _signUpState.value = SignUpState.Loading - val result = authRepository.registerUser( + setEvent(AuthContract.AuthEvent.OnSignUpClicked(signUpState = SignUpState.Loading)) + authRepository.registerUser( user = User( - username = username, - password = password, - hobby = hobby + username = currentState.signUpUsername, + password = currentState.signUpPassword, + hobby = currentState.signUpHobby ) - ) - _signUpState.value = result.fold( - onSuccess = { - SignUpState.Success(it) + ).fold( + onSuccess = { userNo -> + setEvent(AuthContract.AuthEvent.OnSignUpClicked(signUpState = SignUpState.Success(userNo))) }, onFailure = { - SignUpState.Failure(it.localizedMessage ?: "에러 발생") + setEvent(AuthContract.AuthEvent.OnSignUpClicked(signUpState = SignUpState.Failure(it.localizedMessage ?: ""))) } ) } } - fun resetSignInState() { - _signInState.value = SignInState.Idle - } - - fun resetSignUpState() { - _signUpState.value = SignUpState.Idle - } +// fun resetSignInState() { +// _signInState.value = SignInState.Idle +// } +// +// fun resetSignUpState() { +// _signUpState.value = SignUpState.Idle +// } } \ No newline at end of file From 4e2f696dcd19214fb4a344fb61b9abcd62bfa583 Mon Sep 17 00:00:00 2001 From: SYAAINN Date: Tue, 17 Dec 2024 23:56:56 +0900 Subject: [PATCH 4/6] =?UTF-8?q?[refactor/#12]=20MVI=20=EA=B5=AC=EC=A1=B0?= =?UTF-8?q?=EC=97=90=20=EB=A7=9E=EA=B2=8C=20=EC=8A=A4=ED=81=AC=EB=A6=B0?= =?UTF-8?q?=EC=9D=84=20=EC=88=98=EC=A0=95=ED=95=A9=EB=8B=88=EB=8B=A4.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ui/auth/screen/SignInScreen.kt | 30 ++++++++++++++----- .../ui/auth/screen/SignUpScreen.kt | 17 ++++++++++- 2 files changed, 39 insertions(+), 8 deletions(-) diff --git a/app/src/main/java/org/sopt/and/presentation/ui/auth/screen/SignInScreen.kt b/app/src/main/java/org/sopt/and/presentation/ui/auth/screen/SignInScreen.kt index 6985d96..7f8ca4e 100644 --- a/app/src/main/java/org/sopt/and/presentation/ui/auth/screen/SignInScreen.kt +++ b/app/src/main/java/org/sopt/and/presentation/ui/auth/screen/SignInScreen.kt @@ -18,6 +18,7 @@ import androidx.compose.material3.ButtonDefaults import androidx.compose.material3.Text import androidx.compose.material3.VerticalDivider import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf @@ -36,6 +37,9 @@ import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import androidx.hilt.navigation.compose.hiltViewModel +import androidx.lifecycle.compose.LocalLifecycleOwner +import androidx.lifecycle.compose.collectAsStateWithLifecycle +import androidx.lifecycle.flowWithLifecycle import org.sopt.and.R import org.sopt.and.presentation.ui.common.WavveTextField import org.sopt.and.presentation.ui.auth.component.SocialPlatformIconRow @@ -49,23 +53,35 @@ fun SignInRoute( navigateToSignUp: () -> Unit, navigateToMain: () -> Unit, ) { - val signInState by authViewModel.signInState.collectAsState() + val uiState by authViewModel.uiState.collectAsStateWithLifecycle() + val lifecycleOwner = LocalLifecycleOwner.current + + LaunchedEffect(authViewModel.sideEffect, lifecycleOwner) { + authViewModel.sideEffect.flowWithLifecycle(lifecycle = lifecycleOwner.lifecycle) + .collect { authSideEffect -> + when(authSideEffect) { + is AuthContract.AuthSideEffect.NavigateToSignUp -> navigateToSignUp() + is AuthContract.AuthSideEffect.NavigateToMain -> navigateToMain() + else -> {} + } + } + } SignInScreen( - signInState = signInState, + uiState = uiState, resetSignInState = { authViewModel.resetSignInState() }, - onSignUpClick = navigateToSignUp, - onSignInClick = { username, password -> authViewModel.validateSignIn(username, password) }, - navigateToMain = navigateToMain + onSignUpClick = { authViewModel.setSideEffect(AuthContract.AuthSideEffect.NavigateToSignUp) }, + onSignInClick = { authViewModel.validateSignIn() }, + navigateToMain = { authViewModel.setSideEffect(AuthContract.AuthSideEffect.NavigateToMain) } ) } @Composable fun SignInScreen( - signInState: SignInState, + uiState: AuthContract.AuthUiState, resetSignInState: () -> Unit, onSignUpClick: () -> Unit, - onSignInClick: (String, String) -> Unit, + onSignInClick: () -> Unit, navigateToMain: () -> Unit, ) { val context = LocalContext.current diff --git a/app/src/main/java/org/sopt/and/presentation/ui/auth/screen/SignUpScreen.kt b/app/src/main/java/org/sopt/and/presentation/ui/auth/screen/SignUpScreen.kt index cebe5b7..3209606 100644 --- a/app/src/main/java/org/sopt/and/presentation/ui/auth/screen/SignUpScreen.kt +++ b/app/src/main/java/org/sopt/and/presentation/ui/auth/screen/SignUpScreen.kt @@ -16,6 +16,7 @@ import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material3.HorizontalDivider import androidx.compose.material3.Text import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf @@ -34,6 +35,9 @@ import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import androidx.hilt.navigation.compose.hiltViewModel +import androidx.lifecycle.compose.LocalLifecycleOwner +import androidx.lifecycle.compose.collectAsStateWithLifecycle +import androidx.lifecycle.flowWithLifecycle import org.sopt.and.R import org.sopt.and.presentation.ui.common.WavveTextField import org.sopt.and.presentation.ui.auth.component.SocialPlatformIconRow @@ -47,7 +51,18 @@ fun SignUpRoute( navigateToSignIn: () -> Unit, navigateToBack: () -> Unit, ) { - val signUpState by authViewModel.signUpState.collectAsState() + val uiState by authViewModel.uiState.collectAsStateWithLifecycle() + val lifecycleOwner = LocalLifecycleOwner.current + + LaunchedEffect(authViewModel.sideEffect, lifecycleOwner) { + authViewModel.sideEffect.flowWithLifecycle(lifecycle = lifecycleOwner.lifecycle) + .collect { authSideEffect -> + when(authSideEffect) { + is AuthContract.AuthSideEffect.NavigateToSignIn -> navigateToSignIn() + else -> {} + } + } + } SignUpScreen( signUpState = signUpState, From e4ff3bd560191e47ff0559e40d1dbc2b602979a8 Mon Sep 17 00:00:00 2001 From: SYAAINN Date: Thu, 19 Dec 2024 14:47:17 +0900 Subject: [PATCH 5/6] =?UTF-8?q?[refactor/#12]=20MVI=20=EB=A6=AC=ED=8C=A9?= =?UTF-8?q?=ED=86=A0=EB=A7=81=EC=9D=84=20=EC=A7=84=ED=96=89=ED=95=A9?= =?UTF-8?q?=EB=8B=88=EB=8B=A4.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ui/auth/screen/AuthContract.kt | 26 +++--- .../ui/auth/screen/AuthViewModel.kt | 68 +++++++++++---- .../ui/auth/screen/SignInScreen.kt | 73 ++++++++--------- .../ui/auth/screen/SignUpScreen.kt | 82 +++++++++---------- 4 files changed, 142 insertions(+), 107 deletions(-) diff --git a/app/src/main/java/org/sopt/and/presentation/ui/auth/screen/AuthContract.kt b/app/src/main/java/org/sopt/and/presentation/ui/auth/screen/AuthContract.kt index 113be58..6fb5c4e 100644 --- a/app/src/main/java/org/sopt/and/presentation/ui/auth/screen/AuthContract.kt +++ b/app/src/main/java/org/sopt/and/presentation/ui/auth/screen/AuthContract.kt @@ -1,7 +1,5 @@ package org.sopt.and.presentation.ui.auth.screen -import org.sopt.and.domain.model.Token -import org.sopt.and.domain.model.UserNo import org.sopt.and.presentation.util.base.UiEvent import org.sopt.and.presentation.util.base.UiSideEffect import org.sopt.and.presentation.util.base.UiState @@ -15,16 +13,24 @@ class AuthContract { val signUpUsername: String = "", val signUpPassword: String = "", val signUpHobby: String = "", - ): UiState + ) : UiState - sealed interface AuthSideEffect: UiSideEffect { - data object NavigateToSignIn: AuthSideEffect - data object NavigateToSignUp: AuthSideEffect - data object NavigateToMain: AuthSideEffect + sealed class AuthEvent : UiEvent { + data class UpdateSignInUsername(val signInUsername: String) : AuthEvent() + data class UpdateSignInPassword(val signInPassword: String) : AuthEvent() + data class OnSignInClicked(val signInState: SignInState) : AuthEvent() + data object ResetSignInState : AuthEvent() + data class UpdateSignUpUsername(val signUpUsername: String) : AuthEvent() + data class UpdateSignUpPassword(val signUpPassword: String) : AuthEvent() + data class UpdateSignUpHobby(val signUpHobby: String) : AuthEvent() + data class OnSignUpClicked(val signUpState: SignUpState) : AuthEvent() + data object ResetSignUpState : AuthEvent() } - sealed class AuthEvent: UiEvent { - data class OnSignInClicked(val signInState: SignInState): AuthEvent() - data class OnSignUpClicked(val signUpState: SignUpState): AuthEvent() + sealed interface AuthSideEffect : UiSideEffect { + data object NavigateToSignIn : AuthSideEffect + data object NavigateToSignUp : AuthSideEffect + data object NavigateToMain : AuthSideEffect + data class ShowToast(val message: String) : AuthSideEffect } } \ No newline at end of file diff --git a/app/src/main/java/org/sopt/and/presentation/ui/auth/screen/AuthViewModel.kt b/app/src/main/java/org/sopt/and/presentation/ui/auth/screen/AuthViewModel.kt index 603e534..d1190a8 100644 --- a/app/src/main/java/org/sopt/and/presentation/ui/auth/screen/AuthViewModel.kt +++ b/app/src/main/java/org/sopt/and/presentation/ui/auth/screen/AuthViewModel.kt @@ -1,5 +1,6 @@ package org.sopt.and.presentation.ui.auth.screen +import android.util.Log import androidx.lifecycle.viewModelScope import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.launch @@ -18,16 +19,45 @@ class AuthViewModel @Inject constructor( override suspend fun handleEvent(event: AuthContract.AuthEvent) { when (event) { + is AuthContract.AuthEvent.UpdateSignInUsername -> setState { + copy(signInUsername = event.signInUsername) + } + is AuthContract.AuthEvent.UpdateSignInPassword -> setState { + copy(signInPassword = event.signInPassword) + } is AuthContract.AuthEvent.OnSignInClicked -> setState { - copy( - signInState = event.signInState, - ) + copy(signInState = event.signInState) + } + is AuthContract.AuthEvent.ResetSignInState -> setState { + copy(signInState = SignInState.Idle) + } + is AuthContract.AuthEvent.UpdateSignUpUsername -> setState { + copy(signUpUsername = event.signUpUsername) + } + is AuthContract.AuthEvent.UpdateSignUpPassword -> setState { + copy(signUpPassword = event.signUpPassword) + } + is AuthContract.AuthEvent.UpdateSignUpHobby -> setState { + copy(signUpHobby = event.signUpHobby) } is AuthContract.AuthEvent.OnSignUpClicked -> setState { - copy( - signUpState = event.signUpState, - ) + copy(signUpState = event.signUpState) } + is AuthContract.AuthEvent.ResetSignUpState -> setState { + copy(signUpState = SignUpState.Idle) + } + } + } + + fun updateSignInUsername(input: String) { + viewModelScope.launch { + setEvent(AuthContract.AuthEvent.UpdateSignInUsername(signInUsername = input)) + } + } + + fun updateSignInPassword(input: String) { + viewModelScope.launch { + setEvent(AuthContract.AuthEvent.UpdateSignInPassword(signInPassword = input)) } } @@ -46,6 +76,24 @@ class AuthViewModel @Inject constructor( } } + fun updateSignUpUsername(input: String) { + viewModelScope.launch { + setEvent(AuthContract.AuthEvent.UpdateSignUpUsername(signUpUsername = input)) + } + } + + fun updateSignUpPassword(input: String) { + viewModelScope.launch { + setEvent(AuthContract.AuthEvent.UpdateSignUpPassword(signUpPassword = input)) + } + } + + fun updateSignUpHobby(input: String) { + viewModelScope.launch { + setEvent(AuthContract.AuthEvent.UpdateSignUpHobby(signUpHobby = input)) + } + } + fun validateSignUp() { viewModelScope.launch { setEvent(AuthContract.AuthEvent.OnSignUpClicked(signUpState = SignUpState.Loading)) @@ -65,12 +113,4 @@ class AuthViewModel @Inject constructor( ) } } - -// fun resetSignInState() { -// _signInState.value = SignInState.Idle -// } -// -// fun resetSignUpState() { -// _signUpState.value = SignUpState.Idle -// } } \ No newline at end of file diff --git a/app/src/main/java/org/sopt/and/presentation/ui/auth/screen/SignInScreen.kt b/app/src/main/java/org/sopt/and/presentation/ui/auth/screen/SignInScreen.kt index 7f8ca4e..923b3cd 100644 --- a/app/src/main/java/org/sopt/and/presentation/ui/auth/screen/SignInScreen.kt +++ b/app/src/main/java/org/sopt/and/presentation/ui/auth/screen/SignInScreen.kt @@ -1,5 +1,6 @@ package org.sopt.and.presentation.ui.auth.screen +import android.util.Log import androidx.compose.foundation.Image import androidx.compose.foundation.background import androidx.compose.foundation.clickable @@ -19,11 +20,9 @@ import androidx.compose.material3.Text import androidx.compose.material3.VerticalDivider import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect -import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember -import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip @@ -53,8 +52,9 @@ fun SignInRoute( navigateToSignUp: () -> Unit, navigateToMain: () -> Unit, ) { - val uiState by authViewModel.uiState.collectAsStateWithLifecycle() + val authUiState by authViewModel.uiState.collectAsStateWithLifecycle() val lifecycleOwner = LocalLifecycleOwner.current + val context = LocalContext.current LaunchedEffect(authViewModel.sideEffect, lifecycleOwner) { authViewModel.sideEffect.flowWithLifecycle(lifecycle = lifecycleOwner.lifecycle) @@ -62,32 +62,44 @@ fun SignInRoute( when(authSideEffect) { is AuthContract.AuthSideEffect.NavigateToSignUp -> navigateToSignUp() is AuthContract.AuthSideEffect.NavigateToMain -> navigateToMain() + is AuthContract.AuthSideEffect.ShowToast -> showToast(context = context, message = authSideEffect.message) else -> {} } } } + LaunchedEffect(authUiState.signInState) { + when(authUiState.signInState) { + is SignInState.Success -> { + authViewModel.setSideEffect(AuthContract.AuthSideEffect.ShowToast(message = "로그인에 성공하였습니다.")) + authViewModel.setEvent(AuthContract.AuthEvent.ResetSignInState) + authViewModel.setSideEffect(AuthContract.AuthSideEffect.NavigateToMain) + } + is SignInState.Failure -> { + authViewModel.setSideEffect(AuthContract.AuthSideEffect.ShowToast(message = "아이디와 비밀번호를 다시 확인해주세요.")) + authViewModel.setEvent(AuthContract.AuthEvent.ResetSignInState) + } + else -> {} + } + } + SignInScreen( - uiState = uiState, - resetSignInState = { authViewModel.resetSignInState() }, - onSignUpClick = { authViewModel.setSideEffect(AuthContract.AuthSideEffect.NavigateToSignUp) }, + authUiState = authUiState, + updateSignInUsername = { input -> authViewModel.updateSignInUsername(input) }, + updateSignInPassword = { input -> authViewModel.updateSignInPassword(input) }, onSignInClick = { authViewModel.validateSignIn() }, - navigateToMain = { authViewModel.setSideEffect(AuthContract.AuthSideEffect.NavigateToMain) } + onSignUpClick = { authViewModel.setSideEffect(AuthContract.AuthSideEffect.NavigateToSignUp) }, ) } @Composable fun SignInScreen( - uiState: AuthContract.AuthUiState, - resetSignInState: () -> Unit, - onSignUpClick: () -> Unit, + authUiState: AuthContract.AuthUiState, + updateSignInUsername: (String) -> Unit, + updateSignInPassword: (String) -> Unit, onSignInClick: () -> Unit, - navigateToMain: () -> Unit, + onSignUpClick: () -> Unit, ) { - val context = LocalContext.current - var inputEmail by remember { mutableStateOf("") } - var inputPassword by remember { mutableStateOf("") } - Column( modifier = Modifier .fillMaxSize() @@ -121,8 +133,8 @@ fun SignInScreen( Spacer(Modifier.height(40.dp)) WavveTextField( - value = inputEmail, - onValueChange = { newValue -> inputEmail = newValue }, + value = authUiState.signInUsername, + onValueChange = updateSignInUsername, modifier = Modifier .fillMaxWidth() .clip(shape = RoundedCornerShape(6.dp)) @@ -131,8 +143,8 @@ fun SignInScreen( ) Spacer(Modifier.height(4.dp)) WavveTextField( - value = inputPassword, - onValueChange = { newValue -> inputPassword = newValue }, + value = authUiState.signInPassword, + onValueChange = updateSignInPassword, modifier = Modifier .fillMaxWidth() .clip(shape = RoundedCornerShape(6.dp)) @@ -144,7 +156,7 @@ fun SignInScreen( Spacer(Modifier.height(30.dp)) Button( - onClick = { onSignInClick(inputEmail, inputPassword) }, + onClick = onSignInClick, modifier = Modifier.fillMaxWidth(), colors = ButtonDefaults.buttonColors(Color(0xFF0F42C7)) ) { @@ -232,27 +244,6 @@ fun SignInScreen( } Spacer(modifier = Modifier.weight(1f)) - - when (signInState) { - is SignInState.Success -> { - showToast( - context = context, - message = "로그인에 성공했습니다." - ) - resetSignInState() - navigateToMain() - } - - is SignInState.Failure -> { - showToast( - context = context, - message = "아이디와 비밀번호를 다시 확인해주세요." - ) - resetSignInState() - } - - else -> {} - } } } diff --git a/app/src/main/java/org/sopt/and/presentation/ui/auth/screen/SignUpScreen.kt b/app/src/main/java/org/sopt/and/presentation/ui/auth/screen/SignUpScreen.kt index 3209606..12acb5d 100644 --- a/app/src/main/java/org/sopt/and/presentation/ui/auth/screen/SignUpScreen.kt +++ b/app/src/main/java/org/sopt/and/presentation/ui/auth/screen/SignUpScreen.kt @@ -17,7 +17,6 @@ import androidx.compose.material3.HorizontalDivider import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect -import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember @@ -51,41 +50,59 @@ fun SignUpRoute( navigateToSignIn: () -> Unit, navigateToBack: () -> Unit, ) { - val uiState by authViewModel.uiState.collectAsStateWithLifecycle() + val authUiState by authViewModel.uiState.collectAsStateWithLifecycle() val lifecycleOwner = LocalLifecycleOwner.current + val context = LocalContext.current LaunchedEffect(authViewModel.sideEffect, lifecycleOwner) { authViewModel.sideEffect.flowWithLifecycle(lifecycle = lifecycleOwner.lifecycle) .collect { authSideEffect -> when(authSideEffect) { is AuthContract.AuthSideEffect.NavigateToSignIn -> navigateToSignIn() + is AuthContract.AuthSideEffect.ShowToast -> showToast(context = context, message = authSideEffect.message) else -> {} } } } + LaunchedEffect(authUiState.signUpState) { + when(authUiState.signUpState) { + is SignUpState.Success -> { + authViewModel.setSideEffect( + AuthContract.AuthSideEffect.ShowToast(message = "회원가입에 성공했습니다. 유저번호는 ${(authUiState.signUpState as SignUpState.Success).result.no}입니다.") + ) + authViewModel.setEvent(AuthContract.AuthEvent.ResetSignUpState) + authViewModel.setSideEffect(AuthContract.AuthSideEffect.NavigateToSignIn) + } + is SignUpState.Failure -> { + authViewModel.setSideEffect( + AuthContract.AuthSideEffect.ShowToast(message = "회원가입에 실패했습니다. 형식을 다시 확인해주세요.") + ) + authViewModel.setEvent(AuthContract.AuthEvent.ResetSignUpState) + } + else -> {} + } + } + SignUpScreen( - signUpState = signUpState, - resetSignUpState = { authViewModel.resetSignUpState() }, - onSignUpClick = { username, password, hobby -> authViewModel.validateSignUp(username, password, hobby)}, - navigateToSignIn = navigateToSignIn, - onCancelClick = navigateToBack, + authUiState = authUiState, + updateSignUpUsername = { input -> authViewModel.updateSignUpUsername(input) }, + updateSignUpPassword = { input -> authViewModel.updateSignUpPassword(input) }, + updateSignUpHobby = { input -> authViewModel.updateSignUpHobby(input) }, + onSignUpClick = { authViewModel.validateSignUp() }, + onCancelClick = { authViewModel.setSideEffect(AuthContract.AuthSideEffect.NavigateToSignIn) }, ) } @Composable fun SignUpScreen( - signUpState: SignUpState, - resetSignUpState: () -> Unit, - onSignUpClick: (String, String, String) -> Unit, - navigateToSignIn: () -> Unit, + authUiState: AuthContract.AuthUiState, + updateSignUpUsername: (String) -> Unit, + updateSignUpPassword: (String) -> Unit, + updateSignUpHobby: (String) -> Unit, + onSignUpClick: () -> Unit, onCancelClick: () -> Unit, ) { - val context = LocalContext.current - var inputEmail by remember { mutableStateOf("") } - var inputPassword by remember { mutableStateOf("") } - var inputHobby by remember { mutableStateOf("") } - Column( modifier = Modifier .fillMaxSize() @@ -149,8 +166,8 @@ fun SignUpScreen( Spacer(modifier = Modifier.height(18.dp)) WavveTextField( - value = inputEmail, - onValueChange = { newValue -> inputEmail = newValue }, + value = authUiState.signUpUsername, + onValueChange = updateSignUpUsername, modifier = Modifier .fillMaxWidth() .clip(shape = RoundedCornerShape(4.dp)) @@ -181,8 +198,8 @@ fun SignUpScreen( Spacer(modifier = Modifier.height(10.dp)) WavveTextField( - value = inputPassword, - onValueChange = { newValue -> inputPassword = newValue }, + value = authUiState.signUpPassword, + onValueChange = updateSignUpPassword, modifier = Modifier .fillMaxWidth() .clip(shape = RoundedCornerShape(4.dp)) @@ -212,8 +229,8 @@ fun SignUpScreen( } WavveTextField( - value = inputHobby, - onValueChange = { newValue -> inputHobby = newValue }, + value = authUiState.signUpHobby, + onValueChange = updateSignUpHobby, modifier = Modifier .fillMaxWidth() .clip(shape = RoundedCornerShape(4.dp)) @@ -309,7 +326,7 @@ fun SignUpScreen( modifier = Modifier .fillMaxWidth() .background(color = Color(0xFF0F42C7)) - .clickable(onClick = { onSignUpClick(inputEmail, inputPassword, inputHobby) }), + .clickable(onClick = onSignUpClick), contentAlignment = Alignment.Center ) { Text( @@ -318,25 +335,6 @@ fun SignUpScreen( color = Color.White ) } - - when(signUpState) { - is SignUpState.Success -> { - showToast( - context = context, - message = "회원가입에 성공했습니다. 유저번호는 ${signUpState.result.no}입니다." - ) - navigateToSignIn() - resetSignUpState() - } - is SignUpState.Failure -> { - showToast( - context = context, - message = "회원가입에 실패했습니다. 형식을 다시 확인해주세요." - ) - resetSignUpState() - } - else -> {} - } } } From 29516b48dad5a7de7584170da5828d20e3f479b7 Mon Sep 17 00:00:00 2001 From: SYAAINN Date: Thu, 19 Dec 2024 15:33:55 +0900 Subject: [PATCH 6/6] =?UTF-8?q?[chore/#12]=20=EB=B6=88=ED=95=84=EC=9A=94?= =?UTF-8?q?=20import=EB=AC=B8=EC=9D=84=20=EC=A0=9C=EA=B1=B0=ED=95=A9?= =?UTF-8?q?=EB=8B=88=EB=8B=A4.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ui/auth/screen/AuthViewModel.kt | 1 - .../ui/auth/screen/SignInScreen.kt | 20 +++++++++++-------- .../ui/auth/screen/SignUpScreen.kt | 17 +++++++++------- 3 files changed, 22 insertions(+), 16 deletions(-) diff --git a/app/src/main/java/org/sopt/and/presentation/ui/auth/screen/AuthViewModel.kt b/app/src/main/java/org/sopt/and/presentation/ui/auth/screen/AuthViewModel.kt index d1190a8..684301e 100644 --- a/app/src/main/java/org/sopt/and/presentation/ui/auth/screen/AuthViewModel.kt +++ b/app/src/main/java/org/sopt/and/presentation/ui/auth/screen/AuthViewModel.kt @@ -1,6 +1,5 @@ package org.sopt.and.presentation.ui.auth.screen -import android.util.Log import androidx.lifecycle.viewModelScope import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.launch diff --git a/app/src/main/java/org/sopt/and/presentation/ui/auth/screen/SignInScreen.kt b/app/src/main/java/org/sopt/and/presentation/ui/auth/screen/SignInScreen.kt index 923b3cd..9d29d82 100644 --- a/app/src/main/java/org/sopt/and/presentation/ui/auth/screen/SignInScreen.kt +++ b/app/src/main/java/org/sopt/and/presentation/ui/auth/screen/SignInScreen.kt @@ -1,6 +1,5 @@ package org.sopt.and.presentation.ui.auth.screen -import android.util.Log import androidx.compose.foundation.Image import androidx.compose.foundation.background import androidx.compose.foundation.clickable @@ -21,8 +20,6 @@ import androidx.compose.material3.VerticalDivider import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.remember import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip @@ -40,9 +37,9 @@ import androidx.lifecycle.compose.LocalLifecycleOwner import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.lifecycle.flowWithLifecycle import org.sopt.and.R -import org.sopt.and.presentation.ui.common.WavveTextField import org.sopt.and.presentation.ui.auth.component.SocialPlatformIconRow import org.sopt.and.presentation.ui.auth.component.SocialPlatformList +import org.sopt.and.presentation.ui.common.WavveTextField import org.sopt.and.presentation.util.showToast import org.sopt.and.ui.theme.ANDANDROIDTheme @@ -59,26 +56,32 @@ fun SignInRoute( LaunchedEffect(authViewModel.sideEffect, lifecycleOwner) { authViewModel.sideEffect.flowWithLifecycle(lifecycle = lifecycleOwner.lifecycle) .collect { authSideEffect -> - when(authSideEffect) { + when (authSideEffect) { is AuthContract.AuthSideEffect.NavigateToSignUp -> navigateToSignUp() is AuthContract.AuthSideEffect.NavigateToMain -> navigateToMain() - is AuthContract.AuthSideEffect.ShowToast -> showToast(context = context, message = authSideEffect.message) + is AuthContract.AuthSideEffect.ShowToast -> showToast( + context = context, + message = authSideEffect.message + ) + else -> {} } } } LaunchedEffect(authUiState.signInState) { - when(authUiState.signInState) { + when (authUiState.signInState) { is SignInState.Success -> { authViewModel.setSideEffect(AuthContract.AuthSideEffect.ShowToast(message = "로그인에 성공하였습니다.")) authViewModel.setEvent(AuthContract.AuthEvent.ResetSignInState) authViewModel.setSideEffect(AuthContract.AuthSideEffect.NavigateToMain) } + is SignInState.Failure -> { authViewModel.setSideEffect(AuthContract.AuthSideEffect.ShowToast(message = "아이디와 비밀번호를 다시 확인해주세요.")) authViewModel.setEvent(AuthContract.AuthEvent.ResetSignInState) } + else -> {} } } @@ -235,7 +238,8 @@ fun SignInScreen( color = Color(0xFF5D5D5D) ) Text( - text = "SNS계정으로 간편하게 가입하여 서비스를 이용하실 수 있습니다.\n기존 POOQ 계정 또는 Wavve 계정과는 연동되지 않으니 이용에 참고하세요", + text = "SNS계정으로 간편하게 가입하여 서비스를 이용하실 수 있습니다.\n" + + "기존 POOQ 계정 또는 Wavve 계정과는 연동되지 않으니 이용에 참고하세요", modifier = Modifier.fillMaxWidth(), fontSize = 10.sp, lineHeight = 14.sp, diff --git a/app/src/main/java/org/sopt/and/presentation/ui/auth/screen/SignUpScreen.kt b/app/src/main/java/org/sopt/and/presentation/ui/auth/screen/SignUpScreen.kt index 12acb5d..d0bdd0b 100644 --- a/app/src/main/java/org/sopt/and/presentation/ui/auth/screen/SignUpScreen.kt +++ b/app/src/main/java/org/sopt/and/presentation/ui/auth/screen/SignUpScreen.kt @@ -18,9 +18,6 @@ import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.remember -import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip @@ -38,9 +35,9 @@ import androidx.lifecycle.compose.LocalLifecycleOwner import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.lifecycle.flowWithLifecycle import org.sopt.and.R -import org.sopt.and.presentation.ui.common.WavveTextField import org.sopt.and.presentation.ui.auth.component.SocialPlatformIconRow import org.sopt.and.presentation.ui.auth.component.SocialPlatformList +import org.sopt.and.presentation.ui.common.WavveTextField import org.sopt.and.presentation.util.showToast import org.sopt.and.ui.theme.ANDANDROIDTheme @@ -57,16 +54,20 @@ fun SignUpRoute( LaunchedEffect(authViewModel.sideEffect, lifecycleOwner) { authViewModel.sideEffect.flowWithLifecycle(lifecycle = lifecycleOwner.lifecycle) .collect { authSideEffect -> - when(authSideEffect) { + when (authSideEffect) { is AuthContract.AuthSideEffect.NavigateToSignIn -> navigateToSignIn() - is AuthContract.AuthSideEffect.ShowToast -> showToast(context = context, message = authSideEffect.message) + is AuthContract.AuthSideEffect.ShowToast -> showToast( + context = context, + message = authSideEffect.message + ) + else -> {} } } } LaunchedEffect(authUiState.signUpState) { - when(authUiState.signUpState) { + when (authUiState.signUpState) { is SignUpState.Success -> { authViewModel.setSideEffect( AuthContract.AuthSideEffect.ShowToast(message = "회원가입에 성공했습니다. 유저번호는 ${(authUiState.signUpState as SignUpState.Success).result.no}입니다.") @@ -74,12 +75,14 @@ fun SignUpRoute( authViewModel.setEvent(AuthContract.AuthEvent.ResetSignUpState) authViewModel.setSideEffect(AuthContract.AuthSideEffect.NavigateToSignIn) } + is SignUpState.Failure -> { authViewModel.setSideEffect( AuthContract.AuthSideEffect.ShowToast(message = "회원가입에 실패했습니다. 형식을 다시 확인해주세요.") ) authViewModel.setEvent(AuthContract.AuthEvent.ResetSignUpState) } + else -> {} } }