Files
tolaria/src/hooks/useSettings.ts
Luca Rossi 369ecdc009 feat: auto-pull vault changes from Git (background sync + conflict handling) (#79)
* 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>
2026-02-26 00:55:16 +00:00

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 }
}