Menu
Public repository / 3af23b0e937520000e1aa43c7e2eb9de2675abd1
AkurAI-Mail / src/lib/webmail/sync.js
Latest change 10d19fe0941c186ec244d1f0e2af6b19200dcb0c - OLI-248: pure webmail logic as plain ES modules with JSDoc types; inline icon module by Ólafur Búi Ólafsson
/** @typedef {"offline" | "auth-expired" | "server-error"} SyncErrorKind */
/** Pure helpers behind the webmail sync status and error banner. */
export const SYNC_ERROR_MESSAGES = {
offline: "You're offline. Showing the last synced messages.",
"auth-expired": "Your session expired. Sign in again to resume syncing.",
"server-error": "The mail server had a problem. Showing the last synced messages.",
};
export const RETRY_BASE_MS = 2000;
export const RETRY_MAX_MS = 60000;
/**
* Exponential backoff: 2s, 4s, 8s ... capped at one minute.
* @param {number} attempt
*/
export function retryDelay(attempt) {
return Math.min(RETRY_BASE_MS * 2 ** attempt, RETRY_MAX_MS);
}
/**
* @param {unknown} error
* @param {{ status: number }} [response]
* @param {any} [online]
* @returns {SyncErrorKind}
*/
export function classifySyncError(error, response, online = typeof navigator === "undefined" ? true : navigator.onLine) {
if (response?.status === 401) return "auth-expired";
if (online === false || error instanceof TypeError) return "offline";
return "server-error";
}
/**
* "Synced just now", "Synced 12s ago", "Synced 3m ago", "Synced 2h ago".
* @param {number | null} lastSyncAt
* @param {number} [now]
*/
export function syncedLabel(lastSyncAt, now = Date.now()) {
if (!lastSyncAt) return "Never synced";
const seconds = Math.max(0, Math.round((now - lastSyncAt) / 1000));
if (seconds < 5) return "Synced just now";
if (seconds < 60) return `Synced ${seconds}s ago`;
const minutes = Math.round(seconds / 60);
if (minutes < 60) return `Synced ${minutes}m ago`;
return `Synced ${Math.round(minutes / 60)}h ago`;
}