feat(notices): add system notice infrastructure

Server-side notice registry with per-user condition evaluation (firstLogin,
existingUserBeforeVersion, addonEnabled, dateWindow, role, custom).
Notices are sorted by priority then severity, filtered against dismissals
stored in a new user_notice_dismissals table, and served via
GET /api/system-notices/active + POST /api/system-notices/:id/dismiss.

Client renders notices through a host component that partitions by
display type (modal / banner / toast). The modal renderer supports
multi-page pagination with directional slide transitions, keyboard
navigation, and correct dismiss-all semantics on CTA / X / ESC.
Dismissals are optimistic with a single background retry.

Includes 3.0.0 upgrade notices (v3-photos, v3-journey, v3-features),
onboarding welcome modal, and full i18n coverage across 15 languages.
The /journey route is addon-gated on both client and server.

Also includes: unit + integration test suites, registry integrity test
that validates action CTA IDs against client source, and technical
documentation in docs/system-notices.md.
This commit is contained in:
jubnl
2026-04-16 14:36:33 +02:00
parent 3b069bc543
commit 293506217e
46 changed files with 3539 additions and 50 deletions
+14
View File
@@ -1605,6 +1605,20 @@ function runMigrations(db: Database.Database): void {
CREATE INDEX IF NOT EXISTS idx_idempotency_keys_created ON idempotency_keys(created_at);
`);
},
// Migration 101: System notices — user tracking columns + dismissals table
() => {
db.exec(`ALTER TABLE users ADD COLUMN first_seen_version TEXT NOT NULL DEFAULT '0.0.0'`);
db.exec(`ALTER TABLE users ADD COLUMN login_count INTEGER NOT NULL DEFAULT 0`);
db.exec(`
CREATE TABLE IF NOT EXISTS user_notice_dismissals (
user_id INTEGER NOT NULL,
notice_id TEXT NOT NULL,
dismissed_at INTEGER NOT NULL,
PRIMARY KEY (user_id, notice_id),
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE
)
`);
},
];
if (currentVersion < migrations.length) {