Neuer Notification-Typ mentioned; abgeleitete Tabelle page_mentions (Migration), vom Collab-Persist transaktional neu geschrieben — der Diff gegen den Vorzustand wird als pg_notify (page_mentions_changed) emittiert, nur NEU Erwähnte lösen aus (kein Spam bei Folge-Saves). Der api-Listener (erweitert um den zweiten Kanal) ruft NotificationsService.fanoutMentions: Zustellung nur nach canAccessPage-Recheck, die Autoren (pending contributors) benachrich- tigen sich nie selbst; Payload wie gehabt mit Actor-Namen. API-seitig erzeugte Seiten seeden page_mentions aus deriveContent. Glocken-Text de+en; DB-Test (Leser ja / Outsider nein / Autor nein); kompletter Loop live verifiziert (Tippen → Persist → NOTIFY → Notification). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0155v2aT8AG1kZDQEZiCLBWC
48 lines
1.4 KiB
TypeScript
48 lines
1.4 KiB
TypeScript
import { z } from 'zod';
|
|
|
|
/**
|
|
* In-app notifications (issue #94, data-model.md §notifications): watchers
|
|
* learn about page changes (version snapshots, ADR 0013's change unit) and
|
|
* new comments. Generation excludes the actor and re-checks page read
|
|
* permission at delivery time.
|
|
*/
|
|
|
|
export const NOTIFICATION_TYPES = ['page_changed', 'comment_added', 'mentioned'] as const;
|
|
export type NotificationType = (typeof NOTIFICATION_TYPES)[number];
|
|
|
|
export interface NotificationPayload {
|
|
pageId: string;
|
|
pageTitle: string;
|
|
pageSlug: string;
|
|
pondSlug: string;
|
|
pondName: string;
|
|
/** Display names of the acting users (first few, for the list entry). */
|
|
actorNames: string[];
|
|
}
|
|
|
|
export interface NotificationView {
|
|
id: string;
|
|
type: NotificationType;
|
|
payload: NotificationPayload;
|
|
createdAt: string;
|
|
readAt: string | null;
|
|
}
|
|
|
|
export const NOTIFICATION_PAGE_SIZE = 20;
|
|
|
|
export const notificationListQuerySchema = z.object({
|
|
page: z.coerce.number().int().min(1).default(1),
|
|
});
|
|
export type NotificationListQuery = z.infer<typeof notificationListQuerySchema>;
|
|
|
|
export interface NotificationListView {
|
|
notifications: NotificationView[];
|
|
unreadCount: number;
|
|
page: number;
|
|
pageCount: number;
|
|
}
|
|
|
|
/** E-mail digest cadence (issue #95). */
|
|
export const DIGEST_FREQUENCIES = ['hourly', 'daily', 'off'] as const;
|
|
export type DigestFrequency = (typeof DIGEST_FREQUENCIES)[number];
|