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:
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user