fix(dashboard): keep MobileNavBar visible when keyboardOpen stuck true

vpdebug overlay on Android tablet captured the smoking gun: with the
corner-cutoff fixed (viewport now full window), the bar still disappears
because useMobileKeyboard's keyboardOpen state can get stuck at true.

Repro: the Planning column textarea autofocuses on mount; Android Chrome
fires a transient visualViewport.resize during focus that registers as
"keyboard open", but the matching dismiss event never fires once the
keyboard never actually appeared. visualViewport stays at full height
yet useMobileKeyboard's state remains true, and MobileNavBar's render
guard hides the bar forever.

The original guard existed to keep the bar off the iOS soft keyboard.
On Android the keyboard pushes content up so the bar isn't covered
anyway; on iOS the worst case (bar overlapping keyboard) is far less
broken than the bar permanently vanishing on every Android tablet
session. Drop the keyboardOpen check from the render guard.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
gsxdsm
2026-05-22 11:02:14 -07:00
parent d2e31dc107
commit 4bd9711e87

View File

@@ -245,9 +245,18 @@ export function MobileNavBar({
}; };
}, []); }, []);
if (mode !== "mobile" || modalOpen || keyboardOpen) { // We previously hid the bar when keyboardOpen was true to avoid sitting
// over the iOS soft keyboard. On Android Chrome, useMobileKeyboard can
// get stuck at keyboardOpen=true after a focus-triggered visualViewport
// resize whose matching dismiss event never fires — leaving the nav bar
// permanently invisible (FN-Android-tablet repro). Render unconditionally
// when in mobile mode; on Android the keyboard pushes content up so the
// bar isn't covered, and on iOS the worst case (bar overlapping keyboard)
// is far less broken than the bar vanishing.
if (mode !== "mobile" || modalOpen) {
return null; return null;
} }
void keyboardOpen;
const planningHandler = activePlanningSessionCount > 0 && onResumePlanning ? onResumePlanning : onOpenPlanning; const planningHandler = activePlanningSessionCount > 0 && onResumePlanning ? onResumePlanning : onOpenPlanning;