2026-07-27 09:37:24 +02:00
|
|
|
export type BannerSeverity = "error" | "warning" | "notice" | "success";
|
|
|
|
|
|
|
|
|
|
export interface BannerEntry {
|
|
|
|
|
id: number;
|
|
|
|
|
severity: BannerSeverity;
|
|
|
|
|
message: string;
|
|
|
|
|
}
|
|
|
|
|
|
2026-08-04 00:04:03 +02:00
|
|
|
// Non-error banners clean themselves up; errors stay until the user
|
|
|
|
|
// dismisses them, so a failure can't silently vanish before it's read.
|
2026-07-27 09:37:24 +02:00
|
|
|
const DISMISS_AFTER_MS = 8000;
|
|
|
|
|
|
|
|
|
|
let banners: BannerEntry[] = [];
|
|
|
|
|
let nextId = 1;
|
|
|
|
|
const listeners = new Set<() => void>();
|
|
|
|
|
|
|
|
|
|
function notify() {
|
|
|
|
|
for (const listener of listeners) listener();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function show(severity: BannerSeverity, message: string): void {
|
2026-07-27 10:34:57 +02:00
|
|
|
const isDuplicate = banners.some(
|
|
|
|
|
(b) => b.severity === severity && b.message === message,
|
|
|
|
|
);
|
|
|
|
|
if (isDuplicate) return;
|
|
|
|
|
|
2026-07-27 09:37:24 +02:00
|
|
|
const id = nextId++;
|
|
|
|
|
banners = [{ id, severity, message }, ...banners];
|
|
|
|
|
notify();
|
2026-08-04 00:04:03 +02:00
|
|
|
if (severity !== "error") {
|
|
|
|
|
setTimeout(() => dismiss(id), DISMISS_AFTER_MS);
|
|
|
|
|
}
|
2026-07-27 09:37:24 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function showError(message: string): void {
|
|
|
|
|
show("error", message);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function showWarning(message: string): void {
|
|
|
|
|
show("warning", message);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function showNotice(message: string): void {
|
|
|
|
|
show("notice", message);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function showSuccess(message: string): void {
|
|
|
|
|
show("success", message);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function dismiss(id: number): void {
|
|
|
|
|
banners = banners.filter((b) => b.id !== id);
|
|
|
|
|
notify();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function subscribe(listener: () => void): () => void {
|
|
|
|
|
listeners.add(listener);
|
|
|
|
|
return () => listeners.delete(listener);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function getSnapshot(): BannerEntry[] {
|
|
|
|
|
return banners;
|
|
|
|
|
}
|