Use fixed positioning for chat layout to prevent keyboard scroll issues
This commit is contained in:
+19
-48
@@ -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">
|
||||
|
||||
Reference in New Issue
Block a user