2026-04-20 14:43:52 +02:00
|
|
|
import type { VaultEntry } from '../types'
|
|
|
|
|
|
|
|
|
|
interface PulledVaultRefreshOptions {
|
|
|
|
|
activeTabPath: string | null
|
2026-04-30 10:22:02 +02:00
|
|
|
getActiveTabPath?: () => string | null
|
2026-04-20 14:43:52 +02:00
|
|
|
closeAllTabs: () => void
|
|
|
|
|
hasUnsavedChanges: (path: string) => boolean
|
|
|
|
|
reloadFolders: () => Promise<unknown> | unknown
|
|
|
|
|
reloadVault: () => Promise<VaultEntry[]>
|
|
|
|
|
reloadViews: () => Promise<unknown> | unknown
|
|
|
|
|
replaceActiveTab: (entry: VaultEntry) => Promise<void>
|
|
|
|
|
updatedFiles: string[]
|
|
|
|
|
vaultPath: string
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function normalizePath(path: string): string {
|
|
|
|
|
return path
|
|
|
|
|
.replaceAll('\\', '/')
|
|
|
|
|
.replace(/^\/private\/tmp(?=\/|$)/u, '/tmp')
|
|
|
|
|
.replace(/\/+$/u, '')
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-20 16:04:11 +02:00
|
|
|
function resolveUpdatedFilePath(path: string, vaultPath: string): string {
|
|
|
|
|
if (path.startsWith('/')) return normalizePath(path)
|
|
|
|
|
return normalizePath(`${vaultPath}/${path}`)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function didPullUpdateActiveNote(updatedFiles: string[], vaultPath: string, activeTabPath: string): boolean {
|
|
|
|
|
const normalizedActivePath = normalizePath(activeTabPath)
|
|
|
|
|
return updatedFiles.some((path) => resolveUpdatedFilePath(path, vaultPath) === normalizedActivePath)
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-30 10:22:02 +02:00
|
|
|
function didActivePathChange(initialPath: string, latestPath: string): boolean {
|
|
|
|
|
return normalizePath(initialPath) !== normalizePath(latestPath)
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-20 14:43:52 +02:00
|
|
|
export async function refreshPulledVaultState(options: PulledVaultRefreshOptions): Promise<VaultEntry[]> {
|
|
|
|
|
const {
|
|
|
|
|
activeTabPath,
|
|
|
|
|
closeAllTabs,
|
2026-04-30 10:22:02 +02:00
|
|
|
getActiveTabPath,
|
2026-04-20 14:43:52 +02:00
|
|
|
hasUnsavedChanges,
|
|
|
|
|
reloadFolders,
|
|
|
|
|
reloadVault,
|
|
|
|
|
reloadViews,
|
|
|
|
|
replaceActiveTab,
|
2026-04-20 16:04:11 +02:00
|
|
|
updatedFiles,
|
|
|
|
|
vaultPath,
|
2026-04-20 14:43:52 +02:00
|
|
|
} = options
|
|
|
|
|
|
|
|
|
|
const [entries] = await Promise.all([
|
|
|
|
|
reloadVault(),
|
|
|
|
|
Promise.resolve(reloadFolders()),
|
|
|
|
|
Promise.resolve(reloadViews()),
|
|
|
|
|
])
|
|
|
|
|
|
2026-04-30 10:22:02 +02:00
|
|
|
const latestActiveTabPath = getActiveTabPath?.() ?? activeTabPath
|
|
|
|
|
if (!activeTabPath || !latestActiveTabPath) return entries
|
|
|
|
|
if (didActivePathChange(activeTabPath, latestActiveTabPath)) return entries
|
|
|
|
|
if (hasUnsavedChanges(latestActiveTabPath)) return entries
|
2026-04-20 14:43:52 +02:00
|
|
|
|
2026-04-30 10:22:02 +02:00
|
|
|
const refreshedEntry = entries.find(entry => normalizePath(entry.path) === normalizePath(latestActiveTabPath))
|
2026-04-20 14:43:52 +02:00
|
|
|
if (!refreshedEntry) {
|
|
|
|
|
closeAllTabs()
|
|
|
|
|
return entries
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-20 16:04:11 +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.
|
2026-04-30 10:22:02 +02:00
|
|
|
if (didPullUpdateActiveNote(updatedFiles, vaultPath, latestActiveTabPath)) {
|
2026-04-20 16:04:11 +02:00
|
|
|
closeAllTabs()
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-20 14:43:52 +02:00
|
|
|
await replaceActiveTab(refreshedEntry)
|
|
|
|
|
return entries
|
|
|
|
|
}
|