Почтовый кабинет: регистрация, папки, черновики, вложения, грид @jambotron/aria-grid, интерфейс по synthwave-шаблону
Бэкенд: регистрация со входом сразу и /api/config, профиль и смена пароля, свои папки, PATCH письма (прочитано, флажок, перенос; Спам ставит отметку), корзина и очистка, черновики, несколько адресатов и копия, вложения черновика уходят с письмом, вложения и заголовки To/Cc входящих сохраняются, письма заблокированных — в Спам. Исправлено: загрузка файла (415), колонка content_id (500 на любых вложениях), SPA-маршрут с точкой. Фронтенд переписан: все таблицы — грид, оформление — шаблон synthwave-design-showcase.html. α Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,24 @@
|
||||
import { Injectable, inject, signal } from '@angular/core';
|
||||
import { LiveAnnouncer } from '@angular/cdk/a11y';
|
||||
|
||||
export interface Toast { id: number; text: string; kind: 'ok' | 'error'; }
|
||||
|
||||
/** Short messages in the corner; also read out by screen readers. */
|
||||
@Injectable({ providedIn: 'root' })
|
||||
export class ToastService {
|
||||
private announcer = inject(LiveAnnouncer);
|
||||
private next = 1;
|
||||
readonly toasts = signal<Toast[]>([]);
|
||||
|
||||
ok(text: string): void { this.show(text, 'ok', 4000); }
|
||||
error(text: string): void { this.show(text, 'error', 8000); }
|
||||
|
||||
dismiss(id: number): void { this.toasts.update(list => list.filter(t => t.id !== id)); }
|
||||
|
||||
private show(text: string, kind: Toast['kind'], ms: number): void {
|
||||
const id = this.next++;
|
||||
this.toasts.update(list => [...list.slice(-3), { id, text, kind }]);
|
||||
this.announcer.announce(text, kind === 'error' ? 'assertive' : 'polite');
|
||||
setTimeout(() => this.dismiss(id), ms);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user