From ba13d3b9e1de1701d7bcb33d601e193e99c834de Mon Sep 17 00:00:00 2001 From: kev1n77 Date: Fri, 5 Jun 2026 11:35:33 +0800 Subject: [PATCH] fix(session-archive): fix stale expand toggle after archiving sessions add collapsible archived groups with full path --- .../sections/sessions/SessionsSection.tsx | 20 ++++++++ .../components/ArchivedSessionsConfig.scss | 15 ++++++ .../components/ArchivedSessionsConfig.tsx | 50 ++++++++++++++++--- 3 files changed, 78 insertions(+), 7 deletions(-) diff --git a/src/web-ui/src/app/components/NavPanel/sections/sessions/SessionsSection.tsx b/src/web-ui/src/app/components/NavPanel/sections/sessions/SessionsSection.tsx index 565400199..dd8daff7a 100644 --- a/src/web-ui/src/app/components/NavPanel/sections/sessions/SessionsSection.tsx +++ b/src/web-ui/src/app/components/NavPanel/sections/sessions/SessionsSection.tsx @@ -243,6 +243,26 @@ const SessionsSection: React.FC = ({ void loadMetadataPage(SESSIONS_LEVEL_0, undefined, 'sessions_nav_initial'); }, [isVisible, workspacePath, remoteConnectionId, remoteSshHost, loadMetadataPage]); + // When sessions are archived, reset stale metadata so the expand toggle + // doesn't linger with old counts after all sessions are gone. + useEffect(() => { + const handler = () => { + metadataLoadRequestIdRef.current += 1; + setExpandLevel(0); + setMetadataPageState({ + totalTopLevelCount: null, + nextCursor: undefined, + hasMore: false, + isLoading: false, + }); + if (isVisible && workspacePath) { + void loadMetadataPage(SESSIONS_LEVEL_0, undefined, 'sessions_nav_post_archive'); + } + }; + window.addEventListener('bitfun:session-archived', handler); + return () => window.removeEventListener('bitfun:session-archived', handler); + }, [isVisible, workspacePath, loadMetadataPage]); + useEffect(() => { if (!openMenuSessionId) return; const handleOutside = (event: MouseEvent) => { diff --git a/src/web-ui/src/app/scenes/settings/components/ArchivedSessionsConfig.scss b/src/web-ui/src/app/scenes/settings/components/ArchivedSessionsConfig.scss index 8e735b89d..4d53fb2d5 100644 --- a/src/web-ui/src/app/scenes/settings/components/ArchivedSessionsConfig.scss +++ b/src/web-ui/src/app/scenes/settings/components/ArchivedSessionsConfig.scss @@ -51,6 +51,21 @@ color: var(--color-text-muted); text-transform: uppercase; letter-spacing: 0.04em; + width: 100%; + border: none; + background: transparent; + cursor: pointer; + transition: background var(--motion-fast) var(--easing-standard); + + &:hover { + background: var(--element-bg-hover); + } + } + + &__group-chevron { + flex-shrink: 0; + color: var(--color-text-muted); + opacity: 0.6; } &__group-name { diff --git a/src/web-ui/src/app/scenes/settings/components/ArchivedSessionsConfig.tsx b/src/web-ui/src/app/scenes/settings/components/ArchivedSessionsConfig.tsx index 232d896dc..260a381b3 100644 --- a/src/web-ui/src/app/scenes/settings/components/ArchivedSessionsConfig.tsx +++ b/src/web-ui/src/app/scenes/settings/components/ArchivedSessionsConfig.tsx @@ -7,8 +7,8 @@ * confirmation dialog. */ -import React, { useCallback, useEffect, useMemo, useState } from 'react'; -import { Trash2, RotateCcw, Inbox, RefreshCw } from 'lucide-react'; +import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'; +import { Trash2, RotateCcw, Inbox, RefreshCw, ChevronRight, ChevronDown } from 'lucide-react'; import { useTranslation } from 'react-i18next'; import { ConfigPageLayout, @@ -114,6 +114,8 @@ const ArchivedSessionsConfig: React.FC = () => { const [loading, setLoading] = useState(true); const [entries, setEntries] = useState([]); + const [collapsedWorkspaces, setCollapsedWorkspaces] = useState>(new Set()); + const prevLoadingRef = useRef(loading); // ── Load archived sessions from all open workspaces ────────────────────── @@ -180,6 +182,14 @@ const ArchivedSessionsConfig: React.FC = () => { return map; }, [entries]); + // Collapse all workspace groups by default when data finishes loading + useEffect(() => { + if (prevLoadingRef.current && !loading && grouped.size > 0) { + setCollapsedWorkspaces(new Set(grouped.keys())); + } + prevLoadingRef.current = loading; + }, [loading, grouped]); + // ── Remove an entry from local state after mutation ────────────────────── const removeEntry = useCallback((sessionId: string) => { @@ -190,6 +200,18 @@ const ArchivedSessionsConfig: React.FC = () => { setEntries([]); }, []); + const toggleWorkspace = useCallback((workspacePath: string) => { + setCollapsedWorkspaces(prev => { + const next = new Set(prev); + if (next.has(workspacePath)) { + next.delete(workspacePath); + } else { + next.add(workspacePath); + } + return next; + }); + }, []); + // ── Restore single session ─────────────────────────────────────────────── const handleRestore = useCallback(async (entry: ArchivedEntry) => { @@ -333,14 +355,26 @@ const ArchivedSessionsConfig: React.FC = () => { title={t('nav.sessions.archivedSessions')} extra={headerExtra} > - {Array.from(grouped.entries()).map(([workspacePath, group]) => ( + {Array.from(grouped.entries()).map(([workspacePath, group]) => { + const isCollapsed = collapsedWorkspaces.has(workspacePath); + return (
-
- {group.name} +
+ + {!isCollapsed && (
{group.entries.map(entry => ( { /> ))}
+ )}
- ))} + ); + })} )}