diff --git a/packages/dashboard/app/App.tsx b/packages/dashboard/app/App.tsx index ccd18c8b0..a0f3fcdd3 100644 --- a/packages/dashboard/app/App.tsx +++ b/packages/dashboard/app/App.tsx @@ -42,6 +42,7 @@ import { useDeepLink } from "./hooks/useDeepLink"; import { useFavorites } from "./hooks/useFavorites"; import { useAuthOnboarding } from "./hooks/useAuthOnboarding"; import { useMobileKeyboard } from "./hooks/useMobileKeyboard"; +import { useMobileScrollLock } from "./hooks/useMobileScrollLock"; import { useSetupReadiness } from "./hooks/useSetupReadiness"; import { useUpdateCheck } from "./hooks/useUpdateCheck"; import { useViewState, type TaskView } from "./hooks/useViewState"; @@ -292,6 +293,12 @@ function AppInner() { // viewport. Without this guard, modal keyboard state leaks into the app-level // layout, causing stale bottom-padding offsets after the keyboard closes. const mobileKeyboardOpen = isMobile && keyboardOpen && !modalManager.anyModalOpen; + // 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 + // shift the document or visualViewport, and so the dashboard snaps back + // into place when the keyboard dismisses. Modals manage their own lock + // via useMobileScrollLock — the reference-counted hook handles overlap. + useMobileScrollLock(mobileKeyboardOpen); // App-level mailbox unread count state (used for header/mobile nav badges) const [mailboxUnreadCount, setMailboxUnreadCount] = useState(0);