mirror of
https://github.com/aaif-goose/goose.git
synced 2026-06-02 06:14:27 +02:00
fix chat unread viewed-session race
Signed-off-by: morgmart <98432065+morgmart@users.noreply.github.com>
This commit is contained in:
@@ -1,4 +1,10 @@
|
||||
import { useCallback, useEffect, useRef, useState } from "react";
|
||||
import {
|
||||
useCallback,
|
||||
useEffect,
|
||||
useLayoutEffect,
|
||||
useRef,
|
||||
useState,
|
||||
} from "react";
|
||||
import { Sidebar } from "@/features/sidebar/ui/Sidebar";
|
||||
import { CreateProjectDialog } from "@/features/projects/ui/CreateProjectDialog";
|
||||
import { archiveProject } from "@/features/projects/api/projects";
|
||||
@@ -59,6 +65,19 @@ const SETTINGS_SECTIONS = new Set<SectionId>([
|
||||
"doctor",
|
||||
"about",
|
||||
]);
|
||||
|
||||
function syncViewedSessionForView(
|
||||
view: AppView,
|
||||
sessionId: string | null,
|
||||
): void {
|
||||
const viewedSessionId = view === "chat" ? sessionId : null;
|
||||
const liveChatStore = useChatStore.getState();
|
||||
liveChatStore.setViewedSession(viewedSessionId);
|
||||
if (viewedSessionId) {
|
||||
liveChatStore.markSessionRead(viewedSessionId);
|
||||
}
|
||||
}
|
||||
|
||||
export function AppShell({ children }: { children?: React.ReactNode }) {
|
||||
const [sidebarCollapsed, setSidebarCollapsed] = useState(false);
|
||||
const [sidebarWidth, setSidebarWidth] = useState(SIDEBAR_DEFAULT_WIDTH);
|
||||
@@ -144,13 +163,16 @@ export function AppShell({ children }: { children?: React.ReactNode }) {
|
||||
|
||||
const { activeSessionId } = sessionStore;
|
||||
|
||||
useEffect(() => {
|
||||
const viewedSessionId = activeView === "chat" ? activeSessionId : null;
|
||||
const liveChatStore = useChatStore.getState();
|
||||
liveChatStore.setViewedSession(viewedSessionId);
|
||||
if (viewedSessionId) {
|
||||
liveChatStore.markSessionRead(viewedSessionId);
|
||||
}
|
||||
const setActiveViewWithViewedSession = useCallback(
|
||||
(view: AppView, sessionId: string | null = activeSessionId) => {
|
||||
syncViewedSessionForView(view, sessionId);
|
||||
setActiveView(view);
|
||||
},
|
||||
[activeSessionId],
|
||||
);
|
||||
|
||||
useLayoutEffect(() => {
|
||||
syncViewedSessionForView(activeView, activeSessionId);
|
||||
}, [activeSessionId, activeView]);
|
||||
|
||||
const activeSession = activeSessionId
|
||||
@@ -291,7 +313,7 @@ export function AppShell({ children }: { children?: React.ReactNode }) {
|
||||
|
||||
if (existingDraft) {
|
||||
sessionStore.setActiveSession(existingDraft.id);
|
||||
setActiveView("chat");
|
||||
setActiveViewWithViewedSession("chat", existingDraft.id);
|
||||
chatStore.setActiveSession(existingDraft.id);
|
||||
perfLog(
|
||||
`[perf:newtab] ${existingDraft.id.slice(0, 8)} reused draft in ${(performance.now() - tStart).toFixed(1)}ms`,
|
||||
@@ -309,7 +331,7 @@ export function AppShell({ children }: { children?: React.ReactNode }) {
|
||||
modelName: sessionModelPreference.modelName,
|
||||
});
|
||||
sessionStore.setActiveSession(session.id);
|
||||
setActiveView("chat");
|
||||
setActiveViewWithViewedSession("chat", session.id);
|
||||
chatStore.setActiveSession(session.id);
|
||||
perfLog(
|
||||
`[perf:newtab] ${session.id.slice(0, 8)} created session in ${(performance.now() - tStart).toFixed(1)}ms`,
|
||||
@@ -320,6 +342,7 @@ export function AppShell({ children }: { children?: React.ReactNode }) {
|
||||
agentStore.selectedProvider,
|
||||
chatStore,
|
||||
providerInventoryEntries,
|
||||
setActiveViewWithViewedSession,
|
||||
sessionStore,
|
||||
],
|
||||
);
|
||||
@@ -376,9 +399,9 @@ export function AppShell({ children }: { children?: React.ReactNode }) {
|
||||
(sessionId: string) => {
|
||||
chatStore.cleanupSession(sessionId);
|
||||
sessionStore.setActiveSession(null);
|
||||
setActiveView("home");
|
||||
setActiveViewWithViewedSession("home", null);
|
||||
},
|
||||
[chatStore, sessionStore],
|
||||
[chatStore, sessionStore, setActiveViewWithViewedSession],
|
||||
);
|
||||
const openSettings = useCallback((section: SectionId = "appearance") => {
|
||||
setSettingsInitialSection(section);
|
||||
@@ -424,12 +447,12 @@ export function AppShell({ children }: { children?: React.ReactNode }) {
|
||||
}
|
||||
|
||||
sessionStore.setActiveSession(null);
|
||||
setActiveView("home");
|
||||
setActiveViewWithViewedSession("home", null);
|
||||
} catch {
|
||||
// best-effort
|
||||
}
|
||||
},
|
||||
[chatStore, sessionStore],
|
||||
[chatStore, sessionStore, setActiveViewWithViewedSession],
|
||||
);
|
||||
|
||||
const handleEditProject = useCallback(
|
||||
@@ -507,22 +530,25 @@ export function AppShell({ children }: { children?: React.ReactNode }) {
|
||||
setHomeSessionId(null);
|
||||
}
|
||||
sessionStore.setActiveSession(sessionId);
|
||||
setActiveView("chat");
|
||||
setActiveViewWithViewedSession("chat", sessionId);
|
||||
chatStore.setActiveSession(sessionId);
|
||||
useChatStore.getState().markSessionRead(sessionId);
|
||||
},
|
||||
[chatStore, homeSessionId, sessionStore],
|
||||
[chatStore, homeSessionId, sessionStore, setActiveViewWithViewedSession],
|
||||
);
|
||||
|
||||
const handleSelectSession = useCallback(
|
||||
(id: string) => {
|
||||
sessionStore.setActiveSession(id);
|
||||
setActiveView("chat");
|
||||
setActiveViewWithViewedSession("chat", id);
|
||||
chatStore.setActiveSession(id);
|
||||
useChatStore.getState().markSessionRead(id);
|
||||
loadSessionMessages(id);
|
||||
},
|
||||
[sessionStore, chatStore, loadSessionMessages],
|
||||
[
|
||||
sessionStore,
|
||||
chatStore,
|
||||
loadSessionMessages,
|
||||
setActiveViewWithViewedSession,
|
||||
],
|
||||
);
|
||||
|
||||
const handleSelectSearchResult = useCallback(
|
||||
@@ -539,12 +565,14 @@ export function AppShell({ children }: { children?: React.ReactNode }) {
|
||||
|
||||
const handleNavigate = useCallback(
|
||||
(view: AppView) => {
|
||||
const nextSessionId =
|
||||
view === "chat" ? useChatSessionStore.getState().activeSessionId : null;
|
||||
if (view !== "chat") {
|
||||
sessionStore.setActiveSession(null);
|
||||
}
|
||||
setActiveView(view);
|
||||
setActiveViewWithViewedSession(view, nextSessionId);
|
||||
},
|
||||
[sessionStore],
|
||||
[sessionStore, setActiveViewWithViewedSession],
|
||||
);
|
||||
|
||||
const handleCreatePersona = useCreatePersonaNavigation(() =>
|
||||
@@ -626,12 +654,12 @@ export function AppShell({ children }: { children?: React.ReactNode }) {
|
||||
if (e.key === "n" && e.metaKey) {
|
||||
e.preventDefault();
|
||||
sessionStore.setActiveSession(null);
|
||||
setActiveView("home");
|
||||
setActiveViewWithViewedSession("home", null);
|
||||
}
|
||||
};
|
||||
window.addEventListener("keydown", handler);
|
||||
return () => window.removeEventListener("keydown", handler);
|
||||
}, [clearActiveSession, sessionStore]);
|
||||
}, [clearActiveSession, sessionStore, setActiveViewWithViewedSession]);
|
||||
|
||||
return (
|
||||
<div className="flex h-screen w-screen flex-col overflow-hidden bg-background text-foreground">
|
||||
@@ -657,7 +685,7 @@ export function AppShell({ children }: { children?: React.ReactNode }) {
|
||||
onNewChatInProject={handleNewChatInProject}
|
||||
onNewChat={() => {
|
||||
sessionStore.setActiveSession(null);
|
||||
setActiveView("home");
|
||||
setActiveViewWithViewedSession("home", null);
|
||||
}}
|
||||
onCreateProject={() => openCreateProjectDialog()}
|
||||
onEditProject={handleEditProject}
|
||||
|
||||
Reference in New Issue
Block a user