music on atproto
plyr.fm
1// global toast notification state using Svelte 5 runes
2
3export type ToastType = 'success' | 'error' | 'info' | 'warning';
4
5export interface ToastAction {
6 label: string;
7 href: string;
8}
9
10export interface Toast {
11 id: string;
12 message: string;
13 type: ToastType;
14 duration: number;
15 dismissible: boolean;
16 action?: ToastAction;
17}
18
19class ToastState {
20 toasts = $state<Toast[]>([]);
21
22 add(message: string, type: ToastType = 'info', duration = 3000, action?: ToastAction): string {
23 const id = crypto.randomUUID();
24 const toast: Toast = {
25 id,
26 message,
27 type,
28 duration,
29 dismissible: true,
30 action
31 };
32
33 this.toasts = [toast, ...this.toasts];
34
35 if (duration > 0) {
36 setTimeout(() => this.dismiss(id), duration);
37 }
38
39 return id;
40 }
41
42 dismiss(id: string): void {
43 this.toasts = this.toasts.filter(t => t.id !== id);
44 }
45
46 update(id: string, message: string, type?: ToastType): void {
47 const index = this.toasts.findIndex(t => t.id === id);
48 if (index !== -1) {
49 this.toasts[index].message = message;
50 if (type) {
51 this.toasts[index].type = type;
52 }
53 }
54 }
55
56 success(message: string, duration = 3000, action?: ToastAction): string {
57 return this.add(message, 'success', duration, action);
58 }
59
60 error(message: string, duration = 5000): string {
61 return this.add(message, 'error', duration);
62 }
63
64 info(message: string, duration = 3000, action?: ToastAction): string {
65 return this.add(message, 'info', duration, action);
66 }
67
68 warning(message: string, duration = 4000): string {
69 return this.add(message, 'warning', duration);
70 }
71}
72
73export const toast = new ToastState();