fix(toast): clear a toast's auto-dismiss timer before re-arming it (C-3.25)
A coalesced repeat re-surfaced the toast and armed a new dismiss timer without clearing the old one, so it vanished at the ORIGINAL deadline (6s not the expected 11s). Timers are now tracked in a Map<id> and cleared before re-arming (and on manual dismiss).
This commit is contained in:
@@ -129,6 +129,20 @@ let items: Notification[] = load();
|
|||||||
let listeners: Array<() => void> = [];
|
let listeners: Array<() => void> = [];
|
||||||
let counter = items.reduce((m, n) => Math.max(m, n.id), 0) + 1;
|
let counter = items.reduce((m, n) => Math.max(m, n.id), 0) + 1;
|
||||||
|
|
||||||
|
// Per-toast auto-dismiss timers. Keyed by id so re-arming (a coalesced repeat re-surfacing the same
|
||||||
|
// toast) can CLEAR the previous timer first — otherwise the old one still fires at the original
|
||||||
|
// deadline and dismisses the re-surfaced toast early (C-3.25).
|
||||||
|
const dismissTimers = new Map<number, ReturnType<typeof setTimeout>>();
|
||||||
|
|
||||||
|
function armDismiss(id: number, duration: number): void {
|
||||||
|
const existing = dismissTimers.get(id);
|
||||||
|
if (existing) clearTimeout(existing);
|
||||||
|
dismissTimers.set(
|
||||||
|
id,
|
||||||
|
setTimeout(() => dismiss(id), duration),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
// Cached derived snapshots — useSyncExternalStore needs stable references between
|
// Cached derived snapshots — useSyncExternalStore needs stable references between
|
||||||
// emits, so we recompute these only when `items` changes.
|
// emits, so we recompute these only when `items` changes.
|
||||||
let cachedActive: Notification[] = [];
|
let cachedActive: Notification[] = [];
|
||||||
@@ -243,7 +257,7 @@ export function notify(input: NotifyInput): number {
|
|||||||
: n,
|
: n,
|
||||||
);
|
);
|
||||||
emit();
|
emit();
|
||||||
if (duration) setTimeout(() => dismiss(prior.id), duration);
|
if (duration) armDismiss(prior.id, duration);
|
||||||
// Deliberately no re-beep: the first occurrence already sounded; a loop must stay quiet.
|
// Deliberately no re-beep: the first occurrence already sounded; a loop must stay quiet.
|
||||||
return prior.id;
|
return prior.id;
|
||||||
}
|
}
|
||||||
@@ -275,12 +289,17 @@ export function notify(input: NotifyInput): number {
|
|||||||
) {
|
) {
|
||||||
beep();
|
beep();
|
||||||
}
|
}
|
||||||
if (duration) setTimeout(() => dismiss(id), duration);
|
if (duration) armDismiss(id, duration);
|
||||||
return id;
|
return id;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Close the transient toast surface; the entry stays in the center's history. */
|
/** Close the transient toast surface; the entry stays in the center's history. */
|
||||||
export function dismiss(id: number): void {
|
export function dismiss(id: number): void {
|
||||||
|
const t = dismissTimers.get(id);
|
||||||
|
if (t) {
|
||||||
|
clearTimeout(t);
|
||||||
|
dismissTimers.delete(id);
|
||||||
|
}
|
||||||
items = items.map((n) => (n.id === id ? { ...n, dismissed: true } : n));
|
items = items.map((n) => (n.id === id ? { ...n, dismissed: true } : n));
|
||||||
emit();
|
emit();
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user