fix: queue diff opening until tab activation
This commit is contained in:
37
src/App.tsx
37
src/App.tsx
@@ -534,8 +534,8 @@ function App() {
|
||||
},
|
||||
onToast: (msg) => setToastMessage(msg),
|
||||
})
|
||||
const pulseCommitDiffRequestIdRef = useRef(0)
|
||||
const [pulseCommitDiffRequest, setPulseCommitDiffRequest] = useState<CommitDiffRequest | null>(null)
|
||||
const pendingDiffRequestIdRef = useRef(0)
|
||||
const [pendingDiffRequest, setPendingDiffRequest] = useState<CommitDiffRequest | null>(null)
|
||||
|
||||
// Note window: auto-open the note from URL params without scanning the whole vault.
|
||||
const noteWindowOpenedRef = useRef(false)
|
||||
@@ -600,17 +600,17 @@ function App() {
|
||||
onSelectNote: notes.handleSelectNote,
|
||||
})
|
||||
|
||||
const queuePulseCommitDiff = useCallback((path: string, commitHash: string) => {
|
||||
pulseCommitDiffRequestIdRef.current += 1
|
||||
setPulseCommitDiffRequest({
|
||||
requestId: pulseCommitDiffRequestIdRef.current,
|
||||
const queuePendingDiff = useCallback((path: string, commitHash?: string) => {
|
||||
pendingDiffRequestIdRef.current += 1
|
||||
setPendingDiffRequest({
|
||||
requestId: pendingDiffRequestIdRef.current,
|
||||
path,
|
||||
commitHash,
|
||||
})
|
||||
}, [])
|
||||
|
||||
const handlePulseCommitDiffHandled = useCallback((requestId: number) => {
|
||||
setPulseCommitDiffRequest((current) =>
|
||||
const handlePendingDiffHandled = useCallback((requestId: number) => {
|
||||
setPendingDiffRequest((current) =>
|
||||
current?.requestId === requestId ? null : current,
|
||||
)
|
||||
}, [])
|
||||
@@ -621,7 +621,7 @@ function App() {
|
||||
|
||||
if (commitHash) {
|
||||
const targetPath = entry?.path ?? fullPath
|
||||
queuePulseCommitDiff(targetPath, commitHash)
|
||||
queuePendingDiff(targetPath, commitHash)
|
||||
if (entry) {
|
||||
void handleSelectNote(entry)
|
||||
} else {
|
||||
@@ -633,7 +633,7 @@ function App() {
|
||||
if (entry) {
|
||||
void handleSelectNote(entry)
|
||||
}
|
||||
}, [entriesByPath, resolvedPath, queuePulseCommitDiff, handleSelectNote, openTabWithContent])
|
||||
}, [entriesByPath, resolvedPath, queuePendingDiff, handleSelectNote, openTabWithContent])
|
||||
|
||||
const handleOpenFavorite = useCallback(async (entry: VaultEntry) => {
|
||||
await handleReplaceActiveTab(entry)
|
||||
@@ -812,11 +812,18 @@ function App() {
|
||||
}
|
||||
notes.openTabWithContent(entry, previewContent)
|
||||
if (hasDiff) {
|
||||
setTimeout(() => diffToggleRef.current(), 50)
|
||||
queuePendingDiff(entry.path)
|
||||
} else {
|
||||
setToastMessage('Content not available (untracked)')
|
||||
}
|
||||
}, [vault, notes, setToastMessage])
|
||||
}, [vault, notes, queuePendingDiff, setToastMessage])
|
||||
|
||||
const handleReplaceActiveTabWithQueuedDiff = useCallback((entry: VaultEntry) => {
|
||||
notes.handleReplaceActiveTab(entry)
|
||||
if (effectiveSelection.kind === 'filter' && effectiveSelection.filter === 'changes') {
|
||||
queuePendingDiff(entry.path)
|
||||
}
|
||||
}, [effectiveSelection, notes, queuePendingDiff])
|
||||
|
||||
const commitFlow = useCommitFlow({
|
||||
savePending: appSave.savePending,
|
||||
@@ -1444,7 +1451,7 @@ function App() {
|
||||
{effectiveSelection.kind === 'filter' && effectiveSelection.filter === 'pulse' ? (
|
||||
<PulseView vaultPath={resolvedPath} onOpenNote={handlePulseOpenNote} sidebarCollapsed={!sidebarVisible} onExpandSidebar={() => handleSetViewMode('all')} />
|
||||
) : (
|
||||
<NoteList entries={vault.entries} selection={effectiveSelection} selectedNote={activeTab?.entry ?? null} noteListFilter={noteListFilter} onNoteListFilterChange={setNoteListFilter} inboxPeriod={inboxPeriod} modifiedFiles={vault.modifiedFiles} modifiedFilesError={vault.modifiedFilesError} getNoteStatus={vault.getNoteStatus} sidebarCollapsed={!sidebarVisible} onSelectNote={notes.handleSelectNote} onReplaceActiveTab={notes.handleReplaceActiveTab} onEnterNeighborhood={handleEnterNeighborhood} onCreateNote={notes.handleCreateNoteImmediate} onBulkOrganize={explicitOrganizationEnabled ? bulkActions.handleBulkOrganize : undefined} onBulkArchive={bulkActions.handleBulkArchive} onBulkDeletePermanently={deleteActions.handleBulkDeletePermanently} onUpdateTypeSort={notes.handleUpdateFrontmatter} onUpdateViewDefinition={handleUpdateViewDefinition} updateEntry={vault.updateEntry} onOpenInNewWindow={handleOpenEntryInNewWindow} onDiscardFile={handleDiscardFile} onAutoTriggerDiff={() => diffToggleRef.current()} onOpenDeletedNote={handleOpenDeletedNote} allNotesNoteListProperties={vaultConfig.allNotes?.noteListProperties ?? null} onUpdateAllNotesNoteListProperties={handleUpdateAllNotesNoteListProperties} inboxNoteListProperties={vaultConfig.inbox?.noteListProperties ?? null} onUpdateInboxNoteListProperties={handleUpdateInboxNoteListProperties} views={vault.views} visibleNotesRef={visibleNotesRef} multiSelectionCommandRef={multiSelectionCommandRef} />
|
||||
<NoteList entries={vault.entries} selection={effectiveSelection} selectedNote={activeTab?.entry ?? null} noteListFilter={noteListFilter} onNoteListFilterChange={setNoteListFilter} inboxPeriod={inboxPeriod} modifiedFiles={vault.modifiedFiles} modifiedFilesError={vault.modifiedFilesError} getNoteStatus={vault.getNoteStatus} sidebarCollapsed={!sidebarVisible} onSelectNote={notes.handleSelectNote} onReplaceActiveTab={handleReplaceActiveTabWithQueuedDiff} onEnterNeighborhood={handleEnterNeighborhood} onCreateNote={notes.handleCreateNoteImmediate} onBulkOrganize={explicitOrganizationEnabled ? bulkActions.handleBulkOrganize : undefined} onBulkArchive={bulkActions.handleBulkArchive} onBulkDeletePermanently={deleteActions.handleBulkDeletePermanently} onUpdateTypeSort={notes.handleUpdateFrontmatter} onUpdateViewDefinition={handleUpdateViewDefinition} updateEntry={vault.updateEntry} onOpenInNewWindow={handleOpenEntryInNewWindow} onDiscardFile={handleDiscardFile} onOpenDeletedNote={handleOpenDeletedNote} allNotesNoteListProperties={vaultConfig.allNotes?.noteListProperties ?? null} onUpdateAllNotesNoteListProperties={handleUpdateAllNotesNoteListProperties} inboxNoteListProperties={vaultConfig.inbox?.noteListProperties ?? null} onUpdateInboxNoteListProperties={handleUpdateInboxNoteListProperties} views={vault.views} visibleNotesRef={visibleNotesRef} multiSelectionCommandRef={multiSelectionCommandRef} />
|
||||
)}
|
||||
</div>
|
||||
<ResizeHandle onResize={layout.handleNoteListResize} />
|
||||
@@ -1458,8 +1465,8 @@ function App() {
|
||||
onNavigateWikilink={notes.handleNavigateWikilink}
|
||||
onLoadDiff={vault.loadDiff}
|
||||
onLoadDiffAtCommit={vault.loadDiffAtCommit}
|
||||
pendingCommitDiffRequest={pulseCommitDiffRequest}
|
||||
onPendingCommitDiffHandled={handlePulseCommitDiffHandled}
|
||||
pendingCommitDiffRequest={pendingDiffRequest}
|
||||
onPendingCommitDiffHandled={handlePendingDiffHandled}
|
||||
getNoteStatus={vault.getNoteStatus}
|
||||
onCreateNote={notes.handleCreateNoteImmediate}
|
||||
inspectorCollapsed={layout.inspectorCollapsed}
|
||||
|
||||
@@ -35,6 +35,43 @@ describe('useDiffMode', () => {
|
||||
warn.mockRestore()
|
||||
}
|
||||
|
||||
async function expectPendingDiffRequestLoaded(options: {
|
||||
diffContent: string
|
||||
requestId: number
|
||||
commitHash?: string
|
||||
}) {
|
||||
const { diffContent, requestId, commitHash = '' } = options
|
||||
const onPendingCommitDiffHandled = vi.fn()
|
||||
|
||||
if (commitHash) {
|
||||
onLoadDiffAtCommit.mockResolvedValue(diffContent)
|
||||
} else {
|
||||
onLoadDiff.mockResolvedValue(diffContent)
|
||||
}
|
||||
|
||||
const { result } = renderHook(() => useDiffMode({
|
||||
activeTabPath: '/note.md',
|
||||
onLoadDiff,
|
||||
onLoadDiffAtCommit,
|
||||
pendingCommitDiffRequest: { requestId, path: '/note.md', commitHash },
|
||||
onPendingCommitDiffHandled,
|
||||
}))
|
||||
|
||||
await waitFor(() => {
|
||||
if (commitHash) {
|
||||
expect(onLoadDiffAtCommit).toHaveBeenCalledWith('/note.md', commitHash)
|
||||
} else {
|
||||
expect(onLoadDiff).toHaveBeenCalledWith('/note.md')
|
||||
expect(onLoadDiffAtCommit).not.toHaveBeenCalled()
|
||||
}
|
||||
})
|
||||
await waitFor(() => {
|
||||
expect(result.current.diffMode).toBe(true)
|
||||
expect(result.current.diffContent).toBe(diffContent)
|
||||
expect(onPendingCommitDiffHandled).toHaveBeenCalledWith(requestId)
|
||||
})
|
||||
}
|
||||
|
||||
it('starts with diff mode off', () => {
|
||||
const { result } = renderDiffHook()
|
||||
expect(result.current.diffMode).toBe(false)
|
||||
@@ -121,23 +158,17 @@ describe('useDiffMode', () => {
|
||||
})
|
||||
|
||||
it('loads a pending commit diff request when the matching tab is active', async () => {
|
||||
onLoadDiffAtCommit.mockResolvedValue('pulse diff')
|
||||
const onPendingCommitDiffHandled = vi.fn()
|
||||
|
||||
const { result } = renderHook(() => useDiffMode({
|
||||
activeTabPath: '/note.md',
|
||||
onLoadDiffAtCommit,
|
||||
pendingCommitDiffRequest: { requestId: 7, path: '/note.md', commitHash: 'abc123' },
|
||||
onPendingCommitDiffHandled,
|
||||
}))
|
||||
|
||||
await waitFor(() => {
|
||||
expect(onLoadDiffAtCommit).toHaveBeenCalledWith('/note.md', 'abc123')
|
||||
await expectPendingDiffRequestLoaded({
|
||||
diffContent: 'pulse diff',
|
||||
requestId: 7,
|
||||
commitHash: 'abc123',
|
||||
})
|
||||
await waitFor(() => {
|
||||
expect(result.current.diffMode).toBe(true)
|
||||
expect(result.current.diffContent).toBe('pulse diff')
|
||||
expect(onPendingCommitDiffHandled).toHaveBeenCalledWith(7)
|
||||
})
|
||||
|
||||
it('loads a pending working-tree diff request when the matching tab is active', async () => {
|
||||
await expectPendingDiffRequestLoaded({
|
||||
diffContent: 'working tree diff',
|
||||
requestId: 9,
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ import {
|
||||
export interface CommitDiffRequest {
|
||||
requestId: number
|
||||
path: string
|
||||
commitHash: string
|
||||
commitHash?: string | null
|
||||
}
|
||||
|
||||
interface UseDiffModeParams {
|
||||
@@ -73,6 +73,10 @@ function shouldHandlePendingCommitDiffRequest(
|
||||
return !!pendingCommitDiffRequest && pendingCommitDiffRequest.path === activeTabPath
|
||||
}
|
||||
|
||||
function hasCommitHash(pendingCommitDiffRequest: CommitDiffRequest): pendingCommitDiffRequest is CommitDiffRequest & { commitHash: string } {
|
||||
return typeof pendingCommitDiffRequest.commitHash === 'string' && pendingCommitDiffRequest.commitHash.length > 0
|
||||
}
|
||||
|
||||
function buildGuardedDiffStateSetters(
|
||||
cancelledRef: { current: boolean },
|
||||
{ setDiffMode, setDiffContent, setDiffLoading, setDiffPath }: DiffStateSetters,
|
||||
@@ -87,18 +91,27 @@ function buildGuardedDiffStateSetters(
|
||||
|
||||
function runPendingCommitDiffRequest(
|
||||
pendingCommitDiffRequest: CommitDiffRequest,
|
||||
onLoadDiff: ((path: string) => Promise<string>) | undefined,
|
||||
onLoadDiffAtCommit: (path: string, commitHash: string) => Promise<string>,
|
||||
onPendingCommitDiffHandled: ((requestId: number) => void) | undefined,
|
||||
diffState: DiffStateSetters,
|
||||
) {
|
||||
const cancelledRef = { current: false }
|
||||
|
||||
void loadCommitDiffForPath(
|
||||
pendingCommitDiffRequest.path,
|
||||
pendingCommitDiffRequest.commitHash,
|
||||
onLoadDiffAtCommit,
|
||||
buildGuardedDiffStateSetters(cancelledRef, diffState),
|
||||
).finally(() => {
|
||||
const loadDiffPromise = hasCommitHash(pendingCommitDiffRequest)
|
||||
? loadCommitDiffForPath(
|
||||
pendingCommitDiffRequest.path,
|
||||
pendingCommitDiffRequest.commitHash,
|
||||
onLoadDiffAtCommit,
|
||||
buildGuardedDiffStateSetters(cancelledRef, diffState),
|
||||
)
|
||||
: loadDiffForPath(
|
||||
pendingCommitDiffRequest.path,
|
||||
onLoadDiff,
|
||||
buildGuardedDiffStateSetters(cancelledRef, diffState),
|
||||
)
|
||||
|
||||
void loadDiffPromise.finally(() => {
|
||||
if (cancelledRef.current) return
|
||||
onPendingCommitDiffHandled?.(pendingCommitDiffRequest.requestId)
|
||||
})
|
||||
@@ -110,6 +123,7 @@ function runPendingCommitDiffRequest(
|
||||
|
||||
function usePendingCommitDiffRequest({
|
||||
activeTabPath,
|
||||
onLoadDiff,
|
||||
onLoadDiffAtCommit,
|
||||
pendingCommitDiffRequest,
|
||||
onPendingCommitDiffHandled,
|
||||
@@ -120,18 +134,23 @@ function usePendingCommitDiffRequest({
|
||||
}: UseDiffModeParams & DiffStateSetters) {
|
||||
useEffect(() => {
|
||||
if (!shouldHandlePendingCommitDiffRequest(activeTabPath, pendingCommitDiffRequest)) return
|
||||
if (!onLoadDiffAtCommit) {
|
||||
if (hasCommitHash(pendingCommitDiffRequest) && !onLoadDiffAtCommit) {
|
||||
onPendingCommitDiffHandled?.(pendingCommitDiffRequest.requestId)
|
||||
return
|
||||
}
|
||||
if (!hasCommitHash(pendingCommitDiffRequest) && !onLoadDiff) {
|
||||
onPendingCommitDiffHandled?.(pendingCommitDiffRequest.requestId)
|
||||
return
|
||||
}
|
||||
|
||||
return runPendingCommitDiffRequest(
|
||||
pendingCommitDiffRequest,
|
||||
onLoadDiff,
|
||||
onLoadDiffAtCommit,
|
||||
onPendingCommitDiffHandled,
|
||||
{ setDiffMode, setDiffContent, setDiffLoading, setDiffPath },
|
||||
)
|
||||
}, [activeTabPath, onLoadDiffAtCommit, onPendingCommitDiffHandled, pendingCommitDiffRequest, setDiffContent, setDiffLoading, setDiffMode, setDiffPath])
|
||||
}, [activeTabPath, onLoadDiff, onLoadDiffAtCommit, onPendingCommitDiffHandled, pendingCommitDiffRequest, setDiffContent, setDiffLoading, setDiffMode, setDiffPath])
|
||||
}
|
||||
|
||||
export function useDiffMode({
|
||||
@@ -148,6 +167,7 @@ export function useDiffMode({
|
||||
|
||||
usePendingCommitDiffRequest({
|
||||
activeTabPath,
|
||||
onLoadDiff,
|
||||
onLoadDiffAtCommit,
|
||||
pendingCommitDiffRequest,
|
||||
onPendingCommitDiffHandled,
|
||||
|
||||
Reference in New Issue
Block a user