Watches: follow pages and ponds #93

Closed
opened 2026-07-04 14:52:51 +02:00 by fable-5 · 1 comment
Collaborator

Context

Notifications need an explicit subscription model: users watch pages or whole ponds.

Scope

watches model + endpoints (watch/unwatch page or pond, list own watches); auto-watch behaviors (author watches own pages; commenting watches the page — both user-preference toggles); watch toggle buttons on page and pond headers; watch list in account settings with unwatch.

Acceptance criteria

  • watch state round-trips and is per-user
  • auto-watch triggers per preferences; disabling the preference stops it
  • unwatching from settings works for both types

Technical notes

  • data-model.md §watches.

Dependencies

Depends on #91.

Size: ~1 day


Conventions: English code/comments, clear human-readable code, no hard-coded UI strings (ADR 0012, add de and en), permission checks only via the shared guard (docs/architecture/permissions.md). Read the referenced ADRs before starting.

## Context Notifications need an explicit subscription model: users watch pages or whole ponds. ## Scope `watches` model + endpoints (watch/unwatch page or pond, list own watches); auto-watch behaviors (author watches own pages; commenting watches the page — both user-preference toggles); watch toggle buttons on page and pond headers; watch list in account settings with unwatch. ## Acceptance criteria - [ ] watch state round-trips and is per-user - [ ] auto-watch triggers per preferences; disabling the preference stops it - [ ] unwatching from settings works for both types ## Technical notes - data-model.md §watches. ## Dependencies Depends on #91. **Size**: ~1 day --- *Conventions: English code/comments, clear human-readable code, no hard-coded UI strings (ADR 0012, add `de` **and** `en`), permission checks only via the shared guard (docs/architecture/permissions.md). Read the referenced ADRs before starting.*
fable-5 added this to the M9 — Comments & notifications milestone 2026-07-04 14:52:51 +02:00
fable-5 added the
backend
frontend
labels 2026-07-04 14:52:51 +02:00
Author
Collaborator

Implemented in f4f27cb (pipeline green, 8/8; migration 20260711230000_watches auto-applied on Test + Int).

Data modelwatches per data-model.md: (user_id, target_type page|pond, target_id) unique per user, actor-cascade on user delete. The target is deliberately polymorphic (no FK): page purge removes its watch rows through the trash service, and the list endpoint drops targets that vanished or became unreadable — stale rows are invisible and harmless (the #94 notification generators filter through read permission anyway).

API (apps/api/src/watches/): idempotent PUT/DELETE /watches/{page|pond}/:id gated by read access (404 hides what the user cannot see, per #60), GET state for the header toggles, and GET /users/me/watches resolving current names, pond slugs, and page links.

Auto-watch, preference-gated — two new user preferences (autoWatchOwnPages, autoWatchOnComment, default on) on the profile (PATCH /users/me, surfaced in /auth/me): PagesService.create subscribes the author, CommentsService.create subscribes the commenter — both fire-and-forget so a failed watch never breaks the write.

UI — watch toggle (aria-pressed) on the page header and the pond settings header (the app has no persistent pond-home header: the pond route immediately redirects to its first page, so the pond toggle lives on the pond's settings page); the account settings gain the two preference checkboxes in the profile form and a watched pages/ponds list with links and per-entry unwatch. New watches i18n namespace (de+en).

Acceptance criteria (all in watches.e2e.db.test.ts, 4 tests):

  • watch state round-trips and is per-user — the member's subscription never leaks into the owner's state; outsiders get 404 on both target types and never produce a row.
  • auto-watch triggers per preferences; disabling stops it — page create and comment both auto-subscribe under the defaults; after PATCHing both preferences off, the same actions create no watch.
  • unwatching from settings works for both types — the list shows page (with slug) and pond (slug null) entries with names; DELETE removes each and the list reflects it.
  • Plus: trashed targets disappear from the list; purge removes the rows entirely.

The UI toggles ride existing suites; the cross-feature e2e (watch → notification) lands with the #96 regression pack as scoped.

Implemented in `f4f27cb` (pipeline green, 8/8; migration `20260711230000_watches` auto-applied on Test + Int). **Data model** — `watches` per data-model.md: `(user_id, target_type page|pond, target_id)` unique per user, actor-cascade on user delete. The target is deliberately polymorphic (no FK): page purge removes its watch rows through the trash service, and the list endpoint drops targets that vanished or became unreadable — stale rows are invisible and harmless (the #94 notification generators filter through read permission anyway). **API** (`apps/api/src/watches/`): idempotent `PUT`/`DELETE /watches/{page|pond}/:id` gated by **read access** (404 hides what the user cannot see, per #60), `GET` state for the header toggles, and `GET /users/me/watches` resolving current names, pond slugs, and page links. **Auto-watch, preference-gated** — two new user preferences (`autoWatchOwnPages`, `autoWatchOnComment`, default on) on the profile (PATCH `/users/me`, surfaced in `/auth/me`): `PagesService.create` subscribes the author, `CommentsService.create` subscribes the commenter — both fire-and-forget so a failed watch never breaks the write. **UI** — watch toggle (`aria-pressed`) on the page header and the pond settings header (the app has no persistent pond-home header: the pond route immediately redirects to its first page, so the pond toggle lives on the pond's settings page); the account settings gain the two preference checkboxes in the profile form and a **watched pages/ponds list** with links and per-entry unwatch. New `watches` i18n namespace (de+en). **Acceptance criteria** (all in `watches.e2e.db.test.ts`, 4 tests): - *watch state round-trips and is per-user* — the member's subscription never leaks into the owner's state; outsiders get 404 on both target types and never produce a row. - *auto-watch triggers per preferences; disabling stops it* — page create and comment both auto-subscribe under the defaults; after PATCHing both preferences off, the same actions create no watch. - *unwatching from settings works for both types* — the list shows page (with slug) and pond (slug null) entries with names; DELETE removes each and the list reflects it. - Plus: trashed targets disappear from the list; purge removes the rows entirely. The UI toggles ride existing suites; the cross-feature e2e (watch → notification) lands with the #96 regression pack as scoped.
Sign in to join this conversation.
No project
No Assignees
1 Participants
Notifications
Due Date
The due date is invalid or out of range. Please use the format 'yyyy-mm-dd'.

No due date set.

Dependencies

No dependencies set.

Reference: stwaidele/dorfteich#93
No description provided.