diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 26450790..811472a5 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -15,7 +15,7 @@ android:usesCleartextTraffic="true" android:theme="@style/Theme.Kiero"> diff --git a/app/src/main/java/com/kiero/core/navigation/MainTabRoute.kt b/app/src/main/java/com/kiero/core/navigation/MainTabRoute.kt deleted file mode 100644 index b64ec68b..00000000 --- a/app/src/main/java/com/kiero/core/navigation/MainTabRoute.kt +++ /dev/null @@ -1,3 +0,0 @@ -package com.kiero.core.navigation - -interface MainTabRoute : Route \ No newline at end of file diff --git a/app/src/main/java/com/kiero/presentation/auth/AuthScreen.kt b/app/src/main/java/com/kiero/presentation/auth/AuthScreen.kt index fde703dd..caec38b0 100644 --- a/app/src/main/java/com/kiero/presentation/auth/AuthScreen.kt +++ b/app/src/main/java/com/kiero/presentation/auth/AuthScreen.kt @@ -1,39 +1,40 @@ package com.kiero.presentation.auth import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.layout.Spacer 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.lazy.LazyColumn -import androidx.compose.foundation.lazy.items +import androidx.compose.material3.Button +import androidx.compose.material3.SnackbarHostState import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier 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 com.kiero.core.common.extension.noRippleClickable -import com.kiero.core.model.UiState import com.kiero.core.designsystem.theme.KieroTheme -import com.kiero.data.auth.model.DummyEntity -import com.kiero.presentation.auth.component.DummyItem -import kotlinx.collections.immutable.PersistentList +import com.kiero.core.model.UiState @Composable fun AuthRoute( paddingValues: PaddingValues, navigateUp: () -> Unit, - navigateNext: () -> Unit, - state: UiState>, + navigateToParent: () -> Unit, + navigateToKid: () -> Unit, ) { AuthScreen( paddingValues = paddingValues, navigateUp = navigateUp, - navigateNext = navigateNext, - state = state, + navigateToParent = navigateToParent, + navigateToKid = navigateToKid, modifier = Modifier - .fillMaxSize() ) } @@ -41,8 +42,8 @@ fun AuthRoute( fun AuthScreen( paddingValues: PaddingValues, navigateUp: () -> Unit, - navigateNext: () -> Unit, - state: UiState>, + navigateToParent: () -> Unit, + navigateToKid: () -> Unit, modifier: Modifier = Modifier, ) { LazyColumn( @@ -52,51 +53,36 @@ fun AuthScreen( horizontalAlignment = Alignment.CenterHorizontally, verticalArrangement = Arrangement.Center ) { - when (state) { - is UiState.Loading -> { - item { - Text( - modifier = modifier - .noRippleClickable { navigateUp() }, - textAlign = TextAlign.Center, - text = "Dummy", - fontSize = 30.sp - ) - } - } + item { + Column( + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = 32.dp), + horizontalAlignment = Alignment.CenterHorizontally, + verticalArrangement = Arrangement.Center + ) { + Text( + textAlign = TextAlign.Center, + text = "Kiero App", + fontSize = 30.sp + ) - is UiState.Empty -> { - item { - Text( - modifier = modifier - .noRippleClickable { navigateUp() }, - textAlign = TextAlign.Center, - text = "Dummy", - fontSize = 30.sp - ) - } - } + Spacer(modifier = Modifier.height(40.dp)) - is UiState.Failure -> { - item { - Text( - modifier = modifier - .noRippleClickable { navigateUp() }, - textAlign = TextAlign.Center, - text = state.message, - ) + Button( + onClick = navigateToParent, + modifier = Modifier.fillMaxWidth() + ) { + Text(text = "부모 화면으로 이동") } - } - is UiState.Success -> { - items(state.data) { - DummyItem( - id = it.id, - firstName = it.firstName, - lastName = it.lastName, - profileUrl = it.profile, - navigateNext = navigateNext - ) + Spacer(modifier = Modifier.height(16.dp)) + + Button( + onClick = navigateToKid, + modifier = Modifier.fillMaxWidth() + ) { + Text(text = "아이 화면으로 이동") } } } @@ -110,8 +96,8 @@ private fun DummyScreenPreview() { AuthScreen( paddingValues = PaddingValues(), navigateUp = {}, - navigateNext = {}, - state = UiState.Loading + navigateToParent = {}, + navigateToKid = {} ) } } \ No newline at end of file diff --git a/app/src/main/java/com/kiero/presentation/auth/component/DummyItem.kt b/app/src/main/java/com/kiero/presentation/auth/component/DummyItem.kt index f8e649d7..dce4a35d 100644 --- a/app/src/main/java/com/kiero/presentation/auth/component/DummyItem.kt +++ b/app/src/main/java/com/kiero/presentation/auth/component/DummyItem.kt @@ -23,7 +23,6 @@ fun DummyItem( firstName: String, lastName: String, profileUrl: String, - navigateNext: () -> Unit, modifier: Modifier = Modifier, ) { Column( @@ -38,7 +37,6 @@ fun DummyItem( .size(80.dp) .clip(CircleShape) .noRippleClickable { - navigateNext() }, contentScale = ContentScale.Crop ) diff --git a/app/src/main/java/com/kiero/presentation/auth/navigation/AuthNavigation.kt b/app/src/main/java/com/kiero/presentation/auth/navigation/AuthNavigation.kt new file mode 100644 index 00000000..f978ba77 --- /dev/null +++ b/app/src/main/java/com/kiero/presentation/auth/navigation/AuthNavigation.kt @@ -0,0 +1,48 @@ +package com.kiero.presentation.auth.navigation + +import androidx.compose.foundation.layout.PaddingValues +import androidx.navigation.NavController +import androidx.navigation.NavGraphBuilder +import androidx.navigation.NavHostController +import androidx.navigation.NavOptions +import androidx.navigation.compose.composable +import androidx.navigation.compose.navigation +import com.kiero.core.navigation.Route +import com.kiero.presentation.auth.AuthRoute +import kotlinx.serialization.Serializable + +@Serializable +sealed interface Auth : Route + +@Serializable +data object AuthGraph : Route + +@Serializable +data object Login : Auth + +fun NavController.navigateToAuth( + navOptions: NavOptions? = null, +) { + navigate(Login, navOptions) +} + +fun NavGraphBuilder.authNavGraph( + navController: NavHostController, + paddingValues: PaddingValues, + navigateUp: () -> Unit, + navigateToParent: () -> Unit, + navigateToKid: () -> Unit, +) { + navigation( + startDestination = Login + ) { + composable { + AuthRoute( + paddingValues = paddingValues, + navigateUp = navigateUp, + navigateToParent = navigateToParent, + navigateToKid = navigateToKid, + ) + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/kiero/presentation/kid/journey/KidJourneyScreen.kt b/app/src/main/java/com/kiero/presentation/kid/journey/KidJourneyScreen.kt new file mode 100644 index 00000000..58350522 --- /dev/null +++ b/app/src/main/java/com/kiero/presentation/kid/journey/KidJourneyScreen.kt @@ -0,0 +1,45 @@ +package com.kiero.presentation.kid.journey + +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.padding +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.tooling.preview.Preview +import com.kiero.core.designsystem.theme.KieroTheme + +@Composable +fun KidJourneyRoute( + paddingValues: PaddingValues, + navigateUp: () -> Unit, +) { + KidJourneyScreen( + paddingValues = paddingValues, + navigateUp = navigateUp + ) +} + +@Composable +private fun KidJourneyScreen( + paddingValues: PaddingValues, + navigateUp: () -> Unit, + modifier: Modifier = Modifier, +) { + Column( + modifier = modifier + .fillMaxSize() + .padding(paddingValues), + ) { + Text( + text = "오늘의 여정" + ) + } +} + +@Composable +@Preview +private fun KidJourneyScreenPreview() { + KieroTheme {} +} \ No newline at end of file diff --git a/app/src/main/java/com/kiero/presentation/kid/journey/navigation/KidJourneyNavigation.kt b/app/src/main/java/com/kiero/presentation/kid/journey/navigation/KidJourneyNavigation.kt new file mode 100644 index 00000000..25dcdc17 --- /dev/null +++ b/app/src/main/java/com/kiero/presentation/kid/journey/navigation/KidJourneyNavigation.kt @@ -0,0 +1,27 @@ +package com.kiero.presentation.kid.journey.navigation + +import androidx.compose.foundation.layout.PaddingValues +import androidx.navigation.NavController +import androidx.navigation.NavGraphBuilder +import androidx.navigation.NavOptions +import androidx.navigation.compose.composable +import com.kiero.presentation.kid.journey.KidJourneyRoute +import com.kiero.presentation.kid.navigation.Journey + +fun NavController.navigateToJourney( + navOptions: NavOptions? = null, +) { + navigate(Journey, navOptions) +} + +fun NavGraphBuilder.kidJourneyNavGraph( + paddingValues: PaddingValues, + navigateUp: () -> Unit, +) { + composable { + KidJourneyRoute( + paddingValues = paddingValues, + navigateUp = navigateUp, + ) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/kiero/presentation/kid/mission/KidMissionScreen.kt b/app/src/main/java/com/kiero/presentation/kid/mission/KidMissionScreen.kt new file mode 100644 index 00000000..ea60b9ee --- /dev/null +++ b/app/src/main/java/com/kiero/presentation/kid/mission/KidMissionScreen.kt @@ -0,0 +1,45 @@ +package com.kiero.presentation.kid.mission + +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.padding +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.tooling.preview.Preview +import com.kiero.core.designsystem.theme.KieroTheme + +@Composable +fun KidMissionRoute( + paddingValues: PaddingValues, + navigateUp: () -> Unit, +) { + KidMissionScreen( + paddingValues = paddingValues, + navigateUp = navigateUp + ) +} + +@Composable +private fun KidMissionScreen( + paddingValues: PaddingValues, + navigateUp: () -> Unit, + modifier: Modifier = Modifier, +) { + Column( + modifier = modifier + .fillMaxSize() + .padding(paddingValues), + ) { + Text( + text = "금화 미션" + ) + } +} + +@Composable +@Preview +private fun KidMissionScreenPreview() { + KieroTheme {} +} \ No newline at end of file diff --git a/app/src/main/java/com/kiero/presentation/kid/mission/navigation/KidMissionNavigation.kt b/app/src/main/java/com/kiero/presentation/kid/mission/navigation/KidMissionNavigation.kt new file mode 100644 index 00000000..109ecc31 --- /dev/null +++ b/app/src/main/java/com/kiero/presentation/kid/mission/navigation/KidMissionNavigation.kt @@ -0,0 +1,27 @@ +package com.kiero.presentation.kid.mission.navigation + +import androidx.compose.foundation.layout.PaddingValues +import androidx.navigation.NavController +import androidx.navigation.NavGraphBuilder +import androidx.navigation.NavOptions +import androidx.navigation.compose.composable +import com.kiero.presentation.kid.mission.KidMissionRoute +import com.kiero.presentation.kid.navigation.Mission + +fun NavController.navigateToMission( + navOptions: NavOptions? = null, +) { + navigate(Mission, navOptions) +} + +fun NavGraphBuilder.kidMissionNavGraph( + paddingValues: PaddingValues, + navigateUp: () -> Unit, +) { + composable { + KidMissionRoute( + paddingValues = paddingValues, + navigateUp = navigateUp, + ) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/kiero/presentation/kid/navigation/KidNavigation.kt b/app/src/main/java/com/kiero/presentation/kid/navigation/KidNavigation.kt new file mode 100644 index 00000000..24a709fb --- /dev/null +++ b/app/src/main/java/com/kiero/presentation/kid/navigation/KidNavigation.kt @@ -0,0 +1,59 @@ +package com.kiero.presentation.kid.navigation + +import androidx.compose.foundation.layout.PaddingValues +import androidx.navigation.NavController +import androidx.navigation.NavGraphBuilder +import androidx.navigation.NavHostController +import androidx.navigation.NavOptions +import androidx.navigation.compose.navigation +import com.kiero.core.navigation.Route +import com.kiero.presentation.kid.journey.navigation.kidJourneyNavGraph +import com.kiero.presentation.kid.mission.navigation.kidMissionNavGraph +import com.kiero.presentation.kid.wish.navigation.kidWishNavGraph +import kotlinx.serialization.Serializable + +sealed interface KidTab : Route + +@Serializable +data object KidGraph : Route + +@Serializable +data object Journey : KidTab + +@Serializable +data object Mission : KidTab + +@Serializable +data object Wish : KidTab + + +fun NavController.navigateToKid( + navOptions: NavOptions? = null, +) { + navigate(KidGraph, navOptions) +} + +fun NavGraphBuilder.kidNavGraph( + navController: NavHostController, + paddingValues: PaddingValues, + navigateUp: () -> Unit, +) { + navigation( + startDestination = Journey + ) { + kidJourneyNavGraph( + paddingValues = paddingValues, + navigateUp = navigateUp, + ) + + kidMissionNavGraph( + paddingValues = paddingValues, + navigateUp = navigateUp, + ) + + kidWishNavGraph( + paddingValues = paddingValues, + navigateUp = navigateUp, + ) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/kiero/presentation/kid/wish/KidWishScreen.kt b/app/src/main/java/com/kiero/presentation/kid/wish/KidWishScreen.kt new file mode 100644 index 00000000..c71a3985 --- /dev/null +++ b/app/src/main/java/com/kiero/presentation/kid/wish/KidWishScreen.kt @@ -0,0 +1,45 @@ +package com.kiero.presentation.kid.wish + +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.padding +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.tooling.preview.Preview +import com.kiero.core.designsystem.theme.KieroTheme + +@Composable +fun KidWishRoute( + paddingValues: PaddingValues, + navigateUp: () -> Unit, +) { + KidWishScreen( + paddingValues = paddingValues, + navigateUp = navigateUp + ) +} + +@Composable +private fun KidWishScreen( + paddingValues: PaddingValues, + navigateUp: () -> Unit, + modifier: Modifier = Modifier, +) { + Column( + modifier = modifier + .fillMaxSize() + .padding(paddingValues), + ) { + Text( + text = "소원의 우물" + ) + } +} + +@Composable +@Preview +private fun KidWishScreenPreview() { + KieroTheme {} +} \ No newline at end of file diff --git a/app/src/main/java/com/kiero/presentation/kid/wish/navigation/KidWishNavigation.kt b/app/src/main/java/com/kiero/presentation/kid/wish/navigation/KidWishNavigation.kt new file mode 100644 index 00000000..b636d8d0 --- /dev/null +++ b/app/src/main/java/com/kiero/presentation/kid/wish/navigation/KidWishNavigation.kt @@ -0,0 +1,27 @@ +package com.kiero.presentation.kid.wish.navigation + +import androidx.compose.foundation.layout.PaddingValues +import androidx.navigation.NavController +import androidx.navigation.NavGraphBuilder +import androidx.navigation.NavOptions +import androidx.navigation.compose.composable +import com.kiero.presentation.kid.navigation.Wish +import com.kiero.presentation.kid.wish.KidWishRoute + +fun NavController.navigateToWish( + navOptions: NavOptions? = null, +) { + navigate(Wish, navOptions) +} + +fun NavGraphBuilder.kidWishNavGraph( + paddingValues: PaddingValues, + navigateUp: () -> Unit, +) { + composable { + KidWishRoute( + paddingValues = paddingValues, + navigateUp = navigateUp, + ) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/kiero/presentation/main/MainActivity.kt b/app/src/main/java/com/kiero/presentation/main/MainActivity.kt deleted file mode 100644 index c4de60b1..00000000 --- a/app/src/main/java/com/kiero/presentation/main/MainActivity.kt +++ /dev/null @@ -1,42 +0,0 @@ -package com.kiero.presentation.main - -import android.os.Bundle -import androidx.activity.ComponentActivity -import androidx.activity.compose.setContent -import androidx.activity.enableEdgeToEdge -import androidx.activity.viewModels -import androidx.compose.foundation.layout.PaddingValues -import androidx.compose.runtime.LaunchedEffect -import androidx.compose.runtime.getValue -import androidx.lifecycle.compose.collectAsStateWithLifecycle -import com.kiero.core.designsystem.theme.KieroTheme -import com.kiero.presentation.auth.AuthRoute -import com.kiero.presentation.auth.viewmodel.AuthViewModel -import dagger.hilt.android.AndroidEntryPoint - -@AndroidEntryPoint -class MainActivity : ComponentActivity() { - private val viewModel: AuthViewModel by viewModels() - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - enableEdgeToEdge() - - setContent { - KieroTheme { - val state by viewModel.state.collectAsStateWithLifecycle() - - LaunchedEffect(Unit) { - viewModel.getDummyList() - } - - AuthRoute( - paddingValues = PaddingValues(), - navigateUp = { viewModel.navigateUp() }, - navigateNext = { viewModel.navigateNext() }, - state = state.uiState - ) - } - } - } -} \ No newline at end of file diff --git a/app/src/main/java/com/kiero/presentation/main/activity/MainActivity.kt b/app/src/main/java/com/kiero/presentation/main/activity/MainActivity.kt new file mode 100644 index 00000000..92e1ed3a --- /dev/null +++ b/app/src/main/java/com/kiero/presentation/main/activity/MainActivity.kt @@ -0,0 +1,23 @@ +package com.kiero.presentation.main.activity + +import android.os.Bundle +import androidx.activity.ComponentActivity +import androidx.activity.compose.setContent +import androidx.activity.enableEdgeToEdge +import com.kiero.core.designsystem.theme.KieroTheme +import com.kiero.presentation.main.screen.MainRoute +import dagger.hilt.android.AndroidEntryPoint + +@AndroidEntryPoint +class MainActivity : ComponentActivity() { + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + enableEdgeToEdge() + + setContent { + KieroTheme { + MainRoute() + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/kiero/presentation/main/navigation/KieroNavHost.kt b/app/src/main/java/com/kiero/presentation/main/navigation/KieroNavHost.kt new file mode 100644 index 00000000..d4e0954a --- /dev/null +++ b/app/src/main/java/com/kiero/presentation/main/navigation/KieroNavHost.kt @@ -0,0 +1,45 @@ +package com.kiero.presentation.main.navigation + +import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.navigation.compose.NavHost +import com.kiero.core.navigation.Route +import com.kiero.presentation.auth.navigation.AuthGraph +import com.kiero.presentation.auth.navigation.authNavGraph +import com.kiero.presentation.kid.navigation.kidNavGraph +import com.kiero.presentation.parent.navigation.parentNavGraph + +@Composable +fun KieroNavHost( + appState: MainAppState, + paddingValues: PaddingValues, + modifier: Modifier = Modifier, + startDestination: Route = AuthGraph, +) { + NavHost( + navController = appState.navController, + startDestination = startDestination, + modifier = modifier + ) { + authNavGraph( + navController = appState.navController, + paddingValues = paddingValues, + navigateUp = appState::navigateUp, + navigateToParent = appState::navigateToParent, + navigateToKid = appState::navigateToKid, + ) + + parentNavGraph( + navController = appState.navController, + paddingValues = paddingValues, + navigateUp = appState::navigateUp, + ) + + kidNavGraph( + navController = appState.navController, + paddingValues = paddingValues, + navigateUp = appState::navigateUp, + ) + } +} diff --git a/app/src/main/java/com/kiero/presentation/main/navigation/MainAppState.kt b/app/src/main/java/com/kiero/presentation/main/navigation/MainAppState.kt new file mode 100644 index 00000000..8d6e3858 --- /dev/null +++ b/app/src/main/java/com/kiero/presentation/main/navigation/MainAppState.kt @@ -0,0 +1,167 @@ +package com.kiero.presentation.main.navigation + +import androidx.compose.runtime.Composable +import androidx.compose.runtime.Stable +import androidx.compose.runtime.remember +import androidx.compose.runtime.rememberCoroutineScope +import androidx.navigation.NavDestination.Companion.hasRoute +import androidx.navigation.NavHostController +import androidx.navigation.NavOptions +import androidx.navigation.compose.rememberNavController +import androidx.navigation.navOptions +import com.kiero.presentation.auth.navigation.AuthGraph +import com.kiero.presentation.kid.journey.navigation.navigateToJourney +import com.kiero.presentation.kid.mission.navigation.navigateToMission +import com.kiero.presentation.kid.navigation.Journey +import com.kiero.presentation.kid.navigation.KidGraph +import com.kiero.presentation.kid.navigation.Mission +import com.kiero.presentation.kid.navigation.Wish +import com.kiero.presentation.kid.wish.navigation.navigateToWish +import com.kiero.presentation.parent.alarm.navigation.navigateToAlarm +import com.kiero.presentation.parent.navigation.Alarm +import com.kiero.presentation.parent.navigation.ParentGraph +import com.kiero.presentation.parent.navigation.Schedule +import com.kiero.presentation.parent.schedule.navigation.navigateToSchedule +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.flow.SharingStarted +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.stateIn + + +@Stable +class MainAppState( + val navController: NavHostController, + coroutineScope: CoroutineScope, +) { + private val currentDestination = navController.currentBackStackEntryFlow + .map { it.destination } + .stateIn( + scope = coroutineScope, + started = SharingStarted.WhileSubscribed(5_000), + initialValue = null + ) + + val currentParentTab: StateFlow = currentDestination + .map { destination -> + ParentMainTab.find { tab -> destination?.hasRoute(tab::class) == true } + } + .stateIn( + scope = coroutineScope, + started = SharingStarted.WhileSubscribed(5_000), + initialValue = null + ) + + val currentKidTab: StateFlow = currentDestination + .map { destination -> + KidMainTab.find { tab -> destination?.hasRoute(tab::class) == true } + } + .stateIn( + scope = coroutineScope, + started = SharingStarted.WhileSubscribed(5_000), + initialValue = null + ) + + val showParentBottomBar: StateFlow = currentDestination + .map { destination -> + ParentMainTab.contains { tab -> + destination?.hasRoute(tab::class) == true + } + } + .stateIn( + scope = coroutineScope, + started = SharingStarted.WhileSubscribed(5_000), + initialValue = false + ) + + val showKidBottomBar: StateFlow = currentDestination + .map { destination -> + KidMainTab.contains { tab -> + destination?.hasRoute(tab::class) == true + } + } + .stateIn( + scope = coroutineScope, + started = SharingStarted.WhileSubscribed(5_000), + initialValue = false + ) + + fun navigateToParent() { + navController.navigate(ParentGraph) { + popUpTo(AuthGraph) { inclusive = true } + } + } + + fun navigateToKid() { + navController.navigate(KidGraph) { + popUpTo(AuthGraph) { inclusive = true } + } + } + + fun navigateToAuth() { + navController.navigate(AuthGraph) { + popUpTo(0) { inclusive = true } + } + } + + fun navigateParentTab(tab: ParentMainTab) { + val navOptions = navOptions { + popUpTo(ParentGraph) { saveState = true } + launchSingleTop = true + restoreState = true + } + when (tab) { + ParentMainTab.SCHEDULE -> navController.navigate(Schedule, navOptions) + ParentMainTab.ALARM -> navController.navigate(Alarm, navOptions) + } + } + + fun navigateKidTab(tab: KidMainTab) { + val navOptions = navOptions { + popUpTo(KidGraph) { saveState = true } + launchSingleTop = true + restoreState = true + } + when (tab) { + KidMainTab.JOURNEY -> navController.navigate(Journey, navOptions) + KidMainTab.MISSION -> navController.navigate(Mission, navOptions) + KidMainTab.WISH -> navController.navigate(Wish, navOptions) + } + } + + fun navigateToAlarm(navOptions: NavOptions? = null) = + navController.navigateToAlarm(navOptions) + + fun navigateToSchedule(navOptions: NavOptions? = null) = + navController.navigateToSchedule(navOptions) + + fun navigateToJourney(navOptions: NavOptions? = null) = + navController.navigateToJourney(navOptions) + + fun navigateToMission(navOptions: NavOptions? = null) = + navController.navigateToMission(navOptions) + + fun navigateToWish(navOptions: NavOptions? = null) = + navController.navigateToWish(navOptions) + + fun navigateUp() { + navController.navigateUp() + } + + fun popBackStack() { + navController.popBackStack() + } +} + +@Composable +fun rememberMainAppState( + navController: NavHostController = rememberNavController(), + coroutineScope: CoroutineScope = rememberCoroutineScope(), +): MainAppState { + return remember(navController, coroutineScope) { + MainAppState( + navController = navController, + coroutineScope = coroutineScope + ) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/kiero/presentation/main/navigation/MainTab.kt b/app/src/main/java/com/kiero/presentation/main/navigation/MainTab.kt new file mode 100644 index 00000000..e21ea7f9 --- /dev/null +++ b/app/src/main/java/com/kiero/presentation/main/navigation/MainTab.kt @@ -0,0 +1,82 @@ +package com.kiero.presentation.main.navigation + +import androidx.annotation.DrawableRes +import androidx.annotation.StringRes +import com.kiero.R +import com.kiero.presentation.kid.navigation.Journey +import com.kiero.presentation.kid.navigation.KidTab +import com.kiero.presentation.kid.navigation.Mission +import com.kiero.presentation.kid.navigation.Wish + +import com.kiero.presentation.main.navigation.component.BottomBarTab +import com.kiero.presentation.parent.navigation.Alarm +import com.kiero.presentation.parent.navigation.ParentTab +import com.kiero.presentation.parent.navigation.Schedule + +enum class ParentMainTab( + @param:DrawableRes override val iconRes: Int, + @param:StringRes override val contentDescription: Int, + @param:StringRes override val labelRes: Int, + val route: ParentTab, +) : BottomBarTab { + SCHEDULE( + iconRes = R.drawable.ic_parent_tab_schedule, + contentDescription = R.string.schedule_tab_content_description, + labelRes = R.string.schedule_tab_content_description, + route = Schedule, + ), + ALARM( + iconRes = R.drawable.ic_parent_tab_alarm, + contentDescription = R.string.alarm_tab_content_description, + labelRes = R.string.alarm_tab_content_description, + route = Alarm, + ); + + companion object { + + fun find(predicate: (ParentTab) -> Boolean): ParentMainTab? { + return entries.find { predicate(it.route) } + } + + fun contains(predicate: (ParentTab) -> Boolean): Boolean { + return entries.any { predicate(it.route) } + } + } +} + +enum class KidMainTab( + @param:DrawableRes override val iconRes: Int, + @param:StringRes override val contentDescription: Int, + @param:StringRes override val labelRes: Int, + val route: KidTab, +) : BottomBarTab { + JOURNEY( + iconRes = R.drawable.ic_kid_tab_journey, + contentDescription = R.string.journey_tab_content_description, + labelRes = R.string.journey_tab_content_description, + route = Journey, + ), + MISSION( + iconRes = R.drawable.ic_kid_tab_mission, + contentDescription = R.string.mission_tab_content_description, + labelRes = R.string.mission_tab_content_description, + route = Mission, + ), + WISH( + iconRes = R.drawable.ic_kid_tab_wish, + contentDescription = R.string.wish_tab_content_description, + labelRes = R.string.wish_tab_content_description, + route = Wish, + ); + + companion object { + + fun find(predicate: (KidTab) -> Boolean): KidMainTab? { + return entries.find { predicate(it.route) } + } + + fun contains(predicate: (KidTab) -> Boolean): Boolean { + return entries.any { predicate(it.route) } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/kiero/presentation/main/navigation/component/BottomBarTab.kt b/app/src/main/java/com/kiero/presentation/main/navigation/component/BottomBarTab.kt new file mode 100644 index 00000000..8d115c46 --- /dev/null +++ b/app/src/main/java/com/kiero/presentation/main/navigation/component/BottomBarTab.kt @@ -0,0 +1,10 @@ +package com.kiero.presentation.main.navigation.component + +import androidx.annotation.DrawableRes +import androidx.annotation.StringRes + +interface BottomBarTab { + @get:DrawableRes val iconRes: Int + @get:StringRes val contentDescription: Int + @get:StringRes val labelRes: Int +} \ No newline at end of file diff --git a/app/src/main/java/com/kiero/presentation/main/navigation/component/MainBottomBar.kt b/app/src/main/java/com/kiero/presentation/main/navigation/component/MainBottomBar.kt new file mode 100644 index 00000000..76f85c90 --- /dev/null +++ b/app/src/main/java/com/kiero/presentation/main/navigation/component/MainBottomBar.kt @@ -0,0 +1,105 @@ +package com.kiero.presentation.main.navigation.component + +import androidx.compose.animation.AnimatedVisibility +import androidx.compose.animation.EnterTransition +import androidx.compose.animation.ExitTransition +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.selection.selectableGroup +import androidx.compose.material3.Icon +import androidx.compose.material3.Surface +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Shape +import androidx.compose.ui.graphics.vector.ImageVector +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.res.vectorResource +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.unit.dp +import com.kiero.core.common.extension.noRippleClickable +import com.kiero.core.designsystem.theme.KieroTheme +import kotlinx.collections.immutable.ImmutableList + +@Composable +fun MainBottomBar( + isVisible: Boolean, + containerShape: Shape, + tabs: ImmutableList, + currentTab: BottomBarTab?, + onTabSelected: (BottomBarTab) -> Unit, + modifier: Modifier = Modifier, +) { + AnimatedVisibility( + visible = isVisible, + enter = EnterTransition.None, + exit = ExitTransition.None, + modifier = modifier + ) { + Surface( + color = KieroTheme.colors.black, + shape = containerShape, + modifier = Modifier.fillMaxWidth() + ) { + Row( + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = 16.dp, vertical = 12.dp) + .selectableGroup(), + horizontalArrangement = Arrangement.SpaceEvenly, + verticalAlignment = Alignment.CenterVertically + ) { + tabs.forEach { tab -> + MainNavigationBarItem( + selected = tab == currentTab, + tab = tab, + onClick = { onTabSelected(tab) } + ) + } + } + } + } +} + +@Composable +private fun MainNavigationBarItem( + selected: Boolean, + tab: BottomBarTab, + onClick: () -> Unit, + modifier: Modifier = Modifier, +) { + val (iconColor, textColor) = if (selected) { + KieroTheme.colors.white to KieroTheme.colors.white + } else { + KieroTheme.colors.gray800 to KieroTheme.colors.gray800 + } + + Column( + modifier = modifier + .noRippleClickable(onClick) + .padding(horizontal = 12.dp, vertical = 8.dp), + horizontalAlignment = Alignment.CenterHorizontally, + verticalArrangement = Arrangement.Center + ) { + Icon( + imageVector = ImageVector.vectorResource(tab.iconRes), + contentDescription = stringResource(tab.contentDescription), + tint = iconColor + ) + + Spacer(modifier = Modifier.height(4.dp)) + + Text( + text = stringResource(tab.labelRes), + color = textColor, + style = KieroTheme.typography.regular.body5, + textAlign = TextAlign.Center + ) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/kiero/presentation/main/screen/MainScreen.kt b/app/src/main/java/com/kiero/presentation/main/screen/MainScreen.kt new file mode 100644 index 00000000..bfb32a4b --- /dev/null +++ b/app/src/main/java/com/kiero/presentation/main/screen/MainScreen.kt @@ -0,0 +1,84 @@ +package com.kiero.presentation.main.screen + +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material3.Scaffold +import androidx.compose.material3.SnackbarHost +import androidx.compose.material3.SnackbarHostState +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.remember +import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.dp +import androidx.lifecycle.compose.collectAsStateWithLifecycle +import com.kiero.presentation.main.navigation.KidMainTab +import com.kiero.presentation.main.navigation.KieroNavHost +import com.kiero.presentation.main.navigation.MainAppState +import com.kiero.presentation.main.navigation.ParentMainTab +import com.kiero.presentation.main.navigation.component.MainBottomBar +import com.kiero.presentation.main.navigation.rememberMainAppState +import kotlinx.collections.immutable.toImmutableList + +@Composable +fun MainRoute( + appState: MainAppState = rememberMainAppState(), +) { + val snackBarHostState = remember { SnackbarHostState() } + + MainScreen( + appState = appState, + snackBarHostState = snackBarHostState, + ) +} + +@Composable +fun MainScreen( + appState: MainAppState, + snackBarHostState: SnackbarHostState, + modifier: Modifier = Modifier, +) { + val showParentBottomBar by appState.showParentBottomBar.collectAsStateWithLifecycle() + val showKidBottomBar by appState.showKidBottomBar.collectAsStateWithLifecycle() + val currentParentTab by appState.currentParentTab.collectAsStateWithLifecycle() + val currentKidTab by appState.currentKidTab.collectAsStateWithLifecycle() + + val isVisible = showParentBottomBar || showKidBottomBar + val containerShape = if (showParentBottomBar) { + RoundedCornerShape(topStart = 15.dp, topEnd = 15.dp) + } else { + RoundedCornerShape(0.dp) + } + val tabs = if (showParentBottomBar) { + ParentMainTab.entries.toImmutableList() + } else { + KidMainTab.entries.toImmutableList() + } + val currentTab = if (showParentBottomBar) currentParentTab else currentKidTab + + Scaffold( + modifier = modifier.fillMaxSize(), + snackbarHost = { + SnackbarHost(hostState = snackBarHostState) + }, + bottomBar = { + MainBottomBar( + isVisible = isVisible, + containerShape = containerShape, + tabs = tabs, + currentTab = currentTab, + onTabSelected = { tab -> + when (tab) { + is ParentMainTab -> appState.navigateParentTab(tab) + is KidMainTab -> appState.navigateKidTab(tab) + } + } + ) + } + ) { paddingValues -> + KieroNavHost( + appState = appState, + paddingValues = paddingValues, + ) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/kiero/presentation/kid/.gitkeep b/app/src/main/java/com/kiero/presentation/parent/alarm/component/.gitkeep similarity index 100% rename from app/src/main/java/com/kiero/presentation/kid/.gitkeep rename to app/src/main/java/com/kiero/presentation/parent/alarm/component/.gitkeep diff --git a/app/src/main/java/com/kiero/presentation/parent/.gitkeep b/app/src/main/java/com/kiero/presentation/parent/alarm/model/.gitkeep similarity index 100% rename from app/src/main/java/com/kiero/presentation/parent/.gitkeep rename to app/src/main/java/com/kiero/presentation/parent/alarm/model/.gitkeep diff --git a/app/src/main/java/com/kiero/presentation/parent/alarm/navigation/ParentAlarmNavigation.kt b/app/src/main/java/com/kiero/presentation/parent/alarm/navigation/ParentAlarmNavigation.kt new file mode 100644 index 00000000..4fbb8893 --- /dev/null +++ b/app/src/main/java/com/kiero/presentation/parent/alarm/navigation/ParentAlarmNavigation.kt @@ -0,0 +1,27 @@ +package com.kiero.presentation.parent.alarm.navigation + +import androidx.compose.foundation.layout.PaddingValues +import androidx.navigation.NavController +import androidx.navigation.NavGraphBuilder +import androidx.navigation.NavOptions +import androidx.navigation.compose.composable +import com.kiero.presentation.parent.alarm.screen.ParentAlarmRoute +import com.kiero.presentation.parent.navigation.Alarm + +fun NavController.navigateToAlarm( + navOptions: NavOptions? = null, +) { + navigate(Alarm, navOptions) +} + +fun NavGraphBuilder.parentAlarmNavGraph( + paddingValues: PaddingValues, + navigateUp: () -> Unit, +) { + composable { + ParentAlarmRoute( + paddingValues = paddingValues, + navigateUp = navigateUp + ) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/kiero/presentation/parent/alarm/screen/ParentAlarmScreen.kt b/app/src/main/java/com/kiero/presentation/parent/alarm/screen/ParentAlarmScreen.kt new file mode 100644 index 00000000..c1793366 --- /dev/null +++ b/app/src/main/java/com/kiero/presentation/parent/alarm/screen/ParentAlarmScreen.kt @@ -0,0 +1,45 @@ +package com.kiero.presentation.parent.alarm.screen + +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.padding +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.tooling.preview.Preview +import com.kiero.core.designsystem.theme.KieroTheme + +@Composable +fun ParentAlarmRoute( + paddingValues: PaddingValues, + navigateUp: () -> Unit, +) { + ParentAlarmScreen( + paddingValues = paddingValues, + navigateUp = navigateUp + ) +} + +@Composable +private fun ParentAlarmScreen( + paddingValues: PaddingValues, + navigateUp: () -> Unit, + modifier: Modifier = Modifier, +) { + Column( + modifier = modifier + .fillMaxSize() + .padding(paddingValues), + ) { + Text( + text = "알람 피드" + ) + } +} + +@Composable +@Preview +private fun ParentAlarmRoutePreview() { + KieroTheme {} +} \ No newline at end of file diff --git a/app/src/main/java/com/kiero/presentation/parent/alarm/viewmodel/.gitkeep b/app/src/main/java/com/kiero/presentation/parent/alarm/viewmodel/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/app/src/main/java/com/kiero/presentation/parent/navigation/ParentNavigation.kt b/app/src/main/java/com/kiero/presentation/parent/navigation/ParentNavigation.kt new file mode 100644 index 00000000..40431ccf --- /dev/null +++ b/app/src/main/java/com/kiero/presentation/parent/navigation/ParentNavigation.kt @@ -0,0 +1,49 @@ +package com.kiero.presentation.parent.navigation + +import androidx.compose.foundation.layout.PaddingValues +import androidx.navigation.NavController +import androidx.navigation.NavGraphBuilder +import androidx.navigation.NavHostController +import androidx.navigation.NavOptions +import androidx.navigation.compose.navigation +import com.kiero.core.navigation.Route +import com.kiero.presentation.parent.alarm.navigation.parentAlarmNavGraph +import com.kiero.presentation.parent.schedule.navigation.parentScheduleNavGraph +import kotlinx.serialization.Serializable + +sealed interface ParentTab : Route + +@Serializable +data object ParentGraph : Route + +@Serializable +data object Schedule : ParentTab + +@Serializable +data object Alarm : ParentTab + +fun NavController.navigateToParent( + navOptions: NavOptions? = null, +) { + navigate(ParentGraph, navOptions) +} + +fun NavGraphBuilder.parentNavGraph( + navController: NavHostController, + paddingValues: PaddingValues, + navigateUp: () -> Unit, +) { + navigation( + startDestination = Schedule + ) { + parentScheduleNavGraph( + paddingValues = paddingValues, + navigateUp = navigateUp, + ) + + parentAlarmNavGraph( + paddingValues = paddingValues, + navigateUp = navigateUp, + ) + } +} diff --git a/app/src/main/java/com/kiero/presentation/parent/schedule/component/.gitkeep b/app/src/main/java/com/kiero/presentation/parent/schedule/component/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/app/src/main/java/com/kiero/presentation/parent/schedule/model/.gitkeep b/app/src/main/java/com/kiero/presentation/parent/schedule/model/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/app/src/main/java/com/kiero/presentation/parent/schedule/navigation/ParentScheduleNavigation.kt b/app/src/main/java/com/kiero/presentation/parent/schedule/navigation/ParentScheduleNavigation.kt new file mode 100644 index 00000000..2955ea69 --- /dev/null +++ b/app/src/main/java/com/kiero/presentation/parent/schedule/navigation/ParentScheduleNavigation.kt @@ -0,0 +1,27 @@ +package com.kiero.presentation.parent.schedule.navigation + +import androidx.compose.foundation.layout.PaddingValues +import androidx.navigation.NavController +import androidx.navigation.NavGraphBuilder +import androidx.navigation.NavOptions +import androidx.navigation.compose.composable +import com.kiero.presentation.parent.navigation.Schedule +import com.kiero.presentation.parent.schedule.screen.ParentScheduleRoute + +fun NavController.navigateToSchedule( + navOptions: NavOptions? = null, +) { + navigate(Schedule, navOptions) +} + +fun NavGraphBuilder.parentScheduleNavGraph( + paddingValues: PaddingValues, + navigateUp: () -> Unit, +) { + composable { + ParentScheduleRoute( + paddingValues = paddingValues, + navigateUp = navigateUp, + ) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/kiero/presentation/parent/schedule/screen/ParentScheduleScreen.kt b/app/src/main/java/com/kiero/presentation/parent/schedule/screen/ParentScheduleScreen.kt new file mode 100644 index 00000000..2247f5df --- /dev/null +++ b/app/src/main/java/com/kiero/presentation/parent/schedule/screen/ParentScheduleScreen.kt @@ -0,0 +1,46 @@ +package com.kiero.presentation.parent.schedule.screen + +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.padding +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.tooling.preview.Preview +import com.kiero.core.designsystem.theme.KieroTheme + +@Composable +fun ParentScheduleRoute( + paddingValues: PaddingValues, + navigateUp: () -> Unit, +) { + ParentScheduleScreen( + paddingValues = paddingValues, + navigateUp = navigateUp + ) +} + +@Composable +private fun ParentScheduleScreen( + paddingValues: PaddingValues, + navigateUp: () -> Unit, + modifier: Modifier = Modifier, +) { + Column( + modifier = modifier + .fillMaxSize() + .padding(paddingValues), + ) { + Text( + text = "스케쥴 관리" + ) + } +} + +@Composable +@Preview +private fun ParentScheduleScreenPreview() { + KieroTheme {} +} \ No newline at end of file diff --git a/app/src/main/java/com/kiero/presentation/parent/schedule/viewmodel/.gitkeep b/app/src/main/java/com/kiero/presentation/parent/schedule/viewmodel/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index a13294d2..75778ea5 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -1,3 +1,9 @@ Kiero + + 스케쥴 관리 + 알람 피드 + 오늘의 여정 + 금화 미션 + 소원의 우물 \ No newline at end of file