fix: guard note loading without active vault
This commit is contained in:
@@ -160,6 +160,19 @@ describe('useTabManagement (single-note model)', () => {
|
||||
)
|
||||
warnSpy.mockRestore()
|
||||
})
|
||||
|
||||
it('returns to the empty state when no active vault is selected', async () => {
|
||||
const { mockInvoke } = await import('../mock-tauri')
|
||||
vi.mocked(mockInvoke).mockRejectedValueOnce(new Error('No active vault selected'))
|
||||
const warnSpy = vi.spyOn(console, 'warn').mockImplementation(() => {})
|
||||
|
||||
const { result } = renderHook(() => useTabManagement())
|
||||
await selectNote(result, { path: '/vault/note/orphaned.md', title: 'Orphaned Note' })
|
||||
|
||||
expect(result.current.tabs).toEqual([])
|
||||
expect(result.current.activeTabPath).toBeNull()
|
||||
warnSpy.mockRestore()
|
||||
})
|
||||
})
|
||||
|
||||
describe('handleReplaceActiveTab', () => {
|
||||
@@ -328,6 +341,24 @@ describe('useTabManagement (single-note model)', () => {
|
||||
await vi.waitFor(() => expect(vi.mocked(mockInvoke)).toHaveBeenCalledTimes(1))
|
||||
})
|
||||
|
||||
it('swallows no-active-vault prefetch failures and lets a later open recover', async () => {
|
||||
const { mockInvoke } = await import('../mock-tauri')
|
||||
vi.mocked(mockInvoke)
|
||||
.mockRejectedValueOnce(new Error('No active vault selected'))
|
||||
.mockResolvedValueOnce('# Recovered content')
|
||||
|
||||
prefetchNoteContent('/vault/note/recovered.md')
|
||||
await vi.waitFor(() => expect(vi.mocked(mockInvoke)).toHaveBeenCalledTimes(1))
|
||||
await Promise.resolve()
|
||||
await Promise.resolve()
|
||||
|
||||
const { result } = renderHook(() => useTabManagement())
|
||||
await selectNote(result, { path: '/vault/note/recovered.md', title: 'Recovered' })
|
||||
|
||||
expect(result.current.tabs[0].content).toBe('# Recovered content')
|
||||
expect(vi.mocked(mockInvoke)).toHaveBeenCalledTimes(2)
|
||||
})
|
||||
|
||||
it('serves refreshed cached content after a save replaces stale prefetched data', async () => {
|
||||
const mockInvoke = await prefetchResolvedContent('/vault/note/saved.md', '# Stale prefetched content')
|
||||
vi.mocked(mockInvoke).mockResolvedValue('# Persisted content')
|
||||
|
||||
@@ -73,7 +73,10 @@ function requestNoteContent({ path }: Pick<NoteContentCacheEntry, 'path'>): Note
|
||||
* Cache is short-lived: cleared on vault reload via clearPrefetchCache(). */
|
||||
export function prefetchNoteContent(path: string): void {
|
||||
if (prefetchCache.has(path)) return
|
||||
requestNoteContent({ path })
|
||||
void requestNoteContent({ path }).promise.catch((error) => {
|
||||
if (isNoActiveVaultSelectedError(error)) return
|
||||
console.warn('Failed to prefetch note content:', error)
|
||||
})
|
||||
}
|
||||
|
||||
export function cacheNoteContent(path: string, content: string): void {
|
||||
@@ -189,6 +192,15 @@ function isMissingNotePathError(error: unknown): boolean {
|
||||
return /does not exist|not found|enoent/i.test(message)
|
||||
}
|
||||
|
||||
function isNoActiveVaultSelectedError(error: unknown): boolean {
|
||||
const message = error instanceof Error
|
||||
? error.message
|
||||
: typeof error === 'string'
|
||||
? error
|
||||
: String(error)
|
||||
return /no active vault selected/i.test(message)
|
||||
}
|
||||
|
||||
function shouldApplyLoadedEntry(options: {
|
||||
seq: number
|
||||
navSeqRef: React.MutableRefObject<number>
|
||||
@@ -238,6 +250,13 @@ function handleEntryLoadFailure(options: {
|
||||
|
||||
console.warn('Failed to load note content:', error)
|
||||
if (navSeqRef.current !== seq) return
|
||||
if (isNoActiveVaultSelectedError(error)) {
|
||||
clearPrefetchCache()
|
||||
clearTabs(tabsRef, setTabs)
|
||||
syncActiveTabPath(activeTabPathRef, setActiveTabPath, null)
|
||||
failNoteOpenTrace(entry.path, 'missing-active-vault')
|
||||
return
|
||||
}
|
||||
if (isMissingNotePathError(error)) {
|
||||
clearTabs(tabsRef, setTabs)
|
||||
syncActiveTabPath(activeTabPathRef, setActiveTabPath, null)
|
||||
|
||||
Reference in New Issue
Block a user