* feat: add auto-pull vault sync with conflict handling - Add git_pull, has_remote, get_conflict_files to Rust backend - Add GitPullResult type and auto_pull_interval_minutes to Settings - Create useAutoSync hook (pull on launch, focus, periodic interval) - Update StatusBar with real sync status indicator - Add pull interval setting to SettingsPanel - Add mock handler for git_pull - Update existing tests for new Settings field Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * test: add tests for git pull, settings, and useAutoSync hook - Add Rust tests: has_remote, git_pull (no_remote, up_to_date, updated), get_conflict_files, parse_updated_files, GitPullResult serialization - Add frontend tests: useAutoSync (mount pull, focus pull, conflict, error, manual trigger, concurrent prevention, no_remote) - Fix existing settings tests for new auto_pull_interval_minutes field Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * design: add auto-pull-vault wireframes Frames showing: sync idle, syncing, conflict indicator, settings sync section, and conflict toast notification. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: add auto_pull_interval_minutes to all Settings literals Fix build error and test fixtures missing the new field. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: cargo fmt * ci: re-trigger CI after flaky test * ci: retrigger after disk space cleanup --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
49 lines
1.4 KiB
TypeScript
49 lines
1.4 KiB
TypeScript
import { useCallback, useEffect, useState } from 'react'
|
|
import { invoke } from '@tauri-apps/api/core'
|
|
import { isTauri, mockInvoke } from '../mock-tauri'
|
|
import type { Settings } from '../types'
|
|
|
|
function tauriCall<T>(command: string, tauriArgs: Record<string, unknown>, mockArgs?: Record<string, unknown>): Promise<T> {
|
|
return isTauri() ? invoke<T>(command, tauriArgs) : mockInvoke<T>(command, mockArgs ?? tauriArgs)
|
|
}
|
|
|
|
const EMPTY_SETTINGS: Settings = {
|
|
anthropic_key: null,
|
|
openai_key: null,
|
|
google_key: null,
|
|
github_token: null,
|
|
github_username: null,
|
|
auto_pull_interval_minutes: null,
|
|
}
|
|
|
|
export function useSettings() {
|
|
const [settings, setSettings] = useState<Settings>(EMPTY_SETTINGS)
|
|
const [loaded, setLoaded] = useState(false)
|
|
|
|
const loadSettings = useCallback(async () => {
|
|
try {
|
|
const s = await tauriCall<Settings>('get_settings', {})
|
|
setSettings(s)
|
|
} catch (err) {
|
|
console.warn('Failed to load settings:', err)
|
|
} finally {
|
|
setLoaded(true)
|
|
}
|
|
}, [])
|
|
|
|
useEffect(() => {
|
|
loadSettings()
|
|
}, [loadSettings])
|
|
|
|
const saveSettings = useCallback(async (newSettings: Settings) => {
|
|
try {
|
|
await tauriCall<null>('save_settings', { settings: newSettings })
|
|
setSettings(newSettings)
|
|
} catch (err) {
|
|
console.error('Failed to save settings:', err)
|
|
}
|
|
}, [])
|
|
|
|
return { settings, loaded, saveSettings }
|
|
}
|