'use client'; import { createContext, useCallback, useContext, useEffect, useRef, useState, type ReactNode, } from 'react'; import { subscribeBroadcast, type SyncMessage } from '@/lib/queue/broadcast'; import { runSync } from '@/lib/queue/sync'; import { db } from '@/lib/queue/db'; interface SyncState { pendingCount: number; deadLetterCount: number; } const SyncCtx = createContext({ pendingCount: 0, deadLetterCount: 0 }); export const useSyncState = () => useContext(SyncCtx); export function SyncProvider({ children }: { children: ReactNode }) { const [state, setState] = useState({ pendingCount: 0, deadLetterCount: 0 }); const [failedIds, setFailedIds] = useState([]); const timerRef = useRef | null>(null); const refreshCounts = useCallback(async () => { const [p, d] = await Promise.all([db.pending.count(), db.deadLetters.count()]); setState({ pendingCount: p, deadLetterCount: d }); }, []); const sync = useCallback(async () => { await runSync(); await refreshCounts(); }, [refreshCounts]); useEffect(() => { refreshCounts(); const unsub = subscribeBroadcast(async (msg: SyncMessage) => { await refreshCounts(); if (msg.type === 'dead-letter') { setFailedIds((prev) => [...prev, msg.clientRequestId]); } }); const onOnline = () => sync(); const onVisible = () => { if (document.visibilityState === 'visible' && navigator.onLine) sync(); }; window.addEventListener('online', onOnline); document.addEventListener('visibilitychange', onVisible); timerRef.current = setInterval(() => { if (navigator.onLine) sync(); }, 10_000); if ('serviceWorker' in navigator && 'SyncManager' in window) { navigator.serviceWorker.ready // eslint-disable-next-line @typescript-eslint/no-explicit-any .then((reg) => (reg as any).sync.register('mai-call-sync')) .catch(() => {/* not supported or permission denied */}); } if (navigator.onLine) sync(); return () => { unsub(); window.removeEventListener('online', onOnline); document.removeEventListener('visibilitychange', onVisible); if (timerRef.current) clearInterval(timerRef.current); }; }, [sync, refreshCounts]); return ( {children} {failedIds.length > 0 && (
{failedIds.map((id) => (
Pedido {id.slice(0, 8)}… falhou — contacta o supervisor.
))}
)}
); }