diff --git a/feature/main/src/main/java/org/sopt/main/login/LoginContract.kt b/feature/main/src/main/java/org/sopt/main/login/LoginContract.kt index 22d09f2..6654aa8 100644 --- a/feature/main/src/main/java/org/sopt/main/login/LoginContract.kt +++ b/feature/main/src/main/java/org/sopt/main/login/LoginContract.kt @@ -1,19 +1,24 @@ package org.sopt.main.login -import org.sopt.main.model.User - data class LoginState( val id: String = "", val password: String = "", val registeredId: String = "", val registeredPassword: String = "", - val name: String = "", - val hobby: String = "" -) + val registeredName: String = "", + val registeredHobby: String = "", + val isAutoLogin: Boolean = false, +) { + fun checkRegister() = registeredId.isBlank() || registeredPassword.isBlank() + + fun matchesUserInfo(id: String, password: String) = + registeredId == id && registeredPassword == password + +} sealed interface LoginSideEffect { data class showSnackbar(val message: String) : LoginSideEffect - data class LoginSuccess(val user: User) : LoginSideEffect + data object LoginSuccess : LoginSideEffect data object NavigateToSignUp : LoginSideEffect data object SignupSuccess : LoginSideEffect } \ No newline at end of file diff --git a/feature/main/src/main/java/org/sopt/main/login/LoginScreen.kt b/feature/main/src/main/java/org/sopt/main/login/LoginScreen.kt index 0a8a3c2..7cae877 100644 --- a/feature/main/src/main/java/org/sopt/main/login/LoginScreen.kt +++ b/feature/main/src/main/java/org/sopt/main/login/LoginScreen.kt @@ -1,6 +1,6 @@ package org.sopt.main.login -import android.os.Bundle +import android.annotation.SuppressLint import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Spacer @@ -14,33 +14,30 @@ import androidx.compose.material3.SnackbarHost import androidx.compose.material3.SnackbarHostState import androidx.compose.material3.Text import androidx.compose.runtime.Composable -import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.remember +import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.hilt.navigation.compose.hiltViewModel -import androidx.navigation.NavController +import kotlinx.coroutines.launch import org.orbitmvi.orbit.compose.collectAsState import org.orbitmvi.orbit.compose.collectSideEffect import org.sopt.designsystem.component.button.RegularButton import org.sopt.designsystem.component.textfield.RegularTextField import org.sopt.designsystem.ui.theme.NOWSOPTAndroidTheme -import org.sopt.main.model.User -import org.sopt.ui.intent.getParcelableSafe @Composable fun LoginRoute( - navController: NavController, viewModel: LoginViewModel = hiltViewModel(), - popBackStack: () -> Unit, - navigateHome: (User) -> Unit, + navigateHome: () -> Unit, navigateSignup: () -> Unit, -){ +) { val state = viewModel.collectAsState().value val snackBarHostState = remember { SnackbarHostState() } + val scope = rememberCoroutineScope() viewModel.collectSideEffect { sideEffect -> when (sideEffect) { @@ -49,27 +46,25 @@ fun LoginRoute( } is LoginSideEffect.LoginSuccess -> { - navigateHome(sideEffect.user) + navigateHome() } + LoginSideEffect.SignupSuccess -> { - snackBarHostState.currentSnackbarData?.dismiss() - snackBarHostState.showSnackbar("회원가입 성공") + scope.launch { + snackBarHostState.currentSnackbarData?.dismiss() + snackBarHostState.showSnackbar("회원가입 성공") + } } + is LoginSideEffect.showSnackbar -> { - snackBarHostState.currentSnackbarData?.dismiss() - snackBarHostState.showSnackbar(sideEffect.message) + scope.launch { + snackBarHostState.currentSnackbarData?.dismiss() + snackBarHostState.showSnackbar(sideEffect.message) + } } } } - LaunchedEffect(true) { - navController.previousBackStackEntry?.savedStateHandle?.run { - val bundle = get("user") - val user = bundle?.getParcelableSafe("user", User::class.java) - viewModel.signupSuccess(user) - } - } - LoginScreen( snackBarHostState = snackBarHostState, state = state, @@ -80,6 +75,7 @@ fun LoginRoute( ) } +@SuppressLint("UnusedMaterial3ScaffoldPaddingParameter") @Composable fun LoginScreen( snackBarHostState: SnackbarHostState, @@ -87,20 +83,25 @@ fun LoginScreen( onClickLoginBtn: () -> Unit = {}, onClickSignupBtn: () -> Unit = {}, onValueChangeId: (String) -> Unit = {}, - onValueChangePw: (String) -> Unit = {} + onValueChangePw: (String) -> Unit = {}, ) { Scaffold( snackbarHost = { SnackbarHost(hostState = snackBarHostState) } - ){ it -> + ) { Box( modifier = Modifier .fillMaxSize() - .padding(top = 50.dp + it.calculateTopPadding(), bottom = 20.dp + it.calculateBottomPadding(), start = 20.dp, end = 20.dp) + .padding( + top = 50.dp, + bottom = 20.dp, + start = 20.dp, + end = 20.dp + ) ) { Column( horizontalAlignment = Alignment.CenterHorizontally ) { - Box ( + Box( modifier = Modifier .fillMaxWidth(), contentAlignment = Alignment.Center diff --git a/feature/main/src/main/java/org/sopt/main/login/LoginViewModel.kt b/feature/main/src/main/java/org/sopt/main/login/LoginViewModel.kt index 0aa9dfe..1fca45a 100644 --- a/feature/main/src/main/java/org/sopt/main/login/LoginViewModel.kt +++ b/feature/main/src/main/java/org/sopt/main/login/LoginViewModel.kt @@ -4,20 +4,45 @@ import androidx.lifecycle.ViewModel import dagger.hilt.android.lifecycle.HiltViewModel import org.orbitmvi.orbit.Container import org.orbitmvi.orbit.ContainerHost -import org.orbitmvi.orbit.annotation.OrbitExperimental -import org.orbitmvi.orbit.syntax.simple.blockingIntent import org.orbitmvi.orbit.syntax.simple.intent import org.orbitmvi.orbit.syntax.simple.postSideEffect import org.orbitmvi.orbit.syntax.simple.reduce import org.orbitmvi.orbit.viewmodel.container -import org.sopt.main.model.User +import org.sopt.domain.repo.UserDataRepository +import org.sopt.ui.orbit.updateState import javax.inject.Inject @HiltViewModel class LoginViewModel @Inject constructor( + private val userDataRepository: UserDataRepository ) : ContainerHost, ViewModel() { override val container: Container = container(LoginState()) + init { + getUserData() + intent { + container.stateFlow.collect { + if (it.isAutoLogin) { + postSideEffect(LoginSideEffect.LoginSuccess) + } + } + } + } + + fun getUserData() = intent { + userDataRepository.getUserData().collect { + reduce { + state.copy( + registeredId = it.id, + registeredPassword = it.pw, + registeredHobby = it.hobby, + registeredName = it.name, + isAutoLogin = it.autoLogin, + ) + } + } + } + fun signup() = intent { postSideEffect(LoginSideEffect.NavigateToSignUp) } @@ -26,35 +51,15 @@ class LoginViewModel @Inject constructor( with(state) { when { checkRegister() -> postSideEffect(LoginSideEffect.showSnackbar("회원가입 먼저하셈")) - matchesUserInfo(state.id, state.password) -> postSideEffect(LoginSideEffect.LoginSuccess(createUser())) + matchesUserInfo(state.id, state.password) -> { + userDataRepository.setAutoLogin(true) + } + else -> postSideEffect(LoginSideEffect.showSnackbar("로그인 실패")) } } } - private fun LoginState.checkRegister() = registeredId.isBlank() || registeredPassword.isBlank() - - private fun LoginState.matchesUserInfo(id: String, password: String) = registeredId == id && registeredPassword == password - - private fun LoginState.createUser() = User(registeredId, registeredPassword, name, hobby) fun updateId(id: String) = updateState { copy(id = id) } fun updatePw(pw: String) = updateState { copy(password = pw) } - - @OptIn(OrbitExperimental::class) - private fun updateState(reducer: LoginState.() -> LoginState) = blockingIntent { - reduce { state.reducer() } - } - - fun signupSuccess(user: User?) = intent { - postSideEffect(LoginSideEffect.SignupSuccess) - - reduce { - state.copy( - registeredId = user?.id.orEmpty(), - registeredPassword = user?.pw.orEmpty(), - name = user?.name.orEmpty(), - hobby = user?.hobby.orEmpty() - ) - } - } } \ No newline at end of file diff --git a/feature/main/src/main/java/org/sopt/main/login/navigation/LoginNavigation.kt b/feature/main/src/main/java/org/sopt/main/login/navigation/LoginNavigation.kt index 17dc939..85f37a8 100644 --- a/feature/main/src/main/java/org/sopt/main/login/navigation/LoginNavigation.kt +++ b/feature/main/src/main/java/org/sopt/main/login/navigation/LoginNavigation.kt @@ -1,31 +1,25 @@ package org.sopt.main.login.navigation -import android.os.Bundle import androidx.navigation.NavController import androidx.navigation.NavGraphBuilder import androidx.navigation.compose.composable import org.sopt.main.login.LoginRoute -import org.sopt.main.model.User -fun NavController.navigateLogin(user: User) { - val bundle = Bundle().apply { - putParcelable("user", user) - } +fun NavController.navigateLogin() { navigate(LoginRoute.route){ - currentBackStackEntry?.savedStateHandle?.set("user", bundle) + popUpTo(this@navigateLogin.graph.startDestinationId){ + inclusive = true + } + launchSingleTop = true } } fun NavGraphBuilder.loginNavGraph( - navController: NavController, - popBackStack: () -> Unit = {}, navigateSignup: () -> Unit = {}, - navigateHome: (User) -> Unit = {}, + navigateHome: () -> Unit = {}, ){ - composable(route = LoginRoute.route) { navBackStackEntry -> + composable(route = LoginRoute.route) { LoginRoute( - navController = navController, - popBackStack = popBackStack, navigateHome = navigateHome, navigateSignup = navigateSignup )