All files / services notification.service.ts

100% Statements 21/21
88.88% Branches 8/9
100% Functions 5/5
100% Lines 18/18

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 473x 3x     3x 3x 3x     3x 9x   9x 9x     16x 16x     16x   13x 13x   13x                   9x       3x       4x      
import { inject, Injectable } from '@angular/core';
import { MatSnackBar } from '@angular/material/snack-bar';
 
export type ToastKind = 'error' | 'info' | 'success';
const errorDurationMs = 6000;
const messageDurationMs = 3000;
const throttleTimeMs = 2000;
 
@Injectable({ providedIn: 'root' })
export class NotificationService {
    private readonly snackBar = inject(MatSnackBar);
 
    private lastKey: string | undefined = undefined;
    private lastAt = 0;
 
    private show(message: string, kind: ToastKind = 'info'): void {
        const key = `${kind}:${message}`;
        const now = Date.now();
 
        // Simple dedupe/throttle: ignore same message within throttleTimeMs
        if (this.lastKey === key && now - this.lastAt < throttleTimeMs) return;
 
        this.lastKey = key;
        this.lastAt = now;
 
        this.snackBar.open(message, 'OK', {
            duration: kind === 'error' ? errorDurationMs : messageDurationMs,
            horizontalPosition: 'end',
            verticalPosition: 'top',
            panelClass: [`toast-${kind}`],
            politeness: kind === 'error' ? 'assertive' : 'polite',
        });
    }
 
    error(message: string): void {
        this.show(message, 'error');
    }
 
    success(message: string): void {
        this.show(message, 'success');
    }
 
    info(message: string): void {
        this.show(message, 'info');
    }
}