fix(sidebar): key parent-expansion per render context (#1302)
The same session can appear in two render contexts at once — most visibly under "Recent" and under its project's root group. Expansion state was keyed by bare session id, so toggling the chevron in one context flipped it in the other; the user clicked one row and a duplicate elsewhere expanded/collapsed in sync. Switch the expansion set to composite keys of the form `<renderContext>:<active|archived>:<sessionId>`. SessionNodeItem already computes this exact shape for its menu instance key (menuInstanceKey); reuse it as expansionKey so the two render-time instances of one session keep independent expand state. Updated: - isExpanded reads `expandedParents.has(expansionKey)`. - Chevron click / Enter / Space pass `expansionKey` to toggleParent instead of session.id. - The memo-equality check compares the same composite key on both sides so it correctly re-renders when only one context's state changes. - toggleParent's parameter is renamed `expansionKey` to reflect the new shape; the implementation is unchanged. - The auto-expand-parent-on-subagent-navigation effect doesn't know which context the user will look at the parent in, so it fans the parentID out to all four (project|recent) × (active|archived) combinations. Storage is bumped to `oc.sessions.expandedParents.v2`. Existing v1 data (bare session ids) is one-shot migrated by fanning each id across all four contexts and rewritten under the v2 key; the v1 key is then removed so a downgrade-then-reupgrade doesn't re-migrate. Co-authored-by: vhqtvn <8930337+vhqtvn@users.noreply.github.com>
This commit is contained in:
committed by
GitHub
co-authored by
vhqtvn
parent
7582db6df6
commit
9ab90d4f1a
@@ -6,10 +6,16 @@ import { useProjectsStore } from '@/stores/useProjectsStore';
|
||||
type SafeStorageLike = {
|
||||
getItem: (key: string) => string | null;
|
||||
setItem: (key: string, value: string) => void;
|
||||
removeItem?: (key: string) => void;
|
||||
};
|
||||
|
||||
type Keys = {
|
||||
sessionExpanded: string;
|
||||
// v1 key, still on disk for users upgrading from pre-per-context expansion.
|
||||
// When present, its bare-session-id entries are fanned out to all four
|
||||
// (project|recent) × (active|archived) context combinations and rewritten
|
||||
// under `sessionExpanded`. After migration the v1 key is removed.
|
||||
sessionExpandedLegacy: string;
|
||||
projectCollapse: string;
|
||||
sessionPinned: string;
|
||||
groupOrder: string;
|
||||
@@ -17,6 +23,13 @@ type Keys = {
|
||||
groupCollapse: string;
|
||||
};
|
||||
|
||||
const LEGACY_EXPANSION_CONTEXT_PREFIXES = [
|
||||
'project:active:',
|
||||
'project:archived:',
|
||||
'recent:active:',
|
||||
'recent:archived:',
|
||||
];
|
||||
|
||||
type Args = {
|
||||
isVSCode: boolean;
|
||||
hasLoadedGlobalSessions: boolean;
|
||||
@@ -101,6 +114,28 @@ export const useSidebarPersistence = (args: Args) => {
|
||||
if (Array.isArray(parsed)) {
|
||||
setExpandedParents(new Set(parsed.filter((item) => typeof item === 'string')));
|
||||
}
|
||||
} else {
|
||||
// No v2 data — migrate from v1 (bare session ids) if present.
|
||||
const legacyRaw = safeStorage.getItem(keys.sessionExpandedLegacy);
|
||||
if (legacyRaw) {
|
||||
try {
|
||||
const parsedLegacy = JSON.parse(legacyRaw);
|
||||
if (Array.isArray(parsedLegacy)) {
|
||||
const migrated = new Set<string>();
|
||||
parsedLegacy.forEach((item) => {
|
||||
if (typeof item !== 'string' || item.length === 0) return;
|
||||
LEGACY_EXPANSION_CONTEXT_PREFIXES.forEach((prefix) => migrated.add(`${prefix}${item}`));
|
||||
});
|
||||
if (migrated.size > 0) {
|
||||
setExpandedParents(migrated);
|
||||
try { safeStorage.setItem(keys.sessionExpanded, JSON.stringify(Array.from(migrated))); } catch { /* ignored */ }
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
// legacy data was malformed; ignore and let it expire
|
||||
}
|
||||
try { safeStorage.removeItem?.(keys.sessionExpandedLegacy); } catch { /* ignored */ }
|
||||
}
|
||||
}
|
||||
const storedProjects = safeStorage.getItem(keys.projectCollapse);
|
||||
if (storedProjects) {
|
||||
@@ -112,7 +147,7 @@ export const useSidebarPersistence = (args: Args) => {
|
||||
} catch {
|
||||
// ignored
|
||||
}
|
||||
}, [keys.projectCollapse, keys.sessionExpanded, safeStorage, setCollapsedProjects, setExpandedParents]);
|
||||
}, [keys.projectCollapse, keys.sessionExpanded, keys.sessionExpandedLegacy, safeStorage, setCollapsedProjects, setExpandedParents]);
|
||||
|
||||
React.useEffect(() => {
|
||||
if (!hasLoadedGlobalSessions) {
|
||||
|
||||
Reference in New Issue
Block a user