import { writable } from 'svelte/store'; import { sync as syncAPI } from '$lib/api/endpoints.js'; import { getQueue, clearQueue } from '$lib/utils/sync-queue.js'; import { getItem, setItem } from '$lib/utils/storage.js'; import { generateUUID } from '$lib/utils/uuid.js'; /** * @typedef {import('$lib/api/types.js').SyncResult} SyncResult */ /** * @typedef {Object} SyncState * @property {'idle'|'syncing'|'error'} status * @property {number} lastSync - Unix timestamp * @property {string|null} error * @property {number} queueSize * @property {string} clientId */ const SYNC_STATE_KEY = 'opal_sync_state'; const CLIENT_ID_KEY = 'opal_client_id'; /** @returns {string} */ function getClientId() { let clientId = getItem(CLIENT_ID_KEY); if (!clientId) { clientId = generateUUID(); setItem(CLIENT_ID_KEY, clientId); } return clientId; } /** @returns {SyncState} */ function loadSyncState() { const stored = getItem(SYNC_STATE_KEY); return { status: 'idle', lastSync: stored?.lastSync || 0, error: null, queueSize: getQueue().length, clientId: getClientId() }; } function createSyncStore() { const { subscribe, set, update } = writable(loadSyncState()); return { subscribe, /** @returns {Promise} */ async sync() { update(state => ({ ...state, status: 'syncing', error: null })); try { const state = loadSyncState(); const queue = getQueue(); /** @type {SyncResult} */ const result = { pulled: 0, pushed: 0, conflicts_resolved: 0, queued_offline: 0, errors: [] }; if (queue.length > 0) { const tasks = queue.map(q => q.data); try { await syncAPI.push(tasks, state.clientId); clearQueue(); result.pushed = queue.length; } catch (/** @type {any} */ error) { result.errors.push(`Failed to push queue: ${error.message}`); } } try { const changes = await syncAPI.getChanges(state.lastSync, state.clientId); result.pulled = changes.length; // TODO: Apply changes to local state } catch (/** @type {any} */ error) { result.errors.push(`Failed to pull changes: ${error.message}`); } const now = Math.floor(Date.now() / 1000); setItem(SYNC_STATE_KEY, { lastSync: now }); update(state => ({ ...state, status: 'idle', lastSync: now, queueSize: 0, error: null })); return result; } catch (/** @type {any} */ error) { update(state => ({ ...state, status: 'error', error: error.message })); throw error; } }, updateQueueSize() { update(state => ({ ...state, queueSize: getQueue().length })); } }; } export const syncStore = createSyncStore();