Menu
Public repository / c3ef99b092caa55fb6a3eed9d3e776734cc99e97
AkurAI-Mail / src/lib/webmail/SyncBanner.svelte
Latest change 22202d1bde69be4ace8a299dffd91f8ff24378df - Recover deployed webmail frontend (frontend parity with EC2 a6afffc) by Ólafur Búi Ólafsson
<script lang="ts">
import { Check, RefreshCw, TriangleAlert, WifiOff, X } from "lucide-svelte";
import { SYNC_ERROR_MESSAGES, syncedLabel } from "$lib/webmail/sync";
import type { SyncState } from "$lib/webmail/sync.svelte";
let { sync, onRetry, onReauth }: { sync: SyncState; onRetry: () => void; onReauth: () => void } = $props();
</script>
<div class="sync-root">
<div class="sync-status" role="status">
{#if sync.status === "syncing"}
<RefreshCw size={14} class="spin" /> <span>Syncing…</span>
{:else}
<Check size={14} /> <span>{syncedLabel(sync.lastSyncAt)}</span>
{/if}
<button class="sync-now" disabled={sync.status === "syncing"} onclick={onRetry}>Sync now</button>
</div>
{#if sync.errorKind && !sync.dismissed}
<div class="sync-banner" class:auth={sync.errorKind === "auth-expired"} role="alert">
{#if sync.errorKind === "offline"}<WifiOff size={16} />{:else}<TriangleAlert size={16} />{/if}
<span class="sync-banner-text">{SYNC_ERROR_MESSAGES[sync.errorKind]}</span>
{#if sync.errorKind === "auth-expired"}
<button class="sync-action" onclick={onReauth}>Sign in again</button>
{:else}
<button class="sync-action" onclick={onRetry}>Retry now</button>
{/if}
<button class="icon-button" aria-label="Dismiss sync error" onclick={sync.dismiss}><X size={15} /></button>
</div>
{/if}
</div>
<style>
.sync-status {
color: var(--muted);
align-items: center;
gap: 6px;
font-size: 12px;
display: flex;
}
.sync-status :global(.spin) {
animation: 0.8s linear infinite spin;
}
.sync-now {
color: var(--accent);
background: 0 0;
border: 0;
padding: 0 4px;
font-weight: 700;
}
.sync-now:hover {
text-decoration: underline;
}
.sync-banner {
border-bottom: 1px solid var(--line);
background: color-mix(in srgb, var(--danger) 8%, var(--surface));
color: var(--danger);
align-items: center;
gap: 10px;
padding: 9px 14px;
font-size: 13px;
display: flex;
}
.sync-banner.auth {
background: color-mix(in srgb, #d6a62c 14%, var(--surface));
color: #7a5a00;
}
.sync-banner-text {
flex: 1;
min-width: 0;
}
.sync-action {
background: var(--surface);
color: inherit;
border: 0;
border-radius: 6px;
padding: 5px 10px;
font-weight: 700;
}
.sync-action:hover {
background: var(--surface-selected);
}
.icon-button {
color: inherit;
background: 0 0;
border: 0;
padding: 4px;
display: inline-flex;
}
@keyframes spin {
to {
transform: rotate(360deg);
}
}
</style>