4f2c5bf3279b
fix: redirect locked vault destinations (v2.7.14)
- time
- 2026-07-28 21:24 UTC · 2 months ago
- machine
- koh2
- agent
- gpt-5
- session
- locked settings navigation
- parent
- 47e77d4ba508
↓ peach-android-4f2c5bf3.tar.gzatlas at this save
files changed3 files
app/build.gradle+2 −2app/src/main/java/com/peachpasswords/ui/PeachAppNavHost.kt+21 −12app/src/test/java/com/peachpasswords/ui/NavigationFlowTest.kt+23 −0app/build.gradle+2 −2
@@ -16,8 +16,8 @@1616 applicationId "com.peachpasswords"1717 minSdk 261818 targetSdk 3519- versionCode 24014020- versionName "2.7.13"19+ versionCode 24014120+ versionName "2.7.14"2121 2222 testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"2323 vectorDrawables {app/src/main/java/com/peachpasswords/ui/PeachAppNavHost.kt+21 −12
@@ -20,6 +20,7 @@2020 import androidx.navigation.NavType2121 import androidx.navigation.compose.NavHost2222 import androidx.navigation.compose.composable23+import androidx.navigation.compose.currentBackStackEntryAsState2324 import androidx.navigation.compose.rememberNavController2425 import androidx.navigation.navArgument2526 import com.peachpasswords.autofill.AutofillNavRepository@@ -65,6 +66,8 @@6566 // post-unlock screen lingers behind it. Guarded so it never fires on the lock6667 // or onboarding screens (where the vault is legitimately locked).6768 val unlocked = if (isUnlocked == null) true else isUnlocked.collectAsStateWithLifecycle().value69+ val currentBackStackEntry by navController.currentBackStackEntryAsState()70+ val currentRoute = currentBackStackEntry?.destination?.route6871 6972 // Update surfaces, both driven from outside the nav graph: a notification tap7073 // (openRequested) and the once-per-version post-update changelog.@@ -107,18 +110,11 @@107110 }108111 109112 if (isUnlocked != null) {110- LaunchedEffect(unlocked) {111- if (!unlocked) {112- val current = navController.currentDestination?.route113- if (current != null &&114- current != PeachNav.Lock.route &&115- current != PeachNav.Onboarding.route &&116- current != PeachNav.AdoptVault.route117- ) {118- navController.navigate(PeachNav.Lock.route) {119- popUpTo(0) { inclusive = true }120- launchSingleTop = true121- }113+ LaunchedEffect(unlocked, currentRoute) {114+ if (!unlocked && requiresUnlockedVault(currentRoute)) {115+ navController.navigate(PeachNav.Lock.route) {116+ popUpTo(0) { inclusive = true }117+ launchSingleTop = true122118 }123119 }124120 }@@ -356,6 +352,19 @@356352 }357353 CopyFeedbackOverlay()358354 }355+}356+357+internal fun requiresUnlockedVault(route: String?): Boolean {358+ if (route == null) return false359+ return route !in setOf(360+ PeachNav.Onboarding.route,361+ PeachNav.Lock.route,362+ PeachNav.AdoptVault.route,363+ PeachNav.Import.route,364+ PeachNav.CodexRestore.route,365+ PeachNav.RecoveryEntry.route,366+ PeachNav.Update.route,367+ )359368 }360369 361370 internal fun isVaultSheetTransition(fromRoute: String?, toRoute: String?, reverse: Boolean = false): Boolean {app/src/test/java/com/peachpasswords/ui/NavigationFlowTest.kt+23 −0
@@ -57,6 +57,29 @@5757 assertEquals("settings", PeachNav.Settings.route)5858 }5959 60+ @Test fun locked_navigation_allows_only_pre_vault_surfaces() {61+ listOf(62+ PeachNav.Onboarding.route,63+ PeachNav.Lock.route,64+ PeachNav.AdoptVault.route,65+ PeachNav.Import.route,66+ PeachNav.CodexRestore.route,67+ PeachNav.RecoveryEntry.route,68+ PeachNav.Update.route,69+ ).forEach { route ->70+ assertEquals("$route should stay available while locked", false, requiresUnlockedVault(route))71+ }72+73+ listOf(74+ PeachNav.Settings.route,75+ PeachNav.VaultList.route,76+ PeachNav.EntryDetail.ROUTE,77+ PeachNav.Export.route,78+ ).forEach { route ->79+ assertEquals("$route should redirect to Lock", true, requiresUnlockedVault(route))80+ }81+ }82+6083 // ── First-launch gate ─────────────────────────────────────────────────────6184 6285 @Test fun first_launch_shows_onboarding() {