fix(FN-4804): sync quick chat session and room selection

- Clear the active chat room when switching Quick Chat to a direct session
- Keep the hidden session dropdown value and initial-session state aligned with room selection changes
- Add dashboard regression coverage for switching from a room back to a direct chat and include a CLI patch changeset

Fusion-Task-Id: FN-4804
This commit is contained in:
Fusion (runfusion.ai)
2026-05-16 17:47:46 -07:00
committed by gsxdsm
parent c3dd34f070
commit 4691cbe199
3 changed files with 65 additions and 2 deletions

View File

@@ -1785,6 +1785,10 @@ export function QuickChatFAB({
return;
}
if (chatRoomsEnabled && roomsState.activeRoom) {
roomsState.selectRoom(null);
}
markRead("direct", selectedSession.id, selectedSession.lastMessageAt ?? selectedSession.updatedAt);
hasAppliedInitialSessionRef.current = true;
@@ -1798,12 +1802,13 @@ export function QuickChatFAB({
void selectSession(selectedSession);
setSessionMenuOpen(false);
}, [markRead, selectSession, sessions]);
}, [chatRoomsEnabled, markRead, roomsState, selectSession, sessions]);
const handleRoomSwitch = useCallback((roomId: string) => {
const selectedRoom = roomsState.rooms.find((room) => room.id === roomId);
markRead("room", roomId, selectedRoom?.updatedAt);
roomsState.selectRoom(roomId);
hasAppliedInitialSessionRef.current = true;
setSessionMenuOpen(false);
}, [markRead, roomsState]);
@@ -2539,7 +2544,12 @@ export function QuickChatFAB({
<div className="quick-chat-panel-agent-select" data-testid="quick-chat-session-select">
<div className="quick-chat-session-menu" ref={sessionMenuRef}>
<label htmlFor="quick-chat-session-dropdown-trigger" className="visually-hidden">Select session</label>
<input type="hidden" data-testid="quick-chat-session-dropdown" value={activeSession?.id ?? ""} readOnly />
<input
type="hidden"
data-testid="quick-chat-session-dropdown"
value={showRoomGroups && roomsState.activeRoom ? "" : activeSession?.id ?? ""}
readOnly
/>
<button
id="quick-chat-session-dropdown-trigger"
type="button"

View File

@@ -1,3 +1,4 @@
import { useState } from "react";
import { beforeEach, describe, expect, it, vi } from "vitest";
import { fireEvent, render, screen, waitFor } from "@testing-library/react";
import type { Agent } from "../../api";
@@ -454,6 +455,53 @@ describe("QuickChatFAB session-first UX", () => {
expect(screen.getByTestId("quick-chat-input")).toHaveAttribute("placeholder", "Message GPT-4o");
});
it("FN-4804: switching from an active room to a direct session clears room display state", async () => {
const room = {
id: "room-1",
name: "engineering",
slug: "engineering",
memberCount: 2,
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
};
mockUseAppSettings.mockReturnValue({ experimentalFeatures: { chatRooms: true } } as ReturnType<typeof useAppSettings>);
mockUseChatRooms.mockImplementation(() => {
const [activeRoom, setActiveRoom] = useState(room);
return {
rooms: [room],
roomsLoading: false,
roomsError: null,
activeRoom,
activeRoomMembers: [],
messages: [],
messagesLoading: false,
selectRoom: (roomId: string | null) => {
setActiveRoom(roomId ? room : null);
},
createRoom: vi.fn(),
deleteRoom: vi.fn(),
sendRoomMessage: vi.fn(),
refreshRooms: vi.fn(),
};
});
render(<QuickChatFAB addToast={vi.fn()} projectId="proj-1" />);
fireEvent.click(screen.getByTestId("quick-chat-fab"));
expect(await screen.findByTestId("quick-chat-input")).toHaveAttribute("placeholder", "Message #engineering");
expect(screen.getByTestId("quick-chat-session-dropdown")).toHaveValue("");
fireEvent.click(screen.getByTestId("quick-chat-session-dropdown-trigger"));
fireEvent.click(screen.getByTestId("quick-chat-session-option-session-agent"));
await waitFor(() => {
expect(screen.getByTestId("quick-chat-session-dropdown")).toHaveValue("session-agent");
expect(screen.getByTestId("quick-chat-input")).toHaveAttribute("placeholder", "Message Agent One");
expect(screen.getByTestId("quick-chat-session-dropdown-trigger")).not.toHaveTextContent("#engineering");
});
});
describe("FN-4708 room reflection", () => {
it("shows room placeholder and room tag when an active room exists", async () => {
mockUseAppSettings.mockReturnValue({ experimentalFeatures: { chatRooms: true } } as ReturnType<typeof useAppSettings>);