Skip to content

Commit 4921a3e

Browse files
authored
refactor: move sidebar cache management out of archiveSession() (#578)
## Summary `archiveSession()` was a shared API helper with sidebar-specific SWR cache logic baked in, creating two problems: 1. **Split ownership** — session removal was spread across two layers: the helper updated the SWR cache (first-page sessions) while the sidebar's `handleSessionArchived` updated local `extraSessions` state (paginated sessions). You had to trace through both files to understand how a session disappears. 2. **Hidden side effects** — the session page called `archiveSession()` and silently got a sidebar SWR cache mutation it didn't ask for, coupling the helper to one caller's UI state. **Fix:** Make `archiveSession()` a pure API helper (POST + toast). Each call site now owns its post-archive state management: - **Sidebar** (`handleSessionArchived`): optimistic SWR cache removal + `extraSessions` filter + navigation — all in one callback - **Session page** (`handleArchive`): triggers SWR revalidation via `mutate(SIDEBAR_SESSIONS_KEY)` + navigates to `/` Net result is -31/+13 lines — the code is smaller and each layer has a single, clear responsibility. ## Test plan - [x] `npm test -w @open-inspect/web` — 192 tests pass - [x] `npm run typecheck -w @open-inspect/web` — clean - [x] `npm run lint -w @open-inspect/web` — clean - [ ] Manual: archive from sidebar → session disappears instantly - [ ] Manual: archive from session page → redirects to `/`, session gone from sidebar - [ ] Manual: archive fails (e.g., network off) → toast shown, session stays <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Bug Fixes** * Session archival now consistently removes the archived session from sidebar and session lists across the app, ensuring the UI reflects the current state immediately. * The archive operation's success is now treated as API-confirmed; UI/cache updates are handled by the surrounding UI logic to ensure consistent updates. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
1 parent 323e35e commit 4921a3e

3 files changed

Lines changed: 25 additions & 32 deletions

File tree

packages/web/src/app/(app)/session/[id]/page.tsx

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,11 @@ import { ActionBar } from "@/components/action-bar";
3030
import { copyToClipboard, formatModelNameLower } from "@/lib/format";
3131
import { archiveSession } from "@/lib/archive-session";
3232
import { SHORTCUT_LABELS } from "@/lib/keyboard-shortcuts";
33-
import { SIDEBAR_SESSIONS_KEY } from "@/lib/session-list";
33+
import {
34+
removeSessionFromList,
35+
SIDEBAR_SESSIONS_KEY,
36+
type SessionListResponse,
37+
} from "@/lib/session-list";
3438
import { useMediaQuery } from "@/hooks/use-media-query";
3539
import { DEFAULT_MODEL, getDefaultReasoningEffort, type ModelCategory } from "@open-inspect/shared";
3640
import { useEnabledModels } from "@/hooks/use-enabled-models";
@@ -229,6 +233,14 @@ function SessionPageContent() {
229233
const handleArchive = useCallback(async () => {
230234
const didArchive = await archiveSession(sessionId);
231235
if (didArchive) {
236+
await mutate<SessionListResponse>(
237+
SIDEBAR_SESSIONS_KEY,
238+
(current) =>
239+
current
240+
? { ...current, sessions: removeSessionFromList(current.sessions, sessionId) }
241+
: current,
242+
{ revalidate: false, populateCache: true }
243+
);
232244
router.push("/");
233245
}
234246
}, [router, sessionId]);

packages/web/src/components/session-sidebar.tsx

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import { formatRelativeTime, isInactiveSession } from "@/lib/time";
1111
import {
1212
buildSessionsPageKey,
1313
mergeUniqueSessions,
14+
removeSessionFromList,
1415
SIDEBAR_SESSIONS_KEY,
1516
type SessionListResponse,
1617
} from "@/lib/session-list";
@@ -222,8 +223,14 @@ export function SessionSidebar({ onNewSession, onToggle, onSessionSelect }: Sess
222223

223224
const handleSessionArchived = useCallback(
224225
async (sessionId: string) => {
225-
// First-page sessions are removed from the SWR cache by archiveSession() itself.
226-
// Extra sessions (loaded via pagination) are managed in local state.
226+
await mutate<SessionListResponse>(
227+
SIDEBAR_SESSIONS_KEY,
228+
(current) =>
229+
current
230+
? { ...current, sessions: removeSessionFromList(current.sessions, sessionId) }
231+
: current,
232+
{ revalidate: false, populateCache: true }
233+
);
227234
setExtraSessions((prev) => prev.filter((session) => session.id !== sessionId));
228235

229236
if (currentSessionId === sessionId) {

packages/web/src/lib/archive-session.ts

Lines changed: 3 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,35 +1,10 @@
1-
import { mutate } from "swr";
21
import { toast } from "sonner";
3-
import {
4-
removeSessionFromList,
5-
SIDEBAR_SESSIONS_KEY,
6-
type SessionListResponse,
7-
} from "@/lib/session-list";
82

93
/**
10-
* Removes an archived session from the cached sidebar list without triggering revalidation.
11-
*/
12-
async function removeSessionFromSidebarCache(sessionId: string) {
13-
await mutate<SessionListResponse>(
14-
SIDEBAR_SESSIONS_KEY,
15-
(currentData?: SessionListResponse) =>
16-
currentData
17-
? {
18-
...currentData,
19-
sessions: removeSessionFromList(currentData.sessions, sessionId),
20-
}
21-
: currentData,
22-
{
23-
revalidate: false,
24-
populateCache: true,
25-
}
26-
);
27-
}
28-
29-
/**
30-
* Archives a session and updates the sidebar cache so archived sessions disappear immediately.
4+
* Archives a session via the API.
315
*
32-
* Returns `true` only when both the archive request and cache update succeed.
6+
* Returns `true` when the request succeeds. Callers are responsible for
7+
* updating any client-side caches or navigation state.
338
*/
349
export async function archiveSession(sessionId: string): Promise<boolean> {
3510
try {
@@ -39,7 +14,6 @@ export async function archiveSession(sessionId: string): Promise<boolean> {
3914
return false;
4015
}
4116

42-
await removeSessionFromSidebarCache(sessionId);
4317
return true;
4418
} catch {
4519
toast.error("Failed to archive session");

0 commit comments

Comments
 (0)