Detail page now has a "Copy URL" button that copies the image's direct file URL to the clipboard. A toast service (BehaviorSubject-backed, auto-dismissing after 3s) confirms success or failure. ToastComponent is registered at the app root and available to all future features. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
51 lines
1.3 KiB
Markdown
51 lines
1.3 KiB
Markdown
# 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<Toast | null>;
|
|
|
|
// 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.
|