Configurable pond start page, created with every new pond #302

Closed
opened 2026-08-01 06:04:45 +02:00 by opus-5 · 0 comments
Contributor

Opening a pond lands on a page nobody chose. PondHomePage
(apps/web/src/pages/PondHomePage.tsx:34) redirects to pages.data[0]
the first page in the pond's sidebar sort order, alphabetical by default.
It is deterministic, but the rule is invisible to the user and the target
moves silently as soon as someone adds a page that sorts ahead of it.

Pond admins should decide which page a pond opens on, and a new pond
should start with a sensible one instead of empty.

Part 1 — configurable start page

New pond setting startPageId in pondSettingsSchema
(packages/shared/src/ponds.ts:57), plus the matching key in the flat
updatePondInputSchema (:95 — note it is flat, no nesting). Default
null, which keeps today's behaviour.

Referencing by page ID, not slug: renaming or moving a page then leaves
the start page intact, since slugs change on rename.

Resolution order in PondHomePage:

  1. settings.startPageId set and the page appears in the pond's page
    list for this user → redirect there.
  2. Otherwise fall back to today's behaviour (first page per sort order).
  3. No pages at all → the existing empty-pond hint (#26) stays.

Step 1 deliberately checks against the already-loaded page list rather
than fetching the page: the list only contains what this user may see, so
a start page hidden from a particular user by a page-scoped grant falls
back silently instead of producing a 404 on landing. No extra request.

A trashed start page is a stale reference, not an error. The setting is
not cleared eagerly on delete; the pond settings UI shows "no start page
set" when the reference does not resolve, and the admin can pick a new
one. Deleting a page is never blocked because it happens to be the start
page.

UI

Pond settings (apps/web/src/pages/PondSettingsPage.tsx) gets a picker
listing the pond's pages, with an explicit "none — first page by sort
order" option. Localised de+en.

Part 2 — start page for new ponds

Both creation paths in apps/api/src/ponds/ponds.service.ts create one
page and point startPageId at it:

  • createShared (:88)
  • ensurePersonalPond (:118) — a freshly verified user then lands on a
    start page instead of the empty-pond hint.

Use PagesService.create() (apps/api/src/pages/pages.service.ts:273)
rather than writing the row directly: it owns the invariants — unique
slug, appended sort key, derived content cache, search indexing,
emptyPageState() for the Yjs document, auto-watch. A hand-rolled insert
would produce a page the collab server cannot bind to.

Ordering: create the page after the pond transaction commits. The
owner's POND_ADMIN grant is written inside that transaction, and the
permission layer caches per pond (PondPermissionCache) — creating the
page inside the same transaction would query permissions before the grant
is visible. If page creation fails, log it and leave the pond as it is:
a pond without a start page is a valid, degraded state that simply falls
back. It must not roll back the pond itself. Confirm this during
implementation
— if it turns out the failure mode is confusing in
practice, the alternative is to fail the whole creation.

Page title

From the creating user's stored locale (user.locale,
apps/api/prisma/schema.prisma:38, already used this way for mails via
asLocale in apps/api/src/auth/auth.service.ts:156): "Startseite" for
de, "Home" for en. Needs a new key in the api-side i18n namespace
(pattern: apiI18n.t, apps/api/src/mail/mail-templates.ts:19).

The title is written once at creation and is an ordinary page title
afterwards — renaming it is expected and must not break anything, which
is exactly why the setting stores the ID.

The page is created empty, like any new page.

Existing ponds

Untouched. No migration creates pages and none backfills startPageId.
Existing ponds keep today's behaviour until an admin picks a start page
themselves. Rationale: a migration would either put a page into ponds
whose owners never asked for one — including other people's ponds — or
freeze the current implicit choice as if it had been made deliberately.

Accessibility

  • The picker has a visible, localised label and is operable by keyboard
    alone; it is a normal form control, so no custom widget semantics.
  • The "none" option is a real option, not an empty entry that a screen
    reader announces as blank.
  • Saving reports success and failure through the existing settings
    feedback path, not silently.
  • Landing on the start page must not steal focus in a way that breaks the
    skip link — the redirect is a replace navigation as today.

Acceptance criteria

  • A pond admin can set and clear the start page in pond settings;
    opening the pond honours it.
  • Renaming the start page keeps it working; moving it under another
    parent keeps it working.
  • Trashing the start page falls back to the first page by sort order,
    without an error, and pond settings show that none is set.
  • A user who cannot see the designated start page lands on the first
    page they can see, not on a 404.
  • A newly created shared pond opens on a start page titled per the
    creator's locale; the same holds for a personal pond created on
    e-mail verification.
  • The created page behaves as a normal page: it opens in the editor,
    collaborates, appears in search and in the sidebar.
  • Existing ponds are unchanged after deploying.
  • pnpm lint, pnpm typecheck, pnpm i18n:check, api suite and the
    a11y spec pass.
