# Contract: ToastService **Location**: `ui/src/app/services/toast.service.ts` **Provided in**: `root` (singleton) ## Interface ```typescript interface Toast { message: string; type: 'success' | 'error'; } class ToastService { // Observable — emits a Toast when one is active, null when none. readonly current$: Observable; // Show a toast. Replaces any currently-visible toast. // duration defaults to 3000ms. show(message: string, type?: 'success' | 'error', duration?: number): void; } ``` ## Behaviour - `show()` emits the toast immediately on `current$`. - After `duration` ms, emits `null` to dismiss. - Calling `show()` again before the timer expires resets the timer (new toast replaces old). - `type` defaults to `'success'`. - `duration` defaults to `3000`. ## Usage Example ```typescript // In any component: constructor(private toast: ToastService) {} async copyUrl() { try { await navigator.clipboard.writeText(url); this.toast.show('URL copied!'); } catch { this.toast.show('Failed to copy URL', 'error'); } } ``` ## Consumer: ToastComponent `ToastComponent` subscribes to `current$` via the `async` pipe and renders/hides based on the emitted value. It is placed once in `AppComponent` and is always present in the DOM.