diff --git a/src/renderer/src/components/editor/combined-diff/load-sections/combined-diff-section-load-registry.ts b/src/renderer/src/components/editor/combined-diff/load-sections/combined-diff-section-load-registry.ts index f9621bbdf2f..3743a490125 100644 --- a/src/renderer/src/components/editor/combined-diff/load-sections/combined-diff-section-load-registry.ts +++ b/src/renderer/src/components/editor/combined-diff/load-sections/combined-diff-section-load-registry.ts @@ -59,13 +59,15 @@ export function useCombinedDiffSectionLoadRegistry( loadSchedulerRef.current ??= createCombinedDiffLoadScheduler({ loadSection: (index) => loadSectionRef.current(index) }) + // Why here and not in the effect below: child effects run before this parent's, so StrictMode's + // replayed mount would leave a window where the ref reads false while the viewer is live. + registryLiveRef.current = true useEffect(() => { // Why: React StrictMode replays effect cleanup in dev; reset revives the scheduler for the replayed mount. const scheduler = loadSchedulerRef.current const reloadTimers = reloadTimersRef.current scheduler.reset() - registryLiveRef.current = true return () => { registryLiveRef.current = false clearPendingSectionReloadTimers(reloadTimers) diff --git a/src/renderer/src/components/editor/combined-diff/remember-view/use-combined-diff-view-restore.test.tsx b/src/renderer/src/components/editor/combined-diff/remember-view/use-combined-diff-view-restore.test.tsx index 7d11b0a3dd6..99770a2a4e8 100644 --- a/src/renderer/src/components/editor/combined-diff/remember-view/use-combined-diff-view-restore.test.tsx +++ b/src/renderer/src/components/editor/combined-diff/remember-view/use-combined-diff-view-restore.test.tsx @@ -37,6 +37,10 @@ function buildAllModeEntrySet( } } +function statusEntry(path: string): GitStatusEntry { + return { path, status: 'modified', area: 'unstaged', added: 1 } +} + function restoreSections(entrySet: CombinedDiffEntrySet, viewStateKey: string): DiffSection[] { let sections: DiffSection[] = [] renderHook(() => { @@ -148,3 +152,29 @@ describe('useCombinedDiffViewRestore deferral', () => { expect(sections.map((section) => section.loadOnDemand)).toEqual([false, false]) }) }) + +it('clears deferred reload records when the entry set is rebuilt', () => { + // Why: the rebuilt rows load fresh, so a refusal recorded against the old set would charge the + // next save of that path a git diff it no longer needs. + let keys: Set | undefined + const view = renderHook( + ({ entrySet }: { entrySet: CombinedDiffEntrySet }) => { + const registry = useCombinedDiffSectionLoadRegistry(useRef([])) + keys = registry.deferredReloadKeysRef.current + useCombinedDiffViewRestore({ + entrySet, + gitStatusEntries: [], + registry, + setGeneration: () => {}, + setSectionHeights: () => {}, + setSections: () => {}, + setSideBySide: () => {}, + viewStateKey: 'rebuild' + }) + }, + { initialProps: { entrySet: buildAllModeEntrySet([statusEntry('a.ts')], []) } } + ) + keys?.add('a.ts') + view.rerender({ entrySet: buildAllModeEntrySet([statusEntry('a.ts'), statusEntry('b.ts')], []) }) + expect([...(keys ?? [])]).toEqual([]) +}) diff --git a/src/renderer/src/components/editor/combined-diff/remember-view/use-combined-diff-view-restore.ts b/src/renderer/src/components/editor/combined-diff/remember-view/use-combined-diff-view-restore.ts index 7fcc833df91..ee3899e2b43 100644 --- a/src/renderer/src/components/editor/combined-diff/remember-view/use-combined-diff-view-restore.ts +++ b/src/renderer/src/components/editor/combined-diff/remember-view/use-combined-diff-view-restore.ts @@ -57,6 +57,7 @@ export function useCombinedDiffViewRestore({ } = entrySet const { generationRef, + deferredReloadKeysRef, deferredLoadRequestsRef, loadSchedulerRef, loadedIndicesRef, @@ -180,6 +181,9 @@ export function useCombinedDiffViewRestore({ loadedIndicesRef.current.clear() loadingIndicesRef.current.clear() sectionLoadTokensRef.current.clear() + // Why: the rebuilt rows load fresh, so a refusal recorded against the old set would charge the + // next save of that path a git diff it no longer needs. + deferredReloadKeysRef.current.clear() clearPendingSectionReloadTimers(reloadTimersRef.current) loadSchedulerRef.current.reset() generationRef.current += 1 @@ -188,6 +192,7 @@ export function useCombinedDiffViewRestore({ entries, entrySignature, generationRef, + deferredReloadKeysRef, deferredLoadRequestsRef, gitStatusEntries, hasUncommittedEntriesSnapshot,