experiments in a post-browser web
10
fork

Configure Feed

Select the types of activity you want to include in your feed.

feat(sync): surface sync errors in settings status with red indicator

Track last sync error message + timestamp on the sync module and
expose them via getSyncStatus(). Settings sync section now shows
the error inline with a red border and tinted background, cleared
once the next sync succeeds.

+43
+25
app/settings/settings.js
··· 1576 1576 pendingLine.className = 'help-text'; 1577 1577 statusContainer.appendChild(pendingLine); 1578 1578 1579 + // Error line — only shown when the most recent syncAll threw. The 1580 + // background color of the whole status block also goes red so the 1581 + // failure is hard to miss without having to scan text. 1582 + const errorLine = document.createElement('div'); 1583 + errorLine.className = 'help-text'; 1584 + errorLine.style.cssText = 'margin-top: 6px; color: var(--base08, #cc241d); display: none;'; 1585 + statusContainer.appendChild(errorLine); 1586 + 1579 1587 statusSection.appendChild(statusContainer); 1580 1588 1581 1589 // Function to update status display ··· 1601 1609 } 1602 1610 1603 1611 pendingLine.textContent = `Pending items: ${status.pendingCount || 0}`; 1612 + 1613 + // Surface sync errors prominently — the block goes red and an error 1614 + // line appears below pending. Cleared on the next successful sync. 1615 + if (status.lastError) { 1616 + const at = status.lastErrorAt ? new Date(status.lastErrorAt).toLocaleString() : ''; 1617 + errorLine.textContent = at 1618 + ? `Last error (${at}): ${status.lastError}` 1619 + : `Last error: ${status.lastError}`; 1620 + errorLine.style.display = ''; 1621 + statusContainer.style.background = 'var(--base08-bg, rgba(204, 36, 29, 0.12))'; 1622 + statusContainer.style.borderLeft = '3px solid var(--base08, #cc241d)'; 1623 + } else { 1624 + errorLine.style.display = 'none'; 1625 + errorLine.textContent = ''; 1626 + statusContainer.style.background = 'var(--bg-tertiary)'; 1627 + statusContainer.style.borderLeft = ''; 1628 + } 1604 1629 }; 1605 1630 1606 1631 // Initial status update
+18
backend/electron/sync.ts
··· 706 706 707 707 // ==================== Full Bidirectional Sync ==================== 708 708 709 + // Module-level error state — surfaced in getSyncStatus so the settings 710 + // UI (and any other consumer) can show a red indicator on failure 711 + // without each caller having to wrap syncAll in try/catch + post the 712 + // error to a separate channel. Set on the throw path in syncAll, cleared 713 + // on the next successful run. 714 + let lastSyncError: string | null = null; 715 + let lastSyncErrorAt = 0; 716 + 709 717 /** 710 718 * Perform a full bidirectional sync 711 719 * ··· 753 761 754 762 DEBUG && console.log(`[sync] Sync complete: ${pulled} pulled, ${pushed} pushed, ${conflicts} conflicts`); 755 763 764 + // Clear any stale error from a previous failed run. 765 + lastSyncError = null; 766 + lastSyncErrorAt = 0; 767 + 756 768 return { 757 769 pulled, 758 770 pushed, ··· 761 773 }; 762 774 } catch (error) { 763 775 DEBUG && console.error('[sync] Sync failed:', error); 776 + lastSyncError = error instanceof Error ? error.message : String(error); 777 + lastSyncErrorAt = Date.now(); 764 778 throw error; 765 779 } 766 780 } ··· 774 788 configured: boolean; 775 789 lastSyncTime: number; 776 790 pendingCount: number; 791 + lastError: string | null; 792 + lastErrorAt: number; 777 793 } { 778 794 const config = getSyncConfig(); 779 795 const db = getDb(); ··· 791 807 configured: !!(config.serverUrl && config.apiKey), 792 808 lastSyncTime: config.lastSyncTime, 793 809 pendingCount, 810 + lastError: lastSyncError, 811 + lastErrorAt: lastSyncErrorAt, 794 812 }; 795 813 }