diff --git a/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/ui/main/MainScreen.kt b/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/ui/main/MainScreen.kt index 9cbaafac..2946ff5f 100644 --- a/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/ui/main/MainScreen.kt +++ b/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/ui/main/MainScreen.kt @@ -6,12 +6,7 @@ import ac.dnd.bookkeeping.android.presentation.ui.main.home.homeDestination import ac.dnd.bookkeeping.android.presentation.ui.main.login.loginNavGraph import ac.dnd.bookkeeping.android.presentation.ui.main.splash.SplashConstant import ac.dnd.bookkeeping.android.presentation.ui.main.splash.splashDestination -import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.material.MaterialTheme -import androidx.compose.material.Surface import androidx.compose.runtime.Composable -import androidx.compose.ui.Modifier -import androidx.compose.ui.tooling.preview.Preview import androidx.hilt.navigation.compose.hiltViewModel import androidx.navigation.compose.NavHost @@ -20,30 +15,19 @@ fun MainScreen( viewModel: MainViewModel = hiltViewModel() ) { BookkeepingTheme { - Surface( - modifier = Modifier.fillMaxSize(), - color = MaterialTheme.colors.background - ) { - val appState = rememberApplicationState() - ManageSystemUiState(appState = appState) + val appState = rememberApplicationState() + ManageSystemUiState(appState = appState) - NavHost( - navController = appState.navController, - startDestination = makeRoute( - SplashConstant.ROUTE, - listOf(SplashConstant.ROUTE_ARGUMENT_ENTRY_POINT_MAIN to SplashConstant.ROUTE_ARGUMENT_ENTRY_POINT_MAIN) - ) - ) { - loginNavGraph(appState = appState) - homeDestination(appState = appState) - splashDestination(appState = appState) - } + NavHost( + navController = appState.navController, + startDestination = makeRoute( + SplashConstant.ROUTE, + listOf(SplashConstant.ROUTE_ARGUMENT_ENTRY_POINT_MAIN to SplashConstant.ROUTE_ARGUMENT_ENTRY_POINT_MAIN) + ) + ) { + loginNavGraph(appState = appState) + homeDestination(appState = appState) + splashDestination(appState = appState) } } } - -@Preview -@Composable -fun PreviewMainUi() { - MainScreen() -} diff --git a/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/ui/main/MainSystemUiState.kt b/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/ui/main/MainSystemUiState.kt index adee4032..915bf720 100644 --- a/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/ui/main/MainSystemUiState.kt +++ b/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/ui/main/MainSystemUiState.kt @@ -1,7 +1,6 @@ package ac.dnd.bookkeeping.android.presentation.ui.main import ac.dnd.bookkeeping.android.presentation.common.util.parseRoute -import ac.dnd.bookkeeping.android.presentation.ui.main.splash.SplashConstant import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.navigation.compose.currentBackStackEntryAsState @@ -15,15 +14,9 @@ fun ManageSystemUiState( parseRoute(it).first }.orEmpty() - when { - route.startsWith(SplashConstant.ROUTE) -> { - appState.systemUiController.isStatusBarVisible = false - appState.systemUiController.isNavigationBarVisible = false - } - + when (route) { else -> { - appState.systemUiController.isStatusBarVisible = false - appState.systemUiController.isNavigationBarVisible = true + appState.systemUiController.isStatusBarVisible = true } } } diff --git a/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/ui/main/login/LoginConstant.kt b/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/ui/main/login/LoginConstant.kt index 844d9387..53f2e0d6 100644 --- a/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/ui/main/login/LoginConstant.kt +++ b/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/ui/main/login/LoginConstant.kt @@ -2,7 +2,4 @@ package ac.dnd.bookkeeping.android.presentation.ui.main.login object LoginConstant { const val ROUTE = "/login" - - const val ROUTE_STEP_1 = "$ROUTE/step1" - const val ROUTE_STEP_2 = "$ROUTE/step2" } diff --git a/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/ui/main/login/LoginNavGraph.kt b/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/ui/main/login/LoginNavGraph.kt index ef0161e3..b87d7f2e 100644 --- a/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/ui/main/login/LoginNavGraph.kt +++ b/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/ui/main/login/LoginNavGraph.kt @@ -1,39 +1,18 @@ package ac.dnd.bookkeeping.android.presentation.ui.main.login import ac.dnd.bookkeeping.android.presentation.ui.main.ApplicationState -import androidx.compose.runtime.remember -import androidx.hilt.navigation.compose.hiltViewModel +import ac.dnd.bookkeeping.android.presentation.ui.main.login.main.LoginMainConstant +import ac.dnd.bookkeeping.android.presentation.ui.main.login.main.loginMainDestination import androidx.navigation.NavGraphBuilder -import androidx.navigation.compose.composable import androidx.navigation.navigation fun NavGraphBuilder.loginNavGraph( appState: ApplicationState ) { navigation( - startDestination = LoginConstant.ROUTE_STEP_1, + startDestination = LoginMainConstant.ROUTE, route = LoginConstant.ROUTE ) { - // TODO : 분리 - composable( - route = LoginConstant.ROUTE_STEP_1 - ) { - val backStackEntry = remember(it) { - appState.navController.getBackStackEntry(LoginConstant.ROUTE_STEP_1) - } - val loginViewModel: LoginViewModel = hiltViewModel(backStackEntry) - LoginScreen(appState = appState, viewModel = loginViewModel) - } - - // TODO : 분리 - composable( - route = LoginConstant.ROUTE_STEP_2 - ) { - val backStackEntry = remember(it) { - appState.navController.getBackStackEntry(LoginConstant.ROUTE) - } - val loginViewModel: LoginViewModel = hiltViewModel(backStackEntry) - OnBoardScreen(appState = appState, viewModel = loginViewModel) - } + loginMainDestination(appState = appState) } } diff --git a/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/ui/main/login/LoginScreen.kt b/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/ui/main/login/LoginScreen.kt deleted file mode 100644 index 6498de6a..00000000 --- a/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/ui/main/login/LoginScreen.kt +++ /dev/null @@ -1,82 +0,0 @@ -package ac.dnd.bookkeeping.android.presentation.ui.main.login - -import ac.dnd.bookkeeping.android.presentation.common.util.ErrorObserver -import ac.dnd.bookkeeping.android.presentation.ui.main.ApplicationState -import androidx.compose.foundation.background -import androidx.compose.foundation.clickable -import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.shape.RoundedCornerShape -import androidx.compose.material.Surface -import androidx.compose.material.Text -import androidx.compose.runtime.Composable -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.remember -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.text.style.TextAlign -import androidx.compose.ui.unit.dp -import androidx.compose.ui.unit.sp -import androidx.hilt.navigation.compose.hiltViewModel - -@Composable -fun LoginScreen( - appState: ApplicationState, - viewModel: LoginViewModel = hiltViewModel() -) { - - Observer( - appState = appState, - viewModel = viewModel - ) - - val nextStageState = remember { mutableStateOf(false) } - if (nextStageState.value) { - appState.navController.navigate(LoginConstant.ROUTE_STEP_2) - nextStageState.value = false - } - - Box( - modifier = Modifier - .fillMaxSize() - .background(color = Color.Black), - ) { - Text( - text = "Login Screen", - fontSize = 20.sp, - color = Color.White, - modifier = Modifier.align(Alignment.Center) - ) - - Surface( - modifier = Modifier - .align(Alignment.BottomCenter) - .padding(30.dp) - .fillMaxWidth() - .clickable { - nextStageState.value = true - }, - shape = RoundedCornerShape(10.dp), - color = Color.White - ) { - Text( - text = "next", - fontSize = 20.sp, - color = Color.Black, - textAlign = TextAlign.Center, - modifier = Modifier.padding(5.dp) - ) - } - } -} - -@Composable -private fun Observer( - appState: ApplicationState, - viewModel: LoginViewModel -) { - ErrorObserver(viewModel) -} diff --git a/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/ui/main/login/OnBoardScreen.kt b/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/ui/main/login/OnBoardScreen.kt deleted file mode 100644 index 225bc9c2..00000000 --- a/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/ui/main/login/OnBoardScreen.kt +++ /dev/null @@ -1,72 +0,0 @@ -package ac.dnd.bookkeeping.android.presentation.ui.main.login - -import ac.dnd.bookkeeping.android.presentation.ui.main.ApplicationState -import ac.dnd.bookkeeping.android.presentation.ui.main.home.HomeConstant -import androidx.compose.foundation.background -import androidx.compose.foundation.clickable -import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.shape.RoundedCornerShape -import androidx.compose.material.Surface -import androidx.compose.material.Text -import androidx.compose.runtime.Composable -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.remember -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.text.style.TextAlign -import androidx.compose.ui.unit.dp -import androidx.compose.ui.unit.sp -import androidx.hilt.navigation.compose.hiltViewModel - -@Composable -fun OnBoardScreen( - appState: ApplicationState, - viewModel: LoginViewModel = hiltViewModel() -) { - val nextStageState = remember { mutableStateOf(false) } - if (nextStageState.value) { - appState.navController.navigate(HomeConstant.ROUTE) { - popUpTo(LoginConstant.ROUTE_STEP_2) { - inclusive = true - } - } - nextStageState.value = false - } - - Box( - modifier = Modifier - .fillMaxSize() - .background(color = Color.Black), - ) { - Text( - text = "OnBoard Screen", - fontSize = 20.sp, - color = Color.White, - modifier = Modifier.align(Alignment.Center) - ) - - Surface( - modifier = Modifier - .align(Alignment.BottomCenter) - .padding(30.dp) - .fillMaxWidth() - .clickable { - nextStageState.value = true - }, - shape = RoundedCornerShape(10.dp), - color = Color.White - ) { - Text( - text = "next", - fontSize = 20.sp, - color = Color.Black, - textAlign = TextAlign.Center, - modifier = Modifier.padding(5.dp) - ) - } - } -} diff --git a/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/ui/main/login/main/LoginMainConstant.kt b/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/ui/main/login/main/LoginMainConstant.kt new file mode 100644 index 00000000..0ce3f8ba --- /dev/null +++ b/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/ui/main/login/main/LoginMainConstant.kt @@ -0,0 +1,7 @@ +package ac.dnd.bookkeeping.android.presentation.ui.main.login.main + +import ac.dnd.bookkeeping.android.presentation.ui.main.login.LoginConstant + +object LoginMainConstant { + const val ROUTE: String = "${LoginConstant.ROUTE}/main" +} diff --git a/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/ui/main/login/main/LoginMainDestination.kt b/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/ui/main/login/main/LoginMainDestination.kt new file mode 100644 index 00000000..9a1cd758 --- /dev/null +++ b/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/ui/main/login/main/LoginMainDestination.kt @@ -0,0 +1,44 @@ +package ac.dnd.bookkeeping.android.presentation.ui.main.login.main + +import ac.dnd.bookkeeping.android.presentation.common.util.ErrorObserver +import ac.dnd.bookkeeping.android.presentation.ui.main.ApplicationState +import ac.dnd.bookkeeping.android.presentation.ui.main.login.LoginConstant +import ac.dnd.bookkeeping.android.presentation.ui.main.login.LoginViewModel +import androidx.compose.runtime.getValue +import androidx.compose.runtime.remember +import androidx.hilt.navigation.compose.hiltViewModel +import androidx.lifecycle.compose.collectAsStateWithLifecycle +import androidx.navigation.NavGraphBuilder +import androidx.navigation.compose.composable + +fun NavGraphBuilder.loginMainDestination( + appState: ApplicationState +) { + composable( + route = LoginMainConstant.ROUTE + ) { backStackEntry -> + val parentEntry = remember(backStackEntry) { + appState.navController.getBackStackEntry(LoginConstant.ROUTE) + } + val parentViewModel: LoginViewModel = hiltViewModel(parentEntry) + val viewModel: LoginMainViewModel = hiltViewModel() + + val model: LoginMainModel = let { + val state by viewModel.state.collectAsStateWithLifecycle() + + LoginMainModel( + state = state + ) + } + + ErrorObserver(viewModel) + + LoginMainScreen( + appState = appState, + model = model, + event = viewModel.event, + intent = viewModel::onIntent, + handler = viewModel.handler + ) + } +} diff --git a/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/ui/main/login/main/LoginMainEvent.kt b/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/ui/main/login/main/LoginMainEvent.kt new file mode 100644 index 00000000..f6f947e0 --- /dev/null +++ b/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/ui/main/login/main/LoginMainEvent.kt @@ -0,0 +1,11 @@ +package ac.dnd.bookkeeping.android.presentation.ui.main.login.main + +import ac.dnd.bookkeeping.android.domain.model.error.ServerException + +sealed interface LoginMainEvent { + sealed interface Login : LoginMainEvent { + data object Success : Login + data class Failure(val exception: ServerException) : Login + data class Error(val exception: Throwable) : Login + } +} diff --git a/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/ui/main/login/main/LoginMainIntent.kt b/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/ui/main/login/main/LoginMainIntent.kt new file mode 100644 index 00000000..67a7fedc --- /dev/null +++ b/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/ui/main/login/main/LoginMainIntent.kt @@ -0,0 +1,5 @@ +package ac.dnd.bookkeeping.android.presentation.ui.main.login.main + +sealed interface LoginMainIntent { + data object Click : LoginMainIntent +} diff --git a/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/ui/main/login/main/LoginMainModel.kt b/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/ui/main/login/main/LoginMainModel.kt new file mode 100644 index 00000000..45c3082b --- /dev/null +++ b/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/ui/main/login/main/LoginMainModel.kt @@ -0,0 +1,5 @@ +package ac.dnd.bookkeeping.android.presentation.ui.main.login.main + +data class LoginMainModel( + val state: LoginMainState, +) diff --git a/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/ui/main/login/main/LoginMainScreen.kt b/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/ui/main/login/main/LoginMainScreen.kt new file mode 100644 index 00000000..469f614c --- /dev/null +++ b/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/ui/main/login/main/LoginMainScreen.kt @@ -0,0 +1,116 @@ +package ac.dnd.bookkeeping.android.presentation.ui.main.login.main + +import ac.dnd.bookkeeping.android.presentation.common.util.LaunchedEffectWithLifecycle +import ac.dnd.bookkeeping.android.presentation.common.util.coroutine.event.EventFlow +import ac.dnd.bookkeeping.android.presentation.common.util.coroutine.event.MutableEventFlow +import ac.dnd.bookkeeping.android.presentation.common.util.coroutine.event.eventObserve +import ac.dnd.bookkeeping.android.presentation.ui.main.ApplicationState +import ac.dnd.bookkeeping.android.presentation.ui.main.home.HomeConstant +import ac.dnd.bookkeeping.android.presentation.ui.main.login.LoginConstant +import ac.dnd.bookkeeping.android.presentation.ui.main.rememberApplicationState +import androidx.compose.foundation.background +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material.Surface +import androidx.compose.material.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import kotlinx.coroutines.CoroutineExceptionHandler + +@Composable +fun LoginMainScreen( + appState: ApplicationState, + model: LoginMainModel, + event: EventFlow, + intent: (LoginMainIntent) -> Unit, + handler: CoroutineExceptionHandler +) { + fun navigateToLogin() { + appState.navController.navigate(HomeConstant.ROUTE) { + popUpTo(LoginConstant.ROUTE) { + inclusive = true + } + } + } + + fun login(event: LoginMainEvent.Login) { + when (event) { + LoginMainEvent.Login.Success -> { + // TODO : Implement Success Case + } + + is LoginMainEvent.Login.Error -> { + // TODO : Implement Request Error Case + } + + is LoginMainEvent.Login.Failure -> { + // TODO : Implement Internal Server Error Case + } + } + } + + Box( + modifier = Modifier + .fillMaxSize() + .background(color = Color.Black), + ) { + Text( + text = "OnBoard Screen", + fontSize = 20.sp, + color = Color.White, + modifier = Modifier.align(Alignment.Center) + ) + + Surface( + modifier = Modifier + .align(Alignment.BottomCenter) + .padding(30.dp) + .fillMaxWidth() + .clickable { + navigateToLogin() + }, + shape = RoundedCornerShape(10.dp), + color = Color.White + ) { + Text( + text = "next", + fontSize = 20.sp, + color = Color.Black, + textAlign = TextAlign.Center, + modifier = Modifier.padding(5.dp) + ) + } + } + + LaunchedEffectWithLifecycle(event, handler) { + event.eventObserve { event -> + when (event) { + is LoginMainEvent.Login -> login(event) + } + } + } +} + +@Preview +@Composable +fun LoginMainScreenPreview() { + LoginMainScreen( + appState = rememberApplicationState(), + model = LoginMainModel( + state = LoginMainState.Init + ), + event = MutableEventFlow(), + intent = {}, + handler = CoroutineExceptionHandler { _, _ -> } + ) +} diff --git a/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/ui/main/login/main/LoginMainState.kt b/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/ui/main/login/main/LoginMainState.kt new file mode 100644 index 00000000..f1c446f1 --- /dev/null +++ b/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/ui/main/login/main/LoginMainState.kt @@ -0,0 +1,5 @@ +package ac.dnd.bookkeeping.android.presentation.ui.main.login.main + +sealed interface LoginMainState { + data object Init : LoginMainState +} diff --git a/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/ui/main/login/main/LoginMainViewModel.kt b/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/ui/main/login/main/LoginMainViewModel.kt new file mode 100644 index 00000000..7b53e10d --- /dev/null +++ b/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/ui/main/login/main/LoginMainViewModel.kt @@ -0,0 +1,32 @@ +package ac.dnd.bookkeeping.android.presentation.ui.main.login.main + +import ac.dnd.bookkeeping.android.presentation.common.base.BaseViewModel +import ac.dnd.bookkeeping.android.presentation.common.util.coroutine.event.EventFlow +import ac.dnd.bookkeeping.android.presentation.common.util.coroutine.event.MutableEventFlow +import ac.dnd.bookkeeping.android.presentation.common.util.coroutine.event.asEventFlow +import androidx.lifecycle.SavedStateHandle +import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.asStateFlow +import javax.inject.Inject + +@HiltViewModel +class LoginMainViewModel @Inject constructor( + private val savedStateHandle: SavedStateHandle, +) : BaseViewModel() { + + private val _state: MutableStateFlow = MutableStateFlow(LoginMainState.Init) + val state: StateFlow = _state.asStateFlow() + + private val _event: MutableEventFlow = MutableEventFlow() + val event: EventFlow = _event.asEventFlow() + + fun onIntent(intent: LoginMainIntent) { + when (intent) { + LoginMainIntent.Click -> { + + } + } + } +}