fix(dashboard): keep Android keyboard open in main chat; disable kanban pinch-zoom
Two Android-specific fixes: 1. Keyboard dismissing in main chat. App.tsx derives `mobileKeyboardOpen` from useMobileKeyboard and uses it to gate the `project-content--with-mobile-nav` / `--with-footer` className assignment plus MobileNavBar rendering. When the soft keyboard opened on Android, those classes were removed and the nav unmounted, shrinking padding-bottom by ~80px in a single render. Android Chrome treats the resulting jump of the focused chat input as the focus target moving and instantly dismisses the keyboard. With interactive-widget=resizes-content set on Android, the layout viewport itself shrinks with the keyboard, so the hide-nav-on-keyboard pattern was redundant on Android (and harmful). The whole pattern is now gated to iOS via isIOS(). iOS path is unchanged. 2. Pinch-zoom on kanban. Android Chrome ignores user-scalable=no for a11y, and kanban's overflow-x:auto columns combined with the inflated ICB produce a broken visual when the user zooms out. Adds touch-action: pan-x pan-y to html,body inside the mobile media query (keeps scroll panning, blocks pinch-zoom). Chat and MissionManager were unaffected before because they don't expose a wide horizontal scrollable region. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
11
.changeset/android-keyboard-and-zoom.md
Normal file
11
.changeset/android-keyboard-and-zoom.md
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
---
|
||||||
|
"@fusion/dashboard": patch
|
||||||
|
---
|
||||||
|
|
||||||
|
fix(dashboard): keep Android keyboard open in main chat; disable kanban pinch-zoom
|
||||||
|
|
||||||
|
Two Android-specific fixes:
|
||||||
|
|
||||||
|
1. **Keyboard dismissing in main chat.** `mobileKeyboardOpen` in `App.tsx` (derived from `useMobileKeyboard`) gates `project-content--with-mobile-nav` / `--with-footer` className assignment and MobileNavBar rendering. When the soft keyboard opened, those classes were removed and the nav unmounted, shrinking padding-bottom by ~80px in a single render. Android Chrome treats the resulting jump of the focused chat input as the focus target moving and instantly dismisses the keyboard. With `interactive-widget=resizes-content` set on Android, the layout viewport itself shrinks with the keyboard, so the hide-nav-on-keyboard behavior was redundant on Android (and harmful). The whole pattern is now gated to iOS via `isIOS()`. iOS path is unchanged.
|
||||||
|
|
||||||
|
2. **Pinch-zoom on kanban.** Android Chrome ignores `user-scalable=no` for accessibility, and the kanban board's `overflow-x: auto` columns combined with the inflated ICB produce a broken visual when the user zooms out. Adds `touch-action: pan-x pan-y` to `html, body` inside the mobile media query, which keeps scroll panning but disables pinch-zoom (Chat and MissionManager were unaffected because they don't expose a wide horizontal scrollable region).
|
||||||
@@ -57,7 +57,7 @@ import { useDeepLink } from "./hooks/useDeepLink";
|
|||||||
import { useFavorites } from "./hooks/useFavorites";
|
import { useFavorites } from "./hooks/useFavorites";
|
||||||
import { useAuthOnboarding } from "./hooks/useAuthOnboarding";
|
import { useAuthOnboarding } from "./hooks/useAuthOnboarding";
|
||||||
import { useMobileKeyboard } from "./hooks/useMobileKeyboard";
|
import { useMobileKeyboard } from "./hooks/useMobileKeyboard";
|
||||||
import { useMobileScrollLock } from "./hooks/useMobileScrollLock";
|
import { isIOS, useMobileScrollLock } from "./hooks/useMobileScrollLock";
|
||||||
import { useSetupReadiness } from "./hooks/useSetupReadiness";
|
import { useSetupReadiness } from "./hooks/useSetupReadiness";
|
||||||
import { useUpdateCheck } from "./hooks/useUpdateCheck";
|
import { useUpdateCheck } from "./hooks/useUpdateCheck";
|
||||||
import { useViewState, type TaskView } from "./hooks/useViewState";
|
import { useViewState, type TaskView } from "./hooks/useViewState";
|
||||||
@@ -463,7 +463,15 @@ function AppInner() {
|
|||||||
// affecting the underlying dashboard layout — the modal handles its own
|
// affecting the underlying dashboard layout — the modal handles its own
|
||||||
// viewport. Without this guard, modal keyboard state leaks into the app-level
|
// viewport. Without this guard, modal keyboard state leaks into the app-level
|
||||||
// layout, causing stale bottom-padding offsets after the keyboard closes.
|
// layout, causing stale bottom-padding offsets after the keyboard closes.
|
||||||
const mobileKeyboardOpen = isMobile && keyboardOpen && !modalManager.anyModalOpen;
|
//
|
||||||
|
// Android-gated: with `interactive-widget=resizes-content` the layout
|
||||||
|
// viewport itself shrinks with the soft keyboard, so we DON'T want to
|
||||||
|
// also hide the nav bar / strip its padding — that produces a layout
|
||||||
|
// jump while the focused input is settling, which Android Chrome treats
|
||||||
|
// as the focus target moving and dismisses the keyboard immediately.
|
||||||
|
// iOS doesn't shrink the layout viewport, so the iOS path keeps the
|
||||||
|
// hide-nav-on-keyboard behavior intact.
|
||||||
|
const mobileKeyboardOpen = isMobile && keyboardOpen && !modalManager.anyModalOpen && isIOS();
|
||||||
// App-level scroll lock for inline editing (TaskCard inline edit, etc.):
|
// App-level scroll lock for inline editing (TaskCard inline edit, etc.):
|
||||||
// when the keyboard is up outside of any modal, pin the body so iOS can't
|
// when the keyboard is up outside of any modal, pin the body so iOS can't
|
||||||
// shift the document or visualViewport, and so the dashboard snaps back
|
// shift the document or visualViewport, and so the dashboard snaps back
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ function isMobileDevice(): boolean {
|
|||||||
* Chrome shrinks the layout viewport with the keyboard, so no drift
|
* Chrome shrinks the layout viewport with the keyboard, so no drift
|
||||||
* compensation is needed there.
|
* compensation is needed there.
|
||||||
*/
|
*/
|
||||||
function isIOS(): boolean {
|
export function isIOS(): boolean {
|
||||||
if (typeof window === "undefined") return false;
|
if (typeof window === "undefined") return false;
|
||||||
const ua = navigator.userAgent || "";
|
const ua = navigator.userAgent || "";
|
||||||
// iPad on iPadOS 13+ reports as MacIntel + touch — handle that too.
|
// iPad on iPadOS 13+ reports as MacIntel + touch — handle that too.
|
||||||
|
|||||||
@@ -3219,6 +3219,11 @@ input[type="range"]:focus-visible {
|
|||||||
html,
|
html,
|
||||||
body {
|
body {
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
/* Disable pinch-zoom on mobile — Android Chrome ignores user-scalable=no
|
||||||
|
for a11y, and the kanban board's horizontally-scrollable layout
|
||||||
|
interacts badly with zoom-out (exposes the offscreen-right area).
|
||||||
|
`pan-x pan-y` keeps scroll panning but blocks pinch-zoom. */
|
||||||
|
touch-action: pan-x pan-y;
|
||||||
}
|
}
|
||||||
|
|
||||||
#root {
|
#root {
|
||||||
|
|||||||
Reference in New Issue
Block a user