Files
tolaria/src/utils/pulledVaultRefresh.ts

97 lines
3.2 KiB
TypeScript
Raw Normal View History

2026-04-20 14:43:52 +02:00
import type { VaultEntry } from '../types'
import { findByNotePath, joinVaultPath, normalizeNotePathForIdentity, notePathsMatch } from './notePathIdentity'
2026-04-20 14:43:52 +02:00
interface PulledVaultRefreshOptions {
activeTabPath: string | null
getActiveTabPath?: () => string | null
2026-04-20 14:43:52 +02:00
closeAllTabs: () => void
hasUnsavedChanges: (path: string) => boolean
shouldKeepActiveEditorMounted?: () => boolean
2026-04-20 14:43:52 +02:00
reloadFolders: () => Promise<unknown> | unknown
reloadVault: () => Promise<VaultEntry[]>
reloadViews: () => Promise<unknown> | unknown
replaceActiveTab: (entry: VaultEntry) => Promise<void>
updatedFiles: string[]
vaultPath: string
}
interface ActiveTabRefreshGuardOptions {
activeTabPath: string
latestActiveTabPath: string
hasUnsavedChanges: (path: string) => boolean
shouldKeepActiveEditorMounted?: () => boolean
}
function resolveUpdatedFilePath(path: string, vaultPath: string): string {
if (path.startsWith('/')) return normalizeNotePathForIdentity(path)
return normalizeNotePathForIdentity(joinVaultPath(vaultPath, path))
}
function didPullUpdateActiveNote(updatedFiles: string[], vaultPath: string, activeTabPath: string): boolean {
return updatedFiles.some((path) => notePathsMatch(resolveUpdatedFilePath(path, vaultPath), activeTabPath))
}
function didActivePathChange(initialPath: string, latestPath: string): boolean {
return !notePathsMatch(initialPath, latestPath)
}
function shouldKeepCurrentActiveTabMounted({
activeTabPath,
latestActiveTabPath,
hasUnsavedChanges,
shouldKeepActiveEditorMounted,
}: ActiveTabRefreshGuardOptions): boolean {
if (didActivePathChange(activeTabPath, latestActiveTabPath)) return true
if (hasUnsavedChanges(latestActiveTabPath)) return true
return shouldKeepActiveEditorMounted?.() === true
}
export function getPulledVaultUpdateOptions(): { preserveFocusedEditor: true } {
return { preserveFocusedEditor: true }
}
2026-04-20 14:43:52 +02:00
export async function refreshPulledVaultState(options: PulledVaultRefreshOptions): Promise<VaultEntry[]> {
const {
activeTabPath,
closeAllTabs,
getActiveTabPath,
2026-04-20 14:43:52 +02:00
hasUnsavedChanges,
shouldKeepActiveEditorMounted,
2026-04-20 14:43:52 +02:00
reloadFolders,
reloadVault,
reloadViews,
replaceActiveTab,
updatedFiles,
vaultPath,
2026-04-20 14:43:52 +02:00
} = options
const [entries] = await Promise.all([
reloadVault(),
Promise.resolve(reloadFolders()),
Promise.resolve(reloadViews()),
])
const latestActiveTabPath = getActiveTabPath?.() ?? activeTabPath
if (!activeTabPath || !latestActiveTabPath) return entries
if (shouldKeepCurrentActiveTabMounted({
activeTabPath,
latestActiveTabPath,
hasUnsavedChanges,
shouldKeepActiveEditorMounted,
})) return entries
2026-04-20 14:43:52 +02:00
const refreshedEntry = findByNotePath(entries, latestActiveTabPath)
2026-04-20 14:43:52 +02:00
if (!refreshedEntry) {
closeAllTabs()
return entries
}
if (!didPullUpdateActiveNote(updatedFiles, vaultPath, latestActiveTabPath)) return entries
2026-04-20 14:43:52 +02:00
// Native BlockNote can keep rendering the previous document after a pull that
// changes the active file in place. Dropping the tab first forces a full
// reopen for that specific case without affecting unrelated pull updates.
closeAllTabs()
2026-04-20 14:43:52 +02:00
await replaceActiveTab(refreshedEntry)
return entries
}