fix(dashboard): use non-passive touchmove listener on textarea
React's onTouchMove handler is registered as a passive listener by
default, so the previous JSX-handler preventDefault() was silently a
no-op — drags on the composer still scrolled the input box up.
Attaches the listener imperatively via addEventListener with
{ passive: false } so preventDefault actually cancels the drag. Tap
(touchstart + touchend without touchmove between) is still unaffected,
so first-tap focus continues to work.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -905,6 +905,26 @@ export function ChatView({ projectId, addToast }: ChatViewProps) {
|
||||
}
|
||||
}, [contextMenu]);
|
||||
|
||||
// Lock the composer textarea against drag-pan on mobile. React's onTouchMove
|
||||
// is registered as a passive listener so preventDefault() inside a JSX
|
||||
// handler is a no-op — we attach a non-passive listener directly to the
|
||||
// textarea element. touchmove cancellation blocks drags but tap (touchstart
|
||||
// + touchend without touchmove in between) is unaffected, so first-tap
|
||||
// focus still works.
|
||||
useEffect(() => {
|
||||
if (!isMobile) return;
|
||||
const ta = inputRef.current;
|
||||
if (!ta) return;
|
||||
const onTouchMove = (event: TouchEvent) => {
|
||||
if (typeof window === "undefined" || window.innerWidth > 768) return;
|
||||
event.preventDefault();
|
||||
};
|
||||
ta.addEventListener("touchmove", onTouchMove, { passive: false });
|
||||
return () => {
|
||||
ta.removeEventListener("touchmove", onTouchMove);
|
||||
};
|
||||
}, [isMobile]);
|
||||
|
||||
// On mount and on visibility/page restore, if iOS thinks the keyboard is
|
||||
// up but the textarea isn't actually focused (or vice versa), the
|
||||
// visualViewport metrics get stuck in a half-state — composer pushed up
|
||||
@@ -1957,16 +1977,6 @@ export function ChatView({ projectId, addToast }: ChatViewProps) {
|
||||
event.preventDefault();
|
||||
event.currentTarget.focus({ preventScroll: true });
|
||||
}}
|
||||
onTouchMove={(event) => {
|
||||
// Lock the composer in place. touch-action: manipulation
|
||||
// is required for first-tap focus to register on iOS,
|
||||
// but it also permits pan-y, which lets the user drag
|
||||
// the input box up off-screen. Cancelling touchmove
|
||||
// blocks the drag without affecting tap (a tap fires
|
||||
// touchstart + touchend with no touchmove in between).
|
||||
if (typeof window === "undefined" || window.innerWidth > 768) return;
|
||||
event.preventDefault();
|
||||
}}
|
||||
rows={1}
|
||||
data-testid="chat-input"
|
||||
/>
|
||||
|
||||
Reference in New Issue
Block a user