import { browser } from '$app/environment'; import { derived, get, writable } from 'svelte/store'; import { debounce, showSnackbar } from '$lib/util'; import { currentProfile, profiles } from './settings'; import { miscSettings } from './misc'; import { volumes } from './volume-data'; export type SyncConfig = { key: string | null; auto: boolean; notifications: boolean; }; const stored = browser ? window.localStorage.getItem('syncConfig') : undefined; const initial: SyncConfig = stored ? JSON.parse(stored) : { key: null, auto: false, notifications: true }; export const syncConfig = writable(initial); syncConfig.subscribe((value) => { if (browser) { window.localStorage.setItem('syncConfig', JSON.stringify(value)); } }); export const isSyncEnabled = derived(syncConfig, ($cfg) => Boolean($cfg.key)); async function callApi(method: 'GET' | 'POST', payload?: any) { if (!browser) return null; const url = method === 'GET' ? `/api/sync?key=${encodeURIComponent(get(syncConfig).key || '')}` : '/api/sync'; const res = await fetch(url, { method, headers: { 'Content-Type': 'application/json' }, body: method === 'POST' ? JSON.stringify(payload) : undefined }); if (!res.ok) throw new Error(await res.text()); return res.json(); } export async function pullAll() { const cfg = get(syncConfig); if (!cfg.key) throw new Error('No sync key configured'); const data = await callApi('GET'); if (!data) return; if (data.profiles) { profiles.set(data.profiles); } if (data.current_profile) { currentProfile.set(data.current_profile); } if (data.misc_settings) { miscSettings.set(data.misc_settings); } if (data.volumes) { volumes.set(data.volumes); } if (cfg.notifications) { showSnackbar('Pulled from server'); } } export async function pushAll() { const cfg = get(syncConfig); if (!cfg.key) throw new Error('No sync key configured'); const payload = { key: cfg.key, profiles: get(profiles), current_profile: get(currentProfile), misc_settings: get(miscSettings), volumes: get(volumes) }; await callApi('POST', payload); if (cfg.notifications) { showSnackbar('Pushed to server'); } } function setupAutoSync() { if (!browser) return; let unsubscribeFns: Array<() => void> = []; function resubscribe() { unsubscribeFns.forEach((fn) => fn()); unsubscribeFns = []; if (!get(syncConfig).auto || !get(syncConfig).key) return; const schedulePush = () => debounce(() => { pushAll().catch((error) => { console.error('Auto-sync failed:', error); }); }, 300); unsubscribeFns.push(profiles.subscribe(() => schedulePush())); unsubscribeFns.push(currentProfile.subscribe(() => schedulePush())); unsubscribeFns.push(miscSettings.subscribe(() => schedulePush())); unsubscribeFns.push(volumes.subscribe(() => schedulePush())); } resubscribe(); unsubscribeFns.push(syncConfig.subscribe(() => resubscribe())); } if (browser) { setupAutoSync(); }