diff --git a/src/components/layout/aux-panel-file-tree-tab.tsx b/src/components/layout/aux-panel-file-tree-tab.tsx index b2e7871..4f410ff 100644 --- a/src/components/layout/aux-panel-file-tree-tab.tsx +++ b/src/components/layout/aux-panel-file-tree-tab.tsx @@ -1895,8 +1895,23 @@ export function FileTreeTab() { if (!rootPath) return let unlisten: (() => void) | null = null + let disposed = false + let watchStarted = false + let watchReleased = false const normalizedRootPath = normalizeComparePath(rootPath) + const releaseWatch = () => { + if (watchReleased) return + watchReleased = true + if (unlisten) { + unlisten() + unlisten = null + } + if (watchStarted) { + void stopFileTreeWatch(rootPath) + } + } + const scheduleTreeRefresh = (refreshGitStatus: boolean) => { if (!isFileTreeTabActiveRef.current) { pendingTreeRefreshRef.current = true @@ -2027,13 +2042,18 @@ export function FileTreeTab() { const setup = async () => { try { await startFileTreeWatch(rootPath) + watchStarted = true } catch (error) { const message = error instanceof Error ? error.message : String(error) toast.error(t("toasts.watchStartFailed"), { description: message }) } + if (disposed) { + releaseWatch() + return + } try { - unlisten = await subscribe( + const subscribedUnlisten = await subscribe( "folder://file-tree-changed", (payload) => { if ( @@ -2109,6 +2129,12 @@ export function FileTreeTab() { })() } ) + if (disposed) { + subscribedUnlisten() + releaseWatch() + return + } + unlisten = subscribedUnlisten } catch (error) { console.error("[FileTreeTab] failed to listen file watch event:", error) } @@ -2117,6 +2143,7 @@ export function FileTreeTab() { void setup() return () => { + disposed = true if (treeRefreshTimerRef.current) { clearTimeout(treeRefreshTimerRef.current) treeRefreshTimerRef.current = null @@ -2129,8 +2156,7 @@ export function FileTreeTab() { pendingTreeRefreshRef.current = false pendingTreeRefreshNeedsStatusRef.current = false pendingStatusRefreshRef.current = false - unlisten?.() - void stopFileTreeWatch(rootPath) + releaseWatch() } }, [fetchTree, folder?.path, openFilePreview, t]) diff --git a/src/components/layout/aux-panel-git-changes-tab.tsx b/src/components/layout/aux-panel-git-changes-tab.tsx index a87983f..bd82dd2 100644 --- a/src/components/layout/aux-panel-git-changes-tab.tsx +++ b/src/components/layout/aux-panel-git-changes-tab.tsx @@ -615,8 +615,23 @@ export function GitChangesTab() { if (!rootPath || !isChangesTabActive) return let unlisten: (() => void) | null = null + let disposed = false + let watchStarted = false + let watchReleased = false const normalizedRootPath = normalizeComparePath(rootPath) + const releaseWatch = () => { + if (watchReleased) return + watchReleased = true + if (unlisten) { + unlisten() + unlisten = null + } + if (watchStarted) { + void stopFileTreeWatch(rootPath) + } + } + const scheduleRefresh = () => { if (refreshTimerRef.current) { clearTimeout(refreshTimerRef.current) @@ -629,12 +644,17 @@ export function GitChangesTab() { const setup = async () => { try { await startFileTreeWatch(rootPath) + watchStarted = true } catch { // ignore watch startup errors } + if (disposed) { + releaseWatch() + return + } try { - unlisten = await subscribe( + const subscribedUnlisten = await subscribe( "folder://file-tree-changed", (payload) => { if ( @@ -646,6 +666,12 @@ export function GitChangesTab() { scheduleRefresh() } ) + if (disposed) { + subscribedUnlisten() + releaseWatch() + return + } + unlisten = subscribedUnlisten } catch { // ignore listen errors } @@ -654,12 +680,12 @@ export function GitChangesTab() { void setup() return () => { + disposed = true if (refreshTimerRef.current) { clearTimeout(refreshTimerRef.current) refreshTimerRef.current = null } - unlisten?.() - void stopFileTreeWatch(rootPath) + releaseWatch() } }, [fetchChanges, folder?.path, isChangesTabActive])