diff --git a/components/social/comment/post-comments-drawer.tsx b/components/social/comment/post-comments-drawer.tsx index d9eb5a75..2c20ea6c 100644 --- a/components/social/comment/post-comments-drawer.tsx +++ b/components/social/comment/post-comments-drawer.tsx @@ -80,6 +80,51 @@ export function PostCommentsDrawer({ const [toReport, setToReport] = useState(null); const inputRef = useRef(null); + // iOS/Android on-screen keyboard handling for the mobile bottom sheet. + // + // On iOS Safari the keyboard shrinks only the *visual* viewport, not the + // layout viewport, so a `bottom-0` fixed sheet stays anchored behind the + // keyboard; Safari then scrolls the whole webview up to reveal the focused + // composer, dragging the sheet's top off-screen (the "weird upward shift"). + // vaul's own `repositionInputs` is disabled here, so we drive it from the + // VisualViewport API: pin the sheet's bottom to the top of the keyboard and + // cap its height to the visible area. The top never overflows and the + // comment list simply shrinks — Instagram-style — instead of the sheet + // sliding out of the viewport. + const [kbStyle, setKbStyle] = useState< + { bottom: number; height: number } | undefined + >(undefined); + useEffect(() => { + if (!open || !isMobile) { + setKbStyle(undefined); + return; + } + const vv = window.visualViewport; + if (!vv) return; + const update = () => { + // Keyboard height = full layout height minus what's still visible. + const inset = Math.max(0, window.innerHeight - vv.height - vv.offsetTop); + if (inset < 1) { + setKbStyle(undefined); + return; + } + // Keep the sheet at its resting 80vh unless the space above the keyboard + // is smaller, in which case fill exactly that so nothing overflows. + const height = Math.min( + window.innerHeight * 0.8, + window.innerHeight - inset, + ); + setKbStyle({ bottom: inset, height }); + }; + update(); + vv.addEventListener("resize", update); + vv.addEventListener("scroll", update); + return () => { + vv.removeEventListener("resize", update); + vv.removeEventListener("scroll", update); + }; + }, [open, isMobile]); + // Load the first page whenever the drawer opens for a post. useEffect(() => { if (!open) return; @@ -238,6 +283,10 @@ export function PostCommentsDrawer({ direction={direction} // Transparent scrim: keep the post visible behind the sheet. overlayClassName="bg-transparent" + // When the mobile keyboard is up, `kbStyle` pins the sheet above it + // and caps its height (see the VisualViewport effect); otherwise the + // sheet keeps its resting 80vh from the class below. + style={kbStyle} className={cn( "bg-card flex flex-col shadow-2xl", isMobile ? "h-[80vh]" : "h-full w-[420px] max-w-[92vw]",