Revert "fix(dashboard): stop mobile send-button double-fire that aborted chats"

This reverts commit 394b1a655a.
This commit is contained in:
gsxdsm
2026-05-06 20:02:02 -07:00
parent beb0a1a189
commit ca047a714a
4 changed files with 0 additions and 57 deletions

View File

@@ -1,18 +0,0 @@
---
"@runfusion/fusion": patch
---
Fix mobile chat send button silently failing on quick taps. Both the
ChatView and QuickChat send/stop buttons had `onPointerDown` and
`onTouchStart` handlers that each invoked the send/stop action — on a
quick mobile tap both handlers fire, so the action ran twice in rapid
succession. The second invocation closed the first one's SSE stream,
which the server treated as a cancel, leaving the chat with no output.
A long press happened to suppress one of the events, so holding the
button "worked" while quick tapping silently failed.
The handlers now share the existing `handledMobile*Ref` flag so only
the first event for a given tap actually fires the action. The send
button also gets `touch-action: manipulation` and an expanded invisible
hit area so slightly-off taps don't fall through to the surrounding
textarea (which would dismiss the keyboard without sending).

View File

@@ -856,25 +856,6 @@
align-items: center; align-items: center;
justify-content: center; justify-content: center;
flex-shrink: 0; flex-shrink: 0;
/* Disable double-tap zoom delay on mobile so the click handler fires
immediately on tap, and ignore other gestures the browser would
otherwise speculate on. Without this, quick taps can land in the
ambiguity window where the browser dismisses the keyboard (textarea
blur) before deciding whether to register a click on the button. */
touch-action: manipulation;
}
/* Expand the effective tap target without changing the visual size, so
slightly-off taps still land on the button instead of falling through
to the surrounding textarea (which would dismiss the mobile keyboard
without sending). */
.chat-input-send::before {
content: "";
position: absolute;
inset: -8px;
}
.chat-input-send {
position: relative;
} }
.chat-input-send:disabled { .chat-input-send:disabled {

View File

@@ -1998,15 +1998,6 @@ export function ChatView({ projectId, addToast }: ChatViewProps) {
if (typeof window === "undefined" || window.innerWidth > 768) return; if (typeof window === "undefined" || window.innerWidth > 768) return;
event.preventDefault(); event.preventDefault();
if (event.pointerType && event.pointerType !== "mouse") { if (event.pointerType && event.pointerType !== "mouse") {
// On mobile, both onPointerDown and onTouchStart fire
// for a quick tap. Without this guard handleSend runs
// twice — the second call closes the first's stream
// (useChat.ts: streamRef.current.close()) and the
// server-side beginGeneration aborts the in-flight
// generation, leaving the chat with no output. A
// long-press doesn't fire both events the same way,
// which is why holding the button "fixes" sending.
if (handledMobileSendRef.current) return;
handledMobileSendRef.current = true; handledMobileSendRef.current = true;
markPreserveComposerFocus(); markPreserveComposerFocus();
focusComposerInput(); focusComposerInput();
@@ -2019,7 +2010,6 @@ export function ChatView({ projectId, addToast }: ChatViewProps) {
onTouchStart={(event) => { onTouchStart={(event) => {
if (typeof window === "undefined" || window.innerWidth > 768) return; if (typeof window === "undefined" || window.innerWidth > 768) return;
event.preventDefault(); event.preventDefault();
if (handledMobileSendRef.current) return;
handledMobileSendRef.current = true; handledMobileSendRef.current = true;
markPreserveComposerFocus(); markPreserveComposerFocus();
focusComposerInput(); focusComposerInput();

View File

@@ -2471,7 +2471,6 @@ export function QuickChatFAB({
if (typeof window === "undefined" || window.innerWidth > QUICK_CHAT_DESKTOP_BREAKPOINT) return; if (typeof window === "undefined" || window.innerWidth > QUICK_CHAT_DESKTOP_BREAKPOINT) return;
event.preventDefault(); event.preventDefault();
if (event.pointerType && event.pointerType !== "mouse") { if (event.pointerType && event.pointerType !== "mouse") {
if (handledMobileActionRef.current) return;
handledMobileActionRef.current = true; handledMobileActionRef.current = true;
stopStreaming(); stopStreaming();
} }
@@ -2479,7 +2478,6 @@ export function QuickChatFAB({
onTouchStart={(event) => { onTouchStart={(event) => {
if (typeof window === "undefined" || window.innerWidth > QUICK_CHAT_DESKTOP_BREAKPOINT) return; if (typeof window === "undefined" || window.innerWidth > QUICK_CHAT_DESKTOP_BREAKPOINT) return;
event.preventDefault(); event.preventDefault();
if (handledMobileActionRef.current) return;
handledMobileActionRef.current = true; handledMobileActionRef.current = true;
stopStreaming(); stopStreaming();
}} }}
@@ -2507,13 +2505,6 @@ export function QuickChatFAB({
if (typeof window === "undefined" || window.innerWidth > QUICK_CHAT_DESKTOP_BREAKPOINT) return; if (typeof window === "undefined" || window.innerWidth > QUICK_CHAT_DESKTOP_BREAKPOINT) return;
event.preventDefault(); event.preventDefault();
if (event.pointerType && event.pointerType !== "mouse") { if (event.pointerType && event.pointerType !== "mouse") {
// Guard against the same touch firing both
// onPointerDown and onTouchStart, which would call
// handleSendMessage twice — the second call closes
// the first's stream and the server-side
// beginGeneration aborts the in-flight generation,
// producing no output. (See ChatView send button.)
if (handledMobileActionRef.current) return;
handledMobileActionRef.current = true; handledMobileActionRef.current = true;
markPreserveComposerFocus(); markPreserveComposerFocus();
focusComposerInput(); focusComposerInput();
@@ -2523,7 +2514,6 @@ export function QuickChatFAB({
onTouchStart={(event) => { onTouchStart={(event) => {
if (typeof window === "undefined" || window.innerWidth > QUICK_CHAT_DESKTOP_BREAKPOINT) return; if (typeof window === "undefined" || window.innerWidth > QUICK_CHAT_DESKTOP_BREAKPOINT) return;
event.preventDefault(); event.preventDefault();
if (handledMobileActionRef.current) return;
handledMobileActionRef.current = true; handledMobileActionRef.current = true;
markPreserveComposerFocus(); markPreserveComposerFocus();
focusComposerInput(); focusComposerInput();