fix(temporary-notes): live sidebar clock marker + stacked mobile create buttons

Issue 1 — the sidebar tree's temporary-note clock marker did not appear/
disappear until a page reload when a note's temporary state changed.

- Make/unmake permanent from the page header menu and the in-page banner went
  through syncTemporaryExpiresInCache(), which patched the page query cache but
  never touched treeDataAtom, so the sidebar node kept its stale
  temporaryExpiresAt. Patch the tree node there too (via jotai's default store),
  so the marker updates without a reload.
- Creating a note as temporary showed no marker until reload: the create flow's
  cache write (invalidateOnCreatePage) omitted temporaryExpiresAt, so the tree
  rebuild (buildTree -> mergeRootTrees) overwrote the optimistic/socket node's
  marker with undefined. Carry temporaryExpiresAt in that cached entry.
- Thread temporaryExpiresAt through the server addTreeNode broadcast (PAGE_CREATED
  snapshot -> TreeNodeSnapshot -> broadcastPageCreated) so OTHER clients watching
  the space also render the marker immediately, and harden handleCreate's
  idempotency guard to patch the deadline if the broadcast won the insert race.

Issue 2 — the home and space-overview "New note" / "New temporary note" buttons
sat side-by-side and the temporary label clipped on narrow mobile widths. Lay
them out full-width, stacked vertically, and tint the temporary button orange
(matching the clock marker + banner) while the regular one stays neutral gray.

Tests: extend tree-socket-reducers.test.ts (addTreeNode carries
temporaryExpiresAt). Verified live with Playwright: marker appears on create and
toggles both ways with no reload; mobile buttons are stacked, full-width,
unclipped, and differently colored.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
claude code agent 227
2026-06-27 00:29:19 +03:00
parent e9409e245b
commit 12ff76fb89
10 changed files with 92 additions and 14 deletions

View File

@@ -21,6 +21,11 @@ export interface TreeNodeSnapshot {
position: string;
spaceId: string;
parentPageId: string | null;
// Death-timer deadline carried so the `addTreeNode` broadcast shows the
// temporary-note clock marker immediately on every client (incl. the author,
// whose optimistic insert can lose the race to this broadcast). null/absent =>
// permanent.
temporaryExpiresAt?: Date | string | null;
}
export class PageEvent {

View File

@@ -209,6 +209,9 @@ export class PageRepo {
position: result.position,
spaceId: result.spaceId,
parentPageId: result.parentPageId,
// Carry the death-timer deadline so a note created as temporary shows
// its sidebar clock marker on every client without a reload.
temporaryExpiresAt: result.temporaryExpiresAt,
},
],
});

View File

@@ -38,6 +38,10 @@ export class WsTreeService {
spaceId: page.spaceId,
parentPageId: page.parentPageId,
hasChildren: false,
// Carry the death-timer deadline so receivers (and the author, if this
// broadcast wins the race against the optimistic insert) render the
// temporary-note clock marker immediately. null => permanent.
temporaryExpiresAt: page.temporaryExpiresAt ?? null,
children: [],
},
},