Opening a pond lands on a page nobody chose. `PondHomePage` (`apps/web/src/pages/PondHomePage.tsx:34`) redirects to `pages.data[0]` — the first page in the pond's sidebar sort order, alphabetical by default. It is deterministic, but the rule is invisible to the user and the target moves silently as soon as someone adds a page that sorts ahead of it. Pond admins should decide which page a pond opens on, and a new pond should start with a sensible one instead of empty. ## Part 1 — configurable start page New pond setting `startPageId` in `pondSettingsSchema` (`packages/shared/src/ponds.ts:57`), plus the matching key in the flat `updatePondInputSchema` (`:95` — note it is flat, no nesting). Default null, which keeps today's behaviour. Referencing by page ID, not slug: renaming or moving a page then leaves the start page intact, since slugs change on rename. Resolution order in `PondHomePage`: 1. `settings.startPageId` set **and** the page appears in the pond's page list for this user → redirect there. 2. Otherwise fall back to today's behaviour (first page per sort order). 3. No pages at all → the existing empty-pond hint (#26) stays. Step 1 deliberately checks against the already-loaded page list rather than fetching the page: the list only contains what this user may see, so a start page hidden from a particular user by a page-scoped grant falls back silently instead of producing a 404 on landing. No extra request. A trashed start page is a stale reference, not an error. The setting is not cleared eagerly on delete; the pond settings UI shows "no start page set" when the reference does not resolve, and the admin can pick a new one. Deleting a page is never blocked because it happens to be the start page. ### UI Pond settings (`apps/web/src/pages/PondSettingsPage.tsx`) gets a picker listing the pond's pages, with an explicit "none — first page by sort order" option. Localised de+en. ## Part 2 — start page for new ponds Both creation paths in `apps/api/src/ponds/ponds.service.ts` create one page and point `startPageId` at it: - `createShared` (`:88`) - `ensurePersonalPond` (`:118`) — a freshly verified user then lands on a start page instead of the empty-pond hint. Use `PagesService.create()` (`apps/api/src/pages/pages.service.ts:273`) rather than writing the row directly: it owns the invariants — unique slug, appended sort key, derived content cache, search indexing, `emptyPageState()` for the Yjs document, auto-watch. A hand-rolled insert would produce a page the collab server cannot bind to. Ordering: create the page **after** the pond transaction commits. The owner's `POND_ADMIN` grant is written inside that transaction, and the permission layer caches per pond (`PondPermissionCache`) — creating the page inside the same transaction would query permissions before the grant is visible. If page creation fails, log it and leave the pond as it is: a pond without a start page is a valid, degraded state that simply falls back. It must not roll back the pond itself. **Confirm this during implementation** — if it turns out the failure mode is confusing in practice, the alternative is to fail the whole creation. ### Page title From the creating user's stored locale (`user.locale`, `apps/api/prisma/schema.prisma:38`, already used this way for mails via `asLocale` in `apps/api/src/auth/auth.service.ts:156`): "Startseite" for `de`, "Home" for `en`. Needs a new key in the api-side i18n namespace (pattern: `apiI18n.t`, `apps/api/src/mail/mail-templates.ts:19`). The title is written once at creation and is an ordinary page title afterwards — renaming it is expected and must not break anything, which is exactly why the setting stores the ID. The page is created empty, like any new page. ## Existing ponds Untouched. No migration creates pages and none backfills `startPageId`. Existing ponds keep today's behaviour until an admin picks a start page themselves. Rationale: a migration would either put a page into ponds whose owners never asked for one — including other people's ponds — or freeze the current implicit choice as if it had been made deliberately. ## Accessibility - The picker has a visible, localised label and is operable by keyboard alone; it is a normal form control, so no custom widget semantics. - The "none" option is a real option, not an empty entry that a screen reader announces as blank. - Saving reports success and failure through the existing settings feedback path, not silently. - Landing on the start page must not steal focus in a way that breaks the skip link — the redirect is a `replace` navigation as today. ## Acceptance criteria - [ ] A pond admin can set and clear the start page in pond settings; opening the pond honours it. - [ ] Renaming the start page keeps it working; moving it under another parent keeps it working. - [ ] Trashing the start page falls back to the first page by sort order, without an error, and pond settings show that none is set. - [ ] A user who cannot see the designated start page lands on the first page they *can* see, not on a 404. - [ ] A newly created shared pond opens on a start page titled per the creator's locale; the same holds for a personal pond created on e-mail verification. - [ ] The created page behaves as a normal page: it opens in the editor, collaborates, appears in search and in the sidebar. - [ ] Existing ponds are unchanged after deploying. - [ ] `pnpm lint`, `pnpm typecheck`, `pnpm i18n:check`, api suite and the a11y spec pass.
opus-5 added this to the M33 — Tweaks & Feinschliff milestone 2026-08-01 06:04:45 +02:00
opus-5 added the
effort:M
backend
frontend
labels 2026-08-01 06:04:45 +02:00
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#302
No description provided.