ESC to close · ENTER to open first result
// ═══════════════════════════════════════════════════════════════ // SYNC ENGINE // ═══════════════════════════════════════════════════════════════ let _syncState = 'synced'; let _syncTimer = null; function setSyncState(s) { _syncState = s; const dot = document.getElementById('sync-dot'); const btn = document.getElementById('sync-btn'); if (!dot) return; dot.className = 'sync-dot ' + s; if (btn) btn.title = { synced: 'Synced — tap to check', syncing: 'Syncing...', unsynced:'Not synced — tap to sync' }[s] || s; } async function hashState(obj) { const str = JSON.stringify(obj); const buf = await crypto.subtle.digest('SHA-256', new TextEncoder().encode(str)); return Array.from(new Uint8Array(buf)).map(b=>b.toString(16).padStart(2,'0')).join(''); } function scheduleSync() { if (!isAdmin) return; setSyncState('syncing'); clearTimeout(_syncTimer); _syncTimer = setTimeout(doSync, 2000); } async function doSync() { if (!isAdmin) return; if (!_writeKey || _writeKey === 'local') { setSyncState('synced'); return; } setSyncState('syncing'); try { const pullRes = await authRequest('pull', {}); if (!pullRes.ok) { setSyncState('unsynced'); return; } const localHash = await hashState(state); const serverHash = pullRes.serverData ? await hashState(JSON.parse(pullRes.serverData)) : null; if (localHash === serverHash) { setSyncState('synced'); return; } const pushRes = await authRequest('push', { writeKey: _writeKey, data: JSON.stringify(state), timestamp: Date.now() }); setSyncState(pushRes.ok ? 'synced' : 'unsynced'); } catch(e) { setSyncState('unsynced'); } } async function forceSync() { if (_syncState === 'syncing') return; clearTimeout(_syncTimer); await doSync(); toast(_syncState === 'synced' ? 'All synced ✓' : 'Sync failed'); } async function initSyncOnLogin(serverData, serverTs) { if (!_writeKey || _writeKey === 'local') { setSyncState('synced'); return; } setSyncState('syncing'); if (serverData) { await pullAndMerge(serverData, serverTs); } else { await doSync(); } }