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:
5
.changeset/FN-4182-mobile-send-button.md
Normal file
5
.changeset/FN-4182-mobile-send-button.md
Normal 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.
|
||||
@@ -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();
|
||||
}}
|
||||
|
||||
@@ -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({
|
||||
|
||||
Reference in New Issue
Block a user