From c636749cf32af4648a8ce1cabbd0259dbe09d8c7 Mon Sep 17 00:00:00 2001 From: null Date: Thu, 16 Jul 2026 01:06:40 -0500 Subject: [PATCH] fix(nav): recovery back-stack loop + the Today tab's inconsistent back arrow MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Two navigation fixes, one found the expensive way. 1. Post-recovery BACK bounced users to the Recovery screen, forever. The Home entry that routed to Recovery stayed on the back stack holding needsRecovery=true, and its LaunchedEffect re-fires whenever the entry is returned to — so after a successful recovery, BACK landed on stale-Home, which instantly redirected to Recovery again. It looks EXACTLY like the couple key was lost again (it wasn't; verified: the keyset survives process kill and cold-starts straight to Home). Cost an hour of key-loss ghost hunting live before the process-alive evidence gave it away. onRecovered and the partner-restore onDone now popUpTo(HOME) INCLUSIVE + launchSingleTop — the stale entry is replaced, not buried; BACK from Home exits the app like every other root tab. C-NAV-002 class; the restore variant was worse (it left Home AND Recovery underneath). 2. C-TODAY-BACK-001 (open P3 from the report): the Today tab drew a back arrow the other four tabs don't. DAILY_QUESTION is a bottom-nav tab — the bar IS the navigation. onBack is now nullable-defaulting-null and the nav wiring passes none; LocalQuestionContent already renders the arrow only when non-null. Verified live: no arrow, other tabs unchanged. Suite green, verified on-device (BACK exits; Today clean). --- .../app/closer/core/navigation/AppNavigation.kt | 17 ++++++++++++++--- .../closer/ui/questions/DailyQuestionScreen.kt | 5 ++++- 2 files changed, 18 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/app/closer/core/navigation/AppNavigation.kt b/app/src/main/java/app/closer/core/navigation/AppNavigation.kt index ecb4865c..3ab0525b 100644 --- a/app/src/main/java/app/closer/core/navigation/AppNavigation.kt +++ b/app/src/main/java/app/closer/core/navigation/AppNavigation.kt @@ -268,7 +268,9 @@ fun AppNavigation( route = AppRoute.DAILY_QUESTION, deepLinks = listOf(navDeepLink { uriPattern = "closer://closer.app/daily_question" }) ) { - DailyQuestionScreen(onNavigate = navigateRoute, onBack = navigateBackOrHome) + // No onBack: it's a bottom-nav tab (C-TODAY-BACK-001 — the arrow made Today + // inconsistent with the other four tabs, whose bar IS the navigation). + DailyQuestionScreen(onNavigate = navigateRoute) } composable(route = AppRoute.QUESTION_PACKS) { QuestionPackLibraryScreen(onNavigate = navigateRoute) @@ -409,8 +411,14 @@ fun AppNavigation( composable(route = AppRoute.RECOVERY) { RecoveryScreen( onRecovered = { + // Pop up to and INCLUDING the old HOME, not just Recovery: the Home entry that + // routed here still holds needsRecovery=true, and its LaunchedEffect re-fires + // whenever it's returned to — so BACK from the new Home bounced users straight + // back to Recovery, forever, looking exactly like the key had been lost again + // (C-NAV-002 class; cost an hour of key-loss ghost-hunting live). navController.navigate(AppRoute.HOME) { - popUpTo(AppRoute.RECOVERY) { inclusive = true } + popUpTo(AppRoute.HOME) { inclusive = true } + launchSingleTop = true } }, onPartnerRestore = { navController.navigate(AppRoute.RESTORE_REQUEST) } @@ -421,8 +429,11 @@ fun AppNavigation( composable(route = AppRoute.RESTORE_REQUEST) { RestoreRequestScreen( onDone = { + // Same stale-Home disease as onRecovered above — and worse: popping only this + // screen left BOTH the stale Home and the Recovery entry underneath. navController.navigate(AppRoute.HOME) { - popUpTo(AppRoute.RESTORE_REQUEST) { inclusive = true } + popUpTo(AppRoute.HOME) { inclusive = true } + launchSingleTop = true } }, onBack = navigateBackOrHome diff --git a/app/src/main/java/app/closer/ui/questions/DailyQuestionScreen.kt b/app/src/main/java/app/closer/ui/questions/DailyQuestionScreen.kt index 6215df3f..d154ecf2 100644 --- a/app/src/main/java/app/closer/ui/questions/DailyQuestionScreen.kt +++ b/app/src/main/java/app/closer/ui/questions/DailyQuestionScreen.kt @@ -11,7 +11,10 @@ import app.closer.domain.model.Question @Composable fun DailyQuestionScreen( onNavigate: (String) -> Unit = {}, - onBack: () -> Unit = {}, + // Nullable, defaulting to none: DAILY_QUESTION is a bottom-nav TAB, and the bar is the navigation + // there — the other four tabs draw no back arrow, and LocalQuestionContent only renders one when + // onBack is non-null (C-TODAY-BACK-001). Pass a handler only from non-tab embeddings, if any ever exist. + onBack: (() -> Unit)? = null, viewModel: DailyQuestionViewModel = hiltViewModel() ) { val state by viewModel.uiState.collectAsStateWithLifecycle()