consolidate chat docking/location logic into the store

Add `dockChat(location, chatId)` and `resolveDockChatId()` to `chatStore` so the "set location + show chat" transition and the active-or-latest fallback live in one place instead of being re-implemented across components. Call sites become thin dispatchers. Also collapse redundant `setActiveChatId(x) + showChat()` pairs into `showChat(x)` throughout.

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: John Chilton <jmchilton@gmail.com>
This commit is contained in:
Ahmed Awan
2026-06-01 15:49:26 -05:00
co-authored by Claude John Chilton
parent 4af780eb42
commit ae8ef8979f
6 changed files with 37 additions and 38 deletions
+1 -3
View File
@@ -396,9 +396,7 @@ function popOutToWindowManager() {
}
function dockTo(location: "right" | "bottom") {
chatStore.setActiveChatId(currentChatId.value);
chatStore.setLocation(location);
chatStore.showChat();
chatStore.dockChat(location, currentChatId.value);
if (route.path.startsWith("/galaxyai")) {
router.push("/");
}
@@ -56,8 +56,7 @@ function handleItemClick(item: ChatHistoryItem, index: number, event: MouseEvent
if (chatStore.isCenterMode) {
router.push(`/galaxyai/${item.id}`);
} else {
chatStore.setActiveChatId(item.id);
chatStore.showChat();
chatStore.showChat(item.id);
}
}
@@ -65,8 +64,7 @@ function startNewChat() {
if (chatStore.isCenterMode) {
router.push("/galaxyai/new");
} else {
chatStore.setActiveChatId(null);
chatStore.showChat();
chatStore.showChat(null);
}
}
@@ -157,38 +157,36 @@ describe("ChatModeSelector", () => {
// ── openDockedChat – location ──────────────────────────────────────────────
describe("openDockedChat — location", () => {
it("calls setLocation('right') for side panel button", async () => {
describe("openDockedChat — dockChat call", () => {
it("calls dockChat with 'right' for side panel button", async () => {
const { wrapper, store } = mountComponent();
store.activeChatId = "active-chat";
await getButtons(wrapper).at(1).trigger("click");
expect(store.setLocation).toHaveBeenCalledWith("right");
expect(store.dockChat).toHaveBeenCalledWith("right", "active-chat");
});
it("calls setLocation('bottom') for bottom panel button", async () => {
it("calls dockChat with 'bottom' for bottom panel button", async () => {
const { wrapper, store } = mountComponent();
store.activeChatId = "active-chat";
await getButtons(wrapper).at(2).trigger("click");
expect(store.setLocation).toHaveBeenCalledWith("bottom");
expect(store.dockChat).toHaveBeenCalledWith("bottom", "active-chat");
});
});
// ── openDockedChat – chat ID resolution ───────────────────────────────────
describe("openDockedChat — chat ID resolution", () => {
it("uses routedChatId when coming from center mode with exchangeId in route", async () => {
mockRoute = { path: "/galaxyai/routed-chat", params: { exchangeId: "routed-chat" } };
const { wrapper, store } = mountComponent();
store.chatLocation = "center";
await getButtons(wrapper).at(1).trigger("click");
expect(store.showChat).toHaveBeenCalledWith("routed-chat");
expect(store.dockChat).toHaveBeenCalledWith("right", "routed-chat");
});
it("uses activeOrLatestId when in center mode but no exchangeId in route", async () => {
it("uses activeChatId fallback when in center mode but no exchangeId in route", async () => {
mockRoute = { path: "/galaxyai", params: {} };
const { wrapper, store } = mountComponent();
store.chatLocation = "center";
store.activeChatId = "active-chat";
await getButtons(wrapper).at(1).trigger("click");
expect(store.showChat).toHaveBeenCalledWith("active-chat");
expect(store.dockChat).toHaveBeenCalledWith("right", "active-chat");
});
it("uses activeChatId when not in center mode", async () => {
@@ -196,7 +194,7 @@ describe("ChatModeSelector", () => {
store.chatLocation = "right";
store.activeChatId = "active-chat";
await getButtons(wrapper).at(1).trigger("click");
expect(store.showChat).toHaveBeenCalledWith("active-chat");
expect(store.dockChat).toHaveBeenCalledWith("right", "active-chat");
});
it("uses first history item when activeChatId is null but history exists", async () => {
@@ -204,7 +202,7 @@ describe("ChatModeSelector", () => {
store.activeChatId = null;
store.chatHistory = [{ id: "hist-1" } as ChatHistoryItem, { id: "hist-2" } as ChatHistoryItem];
await getButtons(wrapper).at(1).trigger("click");
expect(store.showChat).toHaveBeenCalledWith("hist-1");
expect(store.dockChat).toHaveBeenCalledWith("right", "hist-1");
});
it("uses null when no activeChatId and empty history", async () => {
@@ -212,7 +210,7 @@ describe("ChatModeSelector", () => {
store.activeChatId = null;
store.chatHistory = [];
await getButtons(wrapper).at(1).trigger("click");
expect(store.showChat).toHaveBeenCalledWith(null);
expect(store.dockChat).toHaveBeenCalledWith("right", null);
});
it("prefers activeChatId over first history item when both present", async () => {
@@ -220,7 +218,7 @@ describe("ChatModeSelector", () => {
store.activeChatId = "active-chat";
store.chatHistory = [{ id: "hist-1" } as ChatHistoryItem];
await getButtons(wrapper).at(1).trigger("click");
expect(store.showChat).toHaveBeenCalledWith("active-chat");
expect(store.dockChat).toHaveBeenCalledWith("right", "active-chat");
});
it("does NOT use routedChatId when not in center mode even with exchangeId in route", async () => {
@@ -229,8 +227,8 @@ describe("ChatModeSelector", () => {
store.chatLocation = "right";
store.activeChatId = "active-chat";
await getButtons(wrapper).at(1).trigger("click");
expect(store.showChat).toHaveBeenCalledWith("active-chat");
expect(store.showChat).not.toHaveBeenCalledWith("some-chat");
expect(store.dockChat).toHaveBeenCalledWith("right", "active-chat");
expect(store.dockChat).not.toHaveBeenCalledWith("right", "some-chat");
});
});
});
@@ -15,7 +15,7 @@ const router = useRouter();
const chatStore = useChatStore();
const { activeChatId, chatHistory, isBottomPanelOpen, isCenterMode, isRightPanelOpen } = storeToRefs(chatStore);
const { activeChatId, isBottomPanelOpen, isCenterMode, isRightPanelOpen } = storeToRefs(chatStore);
const isOnGalaxyAIRoute = computed(() => route.path.startsWith("/galaxyai"));
@@ -31,22 +31,15 @@ function openCenterChat() {
function openDockedChat(location: "right" | "bottom") {
/** Stores an id if there is a `/galaxyai/:exchangeId` route param */
const routedChatId = route.path.includes("galaxyai") ? route.params["exchangeId"] || null : null;
// TODO: What if we just looked at a chat, maybe add a chatId to localStorage for last opened chat?
const activeOrLatestId = chatStore.activeChatId || (chatHistory.value.length > 0 ? chatHistory.value[0]!.id : null);
const wasCenterMode = chatStore.isCenterMode;
if (isOnGalaxyAIRoute.value) {
router.push("/");
}
chatStore.setLocation(location);
if (wasCenterMode && routedChatId) {
chatStore.setActiveChatId(routedChatId);
chatStore.showChat(routedChatId);
} else {
chatStore.setActiveChatId(activeOrLatestId);
chatStore.showChat(activeOrLatestId);
}
// TODO: What if we just looked at a chat, maybe add a chatId to localStorage for last opened chat?
const chatId = wasCenterMode && routedChatId ? routedChatId : chatStore.resolveDockChatId();
chatStore.dockChat(location, chatId);
}
</script>
+1 -2
View File
@@ -35,8 +35,7 @@ function close() {
}
function startNewChat() {
chatStore.setActiveChatId(null);
chatStore.showChat();
chatStore.showChat(null);
}
</script>
+13
View File
@@ -86,6 +86,17 @@ export const useChatStore = defineStore("chatStore", () => {
}
}
/** Returns the active chat id, falling back to the most recent history item, or null. */
function resolveDockChatId(): string | null {
return activeChatId.value ?? chatHistory.value[0]?.id ?? null;
}
/** Sets the chat location and shows the chat with the given id in one step. */
function dockChat(location: ChatLocation, chatId?: string | null) {
setLocation(location);
showChat(chatId);
}
return {
chatHistory,
chatLocation,
@@ -103,5 +114,7 @@ export const useChatStore = defineStore("chatStore", () => {
toggleChat,
setLocation,
setActiveChatId,
resolveDockChatId,
dockChat,
};
});