Skip to content

Commit 34d3236

Browse files
author
GCWing
committed
fix(web-ui): satisfy session navigation lint contracts
1 parent f3dca16 commit 34d3236

2 files changed

Lines changed: 39 additions & 35 deletions

File tree

‎src/web-ui/src/app/components/NavPanel/sections/sessions/SessionsSection.tsx‎

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -293,9 +293,11 @@ const SessionsSection: React.FC<SessionsSectionProps> = ({
293293
sessionNavStatusService.getOrderingSnapshot,
294294
sessionNavStatusService.getOrderingSnapshot,
295295
);
296-
const runningSessionIds = useMemo(() => new Set(
297-
[...flowChatState.sessions.keys()].filter(sessionNavStatusService.isRunning),
298-
), [flowChatState.sessions, orderingRevision]);
296+
const runningSessionIds = useMemo(() => {
297+
// The revision invalidates these reads from the live navigation service.
298+
void orderingRevision;
299+
return new Set([...flowChatState.sessions.keys()].filter(sessionNavStatusService.isRunning));
300+
}, [flowChatState.sessions, orderingRevision]);
299301
const [scheduledJobsSessionId, setScheduledJobsSessionId] = useState<string | null>(null);
300302
const [batchWorkspace, setBatchWorkspace] = useState<WorkspaceSessionScope | null>(null);
301303
const editInputRef = useRef<HTMLInputElement>(null);
@@ -798,6 +800,8 @@ const SessionsSection: React.FC<SessionsSectionProps> = ({
798800
);
799801

800802
const { topLevelSessions: allTopLevelSessions, childrenByParent } = useMemo(() => {
803+
// Activity timestamps can change independently of the session records.
804+
void orderingRevision;
801805
const childMap = new Map<string, Session[]>();
802806
const parents: Session[] = [];
803807

‎src/web-ui/src/app/stores/sceneStore.ts‎

Lines changed: 32 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -72,38 +72,6 @@ export interface SessionSceneNavigation {
7272
let sessionNavigation: SessionSceneNavigation | undefined;
7373
let navigationRequest = 0;
7474

75-
export function registerSessionSceneNavigation(adapter: SessionSceneNavigation): () => void {
76-
sessionNavigation = adapter;
77-
return () => {
78-
if (sessionNavigation === adapter) {
79-
sessionNavigation = undefined;
80-
navigationRequest++;
81-
if (useSceneStore.getState().pendingTabId !== null) useSceneStore.setState({ pendingTabId: null });
82-
}
83-
};
84-
}
85-
86-
/** Every navigation path, including history and close fallback, uses this gate. */
87-
function navigateToScene(id: SceneTabId | null, commit: () => void, session?: SessionSceneTarget): void {
88-
const hadPendingNavigation = useSceneStore.getState().pendingTabId !== null;
89-
const request = ++navigationRequest;
90-
const adapter = sessionNavigation;
91-
const target = session ?? useSceneStore.getState().openTabs.find(tab => tab.id === id)?.session;
92-
const isCurrent = () => request === navigationRequest && adapter === sessionNavigation;
93-
if (!target || !adapter || (!hadPendingNavigation && adapter.isActive(target))) {
94-
if (useSceneStore.getState().pendingTabId !== null) useSceneStore.setState({ pendingTabId: null });
95-
commit();
96-
return;
97-
}
98-
99-
useSceneStore.setState({ pendingTabId: id });
100-
void adapter.activate(target, isCurrent).then(activated => {
101-
if (activated && isCurrent()) commit();
102-
}).finally(() => {
103-
if (isCurrent()) useSceneStore.setState({ pendingTabId: null });
104-
});
105-
}
106-
10775
function resolveNavSceneId(sceneId: SceneTabId | null): SceneTabId | null {
10876
if (sceneId === null) return null;
10977
return getSceneNav(sceneId) ? sceneId : null;
@@ -314,6 +282,38 @@ export const useSceneStore = create<SceneState>((set, get) => ({
314282
},
315283
}));
316284

285+
export function registerSessionSceneNavigation(adapter: SessionSceneNavigation): () => void {
286+
sessionNavigation = adapter;
287+
return () => {
288+
if (sessionNavigation === adapter) {
289+
sessionNavigation = undefined;
290+
navigationRequest++;
291+
if (useSceneStore.getState().pendingTabId !== null) useSceneStore.setState({ pendingTabId: null });
292+
}
293+
};
294+
}
295+
296+
/** Every navigation path, including history and close fallback, uses this gate. */
297+
function navigateToScene(id: SceneTabId | null, commit: () => void, session?: SessionSceneTarget): void {
298+
const hadPendingNavigation = useSceneStore.getState().pendingTabId !== null;
299+
const request = ++navigationRequest;
300+
const adapter = sessionNavigation;
301+
const target = session ?? useSceneStore.getState().openTabs.find(tab => tab.id === id)?.session;
302+
const isCurrent = () => request === navigationRequest && adapter === sessionNavigation;
303+
if (!target || !adapter || (!hadPendingNavigation && adapter.isActive(target))) {
304+
if (useSceneStore.getState().pendingTabId !== null) useSceneStore.setState({ pendingTabId: null });
305+
commit();
306+
return;
307+
}
308+
309+
useSceneStore.setState({ pendingTabId: id });
310+
void adapter.activate(target, isCurrent).then(activated => {
311+
if (activated && isCurrent()) commit();
312+
}).finally(() => {
313+
if (isCurrent()) useSceneStore.setState({ pendingTabId: null });
314+
});
315+
}
316+
317317
function openSceneTarget(id: SceneTabId, session?: SessionSceneTarget): void {
318318
const get = useSceneStore.getState;
319319
const set = useSceneStore.setState;

0 commit comments

Comments
 (0)