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
3 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 −0
app/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 {

view file

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 {

view file

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() {

view file