11package com.lanpet.core.navigation
22
3+ import android.annotation.SuppressLint
4+ import android.app.Activity
5+ import android.os.Bundle
6+ import androidx.activity.compose.BackHandler
37import androidx.compose.animation.AnimatedVisibility
48import androidx.compose.animation.core.FastOutSlowInEasing
59import androidx.compose.animation.core.tween
@@ -9,22 +13,29 @@ import androidx.compose.foundation.layout.Box
913import androidx.compose.foundation.layout.Column
1014import androidx.compose.foundation.layout.fillMaxSize
1115import androidx.compose.runtime.Composable
16+ import androidx.compose.runtime.DisposableEffect
1217import androidx.compose.runtime.LaunchedEffect
13- import androidx.compose.runtime.collectAsState
1418import androidx.compose.runtime.getValue
1519import androidx.compose.runtime.mutableStateOf
1620import androidx.compose.runtime.remember
1721import androidx.compose.runtime.saveable.rememberSaveable
1822import androidx.compose.runtime.setValue
19- import androidx.compose.runtime.snapshotFlow
2023import androidx.compose.ui.Alignment
2124import androidx.compose.ui.Modifier
25+ import androidx.compose.ui.platform.LocalContext
26+ import androidx.lifecycle.compose.collectAsStateWithLifecycle
27+ import androidx.navigation.NavController
28+ import androidx.navigation.NavDestination
29+ import androidx.navigation.NavGraph.Companion.findStartDestination
30+ import androidx.navigation.NavHostController
31+ import androidx.navigation.NavOptions
2232import androidx.navigation.compose.NavHost
2333import androidx.navigation.compose.composable
2434import androidx.navigation.compose.currentBackStackEntryAsState
25- import androidx.navigation.compose.navigation
2635import androidx.navigation.compose.rememberNavController
2736import androidx.navigation.navOptions
37+ import androidx.navigation.navigation
38+ import com.lanpet.core.auth.BuildConfig
2839import com.lanpet.core.auth.LocalAuthManager
2940import com.lanpet.core.common.widget.BottomNavItem
3041import com.lanpet.core.common.widget.LanPetBottomNavBar
@@ -70,16 +81,26 @@ import com.lanpet.profile.navigation.profileNavGraph
7081import com.lanpet.wiki.navigation.Wiki
7182import com.lanpet.wiki.navigation.navigateToWikiBaseRoute
7283import com.lanpet.wiki.navigation.wikiNavGraph
73- import kotlinx.coroutines.flow.distinctUntilChanged
74- import kotlinx.coroutines.flow.drop
7584import timber.log.Timber
7685
7786@Composable
78- fun AppNavigation (modifier : Modifier = Modifier ) {
79- val navController = rememberNavController()
87+ fun AppNavigation (
88+ modifier : Modifier = Modifier ,
89+ navController : NavHostController = rememberNavController(),
90+ ) {
91+ val context = LocalContext .current
8092 val authManager = LocalAuthManager .current
8193
82- val authState = authManager.authState.collectAsState()
94+ BackHandler {
95+ authManager.finish()
96+ (context as Activity ).finishAffinity()
97+ }
98+
99+ if (BuildConfig .BUILD_TYPE == " debug" ) {
100+ DestinationLoggerHandler (navController)
101+ }
102+
103+ val authState = authManager.authState.collectAsStateWithLifecycle()
83104
84105 // Handling navigation by AuthState
85106 rememberNavigationHandler(navController, authState.value)
@@ -91,7 +112,7 @@ fun AppNavigation(modifier: Modifier = Modifier) {
91112 }
92113
93114 var navItem by rememberSaveable {
94- mutableStateOf( BottomNavItem . Wiki )
115+ mutableStateOf< BottomNavItem ?>( null )
95116 }
96117
97118 Box {
@@ -135,7 +156,11 @@ fun AppNavigation(modifier: Modifier = Modifier) {
135156 onNavigateToHumanAge = { navController.navigateToProfileCreateHumanAge() },
136157 onNavigateToDone = { navController.navigateToProfileCreateDone() },
137158 onNavigateToPreferPet = { navController.navigateToProfileCreatePreferPet() },
138- onNavigateToMain = { navController.navigateToMainScreen() },
159+ onNavigateToMain = {
160+ navController.navigateToMainScreen(
161+ idRes = navController.graph.findStartDestination().id,
162+ )
163+ },
139164 navController = navController,
140165 )
141166
@@ -251,19 +276,41 @@ fun AppNavigation(modifier: Modifier = Modifier) {
251276 ),
252277 ),
253278 ) {
254- LanPetBottomNavBar (
255- selectedBottomNavItem = navItem,
256- bottomNavItemList =
257- listOf (
258- BottomNavItem .Wiki ,
259- BottomNavItem .Free ,
260- BottomNavItem .MyPage ,
261- ),
262- onItemSelect = { item ->
263- Timber .d(" selected bottom nav item: $item " )
264- navItem = item
265- },
266- )
279+ if (navItem != null ) {
280+ LanPetBottomNavBar (
281+ selectedBottomNavItem = navItem!! ,
282+ bottomNavItemList =
283+ listOf (
284+ BottomNavItem .Wiki ,
285+ BottomNavItem .Free ,
286+ BottomNavItem .MyPage ,
287+ ),
288+ onItemSelect = { item ->
289+ Timber .d(" selected bottom nav item: $item " )
290+ navItem = item
291+
292+ when (item) {
293+ BottomNavItem .Wiki -> {
294+ navController.navigateToWikiBaseRoute(
295+ topLevelDestinationNavOptions(),
296+ )
297+ }
298+
299+ BottomNavItem .Free -> {
300+ navController.navigateToFreeBoardBaseRoute(
301+ topLevelDestinationNavOptions(),
302+ )
303+ }
304+
305+ BottomNavItem .MyPage -> {
306+ navController.navigateToMyProfileBaseRoute(
307+ topLevelDestinationNavOptions(),
308+ )
309+ }
310+ }
311+ },
312+ )
313+ }
267314 }
268315 }
269316
@@ -286,43 +333,41 @@ fun AppNavigation(modifier: Modifier = Modifier) {
286333 true
287334 }
288335
289- toString(),
290- -> true
291-
292336 else -> false
293337 }
294338 }
339+ }
295340
296- val topLevelDestinationNavOptions =
297- navOptions {
298- popUpTo(MainNavigationRoute (BottomNavItem .Wiki )) {
299- saveState = true
300- }
301-
302- launchSingleTop = true
303- restoreState = true
341+ private fun topLevelDestinationNavOptions (): NavOptions =
342+ navOptions {
343+ popUpTo(MainNavigationRoute ) {
344+ saveState = true
345+ inclusive = true
304346 }
305347
306- // BottomNav의 item이 변경되면 해당 item에 맞는 화면으로 이동
307- LaunchedEffect (Unit ) {
308- snapshotFlow { navItem }
309- .distinctUntilChanged()
310- .drop(1 ) // 초기 값은 스킵
311- .collect { newValue ->
312- // value가 변경될 때만 실행되는 로직
313- when (newValue) {
314- BottomNavItem .Wiki -> {
315- navController.navigateToWikiBaseRoute(topLevelDestinationNavOptions)
316- }
348+ launchSingleTop = true
349+ restoreState = true
350+ }
317351
318- BottomNavItem .Free -> {
319- navController.navigateToFreeBoardBaseRoute(topLevelDestinationNavOptions)
320- }
352+ @Composable
353+ private fun DestinationLoggerHandler (navController : NavHostController ) {
354+ @SuppressLint(" RestrictedApi" )
355+ val destinationChangedListener: (NavController , NavDestination , Bundle ? ) -> Unit =
356+ { controller, _, _ ->
357+ val stack =
358+ controller.currentBackStack.value
321359
322- BottomNavItem .MyPage -> {
323- navController.navigateToMyProfileBaseRoute(topLevelDestinationNavOptions)
324- }
325- }
326- }
360+ // 각 항목을 "->"로 구분하여 보기 좋게 출력
361+ val stackLog = stack.joinToString(" \n " ) { " $${it.destination.id} " }
362+ Timber .i(" previousBackStackEntry: ${controller.previousBackStackEntry} \n " )
363+ Timber .i(" currentBackStackEntry: ${controller.currentBackStackEntry} \n " )
364+ Timber .d(" Navigation Stack:\n ====================\n $stackLog \n ====================" )
365+ }
366+
367+ DisposableEffect (Unit ) {
368+ navController.addOnDestinationChangedListener(destinationChangedListener)
369+ onDispose {
370+ navController.removeOnDestinationChangedListener(destinationChangedListener)
371+ }
327372 }
328373}
0 commit comments