Files
tolaria/src/utils/pulledVaultRefresh.ts

145 lines
4.6 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
}
function resolveUpdatedFilePath(options: { path: string; vaultPath: string }): string {
const { path, vaultPath } = options
if (path.startsWith('/')) return normalizeNotePathForIdentity(path)
return normalizeNotePathForIdentity(joinVaultPath(vaultPath, path))
}
function didPullUpdateActiveNote(options: {
updatedFiles: string[]
vaultPath: string
activeTabPath: string
}): boolean {
const { activeTabPath, updatedFiles, vaultPath } = options
return updatedFiles.some((path) => notePathsMatch(resolveUpdatedFilePath({ path, vaultPath }), activeTabPath))
}
function didActivePathChange(options: { initialPath: string; latestPath: string }): boolean {
const { initialPath, latestPath } = options
return !notePathsMatch(initialPath, latestPath)
}
function findExternallyMovedActiveEntry(options: {
activeTabPath: string
entries: VaultEntry[]
updatedFiles: string[]
vaultPath: string
}): VaultEntry | null {
const { activeTabPath, entries, updatedFiles, vaultPath } = options
if (updatedFiles.length === 0) return null
const activeFilename = normalizeNotePathForIdentity(activeTabPath).split('/').pop()
const updatedPaths = new Set(updatedFiles.map((path) => resolveUpdatedFilePath({ path, vaultPath })))
const candidates = entries.filter((entry) =>
!notePathsMatch(entry.path, activeTabPath)
&& entry.filename === activeFilename
&& updatedPaths.has(normalizeNotePathForIdentity(entry.path)),
)
return candidates.length === 1 ? candidates[0] : null
}
function isActivePathBlocked(options: {
activeTabPath: string | null
latestActiveTabPath: string | null
hasUnsavedChanges: PulledVaultRefreshOptions['hasUnsavedChanges']
}): boolean {
const { activeTabPath, latestActiveTabPath, hasUnsavedChanges } = options
if (!activeTabPath) return true
if (!latestActiveTabPath) return true
if (didActivePathChange({ initialPath: activeTabPath, latestPath: latestActiveTabPath })) return true
return hasUnsavedChanges(latestActiveTabPath)
}
function shouldReplaceActiveEntry(options: {
activePath: string
movedEntry: VaultEntry | null
refreshedEntry: VaultEntry | null
shouldKeepActiveEditorMounted?: () => boolean
updatedFiles: string[]
vaultPath: string
}): boolean {
const {
activePath,
movedEntry,
refreshedEntry,
shouldKeepActiveEditorMounted,
updatedFiles,
vaultPath,
} = options
if (movedEntry) return true
if (!refreshedEntry) return false
if (shouldKeepActiveEditorMounted?.() === true) return false
return didPullUpdateActiveNote({ updatedFiles, vaultPath, activeTabPath: activePath })
}
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 (isActivePathBlocked({ activeTabPath, latestActiveTabPath, hasUnsavedChanges })) return entries
const activePath = latestActiveTabPath as string
const refreshedEntry = findByNotePath(entries, activePath)
const movedEntry = refreshedEntry ? null : findExternallyMovedActiveEntry({
activeTabPath: activePath,
entries,
updatedFiles,
vaultPath,
})
const replacementEntry = refreshedEntry ?? movedEntry
2026-04-20 14:43:52 +02:00
if (replacementEntry && shouldReplaceActiveEntry({
activePath,
movedEntry,
refreshedEntry,
shouldKeepActiveEditorMounted,
updatedFiles,
vaultPath,
})) {
2026-04-20 14:43:52 +02:00
closeAllTabs()
await replaceActiveTab(replacementEntry)
2026-04-20 14:43:52 +02:00
return entries
}
if (!replacementEntry) closeAllTabs()
2026-04-20 14:43:52 +02:00
return entries
}