audio streaming app plyr.fm
38
fork

Configure Feed

Select the types of activity you want to include in your feed.

at 2025.1208.053111 150 lines 4.6 kB view raw view rendered
1# toast notification system 2 3## status 4 5**IMPLEMENTED** - this feature is live in production 6 7## overview 8 9lightweight, zero-dependency toast notification system providing immediate user feedback for async operations (file uploads, network errors, etc.). 10 11## use cases 12 13the toast system provides UX feedback for: 14- **uploads**: "uploading track... 45%" → "track uploaded successfully!" 15- **upload errors**: detailed error messages with specific failure reasons 16- **network errors**: "network error: connection failed" 17- **processing updates**: real-time SSE progress updates during transcoding 18- **general notifications**: success/error/info/warning messages throughout the app 19 20## implementation 21 22### state management 23 24uses svelte 5 class with `$state` runes (consistent with `player.svelte.ts` and other state managers): 25 26```typescript 27// frontend/src/lib/toast.svelte.ts 28class ToastState { 29 toasts = $state<Toast[]>([]); 30 31 add(message: string, type: ToastType = 'info', duration = 3000): string 32 dismiss(id: string): void 33 update(id: string, message: string, type?: ToastType): void 34 success(message: string, duration = 3000): string 35 error(message: string, duration = 5000): string 36 info(message: string, duration = 3000): string 37 warning(message: string, duration = 4000): string 38} 39export const toast = new ToastState(); 40``` 41 42### toast types and timing 43 44- **success**: 3s auto-dismiss, ✓ icon, green accent 45- **error**: 5s auto-dismiss, ✕ icon, red accent 46- **info**: 3s auto-dismiss, ℹ icon, blue accent 47- **warning**: 4s auto-dismiss, ⚠ icon, orange accent 48- custom duration supported: `toast.add('message', 'info', 10000)` 49 50### visual design 51 52- dark background with glassmorphism (backdrop-filter blur) 53- type-specific icon colors using CSS variables 54- positioned bottom-left (above player on mobile) 55- fade transitions (respects `prefers-reduced-motion`) 56 57### positioning 58 59**all devices**: bottom-left corner 60- positioned above player footer using `calc(var(--player-height) + 1rem)` 61- doesn't block main content 62- stacks vertically with newest on top 63- responsive padding adjustments for mobile 64 65### accessibility 66 67implemented features: 68- `role="region"` + `aria-live="polite"` on container 69- `role="alert"` on individual toasts 70- `aria-hidden="true"` on decorative icons 71- respects `prefers-reduced-motion` media query 72- auto-dismiss ensures toasts don't linger indefinitely 73 74## usage patterns 75 76### simple notifications 77```typescript 78import { toast } from '$lib/toast.svelte'; 79 80toast.success('track uploaded'); 81toast.error('upload failed'); 82toast.info('processing...'); 83toast.warning('approaching rate limit'); 84``` 85 86### progress updates (uploader pattern) 87```typescript 88// initial upload progress 89const toastId = toast.info('uploading track...'); 90 91// update progress inline 92xhr.upload.addEventListener('progress', (e) => { 93 const percent = Math.round((e.loaded / e.total) * 100); 94 toast.update(toastId, `uploading track... ${percent}%`); 95}); 96 97// processing updates via SSE 98eventSource.onmessage = (event) => { 99 const update = JSON.parse(event.data); 100 if (update.status === 'processing') { 101 toast.update(toastId, update.message); 102 } 103 if (update.status === 'completed') { 104 toast.dismiss(toastId); 105 toast.success('track uploaded successfully!'); 106 } 107}; 108``` 109 110## styling 111 112uses CSS custom properties from the global theme: 113- `--accent`: info toast icon color 114- `--success`: success toast icon color 115- `--error`: error toast icon color 116- `--warning`: warning toast icon color 117- `--text-primary`: message text 118- glassmorphism background with `backdrop-filter: blur(12px)` 119 120## key features 121 122- **in-place updates**: `toast.update(id, newMessage)` allows progress tracking without spawning multiple toasts 123- **long-running tasks**: custom durations (e.g., 30s for uploads) prevent premature dismissal 124- **zero dependencies**: custom implementation, no external libraries 125- **type-safe**: full TypeScript support with exported types 126- **consistent patterns**: matches other Svelte 5 rune-based state managers 127 128### action links 129 130toasts can include an optional action link: 131 132```typescript 133export interface ToastAction { 134 label: string; 135 href: string; 136} 137 138// usage 139toast.success('track uploaded!', 3000, { label: 'view track', href: `/track/${trackId}` }); 140``` 141 142action links render as styled anchor tags. internal links stay in the same tab; external links open in new tabs. 143 144## future enhancements 145 146potential additions (not currently implemented): 147- pause on hover to prevent auto-dismiss 148- manual dismiss buttons 149- progress bars for visual timing 150- toast queue limiting to prevent spam