Use fixed positioning for chat layout to prevent keyboard scroll issues

This commit is contained in:
2026-03-11 21:56:42 -07:00
parent 75d53716fd
commit e281164d8d
+19 -48
View File
@@ -32,7 +32,6 @@ export default function Chat() {
const [input, setInput] = useState('');
const [sending, setSending] = useState(false);
const [viewportHeight, setViewportHeight] = useState<number | undefined>(undefined);
const messagesEndRef = useRef<HTMLDivElement>(null);
const scrollContainerRef = useRef<HTMLDivElement>(null);
@@ -53,46 +52,6 @@ export default function Chat() {
messagesEndRef.current?.scrollIntoView({ behavior });
}, []);
// Lock body scroll and track visual viewport height for mobile keyboard
useEffect(() => {
const html = document.documentElement;
const body = document.body;
html.style.overflow = 'hidden';
html.style.height = '100%';
body.style.overflow = 'hidden';
body.style.height = '100%';
const vv = window.visualViewport;
if (vv) {
const update = () => {
setViewportHeight(vv.height);
// Scroll page back to top in case browser scrolled it
window.scrollTo(0, 0);
if (isAtBottom.current) {
requestAnimationFrame(() => scrollToBottom('instant'));
}
};
update();
vv.addEventListener('resize', update);
vv.addEventListener('scroll', () => window.scrollTo(0, 0));
return () => {
html.style.overflow = '';
html.style.height = '';
body.style.overflow = '';
body.style.height = '';
vv.removeEventListener('resize', update);
};
}
return () => {
html.style.overflow = '';
html.style.height = '';
body.style.overflow = '';
body.style.height = '';
};
}, [scrollToBottom]);
useEffect(() => {
if (authLoading) return;
if (!user) {
@@ -118,6 +77,21 @@ export default function Chat() {
}
}, [messages, scrollToBottom]);
// When mobile keyboard opens/closes, scroll to bottom if user was there
useEffect(() => {
const vv = window.visualViewport;
if (!vv) return;
const handleResize = () => {
if (isAtBottom.current) {
requestAnimationFrame(() => scrollToBottom('instant'));
}
};
vv.addEventListener('resize', handleResize);
return () => vv.removeEventListener('resize', handleResize);
}, [scrollToBottom]);
async function handleSend(e: React.FormEvent) {
e.preventDefault();
if (!input.trim() || !friendId || sending) return;
@@ -145,15 +119,12 @@ export default function Chat() {
if (!user) return null;
// Use visualViewport height when available (handles mobile keyboard),
// subtract header height (3.5rem = 56px)
const containerStyle: React.CSSProperties = viewportHeight
? { height: viewportHeight - 56 }
: { height: 'calc(100dvh - 3.5rem)' };
return (
<PageLayout noPadding>
<div className="flex flex-col" style={containerStyle}>
{/* Fixed container pinned below the navbar.
Fixed positioning keeps it anchored to the viewport
so the mobile keyboard can't push it off-screen. */}
<div className="fixed inset-x-0 top-14 bottom-0 flex flex-col">
{/* Chat header */}
<div className="flex shrink-0 items-center gap-3 border-b border-border bg-background px-4 py-3">
<Link to="/friends">