From ce0fb75c21dfb8fef34ab69ce335b21d76290f5e Mon Sep 17 00:00:00 2001 From: Ray Jang <48707913+ajou4095@users.noreply.github.com> Date: Wed, 7 Feb 2024 02:18:28 +0900 Subject: [PATCH] =?UTF-8?q?[Feature]=20Home=20item=20=EC=97=B0=EA=B2=B0=20?= =?UTF-8?q?(#56)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../model/main/MainBottomBarItem.kt | 9 + .../ui/main/home/HomeBottomBarScreen.kt | 56 ------ .../ui/main/home/HomeDestination.kt | 25 ++- .../presentation/ui/main/home/HomeEvent.kt | 5 + .../presentation/ui/main/home/HomeIntent.kt | 3 + .../presentation/ui/main/home/HomeModel.kt | 5 + .../presentation/ui/main/home/HomeScreen.kt | 181 ++++++++++++++---- .../presentation/ui/main/home/HomeState.kt | 5 + .../ui/main/home/HomeViewModel.kt | 21 +- .../ui/main/home/mypage/MyPageScreen.kt | 28 ++- .../ui/main/home/schedule/ScheduleScreen.kt | 30 ++- .../ui/main/home/setting/SettingViewModel.kt | 11 -- .../main/home/statistics/StatisticsScreen.kt | 28 ++- .../ui/main/home/test/TestConstant.kt | 5 + .../ui/main/home/test/TestEvent.kt | 3 + .../ui/main/home/test/TestIntent.kt | 3 + .../ui/main/home/test/TestModel.kt | 5 + .../SettingScreen.kt => test/TestScreen.kt} | 77 +++++--- .../ui/main/home/test/TestState.kt | 5 + .../ui/main/home/test/TestViewModel.kt | 28 +++ 20 files changed, 402 insertions(+), 131 deletions(-) create mode 100644 presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/model/main/MainBottomBarItem.kt delete mode 100644 presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/ui/main/home/HomeBottomBarScreen.kt create mode 100644 presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/ui/main/home/HomeEvent.kt create mode 100644 presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/ui/main/home/HomeIntent.kt create mode 100644 presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/ui/main/home/HomeModel.kt create mode 100644 presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/ui/main/home/HomeState.kt delete mode 100644 presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/ui/main/home/setting/SettingViewModel.kt create mode 100644 presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/ui/main/home/test/TestConstant.kt create mode 100644 presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/ui/main/home/test/TestEvent.kt create mode 100644 presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/ui/main/home/test/TestIntent.kt create mode 100644 presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/ui/main/home/test/TestModel.kt rename presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/ui/main/home/{setting/SettingScreen.kt => test/TestScreen.kt} (84%) create mode 100644 presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/ui/main/home/test/TestState.kt create mode 100644 presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/ui/main/home/test/TestViewModel.kt diff --git a/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/model/main/MainBottomBarItem.kt b/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/model/main/MainBottomBarItem.kt new file mode 100644 index 00000000..f849dbe2 --- /dev/null +++ b/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/model/main/MainBottomBarItem.kt @@ -0,0 +1,9 @@ +package ac.dnd.bookkeeping.android.presentation.model.main + +import androidx.annotation.DrawableRes + +data class MainBottomBarItem( + val route: String, + val name: String, + @DrawableRes val icon: Int +) diff --git a/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/ui/main/home/HomeBottomBarScreen.kt b/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/ui/main/home/HomeBottomBarScreen.kt deleted file mode 100644 index d5258559..00000000 --- a/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/ui/main/home/HomeBottomBarScreen.kt +++ /dev/null @@ -1,56 +0,0 @@ -package ac.dnd.bookkeeping.android.presentation.ui.main.home - -import ac.dnd.bookkeeping.android.presentation.R -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.height -import androidx.compose.foundation.layout.padding -import androidx.compose.material.BottomNavigation -import androidx.compose.material.BottomNavigationItem -import androidx.compose.material.Icon -import androidx.compose.material.Text -import androidx.compose.runtime.Composable -import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.res.painterResource -import androidx.compose.ui.unit.dp -import androidx.compose.ui.unit.sp - -@Composable -fun HomeBottomBarScreen( - selectedItem: Int, - onClick: (Int) -> Unit -) { - BottomNavigation( - elevation = 0.dp, - backgroundColor = Color.LightGray, - modifier = Modifier - .fillMaxWidth() - .height(80.dp) - ) { - val list = listOf("bookkeeping", "setting") - list.forEachIndexed { index, screen -> - BottomNavigationItem( - selected = index == selectedItem, - icon = { - Icon( - painter = painterResource(id = R.drawable.ic_account), - contentDescription = "bottom icon" - ) - }, - label = { - Text( - text = screen, - fontSize = 12.sp, - color = Color.Black, - modifier = Modifier.padding(0.dp) - ) - }, - selectedContentColor = Color.Black, - unselectedContentColor = Color.Gray, - onClick = { - onClick(index) - } - ) - } - } -} diff --git a/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/ui/main/home/HomeDestination.kt b/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/ui/main/home/HomeDestination.kt index 923a8275..2805b21c 100644 --- a/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/ui/main/home/HomeDestination.kt +++ b/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/ui/main/home/HomeDestination.kt @@ -1,6 +1,11 @@ package ac.dnd.bookkeeping.android.presentation.ui.main.home +import ac.dnd.bookkeeping.android.presentation.common.util.ErrorObserver import ac.dnd.bookkeeping.android.presentation.ui.main.ApplicationState +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 @@ -10,6 +15,24 @@ fun NavGraphBuilder.homeDestination( composable( route = HomeConstant.ROUTE ) { - HomeScreen(appState) + val viewModel: HomeViewModel = hiltViewModel() + + val model: HomeModel = let { + val state by viewModel.state.collectAsStateWithLifecycle() + + HomeModel( + state = state + ) + } + + ErrorObserver(viewModel) + + HomeScreen( + 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/home/HomeEvent.kt b/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/ui/main/home/HomeEvent.kt new file mode 100644 index 00000000..d4a34351 --- /dev/null +++ b/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/ui/main/home/HomeEvent.kt @@ -0,0 +1,5 @@ +package ac.dnd.bookkeeping.android.presentation.ui.main.home + +sealed interface HomeEvent { + data class ShowSnackBar(val message: String) : HomeEvent +} diff --git a/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/ui/main/home/HomeIntent.kt b/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/ui/main/home/HomeIntent.kt new file mode 100644 index 00000000..8b3c9991 --- /dev/null +++ b/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/ui/main/home/HomeIntent.kt @@ -0,0 +1,3 @@ +package ac.dnd.bookkeeping.android.presentation.ui.main.home + +sealed interface HomeIntent diff --git a/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/ui/main/home/HomeModel.kt b/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/ui/main/home/HomeModel.kt new file mode 100644 index 00000000..d9312ac1 --- /dev/null +++ b/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/ui/main/home/HomeModel.kt @@ -0,0 +1,5 @@ +package ac.dnd.bookkeeping.android.presentation.ui.main.home + +data class HomeModel( + val state: HomeState, +) diff --git a/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/ui/main/home/HomeScreen.kt b/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/ui/main/home/HomeScreen.kt index ecd19ec1..cd87080e 100644 --- a/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/ui/main/home/HomeScreen.kt +++ b/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/ui/main/home/HomeScreen.kt @@ -1,46 +1,97 @@ package ac.dnd.bookkeeping.android.presentation.ui.main.home -import ac.dnd.bookkeeping.android.presentation.common.util.ErrorObserver +import ac.dnd.bookkeeping.android.presentation.R +import ac.dnd.bookkeeping.android.presentation.common.theme.Caption4 +import ac.dnd.bookkeeping.android.presentation.common.theme.Icon24 +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.common.view.CustomSnackBarHost +import ac.dnd.bookkeeping.android.presentation.model.main.MainBottomBarItem import ac.dnd.bookkeeping.android.presentation.ui.main.ApplicationState -import ac.dnd.bookkeeping.android.presentation.ui.main.common.gallery.GalleryScreen +import ac.dnd.bookkeeping.android.presentation.ui.main.home.history.HistoryConstant import ac.dnd.bookkeeping.android.presentation.ui.main.home.history.HistoryScreen -import ac.dnd.bookkeeping.android.presentation.ui.main.home.setting.SettingScreen +import ac.dnd.bookkeeping.android.presentation.ui.main.home.mypage.MyPageConstant +import ac.dnd.bookkeeping.android.presentation.ui.main.home.mypage.MyPageScreen +import ac.dnd.bookkeeping.android.presentation.ui.main.home.schedule.ScheduleConstant +import ac.dnd.bookkeeping.android.presentation.ui.main.home.schedule.ScheduleScreen +import ac.dnd.bookkeeping.android.presentation.ui.main.home.statistics.StatisticsConstant +import ac.dnd.bookkeeping.android.presentation.ui.main.home.statistics.StatisticsScreen +import ac.dnd.bookkeeping.android.presentation.ui.main.home.test.TestConstant +import ac.dnd.bookkeeping.android.presentation.ui.main.home.test.TestScreen +import ac.dnd.bookkeeping.android.presentation.ui.main.rememberApplicationState import androidx.compose.foundation.ExperimentalFoundationApi import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size import androidx.compose.foundation.pager.HorizontalPager import androidx.compose.foundation.pager.rememberPagerState +import androidx.compose.material.BottomNavigation +import androidx.compose.material.BottomNavigationItem +import androidx.compose.material.Icon import androidx.compose.material.Scaffold -import androidx.compose.material.SnackbarResult +import androidx.compose.material.Text import androidx.compose.material.rememberScaffoldState import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableIntStateOf -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.remember +import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier -import androidx.hilt.navigation.compose.hiltViewModel +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import kotlinx.coroutines.CoroutineExceptionHandler +import kotlinx.coroutines.launch @OptIn(ExperimentalFoundationApi::class) @Composable fun HomeScreen( appState: ApplicationState, - viewModel: HomeViewModel = hiltViewModel() + model: HomeModel, + event: EventFlow, + intent: (HomeIntent) -> Unit, + handler: CoroutineExceptionHandler ) { - Observer( - appState = appState, - viewModel = viewModel - ) + val scope = rememberCoroutineScope() + val bottomBarItemList: List = listOf( + MainBottomBarItem( + route = HistoryConstant.ROUTE, + name = "내역", + icon = R.drawable.ic_launcher + ), + MainBottomBarItem( + route = ScheduleConstant.ROUTE, + name = "통계", + icon = R.drawable.ic_launcher + ), + MainBottomBarItem( + route = StatisticsConstant.ROUTE, + name = "일정", + icon = R.drawable.ic_launcher + ), + MainBottomBarItem( + route = MyPageConstant.ROUTE, + name = "마이페이지", + icon = R.drawable.ic_launcher + ), + MainBottomBarItem( + route = TestConstant.ROUTE, + name = "Test Page", + icon = R.drawable.ic_warning + ) + ) val scaffoldState = rememberScaffoldState() var selectedItem by rememberSaveable { mutableIntStateOf(0) } - var snackBarIsShowingState by remember { mutableStateOf(false) } val pagerState = rememberPagerState( - pageCount = { 2 } + pageCount = { bottomBarItemList.size } ) LaunchedEffect(selectedItem) { @@ -53,6 +104,7 @@ fun HomeScreen( snackbarHost = CustomSnackBarHost, bottomBar = { HomeBottomBarScreen( + itemList = bottomBarItemList, selectedItem = selectedItem, onClick = { selectedItem = it @@ -60,50 +112,109 @@ fun HomeScreen( ) } ) { innerPadding -> - HorizontalPager( state = pagerState, modifier = Modifier.padding(innerPadding), userScrollEnabled = false ) { page -> - when (page) { - 0 -> { + when (bottomBarItemList[page].route) { + HistoryConstant.ROUTE -> { HistoryScreen( appState = appState ) - GalleryScreen( + } + ScheduleConstant.ROUTE -> { + ScheduleScreen( + appState = appState + ) + } + StatisticsConstant.ROUTE -> { + StatisticsScreen( + appState = appState + ) + } + + MyPageConstant.ROUTE -> { + MyPageScreen( appState = appState ) } - 1 -> { - SettingScreen( - appState = appState, - onShowSnackBar = { - snackBarIsShowingState = true - } + TestConstant.ROUTE -> { + TestScreen( + appState = appState ) } } } } - if (snackBarIsShowingState) { - LaunchedEffect(Unit) { - scaffoldState.snackbarHostState.showSnackbar("show snackBar") - .let { - if (it == SnackbarResult.Dismissed) { - snackBarIsShowingState = false - } + fun showSnackBar(message: String) { + scope.launch(handler) { + scaffoldState.snackbarHostState.showSnackbar(message) + } + } + + LaunchedEffectWithLifecycle(event, handler) { + event.eventObserve { event -> + when (event) { + is HomeEvent.ShowSnackBar -> { + showSnackBar(event.message) } + } } } } @Composable -private fun Observer( - appState: ApplicationState, - viewModel: HomeViewModel +private fun HomeBottomBarScreen( + itemList: List, + selectedItem: Int, + onClick: (Int) -> Unit ) { - ErrorObserver(viewModel) + BottomNavigation( + elevation = 0.dp, + backgroundColor = Color.LightGray, + modifier = Modifier + .fillMaxWidth() + .height(55.dp) + ) { + itemList.forEachIndexed { index, item -> + BottomNavigationItem( + selected = index == selectedItem, + icon = { + Icon( + modifier = Modifier.size(Icon24), + painter = painterResource(id = item.icon), + contentDescription = "bottom icon" + ) + }, + label = { + Text( + text = item.name, + style = Caption4 + ) + }, + selectedContentColor = Color.Black, + unselectedContentColor = Color.Gray, + onClick = { + onClick(index) + } + ) + } + } +} + +@Preview +@Composable +private fun HomeScreenPreview() { + HomeScreen( + appState = rememberApplicationState(), + model = HomeModel( + state = HomeState.Init + ), + event = MutableEventFlow(), + intent = {}, + handler = CoroutineExceptionHandler { _, _ -> } + ) } diff --git a/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/ui/main/home/HomeState.kt b/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/ui/main/home/HomeState.kt new file mode 100644 index 00000000..a1379fac --- /dev/null +++ b/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/ui/main/home/HomeState.kt @@ -0,0 +1,5 @@ +package ac.dnd.bookkeeping.android.presentation.ui.main.home + +sealed interface HomeState { + data object Init : HomeState +} diff --git a/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/ui/main/home/HomeViewModel.kt b/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/ui/main/home/HomeViewModel.kt index 3a044a65..7fec4bbe 100644 --- a/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/ui/main/home/HomeViewModel.kt +++ b/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/ui/main/home/HomeViewModel.kt @@ -1,11 +1,28 @@ package ac.dnd.bookkeeping.android.presentation.ui.main.home 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 HomeViewModel @Inject constructor( - private val savedStateHandle: SavedStateHandle -) : BaseViewModel() + private val savedStateHandle: SavedStateHandle, +) : BaseViewModel() { + + private val _state: MutableStateFlow = MutableStateFlow(HomeState.Init) + val state: StateFlow = _state.asStateFlow() + + private val _event: MutableEventFlow = MutableEventFlow() + val event: EventFlow = _event.asEventFlow() + + fun onIntent(intent: HomeIntent) { + + } +} diff --git a/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/ui/main/home/mypage/MyPageScreen.kt b/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/ui/main/home/mypage/MyPageScreen.kt index 43080cd7..ec4b87a7 100644 --- a/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/ui/main/home/mypage/MyPageScreen.kt +++ b/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/ui/main/home/mypage/MyPageScreen.kt @@ -1,5 +1,6 @@ package ac.dnd.bookkeeping.android.presentation.ui.main.home.mypage +import ac.dnd.bookkeeping.android.presentation.common.util.ErrorObserver 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 @@ -8,12 +9,37 @@ import ac.dnd.bookkeeping.android.presentation.ui.main.ApplicationState import ac.dnd.bookkeeping.android.presentation.ui.main.rememberApplicationState import androidx.compose.foundation.layout.Box import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.ui.tooling.preview.Preview +import androidx.hilt.navigation.compose.hiltViewModel +import androidx.lifecycle.compose.collectAsStateWithLifecycle import kotlinx.coroutines.CoroutineExceptionHandler @Composable fun MyPageScreen( + appState: ApplicationState, + viewModel: MyPageViewModel = hiltViewModel() +) { + val model: MyPageModel = Unit.let { + val state by viewModel.state.collectAsStateWithLifecycle() + MyPageModel( + state = state + ) + } + ErrorObserver(viewModel) + + MyPageScreen( + appState = appState, + model = model, + event = viewModel.event, + intent = viewModel::onIntent, + handler = viewModel.handler + ) +} + +@Composable +private fun MyPageScreen( appState: ApplicationState, model: MyPageModel, event: EventFlow, @@ -35,7 +61,7 @@ fun MyPageScreen( @Preview @Composable -fun MyPageScreenPreview() { +private fun MyPageScreenPreview() { MyPageScreen( appState = rememberApplicationState(), model = MyPageModel( diff --git a/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/ui/main/home/schedule/ScheduleScreen.kt b/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/ui/main/home/schedule/ScheduleScreen.kt index e0ea9d37..e0626c15 100644 --- a/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/ui/main/home/schedule/ScheduleScreen.kt +++ b/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/ui/main/home/schedule/ScheduleScreen.kt @@ -1,5 +1,6 @@ package ac.dnd.bookkeeping.android.presentation.ui.main.home.schedule +import ac.dnd.bookkeeping.android.presentation.common.util.ErrorObserver 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 @@ -8,12 +9,39 @@ import ac.dnd.bookkeeping.android.presentation.ui.main.ApplicationState import ac.dnd.bookkeeping.android.presentation.ui.main.rememberApplicationState import androidx.compose.foundation.layout.Box import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.ui.tooling.preview.Preview +import androidx.hilt.navigation.compose.hiltViewModel +import androidx.lifecycle.compose.collectAsStateWithLifecycle import kotlinx.coroutines.CoroutineExceptionHandler + @Composable fun ScheduleScreen( + appState: ApplicationState, + viewModel: ScheduleViewModel = hiltViewModel() +) { + + val model: ScheduleModel = Unit.let { + val state by viewModel.state.collectAsStateWithLifecycle() + ScheduleModel( + state = state + ) + } + ErrorObserver(viewModel) + + ScheduleScreen( + appState = appState, + model = model, + event = viewModel.event, + intent = viewModel::onIntent, + handler = viewModel.handler + ) +} + +@Composable +private fun ScheduleScreen( appState: ApplicationState, model: ScheduleModel, event: EventFlow, @@ -35,7 +63,7 @@ fun ScheduleScreen( @Preview @Composable -fun ScheduleScreenPreview() { +private fun ScheduleScreenPreview() { ScheduleScreen( appState = rememberApplicationState(), model = ScheduleModel( diff --git a/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/ui/main/home/setting/SettingViewModel.kt b/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/ui/main/home/setting/SettingViewModel.kt deleted file mode 100644 index 5ec16d27..00000000 --- a/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/ui/main/home/setting/SettingViewModel.kt +++ /dev/null @@ -1,11 +0,0 @@ -package ac.dnd.bookkeeping.android.presentation.ui.main.home.setting - -import ac.dnd.bookkeeping.android.presentation.common.base.BaseViewModel -import androidx.lifecycle.SavedStateHandle -import dagger.hilt.android.lifecycle.HiltViewModel -import javax.inject.Inject - -@HiltViewModel -class SettingViewModel @Inject constructor( - private val savedStateHandle: SavedStateHandle -) : BaseViewModel() diff --git a/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/ui/main/home/statistics/StatisticsScreen.kt b/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/ui/main/home/statistics/StatisticsScreen.kt index 0161d5fa..cbd66164 100644 --- a/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/ui/main/home/statistics/StatisticsScreen.kt +++ b/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/ui/main/home/statistics/StatisticsScreen.kt @@ -1,5 +1,6 @@ package ac.dnd.bookkeeping.android.presentation.ui.main.home.statistics +import ac.dnd.bookkeeping.android.presentation.common.util.ErrorObserver 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 @@ -8,12 +9,37 @@ import ac.dnd.bookkeeping.android.presentation.ui.main.ApplicationState import ac.dnd.bookkeeping.android.presentation.ui.main.rememberApplicationState import androidx.compose.foundation.layout.Box import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.ui.tooling.preview.Preview +import androidx.hilt.navigation.compose.hiltViewModel +import androidx.lifecycle.compose.collectAsStateWithLifecycle import kotlinx.coroutines.CoroutineExceptionHandler @Composable fun StatisticsScreen( + appState: ApplicationState, + viewModel: StatisticsViewModel = hiltViewModel() +) { + val model: StatisticsModel = Unit.let { + val state by viewModel.state.collectAsStateWithLifecycle() + StatisticsModel( + state = state + ) + } + ErrorObserver(viewModel) + + StatisticsScreen( + appState = appState, + model = model, + event = viewModel.event, + intent = viewModel::onIntent, + handler = viewModel.handler + ) +} + +@Composable +private fun StatisticsScreen( appState: ApplicationState, model: StatisticsModel, event: EventFlow, @@ -35,7 +61,7 @@ fun StatisticsScreen( @Preview @Composable -fun StatisticsScreenPreview() { +private fun StatisticsScreenPreview() { StatisticsScreen( appState = rememberApplicationState(), model = StatisticsModel( diff --git a/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/ui/main/home/test/TestConstant.kt b/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/ui/main/home/test/TestConstant.kt new file mode 100644 index 00000000..0d641f7d --- /dev/null +++ b/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/ui/main/home/test/TestConstant.kt @@ -0,0 +1,5 @@ +package ac.dnd.bookkeeping.android.presentation.ui.main.home.test + +object TestConstant { + const val ROUTE: String = "/test" +} diff --git a/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/ui/main/home/test/TestEvent.kt b/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/ui/main/home/test/TestEvent.kt new file mode 100644 index 00000000..6b28d82e --- /dev/null +++ b/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/ui/main/home/test/TestEvent.kt @@ -0,0 +1,3 @@ +package ac.dnd.bookkeeping.android.presentation.ui.main.home.test + +sealed interface TestEvent diff --git a/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/ui/main/home/test/TestIntent.kt b/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/ui/main/home/test/TestIntent.kt new file mode 100644 index 00000000..8e4d1c2f --- /dev/null +++ b/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/ui/main/home/test/TestIntent.kt @@ -0,0 +1,3 @@ +package ac.dnd.bookkeeping.android.presentation.ui.main.home.test + +sealed interface TestIntent diff --git a/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/ui/main/home/test/TestModel.kt b/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/ui/main/home/test/TestModel.kt new file mode 100644 index 00000000..640bc0f9 --- /dev/null +++ b/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/ui/main/home/test/TestModel.kt @@ -0,0 +1,5 @@ +package ac.dnd.bookkeeping.android.presentation.ui.main.home.test + +data class TestModel( + val state: TestState, +) diff --git a/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/ui/main/home/setting/SettingScreen.kt b/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/ui/main/home/test/TestScreen.kt similarity index 84% rename from presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/ui/main/home/setting/SettingScreen.kt rename to presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/ui/main/home/test/TestScreen.kt index 240c0d62..be11d8fd 100644 --- a/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/ui/main/home/setting/SettingScreen.kt +++ b/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/ui/main/home/test/TestScreen.kt @@ -1,10 +1,14 @@ -package ac.dnd.bookkeeping.android.presentation.ui.main.home.setting +package ac.dnd.bookkeeping.android.presentation.ui.main.home.test import ac.dnd.bookkeeping.android.presentation.R import ac.dnd.bookkeeping.android.presentation.common.CHANNEL_1 import ac.dnd.bookkeeping.android.presentation.common.theme.Body1 import ac.dnd.bookkeeping.android.presentation.common.theme.Negative import ac.dnd.bookkeeping.android.presentation.common.util.ErrorObserver +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.common.util.showNotification import ac.dnd.bookkeeping.android.presentation.common.view.BottomSheetScreen import ac.dnd.bookkeeping.android.presentation.common.view.DialogScreen @@ -17,6 +21,7 @@ import ac.dnd.bookkeeping.android.presentation.common.view.textfield.TypingTextF import ac.dnd.bookkeeping.android.presentation.ui.main.ApplicationState import ac.dnd.bookkeeping.android.presentation.ui.main.home.common.group.add.AddGroupScreen import ac.dnd.bookkeeping.android.presentation.ui.main.home.common.relation.get.SearchRelationScreen +import ac.dnd.bookkeeping.android.presentation.ui.main.rememberApplicationState import androidx.compose.foundation.background import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Arrangement @@ -42,24 +47,48 @@ import androidx.compose.ui.graphics.Color import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource +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.collectAsStateWithLifecycle +import kotlinx.coroutines.CoroutineExceptionHandler import kotlinx.coroutines.launch import timber.log.Timber +// TODO : Debug 로 이동 @Composable -fun SettingScreen( +fun TestScreen( appState: ApplicationState, - onShowSnackBar: () -> Unit, - viewModel: SettingViewModel = hiltViewModel() + viewModel: TestViewModel = hiltViewModel() ) { - val scope = rememberCoroutineScope() - val context = LocalContext.current - Observer( + val model: TestModel = Unit.let { + val state by viewModel.state.collectAsStateWithLifecycle() + TestModel( + state = state + ) + } + ErrorObserver(viewModel) + + TestScreen( appState = appState, - viewModel = viewModel + model = model, + event = viewModel.event, + intent = viewModel::onIntent, + handler = viewModel.handler ) +} + +@Composable +private fun TestScreen( + appState: ApplicationState, + model: TestModel, + event: EventFlow, + intent: (TestIntent) -> Unit, + handler: CoroutineExceptionHandler +) { + val scope = rememberCoroutineScope() + val context = LocalContext.current var isDialogShowing by remember { mutableStateOf(false) } var isBottomSheetShowing by remember { mutableStateOf(false) } @@ -115,16 +144,6 @@ fun SettingScreen( color = Color.Black, modifier = Modifier.padding(vertical = 10.dp) ) - Text( - text = "snackBarState", - fontSize = 20.sp, - color = Color.Black, - modifier = Modifier - .padding(vertical = 10.dp) - .clickable { - onShowSnackBar() - } - ) Text( text = "dialogState", fontSize = 20.sp, @@ -250,12 +269,24 @@ fun SettingScreen( onDismissRequest = { isDialogShowing = false } ) } + + LaunchedEffectWithLifecycle(event, handler) { + event.eventObserve { event -> + + } + } } +@Preview @Composable -private fun Observer( - appState: ApplicationState, - viewModel: SettingViewModel -) { - ErrorObserver(viewModel) +private fun TestScreenPreview() { + TestScreen( + appState = rememberApplicationState(), + model = TestModel( + state = TestState.Init + ), + event = MutableEventFlow(), + intent = {}, + handler = CoroutineExceptionHandler { _, _ -> } + ) } diff --git a/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/ui/main/home/test/TestState.kt b/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/ui/main/home/test/TestState.kt new file mode 100644 index 00000000..372f586a --- /dev/null +++ b/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/ui/main/home/test/TestState.kt @@ -0,0 +1,5 @@ +package ac.dnd.bookkeeping.android.presentation.ui.main.home.test + +sealed interface TestState { + data object Init : TestState +} diff --git a/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/ui/main/home/test/TestViewModel.kt b/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/ui/main/home/test/TestViewModel.kt new file mode 100644 index 00000000..51a95dcf --- /dev/null +++ b/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/ui/main/home/test/TestViewModel.kt @@ -0,0 +1,28 @@ +package ac.dnd.bookkeeping.android.presentation.ui.main.home.test + +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 TestViewModel @Inject constructor( + private val savedStateHandle: SavedStateHandle, +) : BaseViewModel() { + + private val _state: MutableStateFlow = MutableStateFlow(TestState.Init) + val state: StateFlow = _state.asStateFlow() + + private val _event: MutableEventFlow = MutableEventFlow() + val event: EventFlow = _event.asEventFlow() + + fun onIntent(intent: TestIntent) { + + } +}