fix(dashboard): make ChatView mobile send fire on quick tap

The mobile send button used pointerdown + touchstart with preventDefault
and a focus-preservation dance to keep the keyboard up while sending.
That path silently failed on quick taps on iOS — only a long press
registered. Switching to plain onClick (with touch-action: manipulation
to skip the click delay) fires reliably on tap. The soft keyboard may
dismiss on send now, which is a minor regression vs. the previous
intent but vastly preferable to silent failure.

QuickChat is unchanged because it already works on mobile.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
gsxdsm
2026-05-06 20:15:30 -07:00
parent 073c5047ea
commit 74378dcfc7
2 changed files with 21 additions and 32 deletions

View File

@@ -0,0 +1,13 @@
---
"@runfusion/fusion": patch
---
Workaround long-standing bug where ChatView's mobile send button only
fired on a long press — quick taps silently did nothing. The previous
implementation used `pointerdown` + `touchstart` with `preventDefault`
and a focus-preservation dance so the keyboard would stay up while
sending; on iOS that path made quick taps fall through entirely. The
button now uses plain `onClick` with `touch-action: manipulation`. The
soft keyboard may dismiss on send, which is a minor UX regression
compared to silent failure. QuickChat is unchanged (it already works
on mobile).

View File

@@ -1994,43 +1994,19 @@ export function ChatView({ projectId, addToast }: ChatViewProps) {
<button <button
type="button" type="button"
className="chat-input-send" className="chat-input-send"
onPointerDown={(event) => { // Workaround: previous mobile flow used pointerdown +
if (typeof window === "undefined" || window.innerWidth > 768) return; // touchstart with preventDefault and a focus-preservation
event.preventDefault(); // dance to keep the keyboard up while sending. On iOS that
if (event.pointerType && event.pointerType !== "mouse") { // path made quick taps fail entirely (only long press
handledMobileSendRef.current = true; // registered). Falling back to plain onClick reliably fires
markPreserveComposerFocus(); // the send on tap; the soft keyboard may dismiss but the
focusComposerInput(); // message goes through, which beats silent failure.
void handleSend();
window.setTimeout(() => {
preserveComposerFocusRef.current = false;
}, 1500);
}
}}
onTouchStart={(event) => {
if (typeof window === "undefined" || window.innerWidth > 768) return;
event.preventDefault();
handledMobileSendRef.current = true;
markPreserveComposerFocus();
focusComposerInput();
void handleSend();
window.setTimeout(() => {
preserveComposerFocusRef.current = false;
}, 1500);
}}
onMouseDown={(event) => {
if (typeof window === "undefined" || window.innerWidth > 768) return;
event.preventDefault();
}}
onClick={() => { onClick={() => {
if (handledMobileSendRef.current) {
handledMobileSendRef.current = false;
return;
}
void handleSend(); void handleSend();
}} }}
disabled={!messageInput.trim() && pendingAttachments.length === 0} disabled={!messageInput.trim() && pendingAttachments.length === 0}
data-testid="chat-send-btn" data-testid="chat-send-btn"
style={{ touchAction: "manipulation" }}
> >
<Send size={16} /> <Send size={16} />
</button> </button>