fix: clear missing active vault paths

This commit is contained in:
lucaronin
2026-04-07 22:35:04 +02:00
parent b674d20a05
commit 63eedbdb91
4 changed files with 67 additions and 20 deletions

View File

@@ -94,9 +94,9 @@ describe('WelcomeScreen', () => {
expect(screen.getByText(/could not be found on disk/)).toBeInTheDocument()
})
it('shows the missing vault path in a badge', () => {
it('does not show the missing vault path in a badge', () => {
render(<WelcomeScreen {...missingProps} />)
expect(screen.getByText('~/Laputa')).toBeInTheDocument()
expect(screen.queryByText('~/Laputa')).not.toBeInTheDocument()
})
it('shows "Choose a different folder" instead of "Open existing vault"', () => {

View File

@@ -103,14 +103,6 @@ const OPTION_DESC_STYLE: React.CSSProperties = {
marginTop: 2,
}
const PATH_BADGE_STYLE: React.CSSProperties = {
width: '100%',
background: 'var(--sidebar)',
borderRadius: 6,
padding: '8px 12px',
textAlign: 'center',
}
const ERROR_STYLE: React.CSSProperties = {
fontSize: 13,
color: 'var(--destructive, #e03e3e)',
@@ -155,7 +147,7 @@ function OptionButton({ icon, iconBg, label, description, onClick, disabled, loa
)
}
export function WelcomeScreen({ mode, missingPath, defaultVaultPath, onCreateVault, onCreateNewVault, onOpenFolder, creating, error }: WelcomeScreenProps) {
export function WelcomeScreen({ mode, defaultVaultPath, onCreateVault, onCreateNewVault, onOpenFolder, creating, error }: WelcomeScreenProps) {
const isWelcome = mode === 'welcome'
return (
@@ -185,14 +177,6 @@ export function WelcomeScreen({ mode, missingPath, defaultVaultPath, onCreateVau
</p>
</div>
{!isWelcome && missingPath && (
<div style={PATH_BADGE_STYLE}>
<code style={{ fontSize: 12, color: 'var(--muted-foreground)', fontFamily: 'var(--font-mono, monospace)' }}>
{missingPath}
</code>
</div>
)}
<div style={DIVIDER_STYLE} />
<div style={{ width: '100%', display: 'flex', flexDirection: 'column', gap: 10 }}>

View File

@@ -87,6 +87,38 @@ describe('useOnboarding', () => {
})
})
it('clears the persisted active vault when the saved path no longer exists', async () => {
localStorage.setItem('laputa_welcome_dismissed', '1')
mockInvokeFn.mockImplementation(async (cmd: string) => {
if (cmd === 'get_default_vault_path') return '/mock/Documents/Getting Started'
if (cmd === 'check_vault_exists') return false
if (cmd === 'load_vault_list') {
return {
vaults: [{ label: 'Old Vault', path: '/vault/deleted' }],
active_vault: '/vault/deleted',
hidden_defaults: [],
}
}
if (cmd === 'save_vault_list') return null
return null
})
const { result } = renderHook(() => useOnboarding('/vault/deleted'))
await waitFor(() => {
expect(result.current.state.status).toBe('vault-missing')
})
expect(mockInvokeFn).toHaveBeenCalledWith('save_vault_list', {
list: {
vaults: [{ label: 'Old Vault', path: '/vault/deleted' }],
active_vault: null,
hidden_defaults: [],
},
})
})
it('handleCreateVault creates vault and transitions to ready', async () => {
mockInvokeFn.mockImplementation(async (cmd: string) => {
if (cmd === 'get_default_vault_path') return '/mock/Documents/Getting Started'

View File

@@ -15,6 +15,12 @@ function tauriCall<T>(command: string, args: Record<string, unknown>): Promise<T
const DISMISSED_KEY = 'laputa_welcome_dismissed'
interface PersistedVaultList {
vaults?: Array<{ label: string; path: string }>
active_vault?: string | null
hidden_defaults?: string[]
}
function wasDismissed(): boolean {
try {
return localStorage.getItem(DISMISSED_KEY) === '1'
@@ -31,6 +37,22 @@ function markDismissed(): void {
}
}
async function clearMissingActiveVault(missingPath: string): Promise<void> {
try {
const list = await tauriCall<PersistedVaultList>('load_vault_list', {})
if (!list || list.active_vault !== missingPath) return
await tauriCall('save_vault_list', {
list: {
vaults: list.vaults ?? [],
active_vault: null,
hidden_defaults: list.hidden_defaults ?? [],
},
})
} catch {
// Best effort only — onboarding should still proceed
}
}
export function useOnboarding(initialVaultPath: string) {
const [state, setState] = useState<OnboardingState>({ status: 'loading' })
const [creating, setCreating] = useState(false)
@@ -48,7 +70,16 @@ export function useOnboarding(initialVaultPath: string) {
if (exists) {
setState({ status: 'ready', vaultPath: initialVaultPath })
} else if (wasDismissed()) {
} else {
await clearMissingActiveVault(initialVaultPath)
if (cancelled) return
}
if (exists) {
return
}
if (wasDismissed()) {
// User previously dismissed — show vault-missing instead of welcome
setState({ status: 'vault-missing', vaultPath: initialVaultPath, defaultPath })
} else {