refactor(#619): kill DRAWER_GESTURE_SCREENS — screen-owned drawer gestures via HermesScaffold (#624)

Replaces the fragile DRAWER_GESTURE_SCREENS global set + defensive
LaunchedEffect(snapTo(Closed)) + closeDrawer callback workaround with a
single source of truth: each screen declares its drawer-gesture preference
via HermesScaffold's new drawerGesturesEnabled parameter (default true).

Architecture:
- DrawerGestureController holds the active gesture state. HermesScaffold
  reconciles each screen's preference into it via SideEffect; when a screen
  opts out while the drawer is open, the controller closes the drawer itself.
- LocalDrawerGestureController CompositionLocal flows the controller from
  MainNavigation down to ModalNavigationDrawer, which reads .enabled and
  passes it to Material's gesturesEnabled parameter.
- DisableDrawerGestures() helper for entry screens that don't use
  HermesScaffold (Landing, AuthLogin, PairingCodeEntry).
- All 6 Settings drill-down sub-pages pass drawerGesturesEnabled = false.
- NavigationController.closeDrawer removed; navigateTo no longer needs a
  synchronous drawer-dismiss hook.

Removes: DRAWER_GESTURE_SCREENS set, both LaunchedEffect(gesturesEnabled)
and LaunchedEffect(drawerGesturesEnabled) blocks, drawerGesturesEnabled
local state, closeDrawer callback on NavigationController.

Fixes the recurring drawer-scrim stuck-open bug class (PRs #445, #454, #455).

Verified: ktlintCheck, assembleDebug, testDebugUnitTest all green locally.

Co-authored-by: Hy4ri <hy4ri@users.noreply.github.com>
This commit is contained in:
M57andHy4ri authored and GitHub committed 2026-07-18 14:25:23 +03:00
1 parent 0c2b9679b7
commit 0a0b612d3a
5 files changed
+297 -204

No files matched your search

+19 -11
View File
@@ -77,17 +77,25 @@ Uses `androidx.navigation3` (`NavKey`, `NavBackStack`, `NavDisplay`, `entry<T>`)
deduplication guard. Bypassing it stacks duplicate screen entries that compete for
touch events and become unresponsive.
**⚠ Drawer scrim gets stuck when navigating to a non-gesture screen.** Screens
not in `DRAWER_GESTURE_SCREENS` disable drawer gestures. If the drawer is open
when this happens, the scrim stays visible and blocks touch. Always pair gesture
transitions with a `LaunchedEffect`:
```kotlin
LaunchedEffect(gesturesEnabled) {
if (!gesturesEnabled && drawerState.isOpen) {
drawerState.snapTo(DrawerValue.Closed)
}
}
```
**Drawer gesture state is screen-owned (issue #619).** Each screen declares whether
the modal drawer's swipe gestures should be active while it is visible, via a single
source of truth — no global gesture set, no defensive `LaunchedEffect(snapTo(Closed))`,
no `closeDrawer` callback on `NavigationController`.
- `HermesScaffold(drawerGesturesEnabled = true)` — default; primary screens and
most list screens. The scaffold reconciles this preference into the
`DrawerGestureController` via a `SideEffect`.
- `HermesScaffold(drawerGesturesEnabled = false)` — drill-down sub-pages (e.g.
`SettingsConnectionPage`, `SettingsAppearancePage`, …). The controller closes
the drawer itself if it was open when the screen composed, so the scrim can't
stick around and intercept the next tap.
- `DisableDrawerGestures()` — for entry screens that don't use `HermesScaffold`
(Landing, AuthLogin, PairingCodeEntry).
`ModalNavigationDrawer` in `Navigation.kt` reads `gestureController.enabled`
(provided via `LocalDrawerGestureController`) and passes it to Material's
`gesturesEnabled` parameter. To change a screen's gesture behavior, edit the
screen — not `Navigation.kt`.
### Activity-Scoped ViewModels