Toast system_alert: agregar no catch-up pra não empilhar enxurrada

Bug: acumulando N system_alert não-lidas, o refreshAndMaybeAlert
(mount / visibilitychange / polling 60s) disparava N toasts de uma vez.
Comum após recarregar a página com alertas pendentes do último teste.

Fix: no catch-up, mostra só a notif mais recente, com sufixo "+N
outros alertas no sino" no detail se houver múltiplas. As demais são
marcadas no alertedIds pra não redisparar — continuam visíveis no
sininho/drawer com badge.

Eventos novos via Realtime seguem aparecendo individualmente (fluxo
normal — o usuário está online vendo chegar).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Leonardo
2026-04-23 10:03:24 -03:00
parent 6db06abfc2
commit 4441661f62
+31 -7
View File
@@ -65,16 +65,40 @@ export function useNotifications() {
showToastFor(item);
}
// Re-carrega notifs do DB e dispara toast pras system_alert não-lidas
// que ainda não foram vistas nesta sessão. Usado no mount, no visibilitychange
// e no polling de fallback pro caso de Realtime perder eventos.
// Re-carrega notifs do DB e dispara toast AGREGADO pras system_alert
// não-lidas ainda não vistas nesta sessão. Se tiver várias, mostra só
// a mais recente com sufixo "(+N outros alertas)" no detail pra evitar
// enxurrada de toasts ao voltar pra aba / recarregar. Eventos novos via
// Realtime continuam aparecendo individualmente (showToastFor direto).
async function refreshAndMaybeAlert() {
if (!ownerId) return;
await store.load(ownerId);
for (const item of store.items || []) {
if (item.type !== 'system_alert') continue;
if (item.read_at || item.archived) continue;
showToastFor(item);
const pending = (store.items || [])
.filter((i) => i.type === 'system_alert' && !i.read_at && !i.archived && !alertedIds.has(i.id));
if (pending.length === 0) return;
// Marca os demais como já "alertados" nesta sessão pra não redisparar
// nos próximos ticks de polling/visibility — eles continuam no sininho.
const newest = pending[0]; // store.items já vem ordenado por created_at DESC
for (let i = 1; i < pending.length; i++) {
alertedIds.add(pending[i].id);
}
if (pending.length > 1) {
const extra = pending.length - 1;
const suffix = ` • +${extra} outro${extra === 1 ? '' : 's'} alerta${extra === 1 ? '' : 's'} no sino`;
const patched = {
...newest,
payload: {
...(newest.payload || {}),
detail: `${newest.payload?.detail || ''}${suffix}`
}
};
showToastFor(patched);
} else {
showToastFor(newest);
}
}