feat(FN-4182): keep keyboard open on first tap of mobile room send button

Fixes a mobile UX bug where the keyboard would dismiss when tapping the room send button for the first time, plus adds a regression test in ChatView to guard against this behavior.

Fusion-Task-Id: FN-4182
This commit is contained in:
Fusion
2026-05-12 13:31:47 -07:00
committed by gsxdsm
parent 044b1cba2f
commit 8653a368fe
3 changed files with 66 additions and 0 deletions

View File

@@ -0,0 +1,5 @@
---
"@runfusion/fusion": patch
---
Fix chat room send button on mobile: first touch now sends the message instead of dismissing the keyboard.

View File

@@ -2461,6 +2461,22 @@ export function ChatView({ projectId, addToast, experimentalFeatures }: ChatView
<button
type="button"
className="chat-input-send"
// Keep keyboard up when sending. preventDefault fires on
// pointerdown for touch pointers (BEFORE iOS blurs the
// textarea — the synthesized mousedown is too late on
// iOS), and on mousedown for desktop. Crucially we do NOT
// call preventDefault on touchstart and we do NOT run the
// action here — both of those broke quick taps. Click
// still fires from the iOS touch sequence and runs the
// action reliably.
onPointerDown={(event) => {
if (event.pointerType && event.pointerType !== "mouse") {
event.preventDefault();
}
}}
onMouseDown={(event) => {
event.preventDefault();
}}
onClick={() => {
void handleSendDispatch();
}}

View File

@@ -3304,6 +3304,51 @@ describe("ChatView mobile behavior", () => {
}
});
it("mobile mode: room send button sends on first touch and keeps composer focused", async () => {
const restoreMatchMedia = mockMobileViewport();
const sendRoomMessage = vi.fn().mockResolvedValue(undefined);
const sendMessage = vi.fn();
try {
localStorage.setItem("fusion:chat-scope", "rooms");
setupMockChat({
activeSession: activeSessionFixture,
messages: [],
sendMessage,
});
setupMockRooms({
activeRoom: {
id: "room-001",
projectId: "proj-123",
name: "backend",
createdAt: "2026-04-08T00:00:00.000Z",
updatedAt: "2026-04-08T00:00:00.000Z",
},
sendRoomMessage,
});
render(<ChatView projectId="proj-123" addToast={vi.fn()} experimentalFeatures={{ chatRooms: true }} />);
const input = screen.getByTestId("chat-input") as HTMLTextAreaElement;
fireEvent.change(input, { target: { value: "Hello mobile room" } });
input.focus();
const sendButton = screen.getByTestId("chat-send-btn");
fireEvent.touchStart(sendButton);
fireEvent.click(sendButton);
await waitFor(() => {
expect(sendRoomMessage).toHaveBeenCalledTimes(1);
expect(sendRoomMessage).toHaveBeenCalledWith("Hello mobile room");
});
expect(sendMessage).not.toHaveBeenCalled();
expect(document.activeElement).toBe(input);
} finally {
localStorage.removeItem("fusion:chat-scope");
restoreMatchMedia.mockRestore();
}
});
it("mobile mode: sets and clears keyboard overlap CSS vars on chat thread", async () => {
const restoreMatchMedia = mockMobileViewport();
const { listeners, mockVV } = mockMobileVisualViewport({