From 1895c8f9477f4a90c73360dc2017baf20c323131 Mon Sep 17 00:00:00 2001 From: mawkone Date: Sun, 14 Jun 2026 13:13:50 -0700 Subject: [PATCH] perf(codebase): implement in-memory cache for file tree to persist state across tab navigations --- .../components/project/gitea-file-tree.tsx | 45 +++++++++++++++---- 1 file changed, 37 insertions(+), 8 deletions(-) diff --git a/vibn-frontend/components/project/gitea-file-tree.tsx b/vibn-frontend/components/project/gitea-file-tree.tsx index 9084f64..ceaa0d6 100644 --- a/vibn-frontend/components/project/gitea-file-tree.tsx +++ b/vibn-frontend/components/project/gitea-file-tree.tsx @@ -38,21 +38,50 @@ interface GiteaFileTreeProps { selectedPath?: string; } +// ── In-memory cache to persist tree state across tab navigations ── +const treeCache: Record< + string, + { + rootItems: TreeItem[]; + childrenByPath: Record; + expanded: Set; + } +> = {}; + export function GiteaFileTree({ projectId, rootPath, onSelectFile, selectedPath, }: GiteaFileTreeProps) { - const [rootItems, setRootItems] = useState(null); - const [loading, setLoading] = useState(true); + const cacheKey = `${projectId}::${rootPath}`; + + const [rootItems, setRootItems] = useState(() => { + return treeCache[cacheKey]?.rootItems ?? null; + }); + const [loading, setLoading] = useState(() => !treeCache[cacheKey]); const [error, setError] = useState(null); - const [expanded, setExpanded] = useState>(new Set()); + const [expanded, setExpanded] = useState>(() => { + return treeCache[cacheKey]?.expanded ?? new Set(); + }); const [childrenByPath, setChildrenByPath] = useState< Record - >({}); + >(() => { + return treeCache[cacheKey]?.childrenByPath ?? {}; + }); const [loadingPaths, setLoadingPaths] = useState>(new Set()); + // Keep cache synced with state updates + useEffect(() => { + if (rootItems) { + treeCache[cacheKey] = { + rootItems, + childrenByPath, + expanded, + }; + } + }, [cacheKey, rootItems, childrenByPath, expanded]); + const fetchPath = useCallback( async (path: string): Promise => { const res = await fetch( @@ -71,12 +100,12 @@ export function GiteaFileTree({ // Load root whenever projectId or rootPath changes useEffect(() => { + // If we already loaded this from cache on mount, skip fetching again + if (treeCache[cacheKey]) return; + let cancelled = false; setLoading(true); setError(null); - setRootItems(null); - setExpanded(new Set()); - setChildrenByPath({}); fetchPath(rootPath) .then(async (items) => { @@ -119,7 +148,7 @@ export function GiteaFileTree({ return () => { cancelled = true; }; - }, [projectId, rootPath, fetchPath]); + }, [projectId, rootPath, fetchPath, cacheKey]); const toggleDir = useCallback( async (path: string) => {