Files
fusion/packages/dashboard/app/hooks/useViewportMode.ts
gsxdsm ed4d021d3d fix(dashboard): keep mobile nav visible on Android landscape and during keyboard
- Broaden mobile media query to include (max-height: 480px) so landscape
  phones (which exceed 768 CSS px wide) still render the bottom nav and
  mobile board layout instead of desktop horizontally-scrollable columns.
- Guard useMobileKeyboard against pinch-zoom (vv.scale > 1) — Android
  Chrome ignores user-scalable=no, and a focused textarea + zoom was
  false-positiving keyboard-open and hiding MobileNavBar.
- Read documentElement.clientHeight instead of stale window.innerHeight
  when computing keyboard overlap (Android multi-window can leave
  innerHeight cached at a wildly different value than the actual layout
  viewport — observed 2848 while html was 797).
- Add interactive-widget=resizes-content to the viewport meta so Android
  Chrome shrinks the layout viewport with the soft keyboard, matching iOS.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-22 13:12:36 -07:00

47 lines
1.5 KiB
TypeScript

import { useState, useEffect } from "react";
export type ViewportMode = "mobile" | "tablet" | "desktop";
// `(max-height: 480px)` catches phones held in landscape, which can exceed
// 768 CSS px wide but stay short. Without it, landscape phones fall out of
// mobile mode and lose the bottom nav bar + get the desktop horizontally-
// scrollable board.
export const MOBILE_MEDIA_QUERY = "(max-width: 768px), (max-height: 480px)";
export function getViewportMode(): ViewportMode {
if (typeof window === "undefined") return "desktop";
if (window.matchMedia(MOBILE_MEDIA_QUERY).matches) return "mobile";
if (window.matchMedia("(min-width: 769px) and (max-width: 1024px)").matches) return "tablet";
return "desktop";
}
export function useViewportMode(): ViewportMode {
const [mode, setMode] = useState<ViewportMode>(getViewportMode);
useEffect(() => {
if (typeof window === "undefined") return;
const mobileQuery = window.matchMedia(MOBILE_MEDIA_QUERY);
const tabletQuery = window.matchMedia("(min-width: 769px) and (max-width: 1024px)");
const updateMode = () => {
if (mobileQuery.matches) {
setMode("mobile");
} else if (tabletQuery.matches) {
setMode("tablet");
} else {
setMode("desktop");
}
};
mobileQuery.addEventListener("change", updateMode);
tabletQuery.addEventListener("change", updateMode);
return () => {
mobileQuery.removeEventListener("change", updateMode);
tabletQuery.removeEventListener("change", updateMode);
};
}, []);
return mode;
}