The Settings tab pins itself to whichever settings page you were on, and no settings page has a back arrow #61

Closed
opened 2026-08-20 21:22:36 -05:00 by null · 0 comments
Owner

Reported by Kaspa: 'on the settings page, if you go to app lock you can't unnavigate out of it.' Two causes, both real.

What is true now. app/src/main/kotlin/dev/privacyllc/period/navigation/PeriodApp.kt:105-111 navigates tabs with popUpTo(findStartDestination){ saveState = true } + restoreState = true. From [today, settings, settings/lock], tapping the Settings tab saves the popped [settings, settings/lock] and immediately restores it — you land back on App lock, and the saved stack survives visiting other tabs, so the Settings tab is pinned to App lock for the rest of the process. The KDoc at :142-146 asserts the opposite. The selected-tab check at :102 (hierarchy.any { it.route == destination.route }) also means NO tab renders as selected while on any settings child, so the user taps an unselected Settings tab and lands back where she was.

Secondly, a repo-wide grep for TopAppBar, navigationIcon, ArrowBack, BackHandler, popBackStack and navigateUp over app/, core/ and domain/ returns ZERO hits. No screen has a back arrow; navController is never passed to a screen; sub-pages rely entirely on the system gesture. LockSettingsScreen.kt:106-117 is a full-bleed Surface with a plain Text("App lock") that looks like a top bar and is not one, and after setting a PIN the only button on screen — 'Done' (:185-188) — clears a message and navigates nowhere.

What it costs. The scariest screen in the app is the one that appears to trap you. It applies equally to settings/notifications and settings/export; App lock is simply where it gets reported.

What to do. One shared SettingsSubpage(title, onBack, content) composable in app/.../feature/common/ (beside Confidence.kt, the existing shared-composable precedent) wrapping an M3 TopAppBar with a back arrow; all three sub-pages take onNavigateBack and use it. Make Settings a nested graph so its children sit in the tab's hierarchy, pop to the graph root when the already-selected tab is re-tapped, and pass saveState = !inSettings when leaving so the Settings stack is never saved and never restored.

Traps. A nested graph ALONE does not fix it — the tab tap still saves and restores the graph's stack. Dropping restoreState alone is also not enough: the stack is still saved on leaving, so nav-scoped ViewModels are retained per exit and the tab still reads as unselected on children. Today, Calendar and Insights must keep their current save/restore behaviour. Screens with internal steps need mode-aware back (separate issue). ExportHost() is currently emitted as a sibling BEFORE the Scaffold inside a Box, so it is drawn underneath an opaque Scaffold container and has almost certainly never been visible — moving it to Scaffold(topBar = ...) both reveals it and keeps it clear of the new bars.

Verify: SettingsSubpageTest (Back icon displayed with contentDescription, tapping it invokes onBack once) and a device check — Calendar → Settings lands on the settings tree, and re-tapping Settings from App lock returns to the tree.

Reported by Kaspa: 'on the settings page, if you go to app lock you can't unnavigate out of it.' Two causes, both real. **What is true now.** `app/src/main/kotlin/dev/privacyllc/period/navigation/PeriodApp.kt:105-111` navigates tabs with `popUpTo(findStartDestination){ saveState = true }` + `restoreState = true`. From `[today, settings, settings/lock]`, tapping the Settings tab saves the popped `[settings, settings/lock]` and immediately restores it — you land back on App lock, and the saved stack survives visiting other tabs, so the Settings tab is pinned to App lock for the rest of the process. The KDoc at `:142-146` asserts the opposite. The selected-tab check at `:102` (`hierarchy.any { it.route == destination.route }`) also means NO tab renders as selected while on any settings child, so the user taps an unselected Settings tab and lands back where she was. Secondly, a repo-wide grep for `TopAppBar`, `navigationIcon`, `ArrowBack`, `BackHandler`, `popBackStack` and `navigateUp` over `app/`, `core/` and `domain/` returns ZERO hits. No screen has a back arrow; `navController` is never passed to a screen; sub-pages rely entirely on the system gesture. `LockSettingsScreen.kt:106-117` is a full-bleed Surface with a plain `Text("App lock")` that looks like a top bar and is not one, and after setting a PIN the only button on screen — 'Done' (`:185-188`) — clears a message and navigates nowhere. **What it costs.** The scariest screen in the app is the one that appears to trap you. It applies equally to `settings/notifications` and `settings/export`; App lock is simply where it gets reported. **What to do.** One shared `SettingsSubpage(title, onBack, content)` composable in `app/.../feature/common/` (beside `Confidence.kt`, the existing shared-composable precedent) wrapping an M3 `TopAppBar` with a back arrow; all three sub-pages take `onNavigateBack` and use it. Make Settings a nested graph so its children sit in the tab's hierarchy, pop to the graph root when the already-selected tab is re-tapped, and pass `saveState = !inSettings` when leaving so the Settings stack is never saved and never restored. **Traps.** A nested graph ALONE does not fix it — the tab tap still saves and restores the graph's stack. Dropping `restoreState` alone is also not enough: the stack is still saved on leaving, so nav-scoped ViewModels are retained per exit and the tab still reads as unselected on children. Today, Calendar and Insights must keep their current save/restore behaviour. Screens with internal steps need mode-aware back (separate issue). `ExportHost()` is currently emitted as a sibling BEFORE the Scaffold inside a Box, so it is drawn underneath an opaque Scaffold container and has almost certainly never been visible — moving it to `Scaffold(topBar = ...)` both reveals it and keeps it clear of the new bars. Verify: `SettingsSubpageTest` (Back icon displayed with contentDescription, tapping it invokes onBack once) and a device check — Calendar → Settings lands on the settings tree, and re-tapping Settings from App lock returns to the tree.
null added this to the Batch 11 — Settings and the App Lock milestone 2026-08-20 21:22:36 -05:00
null added the
P1
label 2026-08-20 21:22:36 -05:00
null closed this issue 2026-08-20 21:50:00 -05:00
Sign in to join this conversation.
No Label
P0
P1
P2
release-blocker
No project
No Assignees
1 Participants
Notifications
Due Date
The due date is invalid or out of range. Please use the format 'yyyy-mm-dd'.

No due date set.

Dependencies

No dependencies set.

Reference: null/Privacy-Period-Tracker#61
No description provided.