diff --git a/app/src/main/java/com/konkuk/medicarecall/MainActivity.kt b/app/src/main/java/com/konkuk/medicarecall/MainActivity.kt index 0b9d228f..45577589 100644 --- a/app/src/main/java/com/konkuk/medicarecall/MainActivity.kt +++ b/app/src/main/java/com/konkuk/medicarecall/MainActivity.kt @@ -15,32 +15,17 @@ import androidx.compose.foundation.layout.WindowInsetsSides import androidx.compose.foundation.layout.only import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.systemBars -import androidx.compose.material3.Icon -import androidx.compose.material3.NavigationBar -import androidx.compose.material3.NavigationBarItem -import androidx.compose.material3.NavigationBarItemDefaults import androidx.compose.material3.Scaffold -import androidx.compose.material3.Text -import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableIntStateOf -import androidx.compose.runtime.saveable.rememberSaveable -import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier -import androidx.compose.ui.draw.drawBehind -import androidx.compose.ui.geometry.Offset -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.res.painterResource -import androidx.compose.ui.unit.dp -import androidx.core.view.WindowCompat import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen +import androidx.core.view.WindowCompat import androidx.hilt.navigation.compose.hiltViewModel -import androidx.navigation.compose.currentBackStackEntryAsState -import androidx.navigation.compose.rememberNavController -import com.konkuk.medicarecall.navigation.BottomNavItem -import com.konkuk.medicarecall.navigation.NavGraph -import com.konkuk.medicarecall.navigation.navigateTopLevel import com.konkuk.medicarecall.ui.feature.login.info.viewmodel.LoginViewModel import com.konkuk.medicarecall.ui.feature.login.senior.viewmodel.LoginElderViewModel +import com.konkuk.medicarecall.ui.navigation.NavGraph +import com.konkuk.medicarecall.ui.navigation.component.MainBottomBar +import com.konkuk.medicarecall.ui.navigation.component.MainTab +import com.konkuk.medicarecall.ui.navigation.rememberMainNavigator import com.konkuk.medicarecall.ui.theme.MediCareCallTheme import dagger.hilt.android.AndroidEntryPoint @@ -57,12 +42,12 @@ class MainActivity : ComponentActivity() { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { window.insetsController?.setSystemBarsAppearance( WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS, - WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS + WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS, - ) + ) window.insetsController?.setSystemBarsAppearance( WindowInsetsController.APPEARANCE_LIGHT_NAVIGATION_BARS, - WindowInsetsController.APPEARANCE_LIGHT_NAVIGATION_BARS + WindowInsetsController.APPEARANCE_LIGHT_NAVIGATION_BARS, ) } else { @Suppress("DEPRECATION") @@ -81,110 +66,33 @@ class MainActivity : ComponentActivity() { } - - setContent { - MediCareCallTheme { - val navController = rememberNavController() - - val navBarItems = listOf( - BottomNavItem( - label = "홈", - route = "home", - selectedIcon = R.drawable.ic_home_selected, - unselectedIcon = R.drawable.ic_home_unselected - ), - BottomNavItem( - label = "주간 통계", - route = "statistics", - selectedIcon = R.drawable.ic_statistics_selected, - unselectedIcon = R.drawable.ic_statistics_unselected - ), - BottomNavItem( - label = "설정", - route = "settings", - selectedIcon = R.drawable.ic_settings_selected, - unselectedIcon = R.drawable.ic_settings_unselected, - ) - ) - - val navBackStackEntry by navController.currentBackStackEntryAsState() - val currentRoute = navBackStackEntry?.destination?.route - - - var selectedIndex by rememberSaveable { mutableIntStateOf(0) } + val navigator = rememberMainNavigator() + MediCareCallTheme { val loginViewModel: LoginViewModel = hiltViewModel() val loginElderViewModel: LoginElderViewModel = hiltViewModel() - val bottomBarRoutes = listOf( - "home", "statistics", "settings", - ) Scaffold( modifier = Modifier.background(MediCareCallTheme.colors.bg), - contentWindowInsets = WindowInsets.systemBars - .only(WindowInsetsSides.Horizontal), + contentWindowInsets = WindowInsets.systemBars.only(WindowInsetsSides.Horizontal), bottomBar = { - if (currentRoute in bottomBarRoutes) - NavigationBar( - modifier = Modifier - .drawBehind { - val strokeWidth = 1.dp.toPx() - drawLine( - color = Color(0xFFECECEC), // NavigationBar의 상단 테두리 - start = Offset(0f, 0f), - end = Offset(size.width, 0f), - strokeWidth = strokeWidth, - ) - }, - containerColor = MediCareCallTheme.colors.white - ) - { - navBarItems.forEachIndexed { index, item -> - NavigationBarItem( - selected = currentRoute == item.route, - alwaysShowLabel = true, - label = { - Text( - text = item.label, - style = MediCareCallTheme.typography.R_14 - ) - }, - onClick = { - selectedIndex = index - if (currentRoute != item.route) - navController.navigateTopLevel(item.route) // 네비게이션 아이템 클릭 시 해당 라우트로 이동 - }, - icon = { - Icon( - painter = painterResource( - if (currentRoute == item.route) { - item.selectedIcon - } else item.unselectedIcon - ), - contentDescription = item.label - ) - }, - colors = NavigationBarItemDefaults.colors( - indicatorColor = Color.Transparent, - selectedIconColor = MediCareCallTheme.colors.main, // 선택된 아이콘 색 - unselectedIconColor = Color.Black, // 선택되지 않은 아이콘 색상 - selectedTextColor = MediCareCallTheme.colors.main, // 선택된 텍스트 색 - unselectedTextColor = Color.Black // 선택되지 않은 텍스트 색상 - ) - ) - } - } - } + MainBottomBar( + visible = navigator.shouldShowBottomBar(), + tabs = MainTab.entries.toList(), + currentTab = navigator.currentTab, + onTabSelected = { + navigator.navigateToMainTab(it) + }, + ) + }, ) { innerPadding -> NavGraph( - navController = navController, + navigator = navigator, loginViewModel = loginViewModel, loginElderViewModel = loginElderViewModel, - modifier = Modifier - .padding(bottom = innerPadding.calculateBottomPadding()) + modifier = Modifier.padding(bottom = innerPadding.calculateBottomPadding()), ) - } } } diff --git a/app/src/main/java/com/konkuk/medicarecall/data/dto/response/MyInfoResponseDto.kt b/app/src/main/java/com/konkuk/medicarecall/data/dto/response/MyInfoResponseDto.kt index 59a506ce..12059b5c 100644 --- a/app/src/main/java/com/konkuk/medicarecall/data/dto/response/MyInfoResponseDto.kt +++ b/app/src/main/java/com/konkuk/medicarecall/data/dto/response/MyInfoResponseDto.kt @@ -6,11 +6,11 @@ import kotlinx.serialization.Serializable @Serializable data class MyInfoResponseDto( - val name: String, - val birthDate: String, - val gender: GenderType, - val phone: String, - val pushNotification: PushNotificationDto + val name: String = "", + val birthDate: String = "", + val gender: GenderType = GenderType.MALE, + val phone: String = "", + val pushNotification: PushNotificationDto = PushNotificationDto("", "", "", ""), ) @Serializable @@ -18,6 +18,6 @@ data class PushNotificationDto( val all: String, val carecallCompleted: String, val healthAlert: String, - val carecallMissed: String + val carecallMissed: String, ) diff --git a/app/src/main/java/com/konkuk/medicarecall/navigation/BottomNavItem.kt b/app/src/main/java/com/konkuk/medicarecall/navigation/BottomNavItem.kt deleted file mode 100644 index 943c563b..00000000 --- a/app/src/main/java/com/konkuk/medicarecall/navigation/BottomNavItem.kt +++ /dev/null @@ -1,8 +0,0 @@ -package com.konkuk.medicarecall.navigation - -data class BottomNavItem( - val label: String, - val route: String, - val selectedIcon: Int, - val unselectedIcon: Int -) diff --git a/app/src/main/java/com/konkuk/medicarecall/navigation/NavGraph.kt b/app/src/main/java/com/konkuk/medicarecall/navigation/NavGraph.kt deleted file mode 100644 index aaa4306b..00000000 --- a/app/src/main/java/com/konkuk/medicarecall/navigation/NavGraph.kt +++ /dev/null @@ -1,475 +0,0 @@ -package com.konkuk.medicarecall.navigation - -import android.app.Activity -import androidx.activity.compose.BackHandler -import androidx.compose.animation.EnterTransition -import androidx.compose.animation.ExitTransition -import androidx.compose.runtime.Composable -import androidx.compose.runtime.remember -import androidx.compose.ui.Modifier -import androidx.compose.ui.platform.LocalContext -import androidx.hilt.navigation.compose.hiltViewModel -import androidx.navigation.NavHostController -import androidx.navigation.NavType -import androidx.navigation.compose.NavHost -import androidx.navigation.compose.composable -import androidx.navigation.compose.currentBackStackEntryAsState -import androidx.navigation.navArgument -import androidx.navigation.navigation -import com.konkuk.medicarecall.data.dto.response.EldersHealthResponseDto -import com.konkuk.medicarecall.data.dto.response.EldersInfoResponseDto -import com.konkuk.medicarecall.data.dto.response.EldersSubscriptionResponseDto -import com.konkuk.medicarecall.data.dto.response.MyInfoResponseDto -import com.konkuk.medicarecall.data.dto.response.NoticesResponseDto -import com.konkuk.medicarecall.ui.feature.alarm.screen.AlarmScreen -import com.konkuk.medicarecall.ui.feature.calendar.viewmodel.CalendarViewModel -import com.konkuk.medicarecall.ui.feature.home.screen.HomeScreen -import com.konkuk.medicarecall.ui.feature.home.viewmodel.HomeViewModel -import com.konkuk.medicarecall.ui.feature.homedetail.glucoselevel.screen.GlucoseDetail -import com.konkuk.medicarecall.ui.feature.homedetail.meal.screen.MealDetail -import com.konkuk.medicarecall.ui.feature.homedetail.medicine.screen.MedicineDetail -import com.konkuk.medicarecall.ui.feature.homedetail.sleep.screen.SleepDetail -import com.konkuk.medicarecall.ui.feature.homedetail.statehealth.screen.StateHealthDetail -import com.konkuk.medicarecall.ui.feature.homedetail.statemental.screen.StateMentalDetail -import com.konkuk.medicarecall.ui.feature.login.carecall.screen.CallTimeScreen -import com.konkuk.medicarecall.ui.feature.login.info.screen.LoginMyInfoScreen -import com.konkuk.medicarecall.ui.feature.login.info.screen.LoginPhoneScreen -import com.konkuk.medicarecall.ui.feature.login.info.screen.LoginStartScreen -import com.konkuk.medicarecall.ui.feature.login.info.screen.LoginVerificationScreen -import com.konkuk.medicarecall.ui.feature.login.info.viewmodel.LoginViewModel -import com.konkuk.medicarecall.ui.feature.login.payment.screen.LoginFinishScreen -import com.konkuk.medicarecall.ui.feature.login.payment.screen.NaverPayWebViewScreen -import com.konkuk.medicarecall.ui.feature.login.payment.screen.PaymentScreen -import com.konkuk.medicarecall.ui.feature.login.senior.viewmodel.LoginElderViewModel -import com.konkuk.medicarecall.ui.feature.login.senior.screen.LoginElderMedInfoScreen -import com.konkuk.medicarecall.ui.feature.login.senior.screen.LoginElderScreen -import com.konkuk.medicarecall.ui.feature.settings.screen.AnnouncementDetailScreen -import com.konkuk.medicarecall.ui.feature.settings.screen.AnnouncementScreen -import com.konkuk.medicarecall.ui.feature.settings.screen.HealthDetailScreen -import com.konkuk.medicarecall.ui.feature.settings.screen.HealthInfoScreen -import com.konkuk.medicarecall.ui.feature.settings.screen.MyDataSettingScreen -import com.konkuk.medicarecall.ui.feature.settings.screen.MyDetailScreen -import com.konkuk.medicarecall.ui.feature.settings.screen.PersonalDetailScreen -import com.konkuk.medicarecall.ui.feature.settings.screen.PersonalInfoScreen -import com.konkuk.medicarecall.ui.feature.settings.screen.ServiceCenterScreen -import com.konkuk.medicarecall.ui.feature.settings.screen.SettingAlarmScreen -import com.konkuk.medicarecall.ui.feature.settings.screen.SettingSubscribeScreen -import com.konkuk.medicarecall.ui.feature.settings.screen.SettingsScreen -import com.konkuk.medicarecall.ui.feature.settings.screen.SubscribeDetailScreen -import com.konkuk.medicarecall.ui.feature.splash.screen.SplashScreen -import com.konkuk.medicarecall.ui.feature.statistics.screen.StatisticsScreen -import kotlinx.serialization.json.Json -import java.net.URLDecoder -import java.nio.charset.StandardCharsets - - -// ----- 헬퍼: 탑레벨 전환은 back stack 확장 없이 ----- -fun NavHostController.navigateTopLevel(route: String) { - navigate(route) { - // 그래프 시작점까지 popUp + 상태 저장/복원 - popUpTo(Route.Home.route) { - inclusive = false - saveState = true - } -// launchSingleTop = true - restoreState = true - } -} - -// ---- 헬퍼: 로그인 성공 후 인증 그래프 제거하고 main으로 --- -fun NavHostController.navigateToMainAfterLogin() { - navigate("main") { - popUpTo("login") { inclusive = true } - launchSingleTop = true - restoreState = true - } -} - -// ----- 컴포저블: 탑레벨에서 뒤로가기 = 앱 백그라운드 이동 ----- -@Composable -private fun TopLevelBackHandler(navController: NavHostController) { - val activity = LocalContext.current as? Activity - val topLevel = setOf(Route.Home.route, Route.Statistics.route, Route.Settings.route) - val currentRoute = navController.currentBackStackEntryAsState().value?.destination?.route - val isTopLevel = currentRoute in topLevel - - if (isTopLevel && activity != null) { - BackHandler(true) { - activity.moveTaskToBack(true) - } - } -} - -@Composable -fun NavGraph( - navController: NavHostController, - loginViewModel: LoginViewModel, - loginElderViewModel: LoginElderViewModel, - modifier: Modifier = Modifier, -) { - // val startDestination = if (loginViewModel.isLoggedIn) "main" else "login" - // navController = navController, startDestination = Route.Home.route, // 시작 화면 - val calendarViewModel: CalendarViewModel = hiltViewModel() - - NavHost( - navController = navController, - startDestination = Route.AppSplash.route, // 시작 화면 - enterTransition = { EnterTransition.None }, - exitTransition = { ExitTransition.None }, - modifier = modifier, - ) { - composable(route = Route.AppSplash.route) { - SplashScreen(navController) - } - - - // 메인 내비게이션 - navigation(startDestination = Route.Home.route, route = "main") { - - - // 홈 - composable(route = Route.Home.route) { backStackEntry -> - //TopLevelBackHandler(navController) - val parentEntry = remember(backStackEntry) { - navController.getBackStackEntry("main") - } - val homeViewModel: HomeViewModel = hiltViewModel(parentEntry) - - HomeScreen( - navController = navController, - homeViewModel = homeViewModel, - mainBackStackEntry = parentEntry, - onNavigateToMealDetail = { navController.navigate(Route.MealDetail.route) }, - onNavigateToMedicineDetail = { navController.navigate(Route.MedicineDetail.route) }, - onNavigateToSleepDetail = { navController.navigate(Route.SleepDetail.route) }, - onNavigateToStateHealthDetail = { navController.navigate(Route.StateHealthDetail.route) }, - onNavigateToStateMentalDetail = { navController.navigate(Route.StateMentalDetail.route) }, - onNavigateToGlucoseDetail = { navController.navigate(Route.GlucoseDetail.route) }, - ) - } - - // 홈 상세 화면_식사 화면 - composable(route = Route.MealDetail.route) { - MealDetail( - navController = navController, - ) - } - - - // 홈 상세 화면_복용 화면 - composable(route = Route.MedicineDetail.route) { - MedicineDetail( - navController = navController, - ) - } - - - //홈 상세 화면_수면 화면 - composable(route = Route.SleepDetail.route) { - SleepDetail( - navController = navController, - ) - } - - - //홈 상세 화면_건강 징후 화면 - composable(route = Route.StateHealthDetail.route) { - StateHealthDetail( - navController = navController, - ) - } - - //홈 상세 화면_심리 상태 화면 - composable(route = Route.StateMentalDetail.route) { - StateMentalDetail( - navController = navController, - ) - } - - - //홈 상세 화면_혈당 화면 - - composable(route = Route.GlucoseDetail.route) { - GlucoseDetail(navController = navController) - } - - - // 통계 - composable(route = Route.Statistics.route) { backStackEntry -> - val parentEntry = remember(backStackEntry) { - navController.getBackStackEntry("main") - } - val homeViewModel: HomeViewModel = hiltViewModel(parentEntry) - - StatisticsScreen( - navController = navController, - homeViewModel = homeViewModel, - ) - } - - // 설정 - composable(route = Route.Settings.route) { - //TopLevelBackHandler(navController) - SettingsScreen( - onNavigateToMyDataSetting = { - navController.navigate(Route.MyDataSetting.route) - }, - onNavigateToAnnouncement = { - navController.navigate(Route.Announcement.route) - }, - onNavigateToCenter = { - navController.navigate(Route.ServiceCenter.route) - }, - onNavigateToSubscribe = { - navController.navigate(Route.SettingSubscribe.route) - }, - onNavigateToPersonalInfo = { - navController.navigate(Route.PersonalInfo.route) - }, - onNavigateToHealthInfo = { - navController.navigate(Route.HealthInfo.route) - }, - navController = navController, - ) - } - - composable( - route = Route.MyDataSetting.route, - ) { - MyDataSettingScreen( - onBack = { - navController.popBackStack() - }, - navController = navController, - ) - } - - composable( - route = "my_detail/{myDataJson}", - arguments = listOf(navArgument("myDataJson") { type = NavType.StringType }), - ) { backStackEntry -> - val encodedJson = backStackEntry.arguments?.getString("myDataJson") ?: "" - val decodedJson = URLDecoder.decode(encodedJson, StandardCharsets.UTF_8.toString()) - val myDataInfo = Json.decodeFromString(decodedJson) - MyDetailScreen( - myDataInfo = myDataInfo, - onBack = { - navController.popBackStack() - }, - ) - } - - composable(route = Route.Announcement.route) { - AnnouncementScreen( - onBack = { - navController.popBackStack() - }, - navController = navController, - ) - } - - composable( - route = "announcement_detail/{noticeJson}", - arguments = listOf(navArgument("noticeJson") { type = NavType.StringType }), - ) { backStackEntry -> - val encodedJson = backStackEntry.arguments?.getString("noticeJson") ?: "" - val decodedJson = URLDecoder.decode(encodedJson, StandardCharsets.UTF_8.toString()) - val noticeInfo = Json.decodeFromString(decodedJson) - - AnnouncementDetailScreen( - noticeInfo = noticeInfo, - onBack = { navController.popBackStack() }, - ) - } - - composable(route = Route.ServiceCenter.route) { - ServiceCenterScreen( - onBack = { - navController.popBackStack() - }, - ) - } - - composable(route = Route.SettingSubscribe.route) { - SettingSubscribeScreen( - onBack = { - navController.popBackStack() - }, - navController = navController, - ) - } - - composable( - route = "subscribe_detail/{elderJson}", - // elderJson을 NavArgument로 받아옴 - arguments = listOf(navArgument("elderJson") { type = NavType.StringType }), - ) { backStackEntry -> - val encodedJson = backStackEntry.arguments?.getString("elderJson") ?: "" - val decodedJson = URLDecoder.decode(encodedJson, StandardCharsets.UTF_8.toString()) - val elderInfo = Json.decodeFromString(decodedJson) - - SubscribeDetailScreen( - elderInfo = elderInfo, - onBack = { navController.popBackStack() }, - ) - } - - composable(route = Route.PersonalInfo.route) { - PersonalInfoScreen( - onBack = { - navController.popBackStack() - }, - navController = navController, - ) - } - - composable( - route = "personal_detail/{elderInfo}", - arguments = listOf( - navArgument("elderInfo") { - type = NavType.StringType - }, - ), - ) { backStackEntry -> - val encodedElderInfo = backStackEntry.arguments?.getString("elderInfo") ?: "" - val decodedElderInfo = - URLDecoder.decode(encodedElderInfo, StandardCharsets.UTF_8.toString()) - val eldersInfoResponseDto = - Json.decodeFromString(decodedElderInfo) - PersonalDetailScreen( - onBack = { - navController.popBackStack() - }, - eldersInfoResponseDto = eldersInfoResponseDto, - navController = navController, - ) - } - - composable(route = Route.HealthInfo.route) { - HealthInfoScreen( - onBack = { - navController.popBackStack() - }, - navController = navController, - ) - } - - composable( - route = "health_detail/{healthInfo}", - arguments = listOf( - navArgument("healthInfo") { - type = NavType.StringType - }, - ), - ) { backStackEntry -> - val encodedHealthInfo = backStackEntry.arguments?.getString("healthInfo") ?: "" - val decodedHealthInfo = - URLDecoder.decode(encodedHealthInfo, StandardCharsets.UTF_8.toString()) - val healthInfoResponseDto = - Json.decodeFromString(decodedHealthInfo) - HealthDetailScreen( - onBack = { - navController.popBackStack() - }, - healthInfoResponseDto = healthInfoResponseDto, - - ) - } - - composable( - route = "setting_alarm/{myDataJson}", - arguments = listOf(navArgument("myDataJson") { type = NavType.StringType }), - ) { backStackEntry -> - val encodedJson = backStackEntry.arguments?.getString("myDataJson") ?: "" - val decodedJson = URLDecoder.decode(encodedJson, StandardCharsets.UTF_8.toString()) - val myDataInfo = Json.decodeFromString(decodedJson) - - SettingAlarmScreen( - myDataInfo = myDataInfo, - onBack = { - navController.popBackStack() - }, - ) - } - - composable(route = Route.Alarm.route) { - AlarmScreen( - onBack = { - navController.popBackStack() - }, - navController = navController, - ) - } - } - - // 로그인 내비게이션 - navigation(startDestination = Route.LoginStart.route, route = "login") { - - - composable(route = Route.LoginStart.route) { - LoginStartScreen(navController, loginViewModel) - } - composable(route = Route.LoginPhone.route) { - LoginPhoneScreen(navController, loginViewModel) - } - composable(route = Route.LoginVerification.route) { - LoginVerificationScreen(navController, loginViewModel) - } - composable(route = Route.LoginMyInfo.route) { - LoginMyInfoScreen(navController, loginViewModel) - } - composable(route = Route.LoginElderInfoScreen.route) { - LoginElderScreen(navController, loginElderViewModel) - } - composable(route = Route.LoginElderMedInfoScreen.route) { - LoginElderMedInfoScreen(navController, loginElderViewModel) - } - - composable(route = Route.SetCall.route) { - CallTimeScreen( - onBack = { - navController.popBackStack() - }, - navigatedToPayment = { - //navController.navigate(Route.Payment.route) // 결제화면 이동(베타 버전에서는 결제화면 없음) - navController.navigate(Route.FinishSplash.route) // 결제화면 없이 바로 완료 화면 - }, - ) - } - - composable(route = Route.Payment.route) { - PaymentScreen( - onBack = { - navController.popBackStack() - }, - navController = navController, - ) - } - - composable(route = Route.NaverPay.route) { - NaverPayWebViewScreen( - onBack = { - navController.popBackStack() - }, - navController = navController, - ) - } - - composable(route = Route.FinishSplash.route) { - LoginFinishScreen( - navController = navController, - ) - } - - composable( - route = Route.NaverPayWithCode.route, - arguments = listOf(navArgument("orderCode") { type = NavType.StringType }), - ) { backStackEntry -> - NaverPayWebViewScreen( - onBack = { navController.popBackStack() }, - navController = navController, - ) - } - } - } -} diff --git a/app/src/main/java/com/konkuk/medicarecall/navigation/Route.kt b/app/src/main/java/com/konkuk/medicarecall/navigation/Route.kt deleted file mode 100644 index 553e0a58..00000000 --- a/app/src/main/java/com/konkuk/medicarecall/navigation/Route.kt +++ /dev/null @@ -1,43 +0,0 @@ -package com.konkuk.medicarecall.navigation - -sealed class Route(val route: String) { - object AppSplash : Route("app_splash") - object LoginStart : Route("login_start") - object LoginPhone : Route("login_phone") - object LoginVerification : Route("login_verification") - object LoginMyInfo : Route("login_my_info") - object LoginElderInfoScreen : Route("login_elder_info") - object LoginElderMedInfoScreen : Route("login_elder_med_info") - object SetCall : Route("set_call") - object Payment : Route("payment") - object NaverPay : Route("naver_pay") - object NaverPayWithCode : Route("naver_pay/{orderCode}") { - fun create(orderCode: String) = "naver_pay/$orderCode" - } - - object FinishSplash : Route("finish_splash") - object Home : Route("home") - object Statistics : Route("statistics") - object Settings : Route("settings") - - object Alarm : Route("alarm") - - object Announcement : Route("announcement") - object AnnouncementDetail : Route("announcement_detail") - object HealthInfo : Route("health_info") - object HealthDetail : Route("health_detail") - object MyDataSetting : Route("my_data_setting") - object MyDetail : Route("my_detail") - object PersonalDetail : Route("personal_detail") - object PersonalInfo : Route("personal_info") - object ServiceCenter : Route("service_center") - object SettingAlarm : Route("setting_alarm") - object SettingSubscribe : Route("setting_subscribe") - object SubscribeDetail : Route("subscribe_detail") - object MealDetail : Route("home_meal_detail") - object MedicineDetail : Route("home_medicine_detail") - object SleepDetail : Route("home_sleep_detail") - object StateHealthDetail : Route("home_state_health_detail") - object StateMentalDetail : Route("home_state_mental_detail") - object GlucoseDetail : Route("home_glucose_detail") -} diff --git a/app/src/main/java/com/konkuk/medicarecall/ui/common/component/NameBar.kt b/app/src/main/java/com/konkuk/medicarecall/ui/common/component/NameBar.kt index 3f602517..84e43239 100644 --- a/app/src/main/java/com/konkuk/medicarecall/ui/common/component/NameBar.kt +++ b/app/src/main/java/com/konkuk/medicarecall/ui/common/component/NameBar.kt @@ -20,10 +20,7 @@ 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 androidx.navigation.NavHostController -import androidx.navigation.compose.rememberNavController import com.konkuk.medicarecall.R -import com.konkuk.medicarecall.navigation.Route import com.konkuk.medicarecall.ui.theme.MediCareCallTheme @Composable @@ -31,8 +28,8 @@ fun NameBar( name: String, notificationCount: Int, modifier: Modifier = Modifier, - navController: NavHostController, - onDropdownClick: () -> Unit + navigateToAlarm: () -> Unit = {}, + onDropdownClick: () -> Unit, ) { @@ -43,20 +40,19 @@ fun NameBar( .padding(horizontal = 16.dp, vertical = 14.dp) .background(Color.White), verticalAlignment = Alignment.CenterVertically, - horizontalArrangement = Arrangement.SpaceBetween - + horizontalArrangement = Arrangement.SpaceBetween, ) { Row( Modifier.clickable( indication = null, - interactionSource = null + interactionSource = null, ) { onDropdownClick() }, - verticalAlignment = Alignment.CenterVertically + verticalAlignment = Alignment.CenterVertically, ) { Text( text = name, style = MediCareCallTheme.typography.SB_24, - color = MediCareCallTheme.colors.black + color = MediCareCallTheme.colors.black, ) Spacer(modifier = Modifier.width(4.dp)) Icon( @@ -65,16 +61,17 @@ fun NameBar( .size(18.dp), painter = painterResource(id = R.drawable.ic_arrow_down_big), contentDescription = "arrow down", - tint = MediCareCallTheme.colors.gray3 + tint = MediCareCallTheme.colors.gray3, ) } NotificationIconWithBadge( notificationCount = notificationCount, - onClick = { navController.navigate(route = Route.Alarm.route) } + onClick = navigateToAlarm, ) } + } } @@ -82,15 +79,11 @@ fun NameBar( @Preview @Composable fun PreviewNameBar() { - MediCareCallTheme { - NameBar( name = "김옥자", notificationCount = 4, - navController = rememberNavController(), - onDropdownClick = {} + onDropdownClick = {}, ) } - } diff --git a/app/src/main/java/com/konkuk/medicarecall/ui/common/component/TopAppBar.kt b/app/src/main/java/com/konkuk/medicarecall/ui/common/component/TopAppBar.kt index 70c92825..7e3ded81 100644 --- a/app/src/main/java/com/konkuk/medicarecall/ui/common/component/TopAppBar.kt +++ b/app/src/main/java/com/konkuk/medicarecall/ui/common/component/TopAppBar.kt @@ -27,25 +27,25 @@ import com.konkuk.medicarecall.ui.theme.MediCareCallTheme fun TopAppBar( modifier: Modifier = Modifier, title: String, - navController: NavHostController, + onBack: () -> Unit, ) { Column( modifier = modifier .fillMaxWidth() - .background(Color.White) + .background(Color.White), ) { Row( modifier = Modifier .fillMaxWidth() .padding(vertical = 15.dp, horizontal = 10.dp), - verticalAlignment = Alignment.CenterVertically + verticalAlignment = Alignment.CenterVertically, ) { // 뒤로 가기 Icon( modifier = Modifier .size(24.dp) .clickable { - navController.popBackStack() + onBack() }, painter = painterResource(id = R.drawable.ic_arrow_big_back), contentDescription = "big arrow back", @@ -72,14 +72,14 @@ fun TopAppBar( thickness = 1.dp, ) } -} + } -@Preview(showBackground = true) -@Composable -private fun PreviewTopAppBar() { - TopAppBar( - title = "식사", - navController = rememberNavController(), - ) -} + @Preview(showBackground = true) + @Composable + private fun PreviewTopAppBar() { + TopAppBar( + title = "식사", + onBack = {}, + ) + } diff --git a/app/src/main/java/com/konkuk/medicarecall/ui/common/extension/NavBackStackEntryExt.kt b/app/src/main/java/com/konkuk/medicarecall/ui/common/extension/NavBackStackEntryExt.kt new file mode 100644 index 00000000..fd66a8ac --- /dev/null +++ b/app/src/main/java/com/konkuk/medicarecall/ui/common/extension/NavBackStackEntryExt.kt @@ -0,0 +1,32 @@ +package com.konkuk.medicarecall.ui.common.extension + +import android.util.Log +import androidx.compose.runtime.Composable +import androidx.compose.runtime.remember +import androidx.hilt.navigation.compose.hiltViewModel +import androidx.lifecycle.ViewModel +import androidx.navigation.NavBackStackEntry +import androidx.navigation.NavHostController +import com.konkuk.medicarecall.ui.navigation.Route + +// https://youtu.be/h61Wqy3qcKg?si=OqctoATR5MGbypOW +@Composable +inline fun NavBackStackEntry.sharedViewModel( + navController: NavHostController, +): T { + destination.route ?: return hiltViewModel() + + val entry = try { + navController.getBackStackEntry() + } catch (e: IllegalArgumentException) { + Log.e("NavBackStackEntryExt", "No back stack entry found for route: ${R::class}", e) + null + } + + return if (entry != null) { + val rememberedEntry = remember(this) { entry } + hiltViewModel(rememberedEntry) + } else { + hiltViewModel() + } +} diff --git a/app/src/main/java/com/konkuk/medicarecall/ui/feature/alarm/navigation/AlarmNavigation.kt b/app/src/main/java/com/konkuk/medicarecall/ui/feature/alarm/navigation/AlarmNavigation.kt new file mode 100644 index 00000000..cab1d4d0 --- /dev/null +++ b/app/src/main/java/com/konkuk/medicarecall/ui/feature/alarm/navigation/AlarmNavigation.kt @@ -0,0 +1,21 @@ +package com.konkuk.medicarecall.ui.feature.alarm.navigation + +import androidx.navigation.NavController +import androidx.navigation.NavGraphBuilder +import androidx.navigation.compose.composable +import com.konkuk.medicarecall.ui.feature.alarm.screen.AlarmScreen +import com.konkuk.medicarecall.ui.navigation.Route + +fun NavController.navigateToAlarm() { + navigate(Route.Alarm) +} + +fun NavGraphBuilder.alarmNavGraph( + popBackStack: () -> Unit, +) { + composable { + AlarmScreen( + onBack = popBackStack, + ) + } +} diff --git a/app/src/main/java/com/konkuk/medicarecall/ui/feature/alarm/screen/AlarmScreen.kt b/app/src/main/java/com/konkuk/medicarecall/ui/feature/alarm/screen/AlarmScreen.kt index 9d809297..49dbd25f 100644 --- a/app/src/main/java/com/konkuk/medicarecall/ui/feature/alarm/screen/AlarmScreen.kt +++ b/app/src/main/java/com/konkuk/medicarecall/ui/feature/alarm/screen/AlarmScreen.kt @@ -11,24 +11,22 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.res.painterResource import androidx.compose.ui.unit.dp -import androidx.navigation.NavHostController import com.konkuk.medicarecall.R import com.konkuk.medicarecall.ui.feature.alarm.component.AlarmItem -import com.konkuk.medicarecall.ui.type.AlarmType import com.konkuk.medicarecall.ui.feature.settings.component.SettingsTopAppBar import com.konkuk.medicarecall.ui.theme.MediCareCallTheme +import com.konkuk.medicarecall.ui.type.AlarmType @Composable fun AlarmScreen( onBack: () -> Unit, - navController: NavHostController, - modifier: Modifier = Modifier + modifier: Modifier = Modifier, ) { Column( modifier = modifier .fillMaxSize() .background(MediCareCallTheme.colors.bg) - .statusBarsPadding() + .statusBarsPadding(), ) { SettingsTopAppBar( title = "알림", @@ -39,34 +37,34 @@ fun AlarmScreen( modifier = modifier .size(24.dp) .clickable { onBack() }, - tint = MediCareCallTheme.colors.black + tint = MediCareCallTheme.colors.black, ) }, ) AlarmItem( AlarmType.NEW_ALARM, "✅ 1차 케어콜이 완료되었어요. 확인해 보세요!", - "7월 8일 13:15" + "7월 8일 13:15", ) AlarmItem( AlarmType.READ_ALARM, "❗ 박막례 어르신 건강이상 징후가 탐지되었어요. 확인해 주세요!", - "7월 7일 13:15" + "7월 7일 13:15", ) AlarmItem( AlarmType.READ_ALARM, "📞 김옥자 어르신 케어콜 부재중 상태입니다. 확인해 주세요!", - "7월 7일 13:15" + "7월 7일 13:15", ) AlarmItem( AlarmType.READ_ALARM, "❗ 박막례 어르신 건강이상 징후가 탐지되었어요. 확인해 주세요!", - "7월 7일 13:15" + "7월 7일 13:15", ) AlarmItem( AlarmType.READ_ALARM, "✅ 1차 케어콜이 완료되었어요. 확인해 보세요!", - "7월 7일 13:15" + "7월 7일 13:15", ) } } diff --git a/app/src/main/java/com/konkuk/medicarecall/ui/feature/home/navigation/HomeNavigation.kt b/app/src/main/java/com/konkuk/medicarecall/ui/feature/home/navigation/HomeNavigation.kt new file mode 100644 index 00000000..81369d92 --- /dev/null +++ b/app/src/main/java/com/konkuk/medicarecall/ui/feature/home/navigation/HomeNavigation.kt @@ -0,0 +1,33 @@ +package com.konkuk.medicarecall.ui.feature.home.navigation + +import androidx.navigation.NavController +import androidx.navigation.NavGraphBuilder +import androidx.navigation.NavOptions +import androidx.navigation.compose.composable +import com.konkuk.medicarecall.ui.feature.home.screen.HomeScreen +import com.konkuk.medicarecall.ui.navigation.MainTabRoute + +fun NavController.navigateToHome(navOptions: NavOptions) { + navigate(MainTabRoute.Home, navOptions) +} + +fun NavGraphBuilder.homeNavGraph( + navigateToMealDetail: () -> Unit, + navigateToMedicationDetail: () -> Unit, + navigateToSleepDetail: () -> Unit, + navigateToHealthAnalysisDetail: () -> Unit, + navigateToMentalAnalysisDetail: () -> Unit, + navigateToGlucoseDetail: () -> Unit, +) { + composable { backStackEntry -> + HomeScreen( + navigateToMealDetail = navigateToMealDetail, + navigateToMedicationDetail = navigateToMedicationDetail, + navigateToSleepDetail = navigateToSleepDetail, + navigateToHealthAnalysisDetail = navigateToHealthAnalysisDetail, + navigateToMentalAnalysisDetail = navigateToMentalAnalysisDetail, + navigateToGlucoseDetail = navigateToGlucoseDetail, + mainBackStackEntry = backStackEntry, + ) + } +} diff --git a/app/src/main/java/com/konkuk/medicarecall/ui/feature/home/screen/HomeScreen.kt b/app/src/main/java/com/konkuk/medicarecall/ui/feature/home/screen/HomeScreen.kt index 1fa48ab6..fde66020 100644 --- a/app/src/main/java/com/konkuk/medicarecall/ui/feature/home/screen/HomeScreen.kt +++ b/app/src/main/java/com/konkuk/medicarecall/ui/feature/home/screen/HomeScreen.kt @@ -51,7 +51,6 @@ import androidx.hilt.navigation.compose.hiltViewModel import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.navigation.NavBackStackEntry import androidx.navigation.NavHostController -import androidx.navigation.compose.rememberNavController import com.konkuk.medicarecall.R import com.konkuk.medicarecall.data.dto.response.HomeResponseDto import com.konkuk.medicarecall.ui.common.component.NameBar @@ -74,16 +73,15 @@ import kotlinx.coroutines.launch @Composable fun HomeScreen( - navController: NavHostController, modifier: Modifier = Modifier, homeViewModel: HomeViewModel = hiltViewModel(), + navigateToMealDetail: () -> Unit, + navigateToMedicationDetail: () -> Unit, + navigateToSleepDetail: () -> Unit, + navigateToHealthAnalysisDetail: () -> Unit, + navigateToMentalAnalysisDetail: () -> Unit, + navigateToGlucoseDetail: () -> Unit, mainBackStackEntry: NavBackStackEntry, - onNavigateToMealDetail: () -> Unit, - onNavigateToMedicineDetail: () -> Unit, - onNavigateToSleepDetail: () -> Unit, - onNavigateToStateHealthDetail: () -> Unit, - onNavigateToStateMentalDetail: () -> Unit, - onNavigateToGlucoseDetail: () -> Unit, ) { val homeUiState by homeViewModel.homeUiState.collectAsState() val elderInfoList by homeViewModel.elderInfoList.collectAsState() @@ -109,7 +107,6 @@ fun HomeScreen( HomeScreenLayout( modifier = modifier, - navController = navController, homeUiState = homeUiState, elderInfoList = elderInfoList, selectedElderId = selectedElderId, @@ -121,12 +118,12 @@ fun HomeScreen( homeViewModel.selectElder(selectedName) dropdownOpened = false }, - onNavigateToMealDetail = onNavigateToMealDetail, - onNavigateToMedicineDetail = onNavigateToMedicineDetail, - onNavigateToSleepDetail = onNavigateToSleepDetail, - onNavigateToStateHealthDetail = onNavigateToStateHealthDetail, - onNavigateToStateMentalDetail = onNavigateToStateMentalDetail, - onNavigateToGlucoseDetail = onNavigateToGlucoseDetail, + navigateToMealDetail = navigateToMealDetail, + navigateToMedicineDetail = navigateToMedicationDetail, + navigateToSleepDetail = navigateToSleepDetail, + navigateToStateHealthDetail = navigateToHealthAnalysisDetail, + navigateToStateMentalDetail = navigateToMentalAnalysisDetail, + navigateToGlucoseDetail = navigateToGlucoseDetail, snackbarHostState = snackbarHostState, isLoading = homeUiState.isLoading, onFabClick = { @@ -141,7 +138,7 @@ fun HomeScreen( }, immediateCall = { homeViewModel.callImmediate(it) - } + }, ) } @@ -150,7 +147,6 @@ fun HomeScreen( @Composable fun HomeScreenLayout( modifier: Modifier = Modifier, - navController: NavHostController, homeUiState: HomeUiState, elderInfoList: List, selectedElderId: Int?, @@ -159,17 +155,18 @@ fun HomeScreenLayout( onDropdownClick: () -> Unit, onDropdownDismiss: () -> Unit, onDropdownItemSelected: (String) -> Unit, - onNavigateToMealDetail: () -> Unit, - onNavigateToMedicineDetail: () -> Unit, - onNavigateToSleepDetail: () -> Unit, - onNavigateToStateHealthDetail: () -> Unit, - onNavigateToStateMentalDetail: () -> Unit, - onNavigateToGlucoseDetail: () -> Unit, + navigateToMealDetail: () -> Unit, + navigateToMedicineDetail: () -> Unit, + navigateToSleepDetail: () -> Unit, + navigateToStateHealthDetail: () -> Unit, + navigateToStateMentalDetail: () -> Unit, + navigateToGlucoseDetail: () -> Unit, + navigateToAlarm: () -> Unit = {}, snackbarHostState: SnackbarHostState, isLoading: Boolean, onFabClick: () -> Unit, onRefresh: () -> Unit, - immediateCall: (String) -> Unit + immediateCall: (String) -> Unit, ) { val elderNameList = remember(elderInfoList) { elderInfoList.map { it.name } @@ -205,13 +202,13 @@ fun HomeScreenLayout( Column( horizontalAlignment = Alignment.End, verticalArrangement = Arrangement.spacedBy(12.dp), - modifier = Modifier.padding(end = 16.dp, bottom = 16.dp) + modifier = Modifier.padding(end = 16.dp, bottom = 16.dp), ) { // 세부 FAB들 AnimatedVisibility(visible = expanded) { Column( horizontalAlignment = Alignment.End, - verticalArrangement = Arrangement.spacedBy(12.dp) + verticalArrangement = Arrangement.spacedBy(12.dp), ) { CareCallFloatingButton( modifier = modifier, @@ -220,7 +217,7 @@ fun HomeScreenLayout( immediateCall("FIRST") }, careCallOption = "FIRST", - text = "1차" + text = "1차", ) CareCallFloatingButton( modifier = modifier, @@ -229,7 +226,7 @@ fun HomeScreenLayout( immediateCall("SECOND") }, careCallOption = "SECOND", - text = "2차" + text = "2차", ) CareCallFloatingButton( modifier = modifier, @@ -238,7 +235,7 @@ fun HomeScreenLayout( immediateCall("THIRD") }, careCallOption = "THIRD", - text = "3차" + text = "3차", ) } } @@ -248,11 +245,11 @@ fun HomeScreenLayout( onClick = { expanded = !expanded }, containerColor = MediCareCallTheme.colors.main, contentColor = MediCareCallTheme.colors.white, - shape = CircleShape + shape = CircleShape, ) { Icon( painter = painterResource(R.drawable.ic_carecall), - contentDescription = "메인 FAB" + contentDescription = "메인 FAB", ) } } @@ -260,27 +257,27 @@ fun HomeScreenLayout( snackbarHost = { SnackbarHost( hostState = snackbarHostState, - modifier = Modifier.offset(y = -(10).dp) + modifier = Modifier.offset(y = -(10).dp), - ) { data -> + ) { data -> CareCallSnackBar(snackBarData = data) } - } + }, ) { innerPadding -> Box( modifier = modifier - .fillMaxSize() + .fillMaxSize(), ) { Column( modifier = Modifier .fillMaxSize() .background(Color.White) - .padding(innerPadding) + .padding(innerPadding), ) { NameBar( name = selectedElderName, modifier = Modifier.statusBarsPadding(), - navController = navController, + navigateToAlarm = navigateToAlarm, onDropdownClick = onDropdownClick, // TODO: 실제 알림 개수 데이터 연동 필요 notificationCount = 4, @@ -306,18 +303,18 @@ fun HomeScreenLayout( isRefreshing = isRefreshing, state = refreshState, color = MediCareCallTheme.colors.main, - containerColor = MediCareCallTheme.colors.white + containerColor = MediCareCallTheme.colors.white, ) - } + }, ) { when (isLoading) { true -> Box( Modifier - .fillMaxSize() + .fillMaxSize(), ) { CircularProgressIndicator( color = MediCareCallTheme.colors.main, - modifier = Modifier.align(Alignment.Center) + modifier = Modifier.align(Alignment.Center), ) } @@ -326,7 +323,7 @@ fun HomeScreenLayout( modifier = Modifier .verticalScroll(rememberScrollState()) .fillMaxSize() - .padding(horizontal = 20.dp) + .padding(horizontal = 20.dp), ) { Spacer(Modifier.height(20.dp)) @@ -334,7 +331,7 @@ fun HomeScreenLayout( Text( text = "오늘의 건강 통계", style = MediCareCallTheme.typography.SB_18, - color = MediCareCallTheme.colors.gray6 + color = MediCareCallTheme.colors.gray6, ) Spacer(Modifier.height(20.dp)) @@ -343,10 +340,10 @@ fun HomeScreenLayout( Card( modifier = Modifier.fillMaxWidth(), shape = RoundedCornerShape(16.dp), - colors = CardDefaults.cardColors(containerColor = cardBackgroundColor) + colors = CardDefaults.cardColors(containerColor = cardBackgroundColor), ) { Column( - modifier = Modifier.padding(20.dp) + modifier = Modifier.padding(20.dp), ) { Row(verticalAlignment = Alignment.CenterVertically) { Image( @@ -371,19 +368,19 @@ fun HomeScreenLayout( //건강 항목별 상세 카드 Column( - modifier = Modifier.fillMaxWidth() + modifier = Modifier.fillMaxWidth(), ) { Spacer(Modifier.height(30.dp)) HomeMealContainer( breakfastEaten = homeUiState.breakfastEaten, lunchEaten = homeUiState.lunchEaten, dinnerEaten = homeUiState.dinnerEaten, - onClick = { onNavigateToMealDetail() } + onClick = navigateToMealDetail, ) Spacer(Modifier.height(12.dp)) HomeMedicineContainer( medicines = homeUiState.medicines, - onClick = { onNavigateToMedicineDetail() } + onClick = navigateToMedicineDetail, ) Spacer(Modifier.height(12.dp)) val sleepData = homeUiState.sleep @@ -391,22 +388,22 @@ fun HomeScreenLayout( totalSleepHours = sleepData.meanHours, totalSleepMinutes = sleepData.meanMinutes, isRecorded = sleepData.meanHours > 0 || sleepData.meanMinutes > 0, - onClick = { onNavigateToSleepDetail() } + onClick = navigateToSleepDetail, ) Spacer(Modifier.height(12.dp)) HomeStateHealthContainer( healthStatus = homeUiState.healthStatus, - onClick = { onNavigateToStateHealthDetail() } + onClick = navigateToStateHealthDetail, ) Spacer(Modifier.height(12.dp)) HomeStateMentalContainer( mentalStatus = homeUiState.mentalStatus, - onClick = { onNavigateToStateMentalDetail() } + onClick = navigateToStateMentalDetail, ) Spacer(Modifier.height(12.dp)) HomeGlucoseLevelContainer( glucoseLevelAverageToday = homeUiState.glucoseLevelAverageToday, - onClick = { onNavigateToGlucoseDetail() } + onClick = navigateToGlucoseDetail, ) Spacer(Modifier.height(12.dp)) } @@ -421,7 +418,7 @@ fun HomeScreenLayout( items = elderNameList, selectedName = selectedElderName, onDismiss = onDropdownDismiss, - onItemSelected = onDropdownItemSelected + onItemSelected = onDropdownItemSelected, ) } } @@ -439,24 +436,23 @@ fun PreviewHomeScreen() { dinnerEaten = null, medicines = listOf( MedicineUiState("혈압약", 2, 3, "저녁"), - MedicineUiState("당뇨약", 1, 2, "저녁") + MedicineUiState("당뇨약", 1, 2, "저녁"), ), sleep = HomeResponseDto.SleepDto(meanHours = 8, meanMinutes = 15), healthStatus = "좋음", mentalStatus = "좋음", - glucoseLevelAverageToday = 120 + glucoseLevelAverageToday = 120, ) val previewElderInfoList = listOf( ElderInfo(1, "김옥자", "010-1111-1111"), ElderInfo(2, "박막례", "010-2222-2222"), - ElderInfo(3, "최이순", "010-3333-3333") + ElderInfo(3, "최이순", "010-3333-3333"), ) val previewSelectedId = 1 MediCareCallTheme { HomeScreenLayout( - navController = rememberNavController(), homeUiState = previewUiState, elderInfoList = previewElderInfoList, selectedElderId = previewSelectedId, @@ -465,17 +461,17 @@ fun PreviewHomeScreen() { onDropdownClick = {}, onDropdownDismiss = {}, onDropdownItemSelected = {}, - onNavigateToMealDetail = {}, - onNavigateToMedicineDetail = {}, - onNavigateToSleepDetail = {}, - onNavigateToStateHealthDetail = {}, - onNavigateToStateMentalDetail = {}, - onNavigateToGlucoseDetail = {}, + navigateToMealDetail = {}, + navigateToMedicineDetail = {}, + navigateToSleepDetail = {}, + navigateToStateHealthDetail = {}, + navigateToStateMentalDetail = {}, + navigateToGlucoseDetail = {}, snackbarHostState = SnackbarHostState(), isLoading = false, immediateCall = {}, onRefresh = {}, - onFabClick = {} + onFabClick = {}, ) } } @@ -494,18 +490,17 @@ fun PreviewHomeScreen_Unrecorded() { sleep = HomeResponseDto.SleepDto(0, 0), healthStatus = "", mentalStatus = "", - glucoseLevelAverageToday = 0 + glucoseLevelAverageToday = 0, ) val previewElderInfoList = listOf( ElderInfo(1, "김옥자", "010-1111-1111"), - ElderInfo(2, "박막례", "010-2222-2222") + ElderInfo(2, "박막례", "010-2222-2222"), ) val previewSelectedId = 1 MediCareCallTheme { HomeScreenLayout( - navController = rememberNavController(), homeUiState = unrecordedUiState, elderInfoList = previewElderInfoList, selectedElderId = previewSelectedId, @@ -514,17 +509,18 @@ fun PreviewHomeScreen_Unrecorded() { onDropdownClick = {}, onDropdownDismiss = {}, onDropdownItemSelected = {}, - onNavigateToMealDetail = {}, - onNavigateToMedicineDetail = {}, - onNavigateToSleepDetail = {}, - onNavigateToStateHealthDetail = {}, - onNavigateToStateMentalDetail = {}, - onNavigateToGlucoseDetail = {}, snackbarHostState = SnackbarHostState(), isLoading = false, immediateCall = {}, onRefresh = {}, onFabClick = {}, + navigateToMealDetail = { }, + navigateToMedicineDetail = { }, + navigateToSleepDetail = { }, + navigateToStateHealthDetail = { }, + navigateToStateMentalDetail = { }, + navigateToGlucoseDetail = { }, + navigateToAlarm = { }, ) } } diff --git a/app/src/main/java/com/konkuk/medicarecall/ui/feature/homedetail/glucoselevel/screen/GlucoseDetail.kt b/app/src/main/java/com/konkuk/medicarecall/ui/feature/homedetail/glucoselevel/screen/GlucoseDetail.kt index 7a14206b..84754da3 100644 --- a/app/src/main/java/com/konkuk/medicarecall/ui/feature/homedetail/glucoselevel/screen/GlucoseDetail.kt +++ b/app/src/main/java/com/konkuk/medicarecall/ui/feature/homedetail/glucoselevel/screen/GlucoseDetail.kt @@ -49,6 +49,7 @@ import com.konkuk.medicarecall.ui.feature.homedetail.glucoselevel.viewmodel.Gluc import com.konkuk.medicarecall.ui.feature.homedetail.glucoselevel.viewmodel.GlucoseViewModel import com.konkuk.medicarecall.ui.model.GlucoseTiming import com.konkuk.medicarecall.ui.model.GraphDataPoint +import com.konkuk.medicarecall.ui.navigation.MainTabRoute import com.konkuk.medicarecall.ui.theme.MediCareCallTheme import kotlinx.coroutines.launch import java.time.LocalDate @@ -59,17 +60,14 @@ import java.util.Locale @Composable fun GlucoseDetail( modifier: Modifier = Modifier, - navController: NavHostController + onBack: () -> Unit ) { val scrollState = rememberScrollState() // 어르신 선택 상태(selectedElderId) 관리 - val homeEntry = remember(navController.currentBackStackEntry) { - navController.getBackStackEntry("main") - } - val homeViewModel: HomeViewModel = hiltViewModel(homeEntry) - val viewModel: GlucoseViewModel = hiltViewModel(homeEntry) + val homeViewModel: HomeViewModel = hiltViewModel() + val viewModel: GlucoseViewModel = hiltViewModel() val uiState by viewModel.uiState.collectAsState() @@ -150,7 +148,7 @@ fun GlucoseDetail( // 그래프 점 onPointClick = { newIndex -> viewModel.onClickDots(newIndex) }, scrollState = scrollState, - navController = navController + onBack = onBack ) } @@ -164,7 +162,7 @@ fun GlucoseDetailLayout( onTimingChange: (GlucoseTiming) -> Unit, onPointClick: (Int) -> Unit, scrollState: ScrollState, - navController: NavHostController, + onBack: () -> Unit, ) { val isDataAvailable = uiState.graphDataPoints.isNotEmpty() @@ -183,7 +181,7 @@ fun GlucoseDetailLayout( TopAppBar( title = "혈당", - navController = navController + onBack = onBack ) Spacer(modifier = Modifier.height(24.dp)) @@ -306,7 +304,7 @@ fun PreviewGlucoseDetail_DataAvailable() { selectedIndex = sampleData.lastIndex, onTimingChange = {}, onPointClick = {}, - navController = rememberNavController(), + onBack = {}, scrollState = scrollState ) } @@ -327,7 +325,7 @@ fun PreviewGlucoseDetail_Empty() { selectedIndex = -1, onTimingChange = {}, onPointClick = {}, - navController = rememberNavController(), + onBack = {}, scrollState = scrollState ) } diff --git a/app/src/main/java/com/konkuk/medicarecall/ui/feature/homedetail/meal/screen/MealDetail.kt b/app/src/main/java/com/konkuk/medicarecall/ui/feature/homedetail/meal/screen/MealDetail.kt index 1cd95715..51c9ae48 100644 --- a/app/src/main/java/com/konkuk/medicarecall/ui/feature/homedetail/meal/screen/MealDetail.kt +++ b/app/src/main/java/com/konkuk/medicarecall/ui/feature/homedetail/meal/screen/MealDetail.kt @@ -16,7 +16,6 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue -import androidx.compose.runtime.remember import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.tooling.preview.Preview @@ -24,8 +23,6 @@ import androidx.compose.ui.unit.dp import androidx.hilt.navigation.compose.hiltViewModel import androidx.lifecycle.Lifecycle import androidx.lifecycle.compose.LifecycleEventEffect -import androidx.navigation.NavHostController -import androidx.navigation.compose.rememberNavController import com.konkuk.medicarecall.ui.common.component.TopAppBar import com.konkuk.medicarecall.ui.feature.calendar.DateSelector import com.konkuk.medicarecall.ui.feature.calendar.WeeklyCalendar @@ -41,14 +38,11 @@ import java.time.LocalDate @OptIn(ExperimentalFoundationApi::class) @Composable fun MealDetail( - navController: NavHostController, + onBack: () -> Unit, calendarViewModel: CalendarViewModel = hiltViewModel(), - mealViewModel: MealViewModel = hiltViewModel() + mealViewModel: MealViewModel = hiltViewModel(), ) { - val homeEntry = remember(navController.currentBackStackEntry) { - navController.getBackStackEntry("main") - } - val homeViewModel: HomeViewModel = hiltViewModel(homeEntry) + val homeViewModel: HomeViewModel = hiltViewModel() // 재진입 시 오늘로 초기화 LifecycleEventEffect(Lifecycle.Event.ON_RESUME) { calendarViewModel.resetToToday() @@ -67,12 +61,12 @@ fun MealDetail( val meals by mealViewModel.meals.collectAsState() MealDetailLayout( - navController = navController, + onBack = onBack, selectedDate = selectedDate, meals = meals, weekDates = calendarViewModel.getCurrentWeekDates(), onDateSelected = { calendarViewModel.selectDate(it) }, - onMonthClick = { /* 모달 열기 */ } + onMonthClick = { /* 모달 열기 */ }, ) } @@ -81,38 +75,38 @@ fun MealDetail( @Composable fun MealDetailLayout( modifier: Modifier = Modifier, - navController: NavHostController, + onBack: () -> Unit, selectedDate: LocalDate, meals: List, weekDates: List, onDateSelected: (LocalDate) -> Unit, - onMonthClick: () -> Unit + onMonthClick: () -> Unit, ) { Surface( modifier = modifier.fillMaxSize(), - color = Color.White + color = Color.White, ) { Column( modifier = Modifier .background(MediCareCallTheme.colors.bg) .fillMaxSize() - .statusBarsPadding() + .statusBarsPadding(), ) { TopAppBar( title = "식사", - navController = navController + onBack = onBack, ) Spacer(modifier = Modifier.height(4.dp)) Column( modifier = Modifier .fillMaxSize() .verticalScroll(rememberScrollState()) - .padding(20.dp) + .padding(20.dp), ) { DateSelector( selectedDate = selectedDate, onMonthClick = onMonthClick, - onDateSelected = onDateSelected + onDateSelected = onDateSelected, ) Spacer(Modifier.height(12.dp)) @@ -122,9 +116,9 @@ fun MealDetailLayout( currentYear = selectedDate.year, currentMonth = selectedDate.monthValue, weekDates = weekDates, - selectedDate = selectedDate + selectedDate = selectedDate, ), - onDateSelected = onDateSelected + onDateSelected = onDateSelected, ) Spacer(modifier = Modifier.height(32.dp)) @@ -134,7 +128,7 @@ fun MealDetailLayout( mealTime = meal.mealTime, // 아침 점심 저녁 description = meal.description, // 식사 내용 isRecorded = meal.isRecorded, // 식사 기록 여부 - isEaten = meal.isEaten // 식사 유무 + isEaten = meal.isEaten, // 식사 유무 ) Spacer(modifier = Modifier.height(12.dp)) } @@ -152,20 +146,20 @@ fun PreviewMealDetail_Recorded() { mealTime = "아침", description = "간단히 밥과 반찬을 드셨어요.", isRecorded = true, - isEaten = true + isEaten = true, ), MealUiState( mealTime = "점심", description = "식사하지 않으셨어요.", isRecorded = true, - isEaten = false + isEaten = false, ), MealUiState( mealTime = "저녁", description = "죽을 드셨어요.", isRecorded = true, - isEaten = true - ) + isEaten = true, + ), ) val selectedDate = LocalDate.of(2025, 5, 7) val weekDates = @@ -173,12 +167,12 @@ fun PreviewMealDetail_Recorded() { MediCareCallTheme { MealDetailLayout( - navController = rememberNavController(), + onBack = {}, selectedDate = selectedDate, meals = dummyMeals, weekDates = weekDates, onDateSelected = {}, - onMonthClick = {} + onMonthClick = {}, ) } } @@ -191,20 +185,20 @@ fun PreviewMealDetail_Unrecorded() { mealTime = "아침", description = "식사 기록 전이에요.", isRecorded = false, - isEaten = null + isEaten = null, ), MealUiState( mealTime = "점심", description = "식사 기록 전이에요.", isRecorded = false, - isEaten = null + isEaten = null, ), MealUiState( mealTime = "저녁", description = "식사 기록 전이에요.", isRecorded = false, - isEaten = null - ) + isEaten = null, + ), ) val selectedDate = LocalDate.of(2025, 5, 7) val weekDates = @@ -213,12 +207,12 @@ fun PreviewMealDetail_Unrecorded() { MediCareCallTheme { MealDetailLayout( - navController = rememberNavController(), + onBack = {}, selectedDate = selectedDate, meals = dummyMeals, weekDates = weekDates, onDateSelected = {}, - onMonthClick = {} + onMonthClick = {}, ) } } diff --git a/app/src/main/java/com/konkuk/medicarecall/ui/feature/homedetail/medicine/screen/MedicineDetail.kt b/app/src/main/java/com/konkuk/medicarecall/ui/feature/homedetail/medicine/screen/MedicineDetail.kt index 0e6031c9..dbae7bbf 100644 --- a/app/src/main/java/com/konkuk/medicarecall/ui/feature/homedetail/medicine/screen/MedicineDetail.kt +++ b/app/src/main/java/com/konkuk/medicarecall/ui/feature/homedetail/medicine/screen/MedicineDetail.kt @@ -37,20 +37,18 @@ import com.konkuk.medicarecall.ui.feature.homedetail.medicine.viewmodel.DoseStat import com.konkuk.medicarecall.ui.feature.homedetail.medicine.viewmodel.DoseStatusItem import com.konkuk.medicarecall.ui.feature.homedetail.medicine.viewmodel.MedicineUiState import com.konkuk.medicarecall.ui.feature.homedetail.medicine.viewmodel.MedicineViewModel +import com.konkuk.medicarecall.ui.navigation.MainTabRoute import com.konkuk.medicarecall.ui.theme.MediCareCallTheme import java.time.LocalDate @OptIn(ExperimentalFoundationApi::class) @Composable fun MedicineDetail( - navController: NavHostController, + onBack: () -> Unit, calendarViewModel: CalendarViewModel = hiltViewModel(), medicineViewModel: MedicineViewModel = hiltViewModel() ) { - val homeEntry = remember(navController.currentBackStackEntry) { - navController.getBackStackEntry("main") - } - val homeViewModel: HomeViewModel = hiltViewModel(homeEntry) + val homeViewModel: HomeViewModel = hiltViewModel() // 재진입 시 오늘로 초기화 LifecycleEventEffect(Lifecycle.Event.ON_RESUME) { calendarViewModel.resetToToday() @@ -71,7 +69,7 @@ fun MedicineDetail( MedicineDetailLayout( - navController = navController, + onBack = onBack, selectedDate = selectedDate, medicines = uiState.items, weekDates = calendarViewModel.getCurrentWeekDates(), @@ -85,7 +83,7 @@ fun MedicineDetail( @Composable fun MedicineDetailLayout( modifier: Modifier = Modifier, - navController: NavHostController, + onBack: () -> Unit, selectedDate: LocalDate, medicines: List, weekDates: List, @@ -104,7 +102,7 @@ fun MedicineDetailLayout( ) { TopAppBar( title = "복약", - navController = navController + onBack = onBack ) Spacer(Modifier.height(4.dp)) Column( @@ -165,7 +163,7 @@ fun PreviewMedicineDetail() { MediCareCallTheme { MedicineDetailLayout( - navController = rememberNavController(), + onBack = {}, selectedDate = LocalDate.now(), medicines = dummyMedicines, weekDates = (0..6).map { LocalDate.now().plusDays(it.toLong()) }, diff --git a/app/src/main/java/com/konkuk/medicarecall/ui/feature/homedetail/navigation/HomeDetailNavGraph.kt b/app/src/main/java/com/konkuk/medicarecall/ui/feature/homedetail/navigation/HomeDetailNavGraph.kt new file mode 100644 index 00000000..bfb91c95 --- /dev/null +++ b/app/src/main/java/com/konkuk/medicarecall/ui/feature/homedetail/navigation/HomeDetailNavGraph.kt @@ -0,0 +1,72 @@ +package com.konkuk.medicarecall.ui.feature.homedetail.navigation + +import androidx.navigation.NavController +import androidx.navigation.NavGraphBuilder +import androidx.navigation.compose.composable +import com.konkuk.medicarecall.ui.feature.homedetail.glucoselevel.screen.GlucoseDetail +import com.konkuk.medicarecall.ui.feature.homedetail.meal.screen.MealDetail +import com.konkuk.medicarecall.ui.feature.homedetail.medicine.screen.MedicineDetail +import com.konkuk.medicarecall.ui.feature.homedetail.sleep.screen.SleepDetail +import com.konkuk.medicarecall.ui.feature.homedetail.statehealth.screen.StateHealthDetail +import com.konkuk.medicarecall.ui.feature.homedetail.statemental.screen.StateMentalDetail +import com.konkuk.medicarecall.ui.navigation.Route + +fun NavController.navigateToMealDetail() { + navigate(Route.MealDetail) +} + +fun NavController.navigateToMedicationDetail() { + navigate(Route.MedicationDetail) +} + +fun NavController.navigateToSleepDetail() { + navigate(Route.SleepDetail) +} + +fun NavController.navigateToHealthAnalysisDetail() { + navigate(Route.HealthAnalysisDetail) +} + +fun NavController.navigateToMentalAnalysisDetail() { + navigate(Route.MentalAnalysisDetail) +} + +fun NavController.navigateToGlucoseDetail() { + navigate(Route.GlucoseDetail) +} + +fun NavGraphBuilder.homeDetailNavGraph( + popBackStack: () -> Unit, +) { + // 홈 상세 화면_식사 화면 + composable { + MealDetail( + onBack = popBackStack, + ) + } + + // 홈 상세 화면_복용 화면 + composable { + MedicineDetail(onBack = popBackStack) + } + + //홈 상세 화면_수면 화면 + composable { + SleepDetail(onBack = popBackStack) + } + + //홈 상세 화면_건강 징후 화면 + composable { + StateHealthDetail(onBack = popBackStack) + } + + //홈 상세 화면_심리 상태 화면 + composable { + StateMentalDetail(onBack = popBackStack) + } + + //홈 상세 화면_혈당 화면 + composable { + GlucoseDetail(onBack = popBackStack) + } +} diff --git a/app/src/main/java/com/konkuk/medicarecall/ui/feature/homedetail/sleep/screen/SleepDetail.kt b/app/src/main/java/com/konkuk/medicarecall/ui/feature/homedetail/sleep/screen/SleepDetail.kt index 3a0e2d4c..37f16fce 100644 --- a/app/src/main/java/com/konkuk/medicarecall/ui/feature/homedetail/sleep/screen/SleepDetail.kt +++ b/app/src/main/java/com/konkuk/medicarecall/ui/feature/homedetail/sleep/screen/SleepDetail.kt @@ -33,6 +33,7 @@ import com.konkuk.medicarecall.ui.feature.home.viewmodel.HomeViewModel import com.konkuk.medicarecall.ui.feature.homedetail.sleep.component.SleepDetailCard import com.konkuk.medicarecall.ui.feature.homedetail.sleep.viewmodel.SleepUiState import com.konkuk.medicarecall.ui.feature.homedetail.sleep.viewmodel.SleepViewModel +import com.konkuk.medicarecall.ui.navigation.MainTabRoute import com.konkuk.medicarecall.ui.theme.MediCareCallTheme import java.time.LocalDate @@ -40,15 +41,12 @@ import java.time.LocalDate @OptIn(ExperimentalFoundationApi::class) @Composable fun SleepDetail( - navController: NavHostController, + onBack: () -> Unit, calendarViewModel: CalendarViewModel = hiltViewModel(), sleepViewModel: SleepViewModel = hiltViewModel() ) { - val homeEntry = remember(navController.currentBackStackEntry) { - navController.getBackStackEntry("main") - } - val homeViewModel: HomeViewModel = hiltViewModel(homeEntry) + val homeViewModel: HomeViewModel = hiltViewModel() // 재진입 시 오늘로 초기화 LifecycleEventEffect(Lifecycle.Event.ON_RESUME) { @@ -68,9 +66,10 @@ fun SleepDetail( } } + SleepDetailLayout( modifier = Modifier, - navController = navController, + onBack = onBack, selectedDate = selectedDate, sleep = sleep, weekDates = calendarViewModel.getCurrentWeekDates(), @@ -82,7 +81,7 @@ fun SleepDetail( @Composable fun SleepDetailLayout( modifier: Modifier = Modifier, - navController: NavHostController, + onBack: () -> Unit, selectedDate: LocalDate, sleep: SleepUiState, weekDates: List, @@ -97,7 +96,7 @@ fun SleepDetailLayout( ) { TopAppBar( title = "수면", - navController = navController + onBack = onBack ) Spacer(Modifier.height(4.dp)) Column( @@ -136,7 +135,7 @@ fun SleepDetailLayout( fun PreviewSleepDetail() { MediCareCallTheme { SleepDetailLayout( - navController = rememberNavController(), + onBack = {}, selectedDate = LocalDate.now(), sleep = SleepUiState.Companion.EMPTY, weekDates = (0..6).map { LocalDate.now().plusDays(it.toLong()) }, diff --git a/app/src/main/java/com/konkuk/medicarecall/ui/feature/homedetail/statehealth/screen/StateHealthDetail.kt b/app/src/main/java/com/konkuk/medicarecall/ui/feature/homedetail/statehealth/screen/StateHealthDetail.kt index 9d83d0cd..5f2c1f5a 100644 --- a/app/src/main/java/com/konkuk/medicarecall/ui/feature/homedetail/statehealth/screen/StateHealthDetail.kt +++ b/app/src/main/java/com/konkuk/medicarecall/ui/feature/homedetail/statehealth/screen/StateHealthDetail.kt @@ -35,23 +35,21 @@ import com.konkuk.medicarecall.ui.feature.home.viewmodel.HomeViewModel import com.konkuk.medicarecall.ui.feature.homedetail.statehealth.component.StateHealthDetailCard import com.konkuk.medicarecall.ui.feature.homedetail.statehealth.viewmodel.HealthUiState import com.konkuk.medicarecall.ui.feature.homedetail.statehealth.viewmodel.HealthViewModel +import com.konkuk.medicarecall.ui.navigation.MainTabRoute import com.konkuk.medicarecall.ui.theme.MediCareCallTheme import java.time.LocalDate @Composable fun StateHealthDetail( - navController: NavHostController, + onBack: () -> Unit, calendarViewModel: CalendarViewModel = hiltViewModel(), healthViewModel: HealthViewModel = hiltViewModel() ) { val isLoading = healthViewModel.isLoading.collectAsState() - val homeEntry = remember(navController.currentBackStackEntry) { - navController.getBackStackEntry("main") - } - val homeViewModel: HomeViewModel = hiltViewModel(homeEntry) + val homeViewModel: HomeViewModel = hiltViewModel() // 재진입 시 오늘로 초기화 LifecycleEventEffect(Lifecycle.Event.ON_RESUME) { @@ -76,7 +74,7 @@ fun StateHealthDetail( if (!isLoading.value) StateHealthDetailLayout( modifier = Modifier, - navController = navController, + onBack = onBack, selectedDate = selectedDate, health = health, weekDates = calendarViewModel.getCurrentWeekDates(), @@ -96,7 +94,7 @@ fun StateHealthDetail( @Composable fun StateHealthDetailLayout( modifier: Modifier = Modifier, - navController: NavHostController, + onBack: () -> Unit, selectedDate: LocalDate, health: HealthUiState, weekDates: List, @@ -111,7 +109,7 @@ fun StateHealthDetailLayout( ) { TopAppBar( title = "건강징후", - navController = navController + onBack = onBack ) Spacer(Modifier.height(4.dp)) Column( @@ -150,7 +148,7 @@ fun StateHealthDetailLayout( fun PreviewStateHealthDetail() { MediCareCallTheme { StateHealthDetailLayout( - navController = rememberNavController(), + onBack = {}, selectedDate = LocalDate.now(), health = HealthUiState.Companion.EMPTY, weekDates = (0..6).map { LocalDate.now().plusDays(it.toLong()) }, diff --git a/app/src/main/java/com/konkuk/medicarecall/ui/feature/homedetail/statemental/screen/StateMentalDetail.kt b/app/src/main/java/com/konkuk/medicarecall/ui/feature/homedetail/statemental/screen/StateMentalDetail.kt index f5e69568..1e158589 100644 --- a/app/src/main/java/com/konkuk/medicarecall/ui/feature/homedetail/statemental/screen/StateMentalDetail.kt +++ b/app/src/main/java/com/konkuk/medicarecall/ui/feature/homedetail/statemental/screen/StateMentalDetail.kt @@ -15,7 +15,6 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue -import androidx.compose.runtime.remember import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.tooling.preview.Preview @@ -23,8 +22,6 @@ import androidx.compose.ui.unit.dp import androidx.hilt.navigation.compose.hiltViewModel import androidx.lifecycle.Lifecycle import androidx.lifecycle.compose.LifecycleEventEffect -import androidx.navigation.NavHostController -import androidx.navigation.compose.rememberNavController import com.konkuk.medicarecall.ui.common.component.TopAppBar import com.konkuk.medicarecall.ui.feature.calendar.DateSelector import com.konkuk.medicarecall.ui.feature.calendar.WeeklyCalendar @@ -40,15 +37,12 @@ import java.time.LocalDate @OptIn(ExperimentalFoundationApi::class) @Composable fun StateMentalDetail( - navController: NavHostController, + onBack: () -> Unit, calendarViewModel: CalendarViewModel = hiltViewModel(), mentalViewModel: MentalViewModel = hiltViewModel(), ) { - val homeEntry = remember(navController.currentBackStackEntry) { - navController.getBackStackEntry("main") - } - val homeViewModel: HomeViewModel = hiltViewModel(homeEntry) + val homeViewModel: HomeViewModel = hiltViewModel() // 재진입 시 오늘로 초기화 LifecycleEventEffect(Lifecycle.Event.ON_RESUME) { @@ -68,13 +62,14 @@ fun StateMentalDetail( } } + StateMentalDetailLayout( - navController = navController, + onBack = onBack, selectedDate = selectedDate, mental = mental, weekDates = calendarViewModel.getCurrentWeekDates(), onDateSelected = { calendarViewModel.selectDate(it) }, - onMonthClick = { /* 모달 열기 */ } + onMonthClick = { /* 모달 열기 */ }, ) } @@ -82,37 +77,37 @@ fun StateMentalDetail( @Composable fun StateMentalDetailLayout( modifier: Modifier = Modifier, - navController: NavHostController, + onBack: () -> Unit, selectedDate: LocalDate, mental: MentalUiState, weekDates: List, onDateSelected: (LocalDate) -> Unit, - onMonthClick: () -> Unit + onMonthClick: () -> Unit, ) { Surface( modifier = modifier.fillMaxSize(), - color = Color.White + color = Color.White, ) { Column( modifier = Modifier .fillMaxSize() - .statusBarsPadding() + .statusBarsPadding(), ) { TopAppBar( title = "심리상태 요약", - navController = navController + onBack = onBack, ) Column( modifier = Modifier .background(MediCareCallTheme.colors.bg) .fillMaxSize() .verticalScroll(rememberScrollState()) - .padding(20.dp) + .padding(20.dp), ) { DateSelector( selectedDate = selectedDate, onMonthClick = onMonthClick, - onDateSelected = onDateSelected + onDateSelected = onDateSelected, ) Spacer(Modifier.height(24.dp)) WeeklyCalendar( @@ -120,13 +115,13 @@ fun StateMentalDetailLayout( currentYear = selectedDate.year, currentMonth = selectedDate.monthValue, weekDates = weekDates, - selectedDate = selectedDate + selectedDate = selectedDate, ), - onDateSelected = onDateSelected + onDateSelected = onDateSelected, ) Spacer(modifier = Modifier.height(32.dp)) StateMentalDetailCard( - mental = mental + mental = mental, ) } } @@ -138,11 +133,11 @@ fun StateMentalDetailLayout( @Composable fun PreviewStateMentalDetail() { StateMentalDetailLayout( - navController = rememberNavController(), + onBack = {}, selectedDate = LocalDate.now(), mental = MentalUiState.Companion.EMPTY, weekDates = (0..6).map { LocalDate.now().plusDays(it.toLong()) }, onDateSelected = {}, - onMonthClick = {} + onMonthClick = {}, ) } diff --git a/app/src/main/java/com/konkuk/medicarecall/ui/feature/login/carecall/screen/CallTimeScreen.kt b/app/src/main/java/com/konkuk/medicarecall/ui/feature/login/carecall/screen/CallTimeScreen.kt index 42efb56c..981cd600 100644 --- a/app/src/main/java/com/konkuk/medicarecall/ui/feature/login/carecall/screen/CallTimeScreen.kt +++ b/app/src/main/java/com/konkuk/medicarecall/ui/feature/login/carecall/screen/CallTimeScreen.kt @@ -65,12 +65,10 @@ fun Triple.toDisplayString(): String { fun CallTimeScreen( modifier: Modifier = Modifier, onBack: () -> Unit = {}, - navigatedToPayment: () -> Unit = {}, + navigateToPayment: () -> Unit = {}, eldersInfoViewModel: EldersInfoViewModel = hiltViewModel(), callTimeViewModel: CallTimeViewModel = hiltViewModel(), ) { - - LaunchedEffect(Unit) { eldersInfoViewModel.ensureLoaded() } val isLoading = eldersInfoViewModel.isLoading.value @@ -355,7 +353,7 @@ fun CallTimeScreen( callTimeViewModel.submitAllByIds( elderIds = elderIds, onSuccess = { - navigatedToPayment() + navigateToPayment() Log.d("SetCallScreen", "콜 시간 설정 완료") Log.d("SetCallScreen", "시간 : ${callTimeViewModel.timeMap}") }, diff --git a/app/src/main/java/com/konkuk/medicarecall/ui/feature/login/info/screen/LoginMyInfoScreen.kt b/app/src/main/java/com/konkuk/medicarecall/ui/feature/login/info/screen/LoginMyInfoScreen.kt index d347e58c..f5030a7a 100644 --- a/app/src/main/java/com/konkuk/medicarecall/ui/feature/login/info/screen/LoginMyInfoScreen.kt +++ b/app/src/main/java/com/konkuk/medicarecall/ui/feature/login/info/screen/LoginMyInfoScreen.kt @@ -39,30 +39,29 @@ import androidx.compose.ui.focus.focusRequester import androidx.compose.ui.res.painterResource import androidx.compose.ui.text.input.KeyboardType import androidx.compose.ui.unit.dp -import androidx.navigation.NavController import com.konkuk.medicarecall.R -import com.konkuk.medicarecall.navigation.Route import com.konkuk.medicarecall.ui.common.component.CTAButton import com.konkuk.medicarecall.ui.common.component.DefaultSnackBar import com.konkuk.medicarecall.ui.common.component.DefaultTextField import com.konkuk.medicarecall.ui.common.component.GenderToggleButton +import com.konkuk.medicarecall.ui.common.util.DateOfBirthVisualTransformation +import com.konkuk.medicarecall.ui.common.util.isValidDate import com.konkuk.medicarecall.ui.feature.login.info.component.AgreementItem import com.konkuk.medicarecall.ui.feature.login.info.component.LoginBackButton import com.konkuk.medicarecall.ui.feature.login.info.viewmodel.LoginEvent import com.konkuk.medicarecall.ui.feature.login.info.viewmodel.LoginViewModel +import com.konkuk.medicarecall.ui.theme.MediCareCallTheme import com.konkuk.medicarecall.ui.type.CTAButtonType import com.konkuk.medicarecall.ui.type.GenderType -import com.konkuk.medicarecall.ui.theme.MediCareCallTheme -import com.konkuk.medicarecall.ui.common.util.DateOfBirthVisualTransformation -import com.konkuk.medicarecall.ui.common.util.isValidDate import kotlinx.coroutines.launch @OptIn(ExperimentalMaterial3Api::class) @Composable fun LoginMyInfoScreen( - navController: NavController, + modifier: Modifier = Modifier, + onBack: () -> Unit = {}, + navigateToRegisterElder: () -> Unit = {}, loginViewModel: LoginViewModel, - modifier: Modifier = Modifier ) { var showBottomSheet by remember { mutableStateOf(false) } val scrollState = rememberScrollState() @@ -76,14 +75,14 @@ fun LoginMyInfoScreen( when (event) { is LoginEvent.MemberRegisterSuccess -> { // 인증 성공 시 어르신정보 화면으로 이동 - navController.navigate(Route.LoginElderInfoScreen.route) + navigateToRegisterElder() } is LoginEvent.MemberRegisterFailure -> { coroutineScope.launch { snackBarState.showSnackbar( message = "오류가 발생했습니다 다시 시도해주세요", - duration = SnackbarDuration.Short + duration = SnackbarDuration.Short, ) } } @@ -104,25 +103,23 @@ fun LoginMyInfoScreen( .imePadding(), ) { Column { - LoginBackButton({ - navController.popBackStack() - }) + LoginBackButton(onBack) Column( Modifier - .verticalScroll(scrollState) + .verticalScroll(scrollState), ) { Spacer(Modifier.height(20.dp)) Text( "회원 정보를\n입력해주세요", style = MediCareCallTheme.typography.B_26, - color = MediCareCallTheme.colors.black + color = MediCareCallTheme.colors.black, ) Spacer(Modifier.height(40.dp)) Column(verticalArrangement = Arrangement.spacedBy(10.dp)) { Text( "이름", color = MediCareCallTheme.colors.gray7, - style = MediCareCallTheme.typography.M_17 + style = MediCareCallTheme.typography.M_17, ) DefaultTextField( loginViewModel.name, @@ -130,7 +127,7 @@ fun LoginMyInfoScreen( loginViewModel.onNameChanged(it) }, placeHolder = "이름", - textFieldModifier = Modifier.focusRequester(focusRequester) + textFieldModifier = Modifier.focusRequester(focusRequester), ) } Spacer(Modifier.height(20.dp)) @@ -138,7 +135,7 @@ fun LoginMyInfoScreen( Text( "생년월일", color = MediCareCallTheme.colors.gray7, - style = MediCareCallTheme.typography.M_17 + style = MediCareCallTheme.typography.M_17, ) // 생년월일 입력 텍스트필드 DefaultTextField( @@ -150,16 +147,16 @@ fun LoginMyInfoScreen( placeHolder = "YYYY / MM / DD", keyboardType = KeyboardType.Number, visualTransformation = DateOfBirthVisualTransformation(), - maxLength = 8 + maxLength = 8, - ) + ) } Spacer(Modifier.height(20.dp)) Column(verticalArrangement = Arrangement.spacedBy(10.dp)) { Text( "성별", color = MediCareCallTheme.colors.gray7, - style = MediCareCallTheme.typography.M_17 + style = MediCareCallTheme.typography.M_17, ) GenderToggleButton(loginViewModel.isMale) { loginViewModel.onGenderChanged(it) } @@ -180,14 +177,14 @@ fun LoginMyInfoScreen( coroutineScope.launch { snackBarState.showSnackbar( "이름을 다시 확인해주세요", - duration = SnackbarDuration.Short + duration = SnackbarDuration.Short, ) } } else if (!loginViewModel.dateOfBirth.isValidDate()) { coroutineScope.launch { snackBarState.showSnackbar( "생년월일을 다시 확인해주세요", - duration = SnackbarDuration.Short + duration = SnackbarDuration.Short, ) } } else { @@ -195,12 +192,12 @@ fun LoginMyInfoScreen( } }, - Modifier.padding(bottom = 20.dp) + Modifier.padding(bottom = 20.dp), ) val sheetState = rememberModalBottomSheetState( - skipPartiallyExpanded = true + skipPartiallyExpanded = true, ) @@ -212,7 +209,7 @@ fun LoginMyInfoScreen( sheetState = sheetState, containerColor = MediCareCallTheme.colors.bg, dragHandle = null, - shape = RoundedCornerShape(topStart = 30.dp, topEnd = 30.dp) + shape = RoundedCornerShape(topStart = 30.dp, topEnd = 30.dp), ) { // Sheet content @@ -221,8 +218,8 @@ fun LoginMyInfoScreen( "서비스 이용약관" to Modifier.padding(horizontal = 20.dp, vertical = 8.dp), "개인정보 수집 및 이용 동의" to Modifier.padding( horizontal = 20.dp, - vertical = 8.dp - ) + vertical = 8.dp, + ), ) var checkedStates by remember { mutableStateOf(List(itemList.size) { false }) } val isCheckedAll = checkedStates.all { it } @@ -232,7 +229,7 @@ fun LoginMyInfoScreen( "회원가입을 위해\n약관 동의가 필요합니다", color = MediCareCallTheme.colors.black, style = MediCareCallTheme.typography.B_20, - modifier = modifier.padding(horizontal = 20.dp, vertical = 30.dp) + modifier = modifier.padding(horizontal = 20.dp, vertical = 30.dp), ) var allAgreeCheckState by remember { mutableStateOf(false) } Row( @@ -247,9 +244,9 @@ fun LoginMyInfoScreen( checkedStates = checkedStates.map { allAgreeCheckState } - } + }, ), - verticalAlignment = Alignment.CenterVertically + verticalAlignment = Alignment.CenterVertically, ) { Icon( @@ -262,14 +259,14 @@ fun LoginMyInfoScreen( Text( "전체 동의하기", color = MediCareCallTheme.colors.black, - style = MediCareCallTheme.typography.SB_16 + style = MediCareCallTheme.typography.SB_16, ) } } HorizontalDivider( thickness = 1.4.dp, - color = MediCareCallTheme.colors.gray2 + color = MediCareCallTheme.colors.gray2, ) Spacer(Modifier.height(12.dp)) @@ -282,7 +279,7 @@ fun LoginMyInfoScreen( it[index] = !it[index] } }, - modifier = modifier + modifier = modifier, ) } // 모달 내부 CTA(다음) 버튼 @@ -295,12 +292,12 @@ fun LoginMyInfoScreen( loginViewModel.dateOfBirth, if (loginViewModel.isMale ?: true - ) GenderType.MALE else GenderType.FEMALE + ) GenderType.MALE else GenderType.FEMALE, ) }, modifier .padding(horizontal = 20.dp) - .padding(bottom = 30.dp, top = 20.dp) + .padding(bottom = 30.dp, top = 20.dp), ) } @@ -311,7 +308,7 @@ fun LoginMyInfoScreen( snackBarState, Modifier .align(Alignment.BottomCenter) - .padding(bottom = 14.dp) + .padding(bottom = 14.dp), ) } diff --git a/app/src/main/java/com/konkuk/medicarecall/ui/feature/login/info/screen/LoginPhoneScreen.kt b/app/src/main/java/com/konkuk/medicarecall/ui/feature/login/info/screen/LoginPhoneScreen.kt index f05f852a..67176b0c 100644 --- a/app/src/main/java/com/konkuk/medicarecall/ui/feature/login/info/screen/LoginPhoneScreen.kt +++ b/app/src/main/java/com/konkuk/medicarecall/ui/feature/login/info/screen/LoginPhoneScreen.kt @@ -24,22 +24,23 @@ import androidx.compose.ui.focus.FocusRequester import androidx.compose.ui.focus.focusRequester import androidx.compose.ui.text.input.KeyboardType import androidx.compose.ui.unit.dp -import androidx.navigation.NavController +import androidx.hilt.navigation.compose.hiltViewModel import com.konkuk.medicarecall.ui.common.component.CTAButton import com.konkuk.medicarecall.ui.common.component.DefaultSnackBar import com.konkuk.medicarecall.ui.common.component.DefaultTextField +import com.konkuk.medicarecall.ui.common.util.PhoneNumberVisualTransformation import com.konkuk.medicarecall.ui.feature.login.info.component.LoginBackButton import com.konkuk.medicarecall.ui.feature.login.info.viewmodel.LoginViewModel -import com.konkuk.medicarecall.ui.type.CTAButtonType import com.konkuk.medicarecall.ui.theme.MediCareCallTheme -import com.konkuk.medicarecall.ui.common.util.PhoneNumberVisualTransformation +import com.konkuk.medicarecall.ui.type.CTAButtonType import kotlinx.coroutines.launch @Composable fun LoginPhoneScreen( - navController: NavController, - loginViewModel: LoginViewModel, - modifier: Modifier = Modifier + modifier: Modifier = Modifier, + onBack: () -> Unit = {}, + navigateToVerification: () -> Unit = {}, + loginViewModel: LoginViewModel = hiltViewModel(), ) { val scrollState = rememberScrollState() val focusRequester = remember { FocusRequester() } @@ -57,22 +58,20 @@ fun LoginPhoneScreen( .background(MediCareCallTheme.colors.bg) .padding(horizontal = 20.dp) .statusBarsPadding() - .imePadding() + .imePadding(), ) { Column { - LoginBackButton({ - navController.popBackStack() - }) + LoginBackButton(onBack) Column( Modifier - .verticalScroll(scrollState) + .verticalScroll(scrollState), ) { Spacer(Modifier.height(20.dp)) Text( "휴대폰 번호를\n입력해주세요", style = MediCareCallTheme.typography.B_26, - color = MediCareCallTheme.colors.black + color = MediCareCallTheme.colors.black, ) Spacer(Modifier.height(40.dp)) DefaultTextField( @@ -86,7 +85,7 @@ fun LoginPhoneScreen( visualTransformation = PhoneNumberVisualTransformation(), textFieldModifier = Modifier .focusRequester(focusRequester), - maxLength = 11 + maxLength = 11, ) Spacer(Modifier.height(30.dp)) @@ -97,16 +96,17 @@ fun LoginPhoneScreen( // TODO: 서버에 인증번호 요청하기 if (loginViewModel.phoneNumber.startsWith("010")) { loginViewModel.postPhoneNumber(loginViewModel.phoneNumber) - navController.navigate("login_verification") + navigateToVerification() } else { coroutineScope.launch { snackBarState.showSnackbar( "휴대폰 번호를 다시 확인해주세요", - duration = SnackbarDuration.Short + duration = SnackbarDuration.Short, ) } } - }) + }, + ) } } @@ -114,7 +114,7 @@ fun LoginPhoneScreen( snackBarState, Modifier .align(Alignment.BottomCenter) - .padding(bottom = 14.dp) + .padding(bottom = 14.dp), ) } diff --git a/app/src/main/java/com/konkuk/medicarecall/ui/feature/login/info/screen/LoginStartScreen.kt b/app/src/main/java/com/konkuk/medicarecall/ui/feature/login/info/screen/LoginStartScreen.kt index 1f07a815..03ad2658 100644 --- a/app/src/main/java/com/konkuk/medicarecall/ui/feature/login/info/screen/LoginStartScreen.kt +++ b/app/src/main/java/com/konkuk/medicarecall/ui/feature/login/info/screen/LoginStartScreen.kt @@ -24,38 +24,35 @@ import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.painterResource import androidx.compose.ui.unit.dp -import androidx.navigation.NavController +import androidx.hilt.navigation.compose.hiltViewModel import com.konkuk.medicarecall.R -import com.konkuk.medicarecall.navigation.Route import com.konkuk.medicarecall.ui.common.component.CTAButton import com.konkuk.medicarecall.ui.feature.login.info.viewmodel.LoginViewModel -import com.konkuk.medicarecall.ui.type.CTAButtonType import com.konkuk.medicarecall.ui.model.NavigationDestination import com.konkuk.medicarecall.ui.theme.MediCareCallTheme +import com.konkuk.medicarecall.ui.type.CTAButtonType @SuppressLint("SourceLockedOrientationActivity") @Composable fun LoginStartScreen( - navController: NavController, - loginViewModel: LoginViewModel, - modifier: Modifier = Modifier + modifier: Modifier = Modifier, + navigateToPhone: () -> Unit = {}, + navigateToRegisterElder: () -> Unit = {}, + navigateToCareCallSetting: () -> Unit = {}, + navigateToPurchase: () -> Unit = {}, + navigateToHome: () -> Unit = {}, + loginViewModel: LoginViewModel = hiltViewModel(), ) { val navigationDestination by loginViewModel.navigationDestination.collectAsState() LaunchedEffect(navigationDestination) { navigationDestination?.let { destination -> - val route = when (destination) { - is NavigationDestination.GoToLogin -> Route.LoginPhone.route - is NavigationDestination.GoToRegisterElder -> Route.LoginElderInfoScreen.route - is NavigationDestination.GoToTimeSetting -> Route.SetCall.route - is NavigationDestination.GoToPayment -> Route.Payment.route - is NavigationDestination.GoToHome -> Route.Home.route - } - navController.navigate(route) { - if (route == Route.Home.route) - popUpTo(Route.LoginStart.route) { - inclusive = true - } + when (destination) { + is NavigationDestination.GoToLogin -> navigateToPhone() + is NavigationDestination.GoToRegisterElder -> navigateToRegisterElder() + is NavigationDestination.GoToTimeSetting -> navigateToCareCallSetting() + is NavigationDestination.GoToPayment -> navigateToPurchase() + is NavigationDestination.GoToHome -> navigateToHome() } loginViewModel.onNavigationHandled() } @@ -85,7 +82,7 @@ fun LoginStartScreen( modifier .fillMaxSize() .background(MediCareCallTheme.colors.main) - .navigationBarsPadding() + .navigationBarsPadding(), ) { Image( painter = painterResource(R.drawable.bg_login_start_new), @@ -93,23 +90,23 @@ fun LoginStartScreen( modifier .fillMaxSize() .align(Alignment.Center), - contentScale = ContentScale.FillBounds + contentScale = ContentScale.FillBounds, - ) + ) Column( Modifier .align(Alignment.TopStart) .statusBarsPadding() - .padding(top = 20.dp, start = 20.dp) + .padding(top = 20.dp, start = 20.dp), ) { Image( painter = painterResource(R.drawable.typo_intro), - "AI 기반 케어콜, 부모님 건강관리는 메디케어콜" + "AI 기반 케어콜, 부모님 건강관리는 메디케어콜", ) Spacer(Modifier.height(30.dp)) Image( painter = painterResource(R.drawable.typo_main), - "메디케어콜" + "메디케어콜", ) } @@ -123,9 +120,9 @@ fun LoginStartScreen( modifier .align(Alignment.BottomCenter) .padding(bottom = 20.dp) - .padding(horizontal = 20.dp) + .padding(horizontal = 20.dp), - ) + ) } } diff --git a/app/src/main/java/com/konkuk/medicarecall/ui/feature/login/info/screen/LoginVerificationScreen.kt b/app/src/main/java/com/konkuk/medicarecall/ui/feature/login/info/screen/LoginVerificationScreen.kt index 3024783c..3d4943e8 100644 --- a/app/src/main/java/com/konkuk/medicarecall/ui/feature/login/info/screen/LoginVerificationScreen.kt +++ b/app/src/main/java/com/konkuk/medicarecall/ui/feature/login/info/screen/LoginVerificationScreen.kt @@ -26,24 +26,29 @@ import androidx.compose.ui.focus.FocusRequester import androidx.compose.ui.focus.focusRequester import androidx.compose.ui.text.input.KeyboardType import androidx.compose.ui.unit.dp -import androidx.navigation.NavController -import com.konkuk.medicarecall.navigation.Route +import androidx.hilt.navigation.compose.hiltViewModel import com.konkuk.medicarecall.ui.common.component.CTAButton import com.konkuk.medicarecall.ui.common.component.DefaultSnackBar import com.konkuk.medicarecall.ui.common.component.DefaultTextField import com.konkuk.medicarecall.ui.feature.login.info.component.LoginBackButton import com.konkuk.medicarecall.ui.feature.login.info.viewmodel.LoginEvent import com.konkuk.medicarecall.ui.feature.login.info.viewmodel.LoginViewModel -import com.konkuk.medicarecall.ui.type.CTAButtonType import com.konkuk.medicarecall.ui.model.NavigationDestination import com.konkuk.medicarecall.ui.theme.MediCareCallTheme +import com.konkuk.medicarecall.ui.type.CTAButtonType import kotlinx.coroutines.launch @Composable fun LoginVerificationScreen( - navController: NavController, - loginViewModel: LoginViewModel, - modifier: Modifier = Modifier + modifier: Modifier = Modifier, + onBack: () -> Unit = {}, + navigateToUserInfo: () -> Unit = {}, + navigateToPhone: () -> Unit = {}, + navigateToRegisterElder: () -> Unit = {}, + navigateToCareCallSetting: () -> Unit = {}, + navigateToPurchase: () -> Unit = {}, + navigateToHome: () -> Unit = {}, + loginViewModel: LoginViewModel = hiltViewModel(), ) { val scrollState = rememberScrollState() val snackBarState = remember { SnackbarHostState() } @@ -59,12 +64,7 @@ fun LoginVerificationScreen( when (event) { is LoginEvent.VerificationSuccessNew -> { // 인증 성공 시 회원정보 화면으로 이동 - - navController.navigate(Route.LoginMyInfo.route) { - popUpTo(Route.LoginVerification.route) { - inclusive = true - } - } + navigateToUserInfo() } is LoginEvent.VerificationSuccessExisting -> { @@ -77,7 +77,7 @@ fun LoginVerificationScreen( coroutineScope.launch { snackBarState.showSnackbar( message = "인증번호가 올바르지 않습니다", - duration = SnackbarDuration.Short + duration = SnackbarDuration.Short, ) } } @@ -90,17 +90,14 @@ fun LoginVerificationScreen( LaunchedEffect(navigationDestination) { navigationDestination?.let { destination -> - val route = when (destination) { - is NavigationDestination.GoToLogin -> Route.LoginStart.route - is NavigationDestination.GoToRegisterElder -> Route.LoginElderInfoScreen.route - is NavigationDestination.GoToTimeSetting -> Route.SetCall.route - is NavigationDestination.GoToPayment -> Route.Payment.route - is NavigationDestination.GoToHome -> Route.Home.route - } - navController.navigate(route) { - popUpTo(Route.LoginPhone.route) { - inclusive = true - } + // 기존 사용자는 바로 회원정보 입력 화면으로 이동하지 않고 다른 처리가 필요할 수 있음 + navigateToUserInfo() + when (destination) { + is NavigationDestination.GoToLogin -> navigateToPhone() + is NavigationDestination.GoToRegisterElder -> navigateToRegisterElder() + is NavigationDestination.GoToTimeSetting -> navigateToCareCallSetting() + is NavigationDestination.GoToPayment -> navigateToPurchase() + is NavigationDestination.GoToHome -> navigateToHome() } loginViewModel.onNavigationHandled() } @@ -118,18 +115,18 @@ fun LoginVerificationScreen( ) { Column { - LoginBackButton({ - navController.popBackStack() - }) + LoginBackButton( + onBack, + ) Column( Modifier - .verticalScroll(scrollState) + .verticalScroll(scrollState), ) { Spacer(Modifier.height(20.dp)) Text( "인증번호를\n입력해주세요", style = MediCareCallTheme.typography.B_26, - color = MediCareCallTheme.colors.black + color = MediCareCallTheme.colors.black, ) Spacer(Modifier.height(40.dp)) DefaultTextField( @@ -141,9 +138,9 @@ fun LoginVerificationScreen( placeHolder = "인증번호 입력", keyboardType = KeyboardType.Number, textFieldModifier = Modifier.focusRequester(focusRequester), - maxLength = 6 + maxLength = 6, - ) + ) Spacer(Modifier.height(30.dp)) @@ -154,20 +151,21 @@ fun LoginVerificationScreen( // TODO: 서버에 인증번호 보내서 확인하기 loginViewModel.confirmPhoneNumber( loginViewModel.phoneNumber, - loginViewModel.verificationCode + loginViewModel.verificationCode, ) loginViewModel.onVerificationCodeChanged("") - }) + }, + ) } } DefaultSnackBar( snackBarState, Modifier .align(Alignment.BottomCenter) - .padding(bottom = 14.dp) + .padding(bottom = 14.dp), ) } } diff --git a/app/src/main/java/com/konkuk/medicarecall/ui/feature/login/navigation/LoginNavigation.kt b/app/src/main/java/com/konkuk/medicarecall/ui/feature/login/navigation/LoginNavigation.kt new file mode 100644 index 00000000..52570188 --- /dev/null +++ b/app/src/main/java/com/konkuk/medicarecall/ui/feature/login/navigation/LoginNavigation.kt @@ -0,0 +1,157 @@ +package com.konkuk.medicarecall.ui.feature.login.navigation + +import androidx.compose.runtime.Composable +import androidx.navigation.NavBackStackEntry +import androidx.navigation.NavController +import androidx.navigation.NavGraphBuilder +import androidx.navigation.NavOptions +import androidx.navigation.compose.composable +import com.konkuk.medicarecall.ui.feature.login.carecall.screen.CallTimeScreen +import com.konkuk.medicarecall.ui.feature.login.info.screen.LoginMyInfoScreen +import com.konkuk.medicarecall.ui.feature.login.info.screen.LoginPhoneScreen +import com.konkuk.medicarecall.ui.feature.login.info.screen.LoginStartScreen +import com.konkuk.medicarecall.ui.feature.login.info.screen.LoginVerificationScreen +import com.konkuk.medicarecall.ui.feature.login.info.viewmodel.LoginViewModel +import com.konkuk.medicarecall.ui.feature.login.payment.screen.LoginFinishScreen +import com.konkuk.medicarecall.ui.feature.login.payment.screen.NaverPayWebViewScreen +import com.konkuk.medicarecall.ui.feature.login.payment.screen.PaymentScreen +import com.konkuk.medicarecall.ui.feature.login.senior.screen.LoginElderMedInfoScreen +import com.konkuk.medicarecall.ui.feature.login.senior.screen.LoginElderScreen +import com.konkuk.medicarecall.ui.feature.login.senior.viewmodel.LoginElderViewModel +import com.konkuk.medicarecall.ui.navigation.Route + +fun NavController.navigateToLoginStart() { + navigate(Route.LoginStart) +} + +fun NavController.navigateToLoginPhone() { + navigate(Route.LoginPhone) +} + +fun NavController.navigateToLoginVerification() { + navigate(Route.LoginVerification) +} + +fun NavController.navigateToLoginRegisterUserInfo(navOptions: NavOptions? = null) { + navigate(Route.LoginRegisterUserInfo, navOptions) +} + +fun NavController.navigateToLoginRegisterElder() { + navigate(Route.LoginRegisterElder) +} + +fun NavController.navigateToLoginRegisterElderHealth() { + navigate(Route.LoginRegisterElderHealth) +} + +fun NavController.navigateToLoginCareCallSetting(navOptions: NavOptions? = null) { + navigate(Route.LoginCareCallSetting, navOptions) +} + +fun NavController.navigateToLoginPurchase() { + navigate(Route.LoginPurchase) +} + +fun NavController.navigateToLoginNaverPayView() { + navigate(Route.LoginNaverPayView) +} + +fun NavController.navigateToLoginFinish() { + navigate(Route.LoginFinish) +} + +fun NavGraphBuilder.loginNavGraph( + popBackStack: () -> Unit, + navigateToMainAfterLogin: () -> Unit, + navigateToHome: () -> Unit, + navigateToPhone: () -> Unit, + navigateToVerification: () -> Unit, + navigateToRegisterUserInfo: () -> Unit, + navigateToRegisterElder: () -> Unit, + navigateToRegisterElderHealth: () -> Unit, + navigateToCareCallSetting: () -> Unit, + navigateToCareCallSettingWithPopUpTo: () -> Unit, + navigateToPurchase: () -> Unit, + navigateToNaverPayView: () -> Unit, + navigateToFinish: () -> Unit, + getBackStackLoginViewModel: @Composable (NavBackStackEntry) -> LoginViewModel, + getBackStackLoginElderViewModel: @Composable (NavBackStackEntry) -> LoginElderViewModel, +) { + composable { + LoginStartScreen( + navigateToPhone = navigateToPhone, + navigateToRegisterElder = navigateToRegisterElder, + navigateToCareCallSetting = navigateToCareCallSetting, + navigateToPurchase = navigateToPurchase, + navigateToHome = navigateToHome, + loginViewModel = getBackStackLoginViewModel(it), + ) + } + composable { + LoginPhoneScreen( + onBack = popBackStack, + navigateToVerification = navigateToVerification, + loginViewModel = getBackStackLoginViewModel(it), + ) + } + composable { + LoginVerificationScreen( + onBack = popBackStack, + navigateToUserInfo = navigateToRegisterUserInfo, + navigateToPhone = navigateToPhone, + navigateToRegisterElder = navigateToRegisterElder, + navigateToCareCallSetting = navigateToCareCallSetting, + navigateToPurchase = navigateToPurchase, + navigateToHome = navigateToHome, + loginViewModel = getBackStackLoginViewModel(it), + ) + } + composable { + LoginMyInfoScreen( + onBack = popBackStack, + navigateToRegisterElder = navigateToRegisterElder, + loginViewModel = getBackStackLoginViewModel(it), + ) + } + composable { + LoginElderScreen( + onBack = popBackStack, + navigateToRegisterElderHealth = navigateToRegisterElderHealth, + loginElderViewModel = getBackStackLoginElderViewModel(it), + ) + } + composable { + LoginElderMedInfoScreen( + onBack = popBackStack, + navigateToCareCallSetting = navigateToCareCallSettingWithPopUpTo, + loginElderViewModel = getBackStackLoginElderViewModel(it), + ) + } + + composable { + CallTimeScreen( + onBack = popBackStack, + navigateToPayment = navigateToPurchase, + ) + } + + composable { + PaymentScreen( + onBack = popBackStack, + navigateToNaverPay = navigateToNaverPayView, + ) + } + + composable { + NaverPayWebViewScreen( + onBack = popBackStack, + navigateToFinish = navigateToFinish, + ) + } + + composable { + LoginFinishScreen( + navigateToMain = navigateToMainAfterLogin, + ) + } +} diff --git a/app/src/main/java/com/konkuk/medicarecall/ui/feature/login/payment/screen/LoginFinishScreen.kt b/app/src/main/java/com/konkuk/medicarecall/ui/feature/login/payment/screen/LoginFinishScreen.kt index c79a2f12..4d83b310 100644 --- a/app/src/main/java/com/konkuk/medicarecall/ui/feature/login/payment/screen/LoginFinishScreen.kt +++ b/app/src/main/java/com/konkuk/medicarecall/ui/feature/login/payment/screen/LoginFinishScreen.kt @@ -17,19 +17,17 @@ 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.navigation.NavHostController import androidx.wear.compose.material3.Icon import androidx.wear.compose.material3.Text import com.konkuk.medicarecall.R -import com.konkuk.medicarecall.navigation.navigateToMainAfterLogin import com.konkuk.medicarecall.ui.common.component.CTAButton -import com.konkuk.medicarecall.ui.type.CTAButtonType import com.konkuk.medicarecall.ui.theme.MediCareCallTheme +import com.konkuk.medicarecall.ui.type.CTAButtonType @Composable fun LoginFinishScreen( - navController: NavHostController, modifier: Modifier = Modifier, + navigateToMain: () -> Unit = {}, ) { Column( modifier = modifier @@ -38,19 +36,19 @@ fun LoginFinishScreen( .padding(top = 146.dp) .systemBarsPadding() .imePadding(), - horizontalAlignment = Alignment.CenterHorizontally + horizontalAlignment = Alignment.CenterHorizontally, ) { Icon( painter = painterResource(id = R.drawable.ic_complete), contentDescription = "complete", tint = Color.Unspecified, - modifier = modifier.size(64.dp) + modifier = modifier.size(64.dp), ) Spacer(modifier = modifier.size(47.dp)) Text( "모든 설정이 끝났어요!", style = MediCareCallTheme.typography.B_26, - color = MediCareCallTheme.colors.white + color = MediCareCallTheme.colors.white, ) Spacer(modifier = modifier.height(59.dp)) Image( @@ -63,8 +61,8 @@ fun LoginFinishScreen( Spacer(modifier = modifier.weight(1f)) CTAButton( CTAButtonType.WHITE, "확인", - onClick = { navController.navigateToMainAfterLogin() }, - modifier = modifier.padding(horizontal = 20.dp) + onClick = navigateToMain, + modifier = modifier.padding(horizontal = 20.dp), ) Spacer(modifier = modifier.height(30.dp)) } diff --git a/app/src/main/java/com/konkuk/medicarecall/ui/feature/login/payment/screen/NaverPayWebViewScreen.kt b/app/src/main/java/com/konkuk/medicarecall/ui/feature/login/payment/screen/NaverPayWebViewScreen.kt index 8584f428..dfe88159 100644 --- a/app/src/main/java/com/konkuk/medicarecall/ui/feature/login/payment/screen/NaverPayWebViewScreen.kt +++ b/app/src/main/java/com/konkuk/medicarecall/ui/feature/login/payment/screen/NaverPayWebViewScreen.kt @@ -42,12 +42,10 @@ import androidx.compose.ui.viewinterop.AndroidView import androidx.compose.ui.window.Dialog import androidx.core.net.toUri import androidx.hilt.navigation.compose.hiltViewModel -import androidx.navigation.NavHostController import com.konkuk.medicarecall.R -import com.konkuk.medicarecall.navigation.Route import com.konkuk.medicarecall.ui.feature.login.payment.viewmodel.NaverPayViewModel -import com.konkuk.medicarecall.ui.model.PaymentResult import com.konkuk.medicarecall.ui.feature.settings.component.SettingsTopAppBar +import com.konkuk.medicarecall.ui.model.PaymentResult import com.konkuk.medicarecall.ui.theme.MediCareCallTheme import org.json.JSONObject @@ -55,16 +53,16 @@ import org.json.JSONObject @Composable fun NaverPayWebViewScreen( onBack: () -> Unit, - navController: NavHostController, modifier: Modifier = Modifier, - naverPayViewModel: NaverPayViewModel = hiltViewModel() + navigateToFinish: () -> Unit = {}, + naverPayViewModel: NaverPayViewModel = hiltViewModel(), ) { Column( modifier = modifier .fillMaxSize() .background(MediCareCallTheme.colors.bg) .systemBarsPadding() - .imePadding() + .imePadding(), ) { SettingsTopAppBar( modifier = modifier, @@ -76,7 +74,7 @@ fun NaverPayWebViewScreen( modifier = modifier .size(24.dp) .clickable { onBack() }, - tint = Color.Black + tint = Color.Black, ) }, leftIconClick = onBack, @@ -86,10 +84,10 @@ fun NaverPayWebViewScreen( painter = painterResource(id = R.drawable.ic_arrow_big), contentDescription = "go_next", modifier = modifier.size(24.dp), - tint = Color.Black + tint = Color.Black, ) }, - rightIconClick = { navController.navigate(Route.FinishSplash.route) } + rightIconClick = navigateToFinish, ) val context = LocalContext.current val baseHost = "medicare-call.shop" @@ -115,7 +113,7 @@ fun NaverPayWebViewScreen( modifier = Modifier .fillMaxSize() .background(MediCareCallTheme.colors.gray2), - contentAlignment = Alignment.Center + contentAlignment = Alignment.Center, ) { AndroidView( modifier = Modifier.fillMaxSize(), @@ -136,7 +134,7 @@ fun NaverPayWebViewScreen( view: WebView?, isDialog: Boolean, isUserGesture: Boolean, - resultMsg: Message? + resultMsg: Message?, ): Boolean { val ctx = view?.context ?: return false // 새 팝업 WebView 생성 @@ -153,81 +151,80 @@ fun NaverPayWebViewScreen( // ① 팝업에도 동일한 JS 브릿지(이름 "Android") 추가 // payment-result.html에서 Android.onPaymentComplete/closePage() 호출 // ───────────────────────────────────────────── - addJavascriptInterface(object { - @JavascriptInterface - fun onPaymentComplete(json: String) { - Handler(Looper.getMainLooper()) - .post { - try { - val o = JSONObject(json) - val result = - PaymentResult( - success = o.optBoolean( - "success", - false - ), - orderCode = o.optString( - "orderCode", - null - ), - paymentId = o.optString( - "paymentId", - null - ), - resultCode = o.optString( - "resultCode", - null - ), - message = o.optString( - "message", - null + addJavascriptInterface( + object { + @JavascriptInterface + fun onPaymentComplete(json: String) { + Handler(Looper.getMainLooper()) + .post { + try { + val o = JSONObject(json) + val result = + PaymentResult( + success = o.optBoolean( + "success", + false, + ), + orderCode = o.optString( + "orderCode", + null, + ), + paymentId = o.optString( + "paymentId", + null, + ), + resultCode = o.optString( + "resultCode", + null, + ), + message = o.optString( + "message", + null, + ), ) + Log.d( + "NaverPayScreen", + "Popup payment result: $result", ) - Log.d( - "NaverPayScreen", - "Popup payment result: $result" - ) - if (result.success && !navigated) { - navigated = true - // 팝업 닫고 성공 네비게이션 - popupWebView?.destroy() - popupWebView = null - navController.navigate(Route.FinishSplash.route) { - popUpTo(Route.NaverPay.route) { - inclusive = true - } + if (result.success && !navigated) { + navigated = true + // 팝업 닫고 성공 네비게이션 + popupWebView?.destroy() + popupWebView = null + navigateToFinish() + } else if (!result.success) { + Log.w( + "NaverPayScreen", + "Payment failed: ${result.message ?: "unknown"}", + ) } - } else if (!result.success) { - Log.w( + } catch (e: Exception) { + Log.e( "NaverPayScreen", - "Payment failed: ${result.message ?: "unknown"}" + "Parse error: ${e.message}", ) } - } catch (e: Exception) { - Log.e( - "NaverPayScreen", - "Parse error: ${e.message}" - ) } - } - } + } - @JavascriptInterface - fun closePage() { - Handler(Looper.getMainLooper()) - .post { - popupWebView?.destroy() - popupWebView = null - } - } - }, "Android") + @JavascriptInterface + fun closePage() { + Handler(Looper.getMainLooper()) + .post { + popupWebView?.destroy() + popupWebView = null + } + } + }, + "Android", + ) // ───────────────────────────────────────────── // ② 팝업 내 네비게이션/스킴 처리 + fallback JS // ───────────────────────────────────────────── webViewClient = object : WebViewClient() { override fun shouldOverrideUrlLoading( v: WebView, - req: WebResourceRequest + req: WebResourceRequest, ): Boolean { val url = req.url.toString() val host = req.url.host ?: "" @@ -237,7 +234,7 @@ fun NaverPayWebViewScreen( try { val intent = Intent.parseUri( url, - Intent.URI_INTENT_SCHEME + Intent.URI_INTENT_SCHEME, ) try { v.context.startActivity(intent) @@ -251,7 +248,7 @@ fun NaverPayWebViewScreen( if (!pkg.isNullOrEmpty()) { val market = Intent( Intent.ACTION_VIEW, - ("market://details?id=" + pkg).toUri() + ("market://details?id=" + pkg).toUri(), ) v.context.startActivity(market) } @@ -260,7 +257,7 @@ fun NaverPayWebViewScreen( } catch (e: Exception) { Log.e( "NaverPayWebView", - "intent scheme error: ${e.message}" + "intent scheme error: ${e.message}", ) } return true @@ -269,7 +266,7 @@ fun NaverPayWebViewScreen( try { val i = Intent( Intent.ACTION_VIEW, - req.url + req.url, ) v.context.startActivity(i) } catch (_: Exception) { @@ -282,7 +279,7 @@ fun NaverPayWebViewScreen( if (!token.isNullOrBlank()) { v.loadUrl( url, - mapOf("Authorization" to "Bearer $token") + mapOf("Authorization" to "Bearer $token"), ) return true } @@ -293,7 +290,7 @@ fun NaverPayWebViewScreen( override fun onPageFinished(v: WebView, url: String) { Log.d( "NaverPayWebView", - "POPUP FINISHED: $url" + "POPUP FINISHED: $url", ) // 결제 결과 페이지 방어적 fallback 호출 if (url.contains("/api/payments/result")) { @@ -310,11 +307,11 @@ fun NaverPayWebViewScreen( return 'error:' + (e && e.message); } })(); - """.trimIndent() + """.trimIndent(), ) { ret -> Log.d( "NaverPayWebView", - "popup fallback: $ret" + "popup fallback: $ret", ) } } @@ -323,22 +320,22 @@ fun NaverPayWebViewScreen( override fun onReceivedHttpError( v: WebView, req: WebResourceRequest, - res: WebResourceResponse + res: WebResourceResponse, ) { Log.e( "NaverPayWebView", - "POPUP HTTP ${res.statusCode} on ${req.url}" + "POPUP HTTP ${res.statusCode} on ${req.url}", ) } override fun onReceivedError( v: WebView, req: WebResourceRequest, - err: WebResourceError + err: WebResourceError, ) { Log.e( "NaverPayWebView", - "POPUP ERR ${err.errorCode} on ${req.url}: ${err.description}" + "POPUP ERR ${err.errorCode} on ${req.url}: ${err.description}", ) } } @@ -369,7 +366,7 @@ fun NaverPayWebViewScreen( webViewClient = object : WebViewClient() { override fun shouldOverrideUrlLoading( view: WebView, - request: WebResourceRequest + request: WebResourceRequest, ): Boolean { val url = request.url.toString() val host = request.url.host ?: "" @@ -379,7 +376,7 @@ fun NaverPayWebViewScreen( try { val intent = Intent.parseUri( url, - Intent.URI_INTENT_SCHEME + Intent.URI_INTENT_SCHEME, ) try { view.context.startActivity(intent) @@ -393,7 +390,7 @@ fun NaverPayWebViewScreen( if (!pkg.isNullOrEmpty()) { val market = Intent( Intent.ACTION_VIEW, - "market://details?id=$pkg".toUri() + "market://details?id=$pkg".toUri(), ) view.context.startActivity(market) } @@ -402,7 +399,7 @@ fun NaverPayWebViewScreen( } catch (e: Exception) { Log.e( "NaverPayWebView", - "intent scheme error: ${e.message}" + "intent scheme error: ${e.message}", ) } return true @@ -411,7 +408,7 @@ fun NaverPayWebViewScreen( try { val i = Intent( Intent.ACTION_VIEW, - request.url + request.url, ) view.context.startActivity(i) } catch (_: Exception) { @@ -447,7 +444,7 @@ fun NaverPayWebViewScreen( return 'error:' + (e && e.message); } })(); - """.trimIndent() + """.trimIndent(), ) { ret -> Log.d("NaverPayWebView", "fallback bridge: $ret") } } } // 서버가 호출안하면 우리가 호출 (결과 페이지 도달) @@ -455,74 +452,74 @@ fun NaverPayWebViewScreen( override fun onReceivedHttpError( view: WebView, request: WebResourceRequest, - errorResponse: WebResourceResponse + errorResponse: WebResourceResponse, ) { Log.e( "NaverPayWebView", - "HTTP ${errorResponse.statusCode} on ${request.url}" + "HTTP ${errorResponse.statusCode} on ${request.url}", ) } override fun onReceivedError( view: WebView, request: WebResourceRequest, - error: WebResourceError + error: WebResourceError, ) { Log.e( "NaverPayWebView", - "ERR ${error.errorCode} on ${request.url}: ${error.description}" + "ERR ${error.errorCode} on ${request.url}: ${error.description}", ) } } - addJavascriptInterface(object { - @JavascriptInterface - fun onPaymentComplete(json: String) { - Log.d("NaverPayScreen", "Payment complete callback: $json") - Handler(Looper.getMainLooper()).post { - try { - val o = JSONObject(json) - val result = PaymentResult( - success = o.optBoolean("success", false), - orderCode = o.optString("orderCode", null), - paymentId = o.optString("paymentId", null), - resultCode = o.optString("resultCode", null), - message = o.optString("message", null) - ) - Log.d("NaverPayScreen", "Payment result: $result") - if (result.success && !navigated) { - navigated = true - Log.d("NaverPayScreen", "navigate FinishSplash") - navController.navigate(Route.FinishSplash.route) { - // 결제 화면 스택 정리(원치 않으면 제거) - popUpTo(Route.NaverPay.route) { inclusive = true } - } - } else if (!result.success) { - Log.w( - "NaverPayScreen", - "Payment failed: ${result.message ?: "unknown"}" + addJavascriptInterface( + object { + @JavascriptInterface + fun onPaymentComplete(json: String) { + Log.d("NaverPayScreen", "Payment complete callback: $json") + Handler(Looper.getMainLooper()).post { + try { + val o = JSONObject(json) + val result = PaymentResult( + success = o.optBoolean("success", false), + orderCode = o.optString("orderCode", null), + paymentId = o.optString("paymentId", null), + resultCode = o.optString("resultCode", null), + message = o.optString("message", null), ) - // TODO: 실패 UI/토스트 등 - navController.popBackStack() + Log.d("NaverPayScreen", "Payment result: $result") + if (result.success && !navigated) { + navigated = true + Log.d("NaverPayScreen", "navigate FinishSplash") + navigateToFinish() + } else if (!result.success) { + Log.w( + "NaverPayScreen", + "Payment failed: ${result.message ?: "unknown"}", + ) + // TODO: 실패 UI/토스트 등 + onBack() + } + } catch (e: Exception) { + Log.e("NaverPayScreen", "Parse error: ${e.message}") } - } catch (e: Exception) { - Log.e("NaverPayScreen", "Parse error: ${e.message}") } } - } - }, "Android") + }, + "Android", + ) } - } + }, ) if (popupWebView != null) { Dialog( onDismissRequest = { popupWebView?.destroy() popupWebView = null - } + }, ) { AndroidView( factory = { popupWebView!! }, - modifier = Modifier.fillMaxSize() + modifier = Modifier.fillMaxSize(), ) } } @@ -530,7 +527,7 @@ fun NaverPayWebViewScreen( Log.d("NaverPayScreen", "Waiting for orderCode/accessToken...") CircularProgressIndicator( color = MediCareCallTheme.colors.main, - modifier = Modifier.align(Alignment.Center) + modifier = Modifier.align(Alignment.Center), ) } } diff --git a/app/src/main/java/com/konkuk/medicarecall/ui/feature/login/payment/screen/PaymentScreen.kt b/app/src/main/java/com/konkuk/medicarecall/ui/feature/login/payment/screen/PaymentScreen.kt index 4a3aeafc..54198792 100644 --- a/app/src/main/java/com/konkuk/medicarecall/ui/feature/login/payment/screen/PaymentScreen.kt +++ b/app/src/main/java/com/konkuk/medicarecall/ui/feature/login/payment/screen/PaymentScreen.kt @@ -31,9 +31,7 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.res.painterResource import androidx.compose.ui.unit.dp import androidx.hilt.navigation.compose.hiltViewModel -import androidx.navigation.NavHostController import com.konkuk.medicarecall.R -import com.konkuk.medicarecall.navigation.Route import com.konkuk.medicarecall.ui.common.component.CTAButton import com.konkuk.medicarecall.ui.feature.login.info.component.LoginBackButton import com.konkuk.medicarecall.ui.feature.login.payment.component.PaymentPriceItem @@ -46,8 +44,8 @@ import java.util.Locale @Composable fun PaymentScreen( onBack: () -> Unit, - navController: NavHostController, modifier: Modifier = Modifier, + navigateToNaverPay: () -> Unit = {}, elderInfoViewModel: EldersInfoViewModel = hiltViewModel() ) { val scrollState = rememberScrollState() @@ -162,7 +160,7 @@ fun PaymentScreen( CTAButton( type = if (isClicked) CTAButtonType.GREEN else CTAButtonType.DISABLED, text = "결제하기", - onClick = { if (isClicked) navController.navigate(Route.NaverPay.route) }) + onClick = { if (isClicked) navigateToNaverPay() }) } } diff --git a/app/src/main/java/com/konkuk/medicarecall/ui/feature/login/senior/screen/LoginElderInfoScreen.kt b/app/src/main/java/com/konkuk/medicarecall/ui/feature/login/senior/screen/LoginElderInfoScreen.kt index ac8f4990..b5b26687 100644 --- a/app/src/main/java/com/konkuk/medicarecall/ui/feature/login/senior/screen/LoginElderInfoScreen.kt +++ b/app/src/main/java/com/konkuk/medicarecall/ui/feature/login/senior/screen/LoginElderInfoScreen.kt @@ -38,9 +38,8 @@ import androidx.compose.ui.draw.clip import androidx.compose.ui.focus.FocusRequester import androidx.compose.ui.res.painterResource import androidx.compose.ui.unit.dp -import androidx.navigation.NavController +import androidx.hilt.navigation.compose.hiltViewModel import com.konkuk.medicarecall.R -import com.konkuk.medicarecall.navigation.Route import com.konkuk.medicarecall.ui.common.component.CTAButton import com.konkuk.medicarecall.ui.common.component.DefaultSnackBar import com.konkuk.medicarecall.ui.common.util.isValidDate @@ -55,9 +54,10 @@ import kotlinx.coroutines.launch @Composable fun LoginElderScreen( - navController: NavController, - loginElderViewModel: LoginElderViewModel, modifier: Modifier = Modifier, + onBack: () -> Unit = {}, + navigateToRegisterElderHealth: () -> Unit = {}, + loginElderViewModel: LoginElderViewModel = hiltViewModel(), ) { val scrollState = rememberScrollState() @@ -84,15 +84,7 @@ fun LoginElderScreen( .imePadding(), ) { Column { - LoginBackButton( - { - navController.navigate(Route.LoginElderInfoScreen.route) { - popUpTo(Route.LoginStart.route) { inclusive = false } // ← 스택 정리 - launchSingleTop = true - restoreState = true - } - }, - ) + LoginBackButton(onClick = onBack) Column( modifier .verticalScroll(scrollState), @@ -238,7 +230,7 @@ fun LoginElderScreen( else { loginElderViewModel.initElderHealthData() loginElderViewModel.postElderBulk() - navController.navigate(Route.LoginElderMedInfoScreen.route) + navigateToRegisterElderHealth() } }, modifier.padding(bottom = 20.dp), @@ -253,4 +245,5 @@ fun LoginElderScreen( .padding(bottom = 14.dp), ) } + } diff --git a/app/src/main/java/com/konkuk/medicarecall/ui/feature/login/senior/screen/LoginElderMedInfoScreen.kt b/app/src/main/java/com/konkuk/medicarecall/ui/feature/login/senior/screen/LoginElderMedInfoScreen.kt index 505b7ab5..81e9965e 100644 --- a/app/src/main/java/com/konkuk/medicarecall/ui/feature/login/senior/screen/LoginElderMedInfoScreen.kt +++ b/app/src/main/java/com/konkuk/medicarecall/ui/feature/login/senior/screen/LoginElderMedInfoScreen.kt @@ -29,8 +29,7 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.unit.dp -import androidx.navigation.NavController -import com.konkuk.medicarecall.navigation.Route +import androidx.hilt.navigation.compose.hiltViewModel import com.konkuk.medicarecall.ui.common.component.CTAButton import com.konkuk.medicarecall.ui.common.component.ChipItem import com.konkuk.medicarecall.ui.common.component.DefaultDropdown @@ -42,15 +41,14 @@ import com.konkuk.medicarecall.ui.feature.login.senior.viewmodel.LoginElderViewM import com.konkuk.medicarecall.ui.theme.MediCareCallTheme import com.konkuk.medicarecall.ui.type.CTAButtonType import com.konkuk.medicarecall.ui.type.HealthIssueType -import kotlinx.coroutines.coroutineScope -import kotlinx.coroutines.delay import kotlinx.coroutines.launch @Composable fun LoginElderMedInfoScreen( - navController: NavController, - loginElderViewModel: LoginElderViewModel, modifier: Modifier = Modifier, + onBack: () -> Unit = {}, + navigateToCareCallSetting: () -> Unit = {}, + loginElderViewModel: LoginElderViewModel = hiltViewModel(), ) { val scrollState = rememberScrollState() @@ -72,11 +70,7 @@ fun LoginElderMedInfoScreen( .imePadding(), ) { Column { - LoginBackButton( - { - navController.popBackStack() - }, - ) + LoginBackButton(onBack) Column( Modifier .verticalScroll(scrollState), @@ -193,11 +187,7 @@ fun LoginElderMedInfoScreen( { coroutineScope.launch { loginElderViewModel.postElderHealthInfoBulk() - navController.navigate(Route.SetCall.route) { - popUpTo(Route.LoginElderInfoScreen.route) { - inclusive = true - } - } + navigateToCareCallSetting() } }, Modifier.padding(top = 30.dp, bottom = 20.dp), diff --git a/app/src/main/java/com/konkuk/medicarecall/ui/feature/settings/navigation/SettingNavigation.kt b/app/src/main/java/com/konkuk/medicarecall/ui/feature/settings/navigation/SettingNavigation.kt new file mode 100644 index 00000000..20f2c9ac --- /dev/null +++ b/app/src/main/java/com/konkuk/medicarecall/ui/feature/settings/navigation/SettingNavigation.kt @@ -0,0 +1,219 @@ +package com.konkuk.medicarecall.ui.feature.settings.navigation + +import androidx.navigation.NavController +import androidx.navigation.NavGraphBuilder +import androidx.navigation.NavHostController +import androidx.navigation.NavOptions +import androidx.navigation.compose.composable +import androidx.navigation.toRoute +import com.konkuk.medicarecall.data.dto.response.EldersHealthResponseDto +import com.konkuk.medicarecall.data.dto.response.EldersInfoResponseDto +import com.konkuk.medicarecall.data.dto.response.EldersSubscriptionResponseDto +import com.konkuk.medicarecall.data.dto.response.MyInfoResponseDto +import com.konkuk.medicarecall.data.dto.response.NoticesResponseDto +import com.konkuk.medicarecall.ui.feature.settings.screen.AnnouncementDetailScreen +import com.konkuk.medicarecall.ui.feature.settings.screen.AnnouncementScreen +import com.konkuk.medicarecall.ui.feature.settings.screen.HealthDetailScreen +import com.konkuk.medicarecall.ui.feature.settings.screen.HealthInfoScreen +import com.konkuk.medicarecall.ui.feature.settings.screen.MyDataSettingScreen +import com.konkuk.medicarecall.ui.feature.settings.screen.MyDetailScreen +import com.konkuk.medicarecall.ui.feature.settings.screen.PersonalDetailScreen +import com.konkuk.medicarecall.ui.feature.settings.screen.PersonalInfoScreen +import com.konkuk.medicarecall.ui.feature.settings.screen.ServiceCenterScreen +import com.konkuk.medicarecall.ui.feature.settings.screen.SettingAlarmScreen +import com.konkuk.medicarecall.ui.feature.settings.screen.SettingSubscribeScreen +import com.konkuk.medicarecall.ui.feature.settings.screen.SettingsScreen +import com.konkuk.medicarecall.ui.feature.settings.screen.SubscribeDetailScreen +import com.konkuk.medicarecall.ui.navigation.EldersHealthResponseDtoType +import com.konkuk.medicarecall.ui.navigation.EldersInfoResponseDtoType +import com.konkuk.medicarecall.ui.navigation.EldersSubscriptionResponseDtoType +import com.konkuk.medicarecall.ui.navigation.MainTabRoute +import com.konkuk.medicarecall.ui.navigation.MyInfoResponseDtoType +import com.konkuk.medicarecall.ui.navigation.NoticesResponseDtoType +import com.konkuk.medicarecall.ui.navigation.Route +import kotlin.reflect.typeOf + +fun NavController.navigateToSettings(navOptions: NavOptions) { + navigate(MainTabRoute.Settings, navOptions) +} + +fun NavController.navigateToElderPersonalInfo() { + navigate(Route.ElderPersonalInfo) +} + +fun NavController.navigateToElderPersonalDetail(info: EldersInfoResponseDto) { + navigate(Route.ElderPersonalDetail(info)) +} + +fun NavController.navigateToElderHealthInfo() { + navigate(Route.ElderHealthInfo) +} + +fun NavController.navigateToElderHealthDetail(health: EldersHealthResponseDto) { // EldersHealthResponseDto + navigate(Route.ElderHealthDetail(health)) +} + +fun NavController.navigateToNotificationSetting(myInfo: MyInfoResponseDto) { + navigate(Route.NotificationSetting(myInfo)) +} + +fun NavController.navigateToSubscribeInfo() { + navigate(Route.SubscribeInfo) +} + +fun NavController.navigateToSubscribeDetail(subscription: EldersSubscriptionResponseDto) { + navigate(Route.SubscribeDetail(subscription)) +} + +fun NavController.navigateToNotice() { + navigate(Route.Notice) +} + +fun NavController.navigateToNoticeDetail(notice: NoticesResponseDto) { + navigate(Route.NoticeDetail(notice)) +} + +fun NavController.navigateToServiceCenter() { + navigate(Route.ServiceCenter) +} + +fun NavController.navigateToUserInfo() { + navigate(Route.UserInfo) +} + +fun NavController.navigateToUserInfoSetting(myInfo: MyInfoResponseDto) { + navigate(Route.UserInfoSetting(myInfo)) +} + +fun NavGraphBuilder.settingNavGraph( + popBackStack: () -> Unit, + navigateToElderPersonalInfo: () -> Unit, + navigateToElderPersonalDetail: (EldersInfoResponseDto) -> Unit, + navigateToElderHealthInfo: () -> Unit, + navigateToHealthDetail: (EldersHealthResponseDto) -> Unit, + navigateToNotificationSetting: (MyInfoResponseDto) -> Unit, + navigateToSubscribeInfo: () -> Unit, + navigateToSubscribeDetail: (EldersSubscriptionResponseDto) -> Unit, + navigateToNotice: () -> Unit, + navigateToNoticeDetail: (NoticesResponseDto) -> Unit, + navigateToServiceCenter: () -> Unit, + navigateToUserInfo: () -> Unit, + navigateToUserInfoSetting: (MyInfoResponseDto) -> Unit, + navigateToLoginAfterLogout: () -> Unit, + navController: NavHostController, +) { + composable { + SettingsScreen( + navigateToUserInfo = navigateToUserInfo, + navigateToNotice = navigateToNotice, + navigateToCenter = navigateToServiceCenter, + navigateToSubscribe = navigateToSubscribeInfo, + navigateToElderPersonalInfo = navigateToElderPersonalInfo, + navigateToElderHealthInfo = navigateToElderHealthInfo, + navigateToNotificationSetting = navigateToNotificationSetting, + ) + } + + composable { + PersonalInfoScreen( + onBack = popBackStack, + navigateToElderDetail = navigateToElderPersonalDetail, + ) + } + + composable( + typeMap = mapOf(typeOf() to EldersInfoResponseDtoType), + ) { navBackstackEntry -> + val elderInfo = navBackstackEntry.toRoute().info + PersonalDetailScreen( + onBack = popBackStack, + eldersInfoResponseDto = elderInfo, + navController = navController, + ) + } + + composable { + HealthInfoScreen( + onBack = popBackStack, + navigateToHealthDetail = navigateToHealthDetail, + ) + } + + composable( + typeMap = mapOf(typeOf() to EldersHealthResponseDtoType), + ) { navBackstackEntry -> + val healthInfo = navBackstackEntry.toRoute().health + HealthDetailScreen( + onBack = popBackStack, + healthInfoResponseDto = healthInfo, + ) + } + + composable( + typeMap = mapOf(typeOf() to MyInfoResponseDtoType), + ) { navBackStackEntry -> + val myDataInfo = navBackStackEntry.toRoute().myInfo + SettingAlarmScreen( + myDataInfo = myDataInfo, + onBack = popBackStack, + ) + } + + composable { + SettingSubscribeScreen( + onBack = popBackStack, + navigateToSubscribeDetail = navigateToSubscribeDetail, + ) + } + + composable( + typeMap = mapOf(typeOf() to EldersSubscriptionResponseDtoType), + ) { navBackStackEntry -> + val elderInfo = navBackStackEntry.toRoute().subscription + SubscribeDetailScreen( + elderInfo = elderInfo, + onBack = popBackStack, + ) + } + + composable { + AnnouncementScreen( + onBack = popBackStack, + navigateToNoticeDetail = navigateToNoticeDetail, + ) + } + + composable( + typeMap = mapOf(typeOf() to NoticesResponseDtoType), + ) { navBackStackEntry -> + val noticeInfo = navBackStackEntry.toRoute().notice + AnnouncementDetailScreen( + noticeInfo = noticeInfo, + onBack = popBackStack, + ) + } + + composable { + ServiceCenterScreen( + onBack = popBackStack, + ) + } + + composable { + MyDataSettingScreen( + onBack = popBackStack, + navigateToUserInfoSetting = navigateToUserInfoSetting, + navigateToLoginAfterLogout = navigateToLoginAfterLogout, + ) + } + + composable( + typeMap = mapOf(typeOf() to MyInfoResponseDtoType), + ) { navBackStackEntry -> + val myDataInfo = navBackStackEntry.toRoute().myInfo + MyDetailScreen( + myDataInfo = myDataInfo, + onBack = popBackStack, + ) + } +} diff --git a/app/src/main/java/com/konkuk/medicarecall/ui/feature/settings/screen/AnnouncementScreen.kt b/app/src/main/java/com/konkuk/medicarecall/ui/feature/settings/screen/AnnouncementScreen.kt index 4141be28..797b7220 100644 --- a/app/src/main/java/com/konkuk/medicarecall/ui/feature/settings/screen/AnnouncementScreen.kt +++ b/app/src/main/java/com/konkuk/medicarecall/ui/feature/settings/screen/AnnouncementScreen.kt @@ -16,23 +16,19 @@ import androidx.compose.ui.graphics.Color import androidx.compose.ui.res.painterResource import androidx.compose.ui.unit.dp import androidx.hilt.navigation.compose.hiltViewModel -import androidx.navigation.NavHostController import com.konkuk.medicarecall.R -import com.konkuk.medicarecall.navigation.Route +import com.konkuk.medicarecall.data.dto.response.NoticesResponseDto import com.konkuk.medicarecall.ui.feature.settings.component.AnnouncementCard import com.konkuk.medicarecall.ui.feature.settings.component.SettingsTopAppBar import com.konkuk.medicarecall.ui.feature.settings.viewmodel.NoticeViewModel import com.konkuk.medicarecall.ui.theme.MediCareCallTheme -import kotlinx.serialization.json.Json -import java.net.URLEncoder -import java.nio.charset.StandardCharsets @Composable fun AnnouncementScreen( modifier: Modifier = Modifier, onBack: () -> Unit = {}, - navController: NavHostController, - viewModel: NoticeViewModel = hiltViewModel() + navigateToNoticeDetail: (notice: NoticesResponseDto) -> Unit = {}, + viewModel: NoticeViewModel = hiltViewModel(), ) { val scrollState = rememberScrollState() val notices = viewModel.noticeList @@ -47,7 +43,7 @@ fun AnnouncementScreen( modifier = modifier .fillMaxSize() .background(MediCareCallTheme.colors.bg) - .statusBarsPadding() + .statusBarsPadding(), ) { SettingsTopAppBar( modifier = modifier, @@ -59,12 +55,12 @@ fun AnnouncementScreen( modifier = modifier .size(24.dp) .clickable { onBack() }, - tint = Color.Black + tint = Color.Black, ) - } + }, ) Column( - modifier = modifier.verticalScroll(scrollState) + modifier = modifier.verticalScroll(scrollState), ) { if (error != null) { AnnouncementCard("공지사항 오류 발생", error, onClick = {}) @@ -75,11 +71,8 @@ fun AnnouncementScreen( date = notice.publishedAt.replace("-", "."), onClick = { Log.d("AnnouncementScreen", "공지사항 클릭: ${notice.title}") - val json = Json.encodeToString(notice) - val encodedJson = - URLEncoder.encode(json, StandardCharsets.UTF_8.toString()) - navController.navigate("${Route.AnnouncementDetail.route}/$encodedJson") - } + navigateToNoticeDetail(notice) + }, ) } } diff --git a/app/src/main/java/com/konkuk/medicarecall/ui/feature/settings/screen/ElderDetailScreen.kt b/app/src/main/java/com/konkuk/medicarecall/ui/feature/settings/screen/ElderDetailScreen.kt index 6641b84c..9d5ed326 100644 --- a/app/src/main/java/com/konkuk/medicarecall/ui/feature/settings/screen/ElderDetailScreen.kt +++ b/app/src/main/java/com/konkuk/medicarecall/ui/feature/settings/screen/ElderDetailScreen.kt @@ -26,7 +26,7 @@ import androidx.compose.ui.res.painterResource import androidx.compose.ui.text.input.KeyboardType import androidx.compose.ui.unit.dp import androidx.hilt.navigation.compose.hiltViewModel -import androidx.navigation.NavController +import androidx.navigation.NavHostController import com.konkuk.medicarecall.R import com.konkuk.medicarecall.data.dto.response.EldersInfoResponseDto import com.konkuk.medicarecall.ui.common.component.CTAButton @@ -52,8 +52,8 @@ fun PersonalDetailScreen( modifier: Modifier = Modifier, onBack: () -> Unit = {}, eldersInfoResponseDto: EldersInfoResponseDto, + navController: NavHostController, detailViewModel: DetailElderInfoViewModel = hiltViewModel(), - navController: NavController, ) { val gender = when (eldersInfoResponseDto.gender) { GenderType.MALE -> true @@ -79,7 +79,7 @@ fun PersonalDetailScreen( .fillMaxSize() .background(MediCareCallTheme.colors.bg) .systemBarsPadding() - .imePadding() + .imePadding(), ) { SettingsTopAppBar( title = "어르신 개인정보 설정", @@ -88,7 +88,7 @@ fun PersonalDetailScreen( painterResource(id = R.drawable.ic_settings_back), contentDescription = "setting back", modifier = modifier.clickable { onBack() }, - tint = MediCareCallTheme.colors.black + tint = MediCareCallTheme.colors.black, ) }, ) @@ -108,18 +108,18 @@ fun PersonalDetailScreen( style = MediCareCallTheme.typography.SB_16, modifier = Modifier.clickable { showDeleteDialog = true - } + }, ) } Column( - verticalArrangement = Arrangement.spacedBy(20.dp) + verticalArrangement = Arrangement.spacedBy(20.dp), ) { Column { DefaultTextField( value = name, onValueChange = { name = it }, category = "이름", - placeHolder = "이름" + placeHolder = "이름", ) } Column { @@ -130,14 +130,14 @@ fun PersonalDetailScreen( placeHolder = "YYYY / MM / DD", keyboardType = KeyboardType.Number, visualTransformation = DateOfBirthVisualTransformation(), - maxLength = 8 + maxLength = 8, ) } Column() { Text( "성별", style = MediCareCallTheme.typography.M_17, - color = MediCareCallTheme.colors.gray7 + color = MediCareCallTheme.colors.gray7, ) Spacer(modifier = modifier.height(10.dp)) GenderToggleButton( @@ -145,7 +145,7 @@ fun PersonalDetailScreen( onGenderChange = { newValue -> isMale = newValue - } + }, ) } Column { @@ -155,7 +155,7 @@ fun PersonalDetailScreen( placeHolder = "휴대폰 번호", keyboardType = KeyboardType.Number, visualTransformation = PhoneNumberVisualTransformation(), - maxLength = 11 + maxLength = 11, ) } Column() { @@ -170,7 +170,7 @@ fun PersonalDetailScreen( relationship = RelationshipType.entries.firstOrNull { it.displayName == newValue } ?: RelationshipType.ACQUAINTANCE - } + }, ) } Column { @@ -185,7 +185,7 @@ fun PersonalDetailScreen( residenceType = ElderResidenceType.entries.firstOrNull { it.displayName == newValue } ?: ElderResidenceType.WITH_FAMILY - } + }, ) } @@ -224,15 +224,16 @@ fun PersonalDetailScreen( gender = if (isMale == true) GenderType.MALE else GenderType.FEMALE, phone = phoneNum, relationship = relationship, - residenceType = residenceType - ) + residenceType = residenceType, + ), ) { navController.getBackStackEntry("main") .savedStateHandle["ELDER_NAME_UPDATED"] = name navController.popBackStack() } - }, Modifier.padding(bottom = 20.dp) + }, + Modifier.padding(bottom = 20.dp), ) } @@ -244,7 +245,7 @@ fun PersonalDetailScreen( showDeleteDialog = false detailViewModel.deleteElderInfo(eldersInfoResponseDto.elderId) onBack() // 삭제 후 설정 화면으로 이동 - } + }, ) } } diff --git a/app/src/main/java/com/konkuk/medicarecall/ui/feature/settings/screen/ElderInfoScreen.kt b/app/src/main/java/com/konkuk/medicarecall/ui/feature/settings/screen/ElderInfoScreen.kt index 8c616efa..f5b2c1d9 100644 --- a/app/src/main/java/com/konkuk/medicarecall/ui/feature/settings/screen/ElderInfoScreen.kt +++ b/app/src/main/java/com/konkuk/medicarecall/ui/feature/settings/screen/ElderInfoScreen.kt @@ -23,22 +23,18 @@ import androidx.hilt.navigation.compose.hiltViewModel import androidx.lifecycle.Lifecycle import androidx.lifecycle.LifecycleEventObserver import androidx.lifecycle.compose.LocalLifecycleOwner -import androidx.navigation.NavHostController import com.konkuk.medicarecall.R -import com.konkuk.medicarecall.navigation.Route +import com.konkuk.medicarecall.data.dto.response.EldersInfoResponseDto import com.konkuk.medicarecall.ui.feature.settings.component.PersonalInfoCard import com.konkuk.medicarecall.ui.feature.settings.component.SettingsTopAppBar import com.konkuk.medicarecall.ui.feature.settings.viewmodel.EldersInfoViewModel import com.konkuk.medicarecall.ui.theme.MediCareCallTheme -import kotlinx.serialization.json.Json -import java.net.URLEncoder -import java.nio.charset.StandardCharsets @Composable fun PersonalInfoScreen( onBack: () -> Unit = {}, - navController: NavHostController, - personalViewModel: EldersInfoViewModel = hiltViewModel() + navigateToElderDetail: (elderInfo: EldersInfoResponseDto) -> Unit = {}, + personalViewModel: EldersInfoViewModel = hiltViewModel(), ) { val lifecycleOwner = LocalLifecycleOwner.current DisposableEffect(lifecycleOwner) { @@ -65,7 +61,7 @@ fun PersonalInfoScreen( modifier = Modifier .fillMaxSize() .background(MediCareCallTheme.colors.bg) - .statusBarsPadding() + .statusBarsPadding(), ) { SettingsTopAppBar( title = "어르신 개인정보 설정", @@ -74,7 +70,7 @@ fun PersonalInfoScreen( painterResource(id = R.drawable.ic_settings_back), contentDescription = "setting back", modifier = Modifier.clickable { onBack() }, - tint = MediCareCallTheme.colors.black + tint = MediCareCallTheme.colors.black, ) }, ) @@ -83,19 +79,15 @@ fun PersonalInfoScreen( .fillMaxWidth() .padding(start = 20.dp, end = 20.dp) .verticalScroll(rememberScrollState()), - verticalArrangement = Arrangement.spacedBy(12.dp) + verticalArrangement = Arrangement.spacedBy(12.dp), ) { Spacer(modifier = Modifier.height(20.dp)) eldersInfo.forEach { PersonalInfoCard( name = it.name, onClick = { - val json = Json.encodeToString(it) - val encodedJson = URLEncoder.encode(json, StandardCharsets.UTF_8.toString()) - navController.navigate( - "${Route.PersonalDetail.route}/$encodedJson" - ) - } + navigateToElderDetail(it) + }, ) } // PersonalInfoCard("김옥자", onClick = {navController.navigate(Route.PersonalDetail.route)}) diff --git a/app/src/main/java/com/konkuk/medicarecall/ui/feature/settings/screen/HealthInfoScreen.kt b/app/src/main/java/com/konkuk/medicarecall/ui/feature/settings/screen/HealthInfoScreen.kt index 91f1d1cb..e28b3afc 100644 --- a/app/src/main/java/com/konkuk/medicarecall/ui/feature/settings/screen/HealthInfoScreen.kt +++ b/app/src/main/java/com/konkuk/medicarecall/ui/feature/settings/screen/HealthInfoScreen.kt @@ -23,22 +23,18 @@ import androidx.hilt.navigation.compose.hiltViewModel import androidx.lifecycle.Lifecycle import androidx.lifecycle.LifecycleEventObserver import androidx.lifecycle.compose.LocalLifecycleOwner -import androidx.navigation.NavHostController import com.konkuk.medicarecall.R -import com.konkuk.medicarecall.navigation.Route +import com.konkuk.medicarecall.data.dto.response.EldersHealthResponseDto import com.konkuk.medicarecall.ui.feature.settings.component.PersonalInfoCard import com.konkuk.medicarecall.ui.feature.settings.component.SettingsTopAppBar import com.konkuk.medicarecall.ui.feature.settings.viewmodel.EldersHealthViewModel import com.konkuk.medicarecall.ui.theme.MediCareCallTheme -import kotlinx.serialization.json.Json -import java.net.URLEncoder -import java.nio.charset.StandardCharsets @Composable fun HealthInfoScreen( onBack: () -> Unit = {}, - navController: NavHostController, - healthInfoViewModel: EldersHealthViewModel = hiltViewModel() + navigateToHealthDetail: (EldersHealthResponseDto) -> Unit = {}, + healthInfoViewModel: EldersHealthViewModel = hiltViewModel(), ) { val lifecycleOwner = LocalLifecycleOwner.current DisposableEffect(lifecycleOwner) { @@ -65,7 +61,7 @@ fun HealthInfoScreen( modifier = Modifier .fillMaxSize() .background(MediCareCallTheme.colors.bg) - .statusBarsPadding() + .statusBarsPadding(), ) { SettingsTopAppBar( title = "어르신 건강정보 설정", @@ -74,7 +70,7 @@ fun HealthInfoScreen( painterResource(id = R.drawable.ic_settings_back), contentDescription = "setting back", modifier = Modifier.clickable { onBack() }, - tint = MediCareCallTheme.colors.black + tint = MediCareCallTheme.colors.black, ) }, ) @@ -83,22 +79,15 @@ fun HealthInfoScreen( .fillMaxWidth() .padding(start = 20.dp, end = 20.dp) .verticalScroll(rememberScrollState()), - verticalArrangement = Arrangement.spacedBy(12.dp) + verticalArrangement = Arrangement.spacedBy(12.dp), ) { Spacer(modifier = Modifier.height(20.dp)) healthInfo.forEach { PersonalInfoCard( name = it.name, onClick = { - val json = Json.encodeToString(it) - val encodedJson = URLEncoder.encode(json, StandardCharsets.UTF_8.toString()) - navController.navigate( - "${Route.HealthDetail.route}/$encodedJson" - ) { - launchSingleTop = true // 중복된 화면 방지 - restoreState = true // 이전 상태 복원 - } - } + navigateToHealthDetail(it) + }, ) } // PersonalInfoCard("김옥자", onClick = {navController.navigate(Route.HealthDetail.route)}) diff --git a/app/src/main/java/com/konkuk/medicarecall/ui/feature/settings/screen/MyDataSettingScreen.kt b/app/src/main/java/com/konkuk/medicarecall/ui/feature/settings/screen/MyDataSettingScreen.kt index 99e03311..9b42467c 100644 --- a/app/src/main/java/com/konkuk/medicarecall/ui/feature/settings/screen/MyDataSettingScreen.kt +++ b/app/src/main/java/com/konkuk/medicarecall/ui/feature/settings/screen/MyDataSettingScreen.kt @@ -36,10 +36,9 @@ import androidx.hilt.navigation.compose.hiltViewModel import androidx.lifecycle.Lifecycle import androidx.lifecycle.LifecycleEventObserver import androidx.lifecycle.compose.LocalLifecycleOwner -import androidx.navigation.NavHostController import com.konkuk.medicarecall.MainActivity import com.konkuk.medicarecall.R -import com.konkuk.medicarecall.navigation.Route +import com.konkuk.medicarecall.data.dto.response.MyInfoResponseDto import com.konkuk.medicarecall.ui.feature.settings.component.LogoutConfirmDialog import com.konkuk.medicarecall.ui.feature.settings.component.SettingInfoItem import com.konkuk.medicarecall.ui.feature.settings.component.SettingsTopAppBar @@ -47,14 +46,13 @@ import com.konkuk.medicarecall.ui.feature.settings.viewmodel.MyDataViewModel import com.konkuk.medicarecall.ui.theme.MediCareCallTheme import com.konkuk.medicarecall.ui.theme.figmaShadow import com.konkuk.medicarecall.ui.type.GenderType -import kotlinx.serialization.json.Json -import java.net.URLEncoder @Composable fun MyDataSettingScreen( onBack: () -> Unit, - navController: NavHostController, modifier: Modifier = Modifier, + navigateToUserInfoSetting: (myInfo: MyInfoResponseDto) -> Unit = {}, + navigateToLoginAfterLogout: () -> Unit = {}, myDataViewModel: MyDataViewModel = hiltViewModel(), ) { val myDataInfo = myDataViewModel.myDataInfo @@ -130,11 +128,8 @@ fun MyDataSettingScreen( color = MediCareCallTheme.colors.active, modifier = modifier.clickable( onClick = { - val json = Json.encodeToString(myDataInfo) - val encodedJson = - URLEncoder.encode(json, Charsets.UTF_8.toString()) // 네비게이션을 통해 MyDetail 화면으로 이동 - navController.navigate("${Route.MyDetail.route}/$encodedJson") + navigateToUserInfoSetting(myDataInfo) }, ), ) @@ -193,12 +188,6 @@ fun MyDataSettingScreen( onSuccess = { Log.d("MyDataSettingScreen", "Logout successful") // 로그아웃 성공 후 동작 -// navController.navigate("login") { -// popUpTo("main") { inclusive = true } -// launchSingleTop = true -// restoreState = true -// } - // TODO 추후 삭제 showLogoutDialog = false val intent = Intent(context, MainActivity::class.java).apply { addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK) diff --git a/app/src/main/java/com/konkuk/medicarecall/ui/feature/settings/screen/SettingSubscribeScreen.kt b/app/src/main/java/com/konkuk/medicarecall/ui/feature/settings/screen/SettingSubscribeScreen.kt index 86032147..ecada4ad 100644 --- a/app/src/main/java/com/konkuk/medicarecall/ui/feature/settings/screen/SettingSubscribeScreen.kt +++ b/app/src/main/java/com/konkuk/medicarecall/ui/feature/settings/screen/SettingSubscribeScreen.kt @@ -21,22 +21,19 @@ import androidx.compose.ui.graphics.Color import androidx.compose.ui.res.painterResource import androidx.compose.ui.unit.dp import androidx.hilt.navigation.compose.hiltViewModel -import androidx.navigation.NavHostController import com.konkuk.medicarecall.R +import com.konkuk.medicarecall.data.dto.response.EldersSubscriptionResponseDto import com.konkuk.medicarecall.ui.feature.settings.component.SettingsTopAppBar import com.konkuk.medicarecall.ui.feature.settings.component.SubscribeCard import com.konkuk.medicarecall.ui.feature.settings.viewmodel.SubscribeViewModel import com.konkuk.medicarecall.ui.theme.MediCareCallTheme -import kotlinx.serialization.json.Json -import java.net.URLEncoder -import java.nio.charset.StandardCharsets @Composable fun SettingSubscribeScreen( - modifier: Modifier = Modifier, onBack: () -> Unit, - navController: NavHostController, - viewModel: SubscribeViewModel = hiltViewModel() + modifier: Modifier = Modifier, + navigateToSubscribeDetail: (subscription: EldersSubscriptionResponseDto) -> Unit = {}, + viewModel: SubscribeViewModel = hiltViewModel(), ) { val eldersInfo = viewModel.subscriptions @@ -46,7 +43,7 @@ fun SettingSubscribeScreen( modifier = modifier .fillMaxSize() .background(MediCareCallTheme.colors.bg) - .statusBarsPadding() + .statusBarsPadding(), ) { SettingsTopAppBar( modifier = modifier, @@ -58,25 +55,23 @@ fun SettingSubscribeScreen( modifier = modifier .size(24.dp) .clickable { onBack() }, - tint = Color.Black + tint = Color.Black, ) - } + }, ) Column( modifier = modifier .fillMaxWidth() .padding(horizontal = 20.dp) .verticalScroll(rememberScrollState()), - verticalArrangement = Arrangement.spacedBy(12.dp) + verticalArrangement = Arrangement.spacedBy(12.dp), ) { Spacer(modifier = modifier.height(20.dp)) eldersInfo.forEach { SubscribeCard( elderInfo = it, onClick = { - val json = Json.encodeToString(it) - val encodedJson = URLEncoder.encode(json, StandardCharsets.UTF_8.toString()) - navController.navigate("subscribe_detail/$encodedJson") + navigateToSubscribeDetail(it) }, ) } diff --git a/app/src/main/java/com/konkuk/medicarecall/ui/feature/settings/screen/SettingsScreen.kt b/app/src/main/java/com/konkuk/medicarecall/ui/feature/settings/screen/SettingsScreen.kt index 68e0a695..c596f891 100644 --- a/app/src/main/java/com/konkuk/medicarecall/ui/feature/settings/screen/SettingsScreen.kt +++ b/app/src/main/java/com/konkuk/medicarecall/ui/feature/settings/screen/SettingsScreen.kt @@ -30,26 +30,23 @@ import androidx.hilt.navigation.compose.hiltViewModel import androidx.lifecycle.Lifecycle import androidx.lifecycle.LifecycleEventObserver import androidx.lifecycle.compose.LocalLifecycleOwner -import androidx.navigation.NavHostController import com.konkuk.medicarecall.R -import com.konkuk.medicarecall.navigation.Route +import com.konkuk.medicarecall.data.dto.response.MyInfoResponseDto import com.konkuk.medicarecall.ui.feature.settings.component.SettingsTopAppBar import com.konkuk.medicarecall.ui.feature.settings.viewmodel.MyDataViewModel import com.konkuk.medicarecall.ui.theme.MediCareCallTheme import com.konkuk.medicarecall.ui.theme.figmaShadow -import kotlinx.serialization.json.Json -import java.net.URLEncoder @Composable fun SettingsScreen( - onNavigateToMyDataSetting: () -> Unit = {}, - onNavigateToAnnouncement: () -> Unit = {}, - onNavigateToCenter: () -> Unit = {}, - onNavigateToSubscribe: () -> Unit = {}, - onNavigateToPersonalInfo: () -> Unit = {}, - onNavigateToHealthInfo: () -> Unit = {}, - navController: NavHostController, - myDataViewModel: MyDataViewModel = hiltViewModel() + navigateToUserInfo: () -> Unit = {}, + navigateToNotice: () -> Unit = {}, + navigateToCenter: () -> Unit = {}, + navigateToSubscribe: () -> Unit = {}, + navigateToElderPersonalInfo: () -> Unit = {}, + navigateToElderHealthInfo: () -> Unit = {}, + navigateToNotificationSetting: (myInfo: MyInfoResponseDto) -> Unit = {}, + myDataViewModel: MyDataViewModel = hiltViewModel(), ) { val lifecycleOwner = LocalLifecycleOwner.current DisposableEffect(lifecycleOwner) { @@ -66,48 +63,48 @@ fun SettingsScreen( modifier = Modifier .fillMaxSize() .background(MediCareCallTheme.colors.bg) - .statusBarsPadding() + .statusBarsPadding(), ) { SettingsTopAppBar(title = "설정") // 상단 TopAppBar, Column( modifier = Modifier .fillMaxSize() .padding(horizontal = 20.dp) - .verticalScroll(rememberScrollState()) + .verticalScroll(rememberScrollState()), ) { Spacer(modifier = Modifier.height(20.dp)) // 프로필 Row( modifier = Modifier .fillMaxWidth() - .clickable { onNavigateToMyDataSetting() } + .clickable { navigateToUserInfo() } .padding(10.dp), - verticalAlignment = Alignment.CenterVertically + verticalAlignment = Alignment.CenterVertically, ) { Image( painter = painterResource(id = R.drawable.img_setting_profile), contentDescription = "settings profile image", - modifier = Modifier.size(80.dp) + modifier = Modifier.size(80.dp), ) Spacer(modifier = Modifier.width(14.dp)) Text( text = myInfo?.name ?: "이름이 등록되지 않았습니다.", style = MediCareCallTheme.typography.SB_18, - color = MediCareCallTheme.colors.black + color = MediCareCallTheme.colors.black, - ) // 나중에 값 받아와서 이름 출력되도록 수정 필요 + ) // 나중에 값 받아와서 이름 출력되도록 수정 필요 Spacer(modifier = Modifier.width(5.dp)) Text( text = "님", style = MediCareCallTheme.typography.R_18, - color = MediCareCallTheme.colors.black + color = MediCareCallTheme.colors.black, ) Spacer(modifier = Modifier.weight(1f)) Icon( painter = painterResource(id = R.drawable.ic_arrow_big), contentDescription = "화살표 아이콘", modifier = Modifier.size(28.dp), - tint = MediCareCallTheme.colors.gray2 + tint = MediCareCallTheme.colors.gray2, ) } @@ -120,24 +117,24 @@ fun SettingsScreen( .fillMaxWidth() .figmaShadow( group = MediCareCallTheme.shadow.shadow03, - cornerRadius = 14.dp + cornerRadius = 14.dp, ) .clip(RoundedCornerShape(14.dp)) .background(MediCareCallTheme.colors.white) .padding(20.dp), - horizontalArrangement = Arrangement.SpaceBetween + horizontalArrangement = Arrangement.SpaceBetween, ) { Column( horizontalAlignment = Alignment.CenterHorizontally, modifier = Modifier - .clickable { onNavigateToAnnouncement() } - .weight(2f) + .clickable { navigateToNotice() } + .weight(2f), ) { Icon( painter = painterResource(id = R.drawable.ic_announcement), contentDescription = "공지사항 아이콘", modifier = Modifier.size(32.dp), - tint = MediCareCallTheme.colors.main + tint = MediCareCallTheme.colors.main, ) Spacer(modifier = Modifier.height(6.dp)) Text( @@ -149,15 +146,15 @@ fun SettingsScreen( Column( modifier = Modifier - .clickable { onNavigateToCenter() } + .clickable { navigateToCenter() } .weight(2f), - horizontalAlignment = Alignment.CenterHorizontally + horizontalAlignment = Alignment.CenterHorizontally, ) { Icon( painter = painterResource(id = R.drawable.ic_service_center), contentDescription = "고객센터 아이콘", modifier = Modifier.size(32.dp), - tint = MediCareCallTheme.colors.main + tint = MediCareCallTheme.colors.main, ) Spacer(modifier = Modifier.height(6.dp)) Text( @@ -169,15 +166,15 @@ fun SettingsScreen( Column( modifier = Modifier - .clickable { onNavigateToSubscribe() } + .clickable { navigateToSubscribe() } .weight(2f), - horizontalAlignment = Alignment.CenterHorizontally + horizontalAlignment = Alignment.CenterHorizontally, ) { Icon( painter = painterResource(id = R.drawable.ic_subscription_management), contentDescription = "구독관리 아이콘", modifier = Modifier.size(32.dp), - tint = MediCareCallTheme.colors.main + tint = MediCareCallTheme.colors.main, ) Spacer(modifier = Modifier.height(6.dp)) Text( @@ -191,13 +188,13 @@ fun SettingsScreen( modifier = Modifier .clickable {} .weight(2f), // 결제내역 클릭 시 동작 추가 - horizontalAlignment = Alignment.CenterHorizontally + horizontalAlignment = Alignment.CenterHorizontally, ) { Icon( painter = painterResource(id = R.drawable.ic_payment_detail), contentDescription = "결제내역 아이콘", modifier = Modifier.size(32.dp), - tint = MediCareCallTheme.colors.main + tint = MediCareCallTheme.colors.main, ) Spacer(modifier = Modifier.height(6.dp)) Text( @@ -213,7 +210,7 @@ fun SettingsScreen( .fillMaxWidth() .figmaShadow( group = MediCareCallTheme.shadow.shadow03, - cornerRadius = 14.dp + cornerRadius = 14.dp, ) .clip(RoundedCornerShape(14.dp)) .background(MediCareCallTheme.colors.white) @@ -224,18 +221,18 @@ fun SettingsScreen( horizontalArrangement = Arrangement.SpaceBetween, modifier = Modifier .fillMaxWidth() - .clickable(onClick = { onNavigateToPersonalInfo() }) + .clickable(onClick = { navigateToElderPersonalInfo() }), ) { Text( text = "어르신 개인정보 설정", style = MediCareCallTheme.typography.R_16, - color = MediCareCallTheme.colors.gray8 + color = MediCareCallTheme.colors.gray8, ) Icon( painter = painterResource(id = R.drawable.ic_arrow_right), contentDescription = "화살표 아이콘", modifier = Modifier.size(24.dp), - tint = MediCareCallTheme.colors.gray2 + tint = MediCareCallTheme.colors.gray2, ) } @@ -243,18 +240,18 @@ fun SettingsScreen( horizontalArrangement = Arrangement.SpaceBetween, modifier = Modifier .fillMaxWidth() - .clickable(onClick = { onNavigateToHealthInfo() }), + .clickable(onClick = { navigateToElderHealthInfo() }), ) { Text( text = "어르신 건강정보 설정", style = MediCareCallTheme.typography.R_16, - color = MediCareCallTheme.colors.gray8 + color = MediCareCallTheme.colors.gray8, ) Icon( painter = painterResource(id = R.drawable.ic_arrow_right), contentDescription = "화살표 아이콘", modifier = Modifier.size(24.dp), - tint = MediCareCallTheme.colors.gray2 + tint = MediCareCallTheme.colors.gray2, ) } @@ -267,13 +264,13 @@ fun SettingsScreen( Text( text = "케어콜 스케줄 설정", style = MediCareCallTheme.typography.R_16, - color = MediCareCallTheme.colors.gray8 + color = MediCareCallTheme.colors.gray8, ) Icon( painter = painterResource(id = R.drawable.ic_arrow_right), contentDescription = "화살표 아이콘", modifier = Modifier.size(24.dp), - tint = MediCareCallTheme.colors.gray2 + tint = MediCareCallTheme.colors.gray2, ) } @@ -282,24 +279,19 @@ fun SettingsScreen( modifier = Modifier .fillMaxWidth() .clickable { - val json = Json.encodeToString(myInfo) - val encodedJson = - URLEncoder.encode(json, Charsets.UTF_8.toString()) - navController.navigate( - "${Route.SettingAlarm.route}/$encodedJson" - ) - } + navigateToNotificationSetting(myInfo) + }, ) { Text( text = "푸시 알림 설정", style = MediCareCallTheme.typography.R_16, - color = MediCareCallTheme.colors.gray8 + color = MediCareCallTheme.colors.gray8, ) Icon( painter = painterResource(id = R.drawable.ic_arrow_right), contentDescription = "화살표 아이콘", modifier = Modifier.size(24.dp), - tint = MediCareCallTheme.colors.gray2 + tint = MediCareCallTheme.colors.gray2, ) } } @@ -308,4 +300,3 @@ fun SettingsScreen( } } } - diff --git a/app/src/main/java/com/konkuk/medicarecall/ui/feature/settings/viewmodel/MyDataViewModel.kt b/app/src/main/java/com/konkuk/medicarecall/ui/feature/settings/viewmodel/MyDataViewModel.kt index e75b0941..5624d1e0 100644 --- a/app/src/main/java/com/konkuk/medicarecall/ui/feature/settings/viewmodel/MyDataViewModel.kt +++ b/app/src/main/java/com/konkuk/medicarecall/ui/feature/settings/viewmodel/MyDataViewModel.kt @@ -17,7 +17,7 @@ class MyDataViewModel @Inject constructor( private val userRepository: UserRepository ) : ViewModel() { fun refresh() = getUserData() - var myDataInfo by mutableStateOf(null) + var myDataInfo by mutableStateOf(MyInfoResponseDto()) private set init { @@ -41,7 +41,6 @@ class MyDataViewModel @Inject constructor( } .onFailure { Log.e("MyDataViewModel", "사용자 정보 불러오기 실패: ${it.message}", it) - myDataInfo = null it.printStackTrace() Log.e("MyDataViewModel", "사용자 정보 로딩 실패: ${it.message}", it) } diff --git a/app/src/main/java/com/konkuk/medicarecall/ui/feature/splash/screen/SplashScreen.kt b/app/src/main/java/com/konkuk/medicarecall/ui/feature/splash/screen/SplashScreen.kt index 4658b747..45431dd3 100644 --- a/app/src/main/java/com/konkuk/medicarecall/ui/feature/splash/screen/SplashScreen.kt +++ b/app/src/main/java/com/konkuk/medicarecall/ui/feature/splash/screen/SplashScreen.kt @@ -16,15 +16,20 @@ import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.painterResource import androidx.hilt.navigation.compose.hiltViewModel -import androidx.navigation.NavController import com.konkuk.medicarecall.R -import com.konkuk.medicarecall.navigation.Route import com.konkuk.medicarecall.ui.feature.splash.viewmodel.SplashViewModel import com.konkuk.medicarecall.ui.model.NavigationDestination import com.konkuk.medicarecall.ui.theme.MediCareCallTheme @Composable -fun SplashScreen(navController: NavController) { +fun SplashScreen( + navigateToLogin: () -> Unit = {}, + navigateToPhone: () -> Unit = {}, + navigateToRegisterElder: () -> Unit = {}, + navigateToCareCallSetting: () -> Unit = {}, + navigateToPurchase: () -> Unit = {}, + navigateToHome: () -> Unit = {}, +) { val viewModel: SplashViewModel = hiltViewModel() @@ -32,27 +37,14 @@ fun SplashScreen(navController: NavController) { LaunchedEffect(navigationDestination) { navigationDestination?.let { destination -> - val route = when (destination) { - is NavigationDestination.GoToLogin -> Route.LoginStart.route - is NavigationDestination.GoToRegisterElder -> Route.LoginElderInfoScreen.route - is NavigationDestination.GoToTimeSetting -> Route.SetCall.route - is NavigationDestination.GoToPayment -> Route.Payment.route - is NavigationDestination.GoToHome -> Route.Home.route - - } - navController.navigate(Route.LoginStart.route) { - popUpTo(Route.AppSplash.route) { inclusive = true } - launchSingleTop = true + navigateToLogin() + when (destination) { + is NavigationDestination.GoToLogin -> navigateToPhone() + is NavigationDestination.GoToRegisterElder -> navigateToRegisterElder() + is NavigationDestination.GoToTimeSetting -> navigateToCareCallSetting() + is NavigationDestination.GoToPayment -> navigateToPurchase() + is NavigationDestination.GoToHome -> navigateToHome() } - navController.navigate(route) { - if (route == Route.Home.route) { - popUpTo(Route.LoginStart.route) { - inclusive = true - } - } - launchSingleTop = true - } - } } @@ -85,7 +77,7 @@ fun SplashScreen(navController: NavController) { painterResource(R.drawable.bg_splash_new), "Medicare Call 스플래시", Modifier.fillMaxSize(), - contentScale = ContentScale.FillBounds + contentScale = ContentScale.FillBounds, ) } } diff --git a/app/src/main/java/com/konkuk/medicarecall/ui/feature/statistics/navigation/StatisticsNavigation.kt b/app/src/main/java/com/konkuk/medicarecall/ui/feature/statistics/navigation/StatisticsNavigation.kt new file mode 100644 index 00000000..6a5dd7db --- /dev/null +++ b/app/src/main/java/com/konkuk/medicarecall/ui/feature/statistics/navigation/StatisticsNavigation.kt @@ -0,0 +1,30 @@ +package com.konkuk.medicarecall.ui.feature.statistics.navigation + +import androidx.compose.runtime.Composable +import androidx.navigation.NavBackStackEntry +import androidx.navigation.NavController +import androidx.navigation.NavGraphBuilder +import androidx.navigation.NavHostController +import androidx.navigation.NavOptions +import androidx.navigation.compose.composable +import com.konkuk.medicarecall.ui.feature.home.viewmodel.HomeViewModel +import com.konkuk.medicarecall.ui.feature.statistics.screen.StatisticsScreen +import com.konkuk.medicarecall.ui.navigation.MainTabRoute + +fun NavController.navigateToStatistics(navOptions: NavOptions) { + navigate(MainTabRoute.WeeklyStatistics, navOptions) +} + +fun NavGraphBuilder.statisticsNavGraph( + navController: NavHostController, // 나중에 바꿔야함. + getBackStackHomeViewModel: @Composable (NavBackStackEntry) -> HomeViewModel, +) { + composable { backStackEntry -> + val homeViewModel: HomeViewModel = getBackStackHomeViewModel(backStackEntry) + + StatisticsScreen( + navController = navController, + homeViewModel = homeViewModel, + ) + } +} diff --git a/app/src/main/java/com/konkuk/medicarecall/ui/feature/statistics/screen/StatisticsScreen.kt b/app/src/main/java/com/konkuk/medicarecall/ui/feature/statistics/screen/StatisticsScreen.kt index 43d658f9..85d06e35 100644 --- a/app/src/main/java/com/konkuk/medicarecall/ui/feature/statistics/screen/StatisticsScreen.kt +++ b/app/src/main/java/com/konkuk/medicarecall/ui/feature/statistics/screen/StatisticsScreen.kt @@ -32,7 +32,6 @@ import androidx.lifecycle.Lifecycle import androidx.lifecycle.LifecycleEventObserver import androidx.lifecycle.compose.LocalLifecycleOwner import androidx.navigation.NavHostController -import androidx.navigation.compose.rememberNavController import com.konkuk.medicarecall.ui.common.component.NameBar import com.konkuk.medicarecall.ui.common.component.NameDropdown import com.konkuk.medicarecall.ui.feature.home.viewmodel.HomeViewModel @@ -59,6 +58,7 @@ import java.time.LocalDate fun StatisticsScreen( modifier: Modifier = Modifier, navController: NavHostController, + navigateToAlarm: () -> Unit = {}, homeViewModel: HomeViewModel, statisticsViewModel: StatisticsViewModel = hiltViewModel(), ) { @@ -124,7 +124,6 @@ fun StatisticsScreen( modifier = modifier, uiState = uiState, elderNameList = elderNameList, - navController = navController, currentWeek = currentWeek, isLatestWeek = isLatestWeek, isEarliestWeek = isEarliestWeek, @@ -141,7 +140,7 @@ fun StatisticsScreenLayout( modifier: Modifier = Modifier, uiState: StatisticsUiState, elderNameList: List, - navController: NavHostController, + navigateToAlarm: () -> Unit = {}, currentWeek: Pair, isLatestWeek: Boolean, isEarliestWeek: Boolean, @@ -160,7 +159,7 @@ fun StatisticsScreenLayout( NameBar( name = currentElderName, modifier = Modifier.statusBarsPadding(), - navController = navController, + navigateToAlarm = navigateToAlarm, onDropdownClick = { dropdownOpened.value = !dropdownOpened.value }, notificationCount = 4,//TODO: 실제 알림 개수 데이터 연동 필요 ) @@ -320,7 +319,6 @@ fun PreviewStatisticsScreen_Recorded() { StatisticsScreenLayout( uiState = dummyUiState, elderNameList = listOf("김옥자", "박막례"), - navController = rememberNavController(), currentWeek = Pair(LocalDate.now(), LocalDate.now().plusDays(6)), isLatestWeek = false, isEarliestWeek = false, @@ -344,7 +342,6 @@ fun PreviewStatisticsScreen_Unrecorded() { summary = WeeklySummaryUiState.EMPTY.copy(elderName = "김옥자"), ), elderNameList = listOf("김옥자", "박막례"), - navController = rememberNavController(), currentWeek = Pair(LocalDate.now(), LocalDate.now().plusDays(6)), isLatestWeek = true, isEarliestWeek = true, diff --git a/app/src/main/java/com/konkuk/medicarecall/ui/navigation/MainNavigator.kt b/app/src/main/java/com/konkuk/medicarecall/ui/navigation/MainNavigator.kt new file mode 100644 index 00000000..0c0baf2f --- /dev/null +++ b/app/src/main/java/com/konkuk/medicarecall/ui/navigation/MainNavigator.kt @@ -0,0 +1,240 @@ +package com.konkuk.medicarecall.ui.navigation + +import androidx.compose.runtime.Composable +import androidx.compose.runtime.remember +import androidx.navigation.NavDestination +import androidx.navigation.NavDestination.Companion.hasRoute +import androidx.navigation.NavGraph.Companion.findStartDestination +import androidx.navigation.NavHostController +import androidx.navigation.compose.currentBackStackEntryAsState +import androidx.navigation.compose.rememberNavController +import androidx.navigation.navOptions +import com.konkuk.medicarecall.data.dto.response.EldersHealthResponseDto +import com.konkuk.medicarecall.data.dto.response.EldersInfoResponseDto +import com.konkuk.medicarecall.data.dto.response.EldersSubscriptionResponseDto +import com.konkuk.medicarecall.data.dto.response.MyInfoResponseDto +import com.konkuk.medicarecall.data.dto.response.NoticesResponseDto +import com.konkuk.medicarecall.ui.feature.alarm.navigation.navigateToAlarm +import com.konkuk.medicarecall.ui.feature.home.navigation.navigateToHome +import com.konkuk.medicarecall.ui.feature.homedetail.navigation.navigateToGlucoseDetail +import com.konkuk.medicarecall.ui.feature.homedetail.navigation.navigateToHealthAnalysisDetail +import com.konkuk.medicarecall.ui.feature.homedetail.navigation.navigateToMealDetail +import com.konkuk.medicarecall.ui.feature.homedetail.navigation.navigateToMedicationDetail +import com.konkuk.medicarecall.ui.feature.homedetail.navigation.navigateToMentalAnalysisDetail +import com.konkuk.medicarecall.ui.feature.homedetail.navigation.navigateToSleepDetail +import com.konkuk.medicarecall.ui.feature.login.navigation.navigateToLoginCareCallSetting +import com.konkuk.medicarecall.ui.feature.login.navigation.navigateToLoginFinish +import com.konkuk.medicarecall.ui.feature.login.navigation.navigateToLoginNaverPayView +import com.konkuk.medicarecall.ui.feature.login.navigation.navigateToLoginPhone +import com.konkuk.medicarecall.ui.feature.login.navigation.navigateToLoginPurchase +import com.konkuk.medicarecall.ui.feature.login.navigation.navigateToLoginRegisterElder +import com.konkuk.medicarecall.ui.feature.login.navigation.navigateToLoginRegisterElderHealth +import com.konkuk.medicarecall.ui.feature.login.navigation.navigateToLoginRegisterUserInfo +import com.konkuk.medicarecall.ui.feature.login.navigation.navigateToLoginStart +import com.konkuk.medicarecall.ui.feature.login.navigation.navigateToLoginVerification +import com.konkuk.medicarecall.ui.feature.settings.navigation.navigateToElderHealthDetail +import com.konkuk.medicarecall.ui.feature.settings.navigation.navigateToElderHealthInfo +import com.konkuk.medicarecall.ui.feature.settings.navigation.navigateToElderPersonalDetail +import com.konkuk.medicarecall.ui.feature.settings.navigation.navigateToElderPersonalInfo +import com.konkuk.medicarecall.ui.feature.settings.navigation.navigateToNotice +import com.konkuk.medicarecall.ui.feature.settings.navigation.navigateToNoticeDetail +import com.konkuk.medicarecall.ui.feature.settings.navigation.navigateToNotificationSetting +import com.konkuk.medicarecall.ui.feature.settings.navigation.navigateToServiceCenter +import com.konkuk.medicarecall.ui.feature.settings.navigation.navigateToSettings +import com.konkuk.medicarecall.ui.feature.settings.navigation.navigateToSubscribeDetail +import com.konkuk.medicarecall.ui.feature.settings.navigation.navigateToSubscribeInfo +import com.konkuk.medicarecall.ui.feature.settings.navigation.navigateToUserInfo +import com.konkuk.medicarecall.ui.feature.settings.navigation.navigateToUserInfoSetting +import com.konkuk.medicarecall.ui.feature.statistics.navigation.navigateToStatistics +import com.konkuk.medicarecall.ui.navigation.component.MainTab + +class MainNavigator( + val navController: NavHostController, +) { + private val currentDestination: NavDestination? + @Composable get() = navController + .currentBackStackEntryAsState().value?.destination + val startDestination = Route.Splash + + val currentTab: MainTab? + @Composable get() = MainTab.find { tab -> + currentDestination?.hasRoute(tab::class) == true + } + + // 메인 탭 이동 함수 + fun navigateToMainTab(tab: MainTab) { + val navOptions = navOptions { + popUpTo(navController.graph.findStartDestination().id) { + saveState = true + } + launchSingleTop = true + restoreState = true + } + + when (tab) { + MainTab.HOME -> navController.navigateToHome(navOptions) + MainTab.WEEKLY_STATISTICS -> navController.navigateToStatistics(navOptions) + MainTab.SETTINGS -> navController.navigateToSettings(navOptions) + } + } + + // 뒤로 가기 함수 + fun popBackStack() { + navController.popBackStack() + } + + /* 로그인 */ + fun navigateToLoginStart() { + navController.navigateToLoginStart() + } + + fun navigateToLoginPhone() { + navController.navigateToLoginPhone() + } + + fun navigateToLoginVerification() { + navController.navigateToLoginVerification() + } + + fun navigateToLoginRegisterUserInfo() { + navController.navigateToLoginRegisterUserInfo( + navOptions { + popUpTo(Route.LoginVerification) { inclusive = true } + } + ) + } + + fun navigateToLoginRegisterElder() { + navController.navigateToLoginRegisterElder() + } + + fun navigateToLoginRegisterElderHealth() { + navController.navigateToLoginRegisterElderHealth() + } + + fun navigateToLoginCareCallSetting() { + navController.navigateToLoginCareCallSetting( + navOptions { + popUpTo(Route.LoginRegisterElder) { + inclusive = true + } + } + ) + } + + fun navigateToLoginPurchase() { + navController.navigateToLoginPurchase() + } + + fun navigateToLoginNaverPayView() { + navController.navigateToLoginNaverPayView() + } + + fun navigateToLoginFinish() { + navController.navigateToLoginFinish() + } + + /* 홈 화면 */ + fun navigateToHome() { + this.navigateToMainTab(MainTab.HOME) + } + + fun navigateToMealDetail() { + navController.navigateToMealDetail() + } + + fun navigateToMedicationDetail() { + navController.navigateToMedicationDetail() + } + + fun navigateToSleepDetail() { + navController.navigateToSleepDetail() + } + + fun navigateToHealthAnalysisDetail() { + navController.navigateToHealthAnalysisDetail() + } + + fun navigateToMentalAnalysisDetail() { + navController.navigateToMentalAnalysisDetail() + } + + fun navigateToGlucoseDetail() { + navController.navigateToGlucoseDetail() + } + + /* 설정 화면 */ + fun navigateToElderPersonalInfo() { + navController.navigateToElderPersonalInfo() + } + + fun navigateToElderPersonalDetail(info: EldersInfoResponseDto) { + navController.navigateToElderPersonalDetail(info) + } + + fun navigateToHealthInfo() { + navController.navigateToElderHealthInfo() + } + + fun navigateToHealthDetail(health: EldersHealthResponseDto) { // EldersHealthResponseDto + navController.navigateToElderHealthDetail(health) + } + + fun navigateToNotificationSetting(myInfo: MyInfoResponseDto) { + navController.navigateToNotificationSetting(myInfo) + } + + fun navigateToSubscribeInfo() { + navController.navigateToSubscribeInfo() + } + + fun navigateToSubscribeDetail(subscription: EldersSubscriptionResponseDto) { // EldersSubscriptionResponseDto + navController.navigateToSubscribeDetail(subscription) + } + + fun navigateToNotice() { + navController.navigateToNotice() + } + + fun navigateToNoticeDetail(notice: NoticesResponseDto) { + navController.navigateToNoticeDetail(notice) + } + + fun navigateToServiceCenter() { + navController.navigateToServiceCenter() + } + + fun navigateToUserInfo() { + navController.navigateToUserInfo() + } + + fun navigateToUserInfoSetting(myInfo: MyInfoResponseDto) { + navController.navigateToUserInfoSetting(myInfo) + } + + fun navigateToLoginAfterLogout() { + navController.navigate(Route.LoginStart) { + popUpTo(MainTabRoute.Home) { inclusive = true } + launchSingleTop = true + restoreState = true + } + } + + /* 알림 */ + fun navigateToAlarm() { + navController.navigateToAlarm() + } + + // 현재 화면이 BottomBar를 보여줘야 하는지 여부 + @Composable + fun shouldShowBottomBar() = MainTab.contains { + currentDestination?.hasRoute(it::class) == true + } +} + +@Composable +fun rememberMainNavigator( + navController: NavHostController = rememberNavController(), +): MainNavigator = remember(navController) { + MainNavigator(navController) +} diff --git a/app/src/main/java/com/konkuk/medicarecall/ui/navigation/NavGraph.kt b/app/src/main/java/com/konkuk/medicarecall/ui/navigation/NavGraph.kt new file mode 100644 index 00000000..b435b610 --- /dev/null +++ b/app/src/main/java/com/konkuk/medicarecall/ui/navigation/NavGraph.kt @@ -0,0 +1,525 @@ +package com.konkuk.medicarecall.ui.navigation + +import androidx.compose.animation.EnterTransition +import androidx.compose.animation.ExitTransition +import androidx.compose.runtime.Composable +import androidx.compose.runtime.remember +import androidx.compose.ui.Modifier +import androidx.hilt.navigation.compose.hiltViewModel +import androidx.navigation.NavHostController +import androidx.navigation.compose.NavHost +import androidx.navigation.compose.composable +import androidx.navigation.toRoute +import com.konkuk.medicarecall.data.dto.response.EldersHealthResponseDto +import com.konkuk.medicarecall.data.dto.response.EldersInfoResponseDto +import com.konkuk.medicarecall.data.dto.response.EldersSubscriptionResponseDto +import com.konkuk.medicarecall.data.dto.response.MyInfoResponseDto +import com.konkuk.medicarecall.data.dto.response.NoticesResponseDto +import com.konkuk.medicarecall.ui.feature.alarm.screen.AlarmScreen +import com.konkuk.medicarecall.ui.feature.home.navigation.homeNavGraph +import com.konkuk.medicarecall.ui.feature.home.viewmodel.HomeViewModel +import com.konkuk.medicarecall.ui.feature.homedetail.glucoselevel.screen.GlucoseDetail +import com.konkuk.medicarecall.ui.feature.homedetail.meal.screen.MealDetail +import com.konkuk.medicarecall.ui.feature.homedetail.medicine.screen.MedicineDetail +import com.konkuk.medicarecall.ui.feature.homedetail.sleep.screen.SleepDetail +import com.konkuk.medicarecall.ui.feature.homedetail.statehealth.screen.StateHealthDetail +import com.konkuk.medicarecall.ui.feature.homedetail.statemental.screen.StateMentalDetail +import com.konkuk.medicarecall.ui.feature.login.carecall.screen.CallTimeScreen +import com.konkuk.medicarecall.ui.feature.login.info.screen.LoginMyInfoScreen +import com.konkuk.medicarecall.ui.feature.login.info.screen.LoginPhoneScreen +import com.konkuk.medicarecall.ui.feature.login.info.screen.LoginStartScreen +import com.konkuk.medicarecall.ui.feature.login.info.screen.LoginVerificationScreen +import com.konkuk.medicarecall.ui.feature.login.info.viewmodel.LoginViewModel +import com.konkuk.medicarecall.ui.feature.login.payment.screen.LoginFinishScreen +import com.konkuk.medicarecall.ui.feature.login.payment.screen.NaverPayWebViewScreen +import com.konkuk.medicarecall.ui.feature.login.payment.screen.PaymentScreen +import com.konkuk.medicarecall.ui.feature.login.senior.screen.LoginElderMedInfoScreen +import com.konkuk.medicarecall.ui.feature.login.senior.screen.LoginElderScreen +import com.konkuk.medicarecall.ui.feature.login.senior.viewmodel.LoginElderViewModel +import com.konkuk.medicarecall.ui.feature.settings.screen.AnnouncementDetailScreen +import com.konkuk.medicarecall.ui.feature.settings.screen.AnnouncementScreen +import com.konkuk.medicarecall.ui.feature.settings.screen.HealthDetailScreen +import com.konkuk.medicarecall.ui.feature.settings.screen.HealthInfoScreen +import com.konkuk.medicarecall.ui.feature.settings.screen.MyDataSettingScreen +import com.konkuk.medicarecall.ui.feature.settings.screen.MyDetailScreen +import com.konkuk.medicarecall.ui.feature.settings.screen.PersonalDetailScreen +import com.konkuk.medicarecall.ui.feature.settings.screen.PersonalInfoScreen +import com.konkuk.medicarecall.ui.feature.settings.screen.ServiceCenterScreen +import com.konkuk.medicarecall.ui.feature.settings.screen.SettingAlarmScreen +import com.konkuk.medicarecall.ui.feature.settings.screen.SettingSubscribeScreen +import com.konkuk.medicarecall.ui.feature.settings.screen.SettingsScreen +import com.konkuk.medicarecall.ui.feature.settings.screen.SubscribeDetailScreen +import com.konkuk.medicarecall.ui.feature.splash.screen.SplashScreen +import com.konkuk.medicarecall.ui.feature.statistics.screen.StatisticsScreen +import kotlin.reflect.typeOf + +// ---- 헬퍼: 로그인 성공 후 인증 그래프 제거하고 main으로 --- +fun NavHostController.navigateToMainAfterLogin() { + navigate(MainTabRoute.Home) { + popUpTo(Route.LoginStart) { inclusive = true } + launchSingleTop = true + restoreState = true + } +} + +@Composable +fun NavGraph( + navigator: MainNavigator, + loginViewModel: LoginViewModel, + loginElderViewModel: LoginElderViewModel, + modifier: Modifier = Modifier, +) { + val navController = navigator.navController + + NavHost( + navController = navController, + startDestination = navigator.startDestination, + enterTransition = { EnterTransition.None }, + exitTransition = { ExitTransition.None }, + modifier = modifier, + ) { + composable { + SplashScreen( + navigateToLogin = { + navController.navigate(Route.LoginStart) { + popUpTo(Route.Splash) { inclusive = true } + launchSingleTop = true + } + }, + navigateToPhone = { navController.navigate(Route.LoginPhone) }, + navigateToRegisterElder = { navController.navigate(Route.LoginRegisterElder) }, + navigateToCareCallSetting = { navController.navigate(Route.LoginCareCallSetting) }, + navigateToPurchase = { navController.navigate(Route.LoginPurchase) }, + navigateToHome = { + navController.navigate(MainTabRoute.Home) { + popUpTo(Route.LoginStart) { + inclusive = true + } + } + }, + ) + } + + // 홈 +// composable { backStackEntry -> +// HomeScreen( +// navigateToMealDetail = { navController.navigate(Route.MealDetail) }, +// navigateToMedicationDetail = { navController.navigate(Route.MedicationDetail) }, +// navigateToSleepDetail = { navController.navigate(Route.SleepDetail) }, +// navigateToHealthAnalysisDetail = { navController.navigate(Route.HealthAnalysisDetail) }, +// navigateToMentalAnalysisDetail = { navController.navigate(Route.MentalAnalysisDetail) }, +// navigateToGlucoseDetail = { navController.navigate(Route.GlucoseDetail) }, +// ) +// } + homeNavGraph( + navigateToMealDetail = navigator::navigateToMealDetail, + navigateToMedicationDetail = navigator::navigateToMedicationDetail, + navigateToSleepDetail = navigator::navigateToSleepDetail, + navigateToHealthAnalysisDetail = navigator::navigateToHealthAnalysisDetail, + navigateToMentalAnalysisDetail = navigator::navigateToMentalAnalysisDetail, + navigateToGlucoseDetail = navigator::navigateToGlucoseDetail, + ) + + // 홈 상세 화면_식사 화면 + composable { + MealDetail( + onBack = { navController.popBackStack() }, + ) + } + + + // 홈 상세 화면_복용 화면 + composable { + MedicineDetail( + onBack = { navController.popBackStack() }, + ) + } + + + //홈 상세 화면_수면 화면 + composable { + SleepDetail( + onBack = { navController.popBackStack() }, + ) + } + + + //홈 상세 화면_건강 징후 화면 + composable { + StateHealthDetail( + onBack = { navController.popBackStack() }, + ) + } + + //홈 상세 화면_심리 상태 화면 + composable { + StateMentalDetail( + onBack = { navController.popBackStack() }, + ) + } + + + //홈 상세 화면_혈당 화면 + composable { + GlucoseDetail( + onBack = { navController.popBackStack() }, + ) + } + + + // 통계 + composable { backStackEntry -> + val parentEntry = remember(backStackEntry) { + navController.getBackStackEntry(MainTabRoute.Home) + } + val homeViewModel: HomeViewModel = hiltViewModel(parentEntry) + + StatisticsScreen( + navController = navController, + homeViewModel = homeViewModel, + ) + } + +// statisticsNavGraph( +// navController = navController, +// getBackStackHomeViewModel = { backStackEntry -> +// backStackEntry.sharedViewModel(navController) +// }, +// ) + + // 설정 + composable { + //TopLevelBackHandler(navController) + SettingsScreen( + navigateToUserInfo = { + navController.navigate(Route.UserInfo) + }, + navigateToNotice = { + navController.navigate(Route.Notice) + }, + navigateToCenter = { + navController.navigate(Route.ServiceCenter) + }, + navigateToSubscribe = { + navController.navigate(Route.SubscribeInfo) + }, + navigateToElderPersonalInfo = { + navController.navigate(Route.ElderPersonalInfo) + }, + navigateToElderHealthInfo = { + navController.navigate(Route.ElderHealthInfo) + }, + navigateToNotificationSetting = { myInfo -> + navController.navigate(Route.NotificationSetting(myInfo)) + }, + ) + } + + composable { + PersonalInfoScreen( + onBack = { + navController.popBackStack() + }, + navigateToElderDetail = { elderInfo -> + navController.navigate(Route.ElderPersonalDetail(elderInfo)) + }, + ) + } + + composable( + typeMap = mapOf(typeOf() to EldersInfoResponseDtoType), + ) { navBackstackEntry -> + val elderInfo = navBackstackEntry.toRoute().info + PersonalDetailScreen( + onBack = { navController.popBackStack() }, + eldersInfoResponseDto = elderInfo, + navController = navController, + ) + } + + composable { + HealthInfoScreen( + onBack = { + navController.popBackStack() + }, + navigateToHealthDetail = { healthInfo -> + navController.navigate(Route.ElderHealthDetail(healthInfo)) + }, + ) + } + + composable( + typeMap = mapOf(typeOf() to EldersHealthResponseDtoType), + ) { navBackstackEntry -> + val healthInfo = navBackstackEntry.toRoute().health + HealthDetailScreen( + onBack = { + navController.popBackStack() + }, + healthInfoResponseDto = healthInfo, + ) + } + + composable( + typeMap = mapOf(typeOf() to MyInfoResponseDtoType), + ) { navBackStackEntry -> + val myDataInfo = navBackStackEntry.toRoute().myInfo + SettingAlarmScreen( + myDataInfo = myDataInfo, + onBack = { + navController.popBackStack() + }, + ) + } + + composable { + SettingSubscribeScreen( + onBack = { + navController.popBackStack() + }, + navigateToSubscribeDetail = { subscription -> + navController.navigate(Route.SubscribeDetail(subscription)) + }, + ) + } + + composable( + typeMap = mapOf(typeOf() to EldersSubscriptionResponseDtoType), + ) { navBackStackEntry -> + val elderInfo = navBackStackEntry.toRoute().subscription + SubscribeDetailScreen( + elderInfo = elderInfo, + onBack = { navController.popBackStack() }, + ) + } + + composable { + AnnouncementScreen( + onBack = { + navController.popBackStack() + }, + navigateToNoticeDetail = { notice -> + navController.navigate(Route.NoticeDetail(notice)) + }, + ) + } + + composable( + typeMap = mapOf(typeOf() to NoticesResponseDtoType), + ) { navBackStackEntry -> + val noticeInfo = navBackStackEntry.toRoute().notice + AnnouncementDetailScreen( + noticeInfo = noticeInfo, + onBack = { navController.popBackStack() }, + ) + } + + composable { + ServiceCenterScreen( + onBack = { + navController.popBackStack() + }, + ) + } + + composable { + MyDataSettingScreen( + onBack = { + navController.popBackStack() + }, + navigateToUserInfoSetting = { myInfo -> + navController.navigate(Route.UserInfoSetting(myInfo)) + }, + navigateToLoginAfterLogout = { + navController.navigate(Route.LoginStart) { + popUpTo(MainTabRoute.Home) { inclusive = true } + launchSingleTop = true + restoreState = true + } + }, + ) + } + + composable( + typeMap = mapOf(typeOf() to MyInfoResponseDtoType), + ) { navBackStackEntry -> + val myDataInfo = navBackStackEntry.toRoute().myInfo + MyDetailScreen( + myDataInfo = myDataInfo, + onBack = { + navController.popBackStack() + }, + ) + } + +// settingNavGraph( +// popBackStack = navigator::popBackStack, +// navigateToElderHealthInfo = navigator::navigateToElderPersonalInfo, +// navigateToElderPersonalInfo = navigator::navigateToElderPersonalInfo, +// navigateToElderPersonalDetail = navigator::navigateToElderPersonalDetail, +// navigateToHealthDetail = navigator::navigateToHealthDetail, +// navigateToNotificationSetting = navigator::navigateToNotificationSetting, +// navigateToSubscribeInfo = navigator::navigateToSubscribeInfo, +// navigateToSubscribeDetail = navigator::navigateToSubscribeDetail, +// navigateToNotice = navigator::navigateToNotice, +// navigateToNoticeDetail = navigator::navigateToNoticeDetail, +// navigateToServiceCenter = navigator::navigateToServiceCenter, +// navigateToUserInfo = navigator::navigateToUserInfo, +// navigateToUserInfoSetting = navigator::navigateToUserInfoSetting, +// navigateToLoginAfterLogout = navigator::navigateToLoginAfterLogout +// ) + + composable { + AlarmScreen( + onBack = { + navController.popBackStack() + }, + ) + } + + // 로그인 내비게이션 + composable { + LoginStartScreen( + navigateToPhone = { navController.navigate(Route.LoginPhone) }, + navigateToRegisterElder = { navController.navigate(Route.LoginRegisterElder) }, + navigateToCareCallSetting = { navController.navigate(Route.LoginCareCallSetting) }, + navigateToPurchase = { navController.navigate(Route.LoginPurchase) }, + navigateToHome = { + navController.navigate(MainTabRoute.Home) { + popUpTo(Route.LoginStart) { + inclusive = true + } + } + }, + loginViewModel = loginViewModel, + ) + } + composable { + LoginPhoneScreen( + onBack = { navController.popBackStack() }, + navigateToVerification = { navController.navigate(Route.LoginVerification) }, + loginViewModel = loginViewModel, + ) + } + composable { + LoginVerificationScreen( + onBack = { navController.popBackStack() }, + navigateToUserInfo = { + navController.navigate(Route.LoginRegisterUserInfo) { + popUpTo(Route.LoginVerification) { inclusive = true } + } + }, + navigateToPhone = { navController.navigate(Route.LoginPhone) }, + navigateToRegisterElder = { navController.navigate(Route.LoginRegisterElder) }, + navigateToCareCallSetting = { navController.navigate(Route.LoginCareCallSetting) }, + navigateToPurchase = { navController.navigate(Route.LoginPurchase) }, + navigateToHome = { + navController.navigate(MainTabRoute.Home) { + popUpTo(Route.LoginStart) { + inclusive = true + } + } + }, + loginViewModel = loginViewModel, + ) + } + composable { + LoginMyInfoScreen( + onBack = { navController.popBackStack() }, + navigateToRegisterElder = { + navController.navigate(Route.LoginRegisterElder) + }, + loginViewModel = loginViewModel, + ) + } + composable { + LoginElderScreen( + onBack = { navController.popBackStack() }, + navigateToRegisterElderHealth = { + navController.navigate(Route.LoginRegisterElderHealth) + }, + loginElderViewModel = loginElderViewModel, + ) + } + composable { + LoginElderMedInfoScreen( + onBack = { navController.popBackStack() }, + navigateToCareCallSetting = { + navController.navigate(Route.LoginCareCallSetting) { + popUpTo(Route.LoginRegisterElder) { + inclusive = true + } + } + }, + loginElderViewModel = loginElderViewModel, + ) + } + + composable { + CallTimeScreen( + onBack = { + navController.popBackStack() + }, + navigateToPayment = { + navController.navigate(Route.LoginPurchase) + }, + ) + } + + composable { + PaymentScreen( + onBack = { + navController.popBackStack() + }, + navigateToNaverPay = { + navController.navigate(Route.LoginNaverPayView) + }, + ) + } + + composable { + NaverPayWebViewScreen( + onBack = { + navController.popBackStack() + }, + navigateToFinish = { + navController.navigate(Route.LoginFinish) { + popUpTo(Route.LoginNaverPayView) { inclusive = true } + } + }, + ) + } + + composable { + LoginFinishScreen( + navigateToMain = { + navController.navigateToMainAfterLogin() + }, + ) + } + +// loginNavGraph( +// popBackStack = navigator::popBackStack, +// navigateToHome = navigator::navigateToHome, +// navigateToPhone = navigator::navigateToLoginPhone, +// navigateToVerification = navigator::navigateToLoginVerification, +// navigateTpRegisterUserInfo = navigator::navigateToLoginRegisterUserInfo, +// navigateToRegisterElder = navigator::navigateToLoginRegisterElder, +// navigateToRegisterElderHealth = navigator::navigateToLoginRegisterElderHealth, +// navigateToCareCallSetting = navigator::navigateToLoginCareCallSetting, +// navigateToCareCallSettingWithPopUpTo = navigator::navigateToLoginCareCallSetting, +// navigateToPurchase = navigator::navigateToLoginPurchase, +// navigateToNaverPayView = navigator::navigateToLoginNaverPayView, +// navigateToFinish = navigator::navigateToLoginFinish, +// navigateToMainAfterLogin = navController::navigateToMainAfterLogin, +// getBackStackLoginViewModel = { backStackEntry -> +// backStackEntry +// .sharedViewModel(navController) +// }, +// getBackStackLoginElderViewModel = { backStackEntry -> +// backStackEntry +// .sharedViewModel(navController) +// } +// ) + } +} diff --git a/app/src/main/java/com/konkuk/medicarecall/ui/navigation/NavType.kt b/app/src/main/java/com/konkuk/medicarecall/ui/navigation/NavType.kt new file mode 100644 index 00000000..9de18e29 --- /dev/null +++ b/app/src/main/java/com/konkuk/medicarecall/ui/navigation/NavType.kt @@ -0,0 +1,101 @@ +package com.konkuk.medicarecall.ui.navigation + +import android.os.Bundle +import androidx.navigation.NavType +import com.konkuk.medicarecall.data.dto.response.EldersHealthResponseDto +import com.konkuk.medicarecall.data.dto.response.EldersInfoResponseDto +import com.konkuk.medicarecall.data.dto.response.EldersSubscriptionResponseDto +import com.konkuk.medicarecall.data.dto.response.MyInfoResponseDto +import com.konkuk.medicarecall.data.dto.response.NoticesResponseDto +import kotlinx.serialization.json.Json + +// NavType definitions for complex DTOs +val MyInfoResponseDtoType = object : NavType(isNullableAllowed = false) { + override fun get(bundle: Bundle, key: String): MyInfoResponseDto? { + return bundle.getString(key)?.let { Json.decodeFromString(it) } + } + + override fun parseValue(value: String): MyInfoResponseDto { + return Json.decodeFromString(value) + } + + override fun serializeAsValue(value: MyInfoResponseDto): String { + return Json.encodeToString(value) + } + + override fun put(bundle: Bundle, key: String, value: MyInfoResponseDto) { + bundle.putString(key, Json.encodeToString(value)) + } +} + +val EldersInfoResponseDtoType = object : NavType(isNullableAllowed = false) { + override fun get(bundle: Bundle, key: String): EldersInfoResponseDto? { + return bundle.getString(key)?.let { Json.decodeFromString(it) } + } + + override fun parseValue(value: String): EldersInfoResponseDto { + return Json.decodeFromString(value) + } + + override fun serializeAsValue(value: EldersInfoResponseDto): String { + return Json.encodeToString(value) + } + + override fun put(bundle: Bundle, key: String, value: EldersInfoResponseDto) { + bundle.putString(key, Json.encodeToString(value)) + } +} + +val EldersHealthResponseDtoType = object : NavType(isNullableAllowed = false) { + override fun get(bundle: Bundle, key: String): EldersHealthResponseDto? { + return bundle.getString(key)?.let { Json.decodeFromString(it) } + } + + override fun parseValue(value: String): EldersHealthResponseDto { + return Json.decodeFromString(value) + } + + override fun serializeAsValue(value: EldersHealthResponseDto): String { + return Json.encodeToString(value) + } + + override fun put(bundle: Bundle, key: String, value: EldersHealthResponseDto) { + bundle.putString(key, Json.encodeToString(value)) + } +} + +val EldersSubscriptionResponseDtoType = object : NavType(isNullableAllowed = false) { + override fun get(bundle: Bundle, key: String): EldersSubscriptionResponseDto? { + return bundle.getString(key)?.let { Json.decodeFromString(it) } + } + + override fun parseValue(value: String): EldersSubscriptionResponseDto { + return Json.decodeFromString(value) + } + + override fun serializeAsValue(value: EldersSubscriptionResponseDto): String { + return Json.encodeToString(value) + } + + override fun put(bundle: Bundle, key: String, value: EldersSubscriptionResponseDto) { + bundle.putString(key, Json.encodeToString(value)) + } +} + +val NoticesResponseDtoType = object : NavType(isNullableAllowed = false) { + override fun get(bundle: Bundle, key: String): NoticesResponseDto? { + return bundle.getString(key)?.let { Json.decodeFromString(it) } + } + + override fun parseValue(value: String): NoticesResponseDto { + return Json.decodeFromString(value) + } + + override fun serializeAsValue(value: NoticesResponseDto): String { + return Json.encodeToString(value) + } + + override fun put(bundle: Bundle, key: String, value: NoticesResponseDto) { + bundle.putString(key, Json.encodeToString(value)) + } +} diff --git a/app/src/main/java/com/konkuk/medicarecall/ui/navigation/Route.kt b/app/src/main/java/com/konkuk/medicarecall/ui/navigation/Route.kt new file mode 100644 index 00000000..f116be7e --- /dev/null +++ b/app/src/main/java/com/konkuk/medicarecall/ui/navigation/Route.kt @@ -0,0 +1,117 @@ +package com.konkuk.medicarecall.ui.navigation + +import com.konkuk.medicarecall.data.dto.response.EldersHealthResponseDto +import com.konkuk.medicarecall.data.dto.response.EldersInfoResponseDto +import com.konkuk.medicarecall.data.dto.response.EldersSubscriptionResponseDto +import com.konkuk.medicarecall.data.dto.response.MyInfoResponseDto +import com.konkuk.medicarecall.data.dto.response.NoticesResponseDto +import kotlinx.serialization.Serializable + +sealed interface Route { + + // 로그인 및 온보딩 + @Serializable + data object Splash : Route + + @Serializable + data object LoginStart : Route + + @Serializable + data object LoginPhone : Route + + @Serializable + data object LoginVerification : Route + + @Serializable + data object LoginRegisterUserInfo : Route + + @Serializable + data object LoginRegisterElder : Route + + @Serializable + data object LoginRegisterElderHealth : Route + + @Serializable + data object LoginCareCallSetting : Route + + @Serializable + data object LoginPurchase : Route + + @Serializable + data object LoginNaverPayView : Route + + @Serializable + data object LoginFinish : Route + + // 홈 (하루 요약) + @Serializable + data object MealDetail : Route + + @Serializable + data object MedicationDetail : Route + + @Serializable + data object SleepDetail : Route + + @Serializable + data object HealthAnalysisDetail : Route + + @Serializable + data object MentalAnalysisDetail : Route + + @Serializable + data object GlucoseDetail : Route + + // 설정 + @Serializable + data object ElderPersonalInfo : Route + + @Serializable + data class ElderPersonalDetail(val info: EldersInfoResponseDto) : Route + + + @Serializable + data object ElderHealthInfo : Route + + @Serializable + data class ElderHealthDetail(val health: EldersHealthResponseDto) : Route + + @Serializable + data class NotificationSetting(val myInfo: MyInfoResponseDto) : Route + + @Serializable + data object SubscribeInfo : Route + + @Serializable + data class SubscribeDetail(val subscription: EldersSubscriptionResponseDto) : Route + + @Serializable + data object Notice : Route + + @Serializable + data class NoticeDetail(val notice: NoticesResponseDto) : Route + + @Serializable + data object ServiceCenter : Route + + @Serializable + data object UserInfo : Route + + @Serializable + data class UserInfoSetting(val myInfo: MyInfoResponseDto) : Route + + // 알림 + @Serializable + data object Alarm : Route +} + +sealed interface MainTabRoute : Route { + @Serializable + data object Home : MainTabRoute + + @Serializable + data object WeeklyStatistics : MainTabRoute + + @Serializable + data object Settings : MainTabRoute +} diff --git a/app/src/main/java/com/konkuk/medicarecall/ui/navigation/component/MainBottomBar.kt b/app/src/main/java/com/konkuk/medicarecall/ui/navigation/component/MainBottomBar.kt new file mode 100644 index 00000000..296522f2 --- /dev/null +++ b/app/src/main/java/com/konkuk/medicarecall/ui/navigation/component/MainBottomBar.kt @@ -0,0 +1,132 @@ +package com.konkuk.medicarecall.ui.navigation.component + +import androidx.compose.animation.AnimatedVisibility +import androidx.compose.animation.fadeIn +import androidx.compose.animation.fadeOut +import androidx.compose.animation.slideIn +import androidx.compose.animation.slideOut +import androidx.compose.foundation.border +import androidx.compose.foundation.interaction.MutableInteractionSource +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.RowScope +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxHeight +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.navigationBarsPadding +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.selection.selectable +import androidx.compose.material3.Icon +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +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.graphics.vector.ImageVector +import androidx.compose.ui.res.vectorResource +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.IntOffset +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import com.konkuk.medicarecall.ui.theme.MediCareCallTheme +import com.konkuk.medicarecall.ui.theme.gray1 +import com.konkuk.medicarecall.ui.theme.gray2 +import com.konkuk.medicarecall.ui.theme.main + +@Composable +internal fun MainBottomBar( + visible: Boolean, + tabs: List, + currentTab: MainTab?, + onTabSelected: (MainTab) -> Unit, +) { + AnimatedVisibility( + visible = visible, + enter = fadeIn() + slideIn { IntOffset(0, it.height) }, + exit = fadeOut() + slideOut { IntOffset(0, it.height) }, + ) { + Box( + modifier = Modifier.border( + width = 1.dp, + color = gray1, + ), + ) { + Column { + Row( + modifier = Modifier + .navigationBarsPadding() + .fillMaxWidth() + .padding(horizontal = 14.dp) + .height(60.dp), + verticalAlignment = Alignment.CenterVertically, + ) { + tabs.forEach { tab -> + MainBottomBarItem( + tab = tab, + selected = tab == currentTab, + onClick = { + if (tab != currentTab) { + onTabSelected(tab) + } + }, + ) + } + } + } + } + } +} + +@Composable +private fun RowScope.MainBottomBarItem( + tab: MainTab, + selected: Boolean, + onClick: () -> Unit, +) { + Box( + modifier = Modifier + .weight(1f) + .fillMaxHeight() + .selectable( + selected = selected, + indication = null, + role = null, + interactionSource = remember { MutableInteractionSource() }, + onClick = onClick, + ), + contentAlignment = Alignment.Center, + ) { + Column( + horizontalAlignment = Alignment.CenterHorizontally, + ) { + Icon( + imageVector = if (selected) ImageVector.vectorResource(tab.selectedIcon) + else ImageVector.vectorResource(tab.unselectedIcon), + contentDescription = tab.description, + tint = Color.Unspecified, + ) + Spacer(modifier = Modifier.height(2.dp)) + Text( + text = tab.label, + style = MediCareCallTheme.typography.M_16.copy(fontSize = 11.sp), + color = if (selected) main else gray2, + ) + } + } +} + +@Preview(showBackground = true) +@Composable +private fun MainBottomBarPreview() { + MediCareCallTheme { + MainBottomBar( + visible = true, + tabs = MainTab.entries, + currentTab = MainTab.HOME, + onTabSelected = {}, + ) + } +} diff --git a/app/src/main/java/com/konkuk/medicarecall/ui/navigation/component/MainTab.kt b/app/src/main/java/com/konkuk/medicarecall/ui/navigation/component/MainTab.kt new file mode 100644 index 00000000..c8888bea --- /dev/null +++ b/app/src/main/java/com/konkuk/medicarecall/ui/navigation/component/MainTab.kt @@ -0,0 +1,50 @@ +package com.konkuk.medicarecall.ui.navigation.component + +import androidx.annotation.DrawableRes +import androidx.compose.runtime.Composable +import com.konkuk.medicarecall.R +import com.konkuk.medicarecall.ui.navigation.MainTabRoute +import com.konkuk.medicarecall.ui.navigation.Route + +enum class MainTab( + val label: String, + val route: MainTabRoute, + val description: String, + @DrawableRes val selectedIcon: Int, + @DrawableRes val unselectedIcon: Int, +) { + HOME( + label = "하루 요약", + route = MainTabRoute.Home, + description = "Home Icon", + selectedIcon = R.drawable.ic_home_selected, + unselectedIcon = R.drawable.ic_home_unselected, + ), + WEEKLY_STATISTICS( + label = "주간 통계", + route = MainTabRoute.WeeklyStatistics, + description = "Statistics Icon", + selectedIcon = R.drawable.ic_statistics_selected, + unselectedIcon = R.drawable.ic_statistics_unselected, + ), + SETTINGS( + label = "설정", + route = MainTabRoute.Settings, + description = "Settings Icon", + selectedIcon = R.drawable.ic_settings_selected, + unselectedIcon = R.drawable.ic_settings_unselected, + ), + ; + + companion object { + @Composable + fun find(predicate: @Composable (MainTabRoute) -> Boolean): MainTab? { + return MainTab.entries.find { predicate(it.route) } + } + + @Composable + fun contains(predicate: @Composable (Route) -> Boolean): Boolean { + return MainTab.entries.map { it.route }.any { predicate(it) } + } + } +} diff --git a/app/src/main/res/drawable/ic_home_selected.xml b/app/src/main/res/drawable/ic_home_selected.xml index c19c1df3..88ced2cd 100644 --- a/app/src/main/res/drawable/ic_home_selected.xml +++ b/app/src/main/res/drawable/ic_home_selected.xml @@ -4,6 +4,15 @@ android:viewportWidth="24" android:viewportHeight="24"> + android:pathData="M17.25,16.942C17.051,16.942 16.86,17.021 16.72,17.162C16.579,17.303 16.5,17.494 16.5,17.692V19.313C16.5,19.362 16.48,19.41 16.445,19.445C16.41,19.48 16.362,19.5 16.313,19.5H4.688C4.64,19.5 4.595,19.483 4.56,19.451C4.525,19.419 4.504,19.375 4.5,19.327L4.5,6.577C4.5,6.528 4.52,6.48 4.555,6.445C4.59,6.41 4.638,6.39 4.688,6.39H5.625C5.824,6.39 6.015,6.311 6.155,6.17C6.296,6.03 6.375,5.839 6.375,5.64C6.375,5.441 6.296,5.25 6.155,5.11C6.015,4.969 5.824,4.89 5.625,4.89H4.5C4.102,4.89 3.721,5.048 3.439,5.329C3.158,5.611 3,5.992 3,6.39L3,19.5C3,19.898 3.158,20.279 3.439,20.561C3.721,20.842 4.102,21 4.5,21H16.5C16.898,21 17.279,20.842 17.561,20.561C17.842,20.279 18,19.898 18,19.5V17.692C18,17.494 17.921,17.303 17.78,17.162C17.64,17.021 17.449,16.942 17.25,16.942ZM15.375,6.375H16.313C16.362,6.375 16.41,6.395 16.445,6.43C16.48,6.465 16.5,6.513 16.5,6.563V7.125C16.5,7.324 16.579,7.515 16.72,7.655C16.86,7.796 17.051,7.875 17.25,7.875C17.449,7.875 17.64,7.796 17.78,7.655C17.921,7.515 18,7.324 18,7.125V6.375C18,5.977 17.842,5.596 17.561,5.314C17.279,5.033 16.898,4.875 16.5,4.875H15.375C15.176,4.875 14.985,4.954 14.845,5.095C14.704,5.235 14.625,5.426 14.625,5.625C14.625,5.824 14.704,6.015 14.845,6.155C14.985,6.296 15.176,6.375 15.375,6.375Z" + android:fillColor="#35C156"/> + + + diff --git a/app/src/main/res/drawable/ic_home_unselected.xml b/app/src/main/res/drawable/ic_home_unselected.xml index 59cc938f..9b0877e2 100644 --- a/app/src/main/res/drawable/ic_home_unselected.xml +++ b/app/src/main/res/drawable/ic_home_unselected.xml @@ -4,6 +4,15 @@ android:viewportWidth="24" android:viewportHeight="24"> + android:pathData="M17.25,16.942C17.051,16.942 16.86,17.021 16.72,17.162C16.579,17.303 16.5,17.494 16.5,17.692V19.313C16.5,19.362 16.48,19.41 16.445,19.445C16.41,19.48 16.362,19.5 16.313,19.5H4.688C4.64,19.5 4.595,19.483 4.56,19.451C4.525,19.419 4.504,19.375 4.5,19.327L4.5,6.577C4.5,6.528 4.52,6.48 4.555,6.445C4.59,6.41 4.638,6.39 4.688,6.39H5.625C5.824,6.39 6.015,6.311 6.155,6.17C6.296,6.03 6.375,5.839 6.375,5.64C6.375,5.441 6.296,5.25 6.155,5.11C6.015,4.969 5.824,4.89 5.625,4.89H4.5C4.102,4.89 3.721,5.048 3.439,5.329C3.158,5.611 3,5.992 3,6.39L3,19.5C3,19.898 3.158,20.279 3.439,20.561C3.721,20.842 4.102,21 4.5,21H16.5C16.898,21 17.279,20.842 17.561,20.561C17.842,20.279 18,19.898 18,19.5V17.692C18,17.494 17.921,17.303 17.78,17.162C17.64,17.021 17.449,16.942 17.25,16.942ZM15.375,6.375H16.313C16.362,6.375 16.41,6.395 16.445,6.43C16.48,6.465 16.5,6.513 16.5,6.563V7.125C16.5,7.324 16.579,7.515 16.72,7.655C16.86,7.796 17.051,7.875 17.25,7.875C17.449,7.875 17.64,7.796 17.78,7.655C17.921,7.515 18,7.324 18,7.125V6.375C18,5.977 17.842,5.596 17.561,5.314C17.279,5.033 16.898,4.875 16.5,4.875H15.375C15.176,4.875 14.985,4.954 14.845,5.095C14.704,5.235 14.625,5.426 14.625,5.625C14.625,5.824 14.704,6.015 14.845,6.155C14.985,6.296 15.176,6.375 15.375,6.375Z" + android:fillColor="#D2D2D2"/> + + + diff --git a/app/src/main/res/drawable/ic_settings_selected.xml b/app/src/main/res/drawable/ic_settings_selected.xml index 44645425..c261259c 100644 --- a/app/src/main/res/drawable/ic_settings_selected.xml +++ b/app/src/main/res/drawable/ic_settings_selected.xml @@ -4,6 +4,13 @@ android:viewportWidth="24" android:viewportHeight="24"> + android:pathData="M10.825,3H13.176C13.379,3 13.523,3.059 13.66,3.186C13.82,3.333 13.905,3.494 13.936,3.7L14.159,5.335L14.239,5.921L14.791,6.134C14.954,6.196 15.105,6.27 15.244,6.354C15.412,6.456 15.576,6.566 15.736,6.683L16.191,7.016L16.712,6.797L18.262,6.147L18.271,6.144L18.278,6.141C18.468,6.057 18.619,6.056 18.787,6.116C18.983,6.187 19.126,6.302 19.241,6.489L20.408,8.522L20.414,8.534L20.421,8.545C20.52,8.707 20.542,8.849 20.501,9.025C20.452,9.236 20.351,9.399 20.175,9.543L18.873,10.526L18.402,10.882L18.485,11.467C18.496,11.54 18.5,11.606 18.5,11.663V12.337C18.5,12.343 18.5,12.363 18.489,12.4L18.301,13.06L18.848,13.474L20.154,14.46C20.328,14.603 20.428,14.765 20.477,14.975C20.517,15.151 20.495,15.293 20.396,15.455L20.392,15.462L20.388,15.47L19.196,17.505C19.08,17.696 18.935,17.812 18.736,17.884C18.569,17.944 18.418,17.942 18.229,17.859L18.223,17.857L16.723,17.207L16.199,16.98L15.737,17.316C15.582,17.429 15.418,17.536 15.247,17.636C15.083,17.732 14.922,17.812 14.766,17.877L14.237,18.098L14.159,18.665L13.936,20.304C13.903,20.509 13.818,20.669 13.66,20.815C13.524,20.941 13.38,21 13.176,21H10.825C10.622,21 10.478,20.941 10.342,20.815C10.183,20.669 10.097,20.507 10.064,20.302L9.841,18.665L9.762,18.079L9.209,17.866C9.047,17.804 8.897,17.731 8.759,17.646C8.591,17.544 8.425,17.433 8.263,17.315L7.807,16.985L7.289,17.203L5.738,17.853L5.73,17.856L5.723,17.859C5.533,17.943 5.382,17.944 5.214,17.884C5.065,17.83 4.947,17.751 4.848,17.635L4.755,17.505L3.593,15.477L3.587,15.466L3.58,15.455L3.52,15.336C3.474,15.219 3.47,15.107 3.5,14.975C3.549,14.764 3.649,14.6 3.825,14.456L5.128,13.474L5.599,13.118L5.516,12.533C5.505,12.46 5.5,12.394 5.5,12.337V11.662C5.5,11.606 5.505,11.541 5.516,11.467L5.599,10.882L5.128,10.526L3.825,9.543C3.65,9.399 3.549,9.236 3.5,9.025C3.459,8.849 3.481,8.707 3.58,8.545L3.587,8.534L3.593,8.522L4.755,6.495C4.871,6.304 5.015,6.188 5.214,6.116C5.382,6.056 5.533,6.057 5.723,6.141L5.73,6.144L5.738,6.147L7.289,6.797L7.809,7.015L8.264,6.684C8.419,6.571 8.583,6.464 8.754,6.364C8.918,6.268 9.079,6.188 9.235,6.123L9.764,5.902L9.841,5.335L10.064,3.694C10.097,3.491 10.183,3.332 10.342,3.186C10.445,3.09 10.551,3.033 10.684,3.011L10.825,3Z" + android:strokeWidth="2" + android:fillColor="#35C156" + android:strokeColor="#35C156"/> + diff --git a/app/src/main/res/drawable/ic_settings_unselected.xml b/app/src/main/res/drawable/ic_settings_unselected.xml index 82c63c7b..7c41836d 100644 --- a/app/src/main/res/drawable/ic_settings_unselected.xml +++ b/app/src/main/res/drawable/ic_settings_unselected.xml @@ -4,13 +4,13 @@ android:viewportWidth="24" android:viewportHeight="24"> + android:fillColor="#D2D2D2" + android:strokeColor="#D2D2D2"/> + android:fillColor="#ffffff" + android:strokeColor="#D2D2D2"/> diff --git a/app/src/main/res/drawable/ic_statistics_selected.xml b/app/src/main/res/drawable/ic_statistics_selected.xml index 6d755590..414fcf92 100644 --- a/app/src/main/res/drawable/ic_statistics_selected.xml +++ b/app/src/main/res/drawable/ic_statistics_selected.xml @@ -4,6 +4,6 @@ android:viewportWidth="24" android:viewportHeight="24"> + android:pathData="M21,20.6C21,20.821 20.821,21 20.6,21H3.4C3.179,21 3,20.821 3,20.6V19.4C3,19.179 3.179,19 3.4,19H20.6C20.821,19 21,19.179 21,19.4V20.6ZM8,10.4C8,10.179 7.821,10 7.6,10H4.4C4.179,10 4,10.179 4,10.4V16.6C4,16.821 4.179,17 4.4,17H7.6C7.821,17 8,16.821 8,16.6V10.4ZM14,3.4C14,3.179 13.821,3 13.6,3H10.4C10.179,3 10,3.179 10,3.4V16.6C10,16.821 10.179,17 10.4,17H13.6C13.821,17 14,16.821 14,16.6V3.4ZM20,6.4C20,6.179 19.821,6 19.6,6H16.4C16.179,6 16,6.179 16,6.4V16.6C16,16.821 16.179,17 16.4,17H19.6C19.821,17 20,16.821 20,16.6V6.4Z" + android:fillColor="#35C156"/> diff --git a/app/src/main/res/drawable/ic_statistics_unselected.xml b/app/src/main/res/drawable/ic_statistics_unselected.xml index 2ecb2cc9..2f3452b8 100644 --- a/app/src/main/res/drawable/ic_statistics_unselected.xml +++ b/app/src/main/res/drawable/ic_statistics_unselected.xml @@ -4,6 +4,6 @@ android:viewportWidth="24" android:viewportHeight="24"> + android:pathData="M21,20.6C21,20.821 20.821,21 20.6,21H3.4C3.179,21 3,20.821 3,20.6V19.4C3,19.179 3.179,19 3.4,19H20.6C20.821,19 21,19.179 21,19.4V20.6ZM8,10.4C8,10.179 7.821,10 7.6,10H4.4C4.179,10 4,10.179 4,10.4V16.6C4,16.821 4.179,17 4.4,17H7.6C7.821,17 8,16.821 8,16.6V10.4ZM14,3.4C14,3.179 13.821,3 13.6,3H10.4C10.179,3 10,3.179 10,3.4V16.6C10,16.821 10.179,17 10.4,17H13.6C13.821,17 14,16.821 14,16.6V3.4ZM20,6.4C20,6.179 19.821,6 19.6,6H16.4C16.179,6 16,6.179 16,6.4V16.6C16,16.821 16.179,17 16.4,17H19.6C19.821,17 20,16.821 20,16.6V6.4Z" + android:fillColor="#D2D2D2"/>