Skip to content

feat(desktop): add message bookmarks + Saved view - #3712

Open
divljak wants to merge 1 commit into
block:mainfrom
divljak:feat/message-bookmarks
Open

feat(desktop): add message bookmarks + Saved view#3712
divljak wants to merge 1 commit into
block:mainfrom
divljak:feat/message-bookmarks

Conversation

@divljak

@divljak divljak commented Jul 30, 2026

Copy link
Copy Markdown

Summary

Adds a Slack-style "Save for later" bookmark feature to the desktop app:

  • Bookmark toggle in the message hover toolbar, between the emoji-react and reply buttons (filled icon when saved; Save for later / Remove bookmark tooltip). Works on both the channel timeline and the home inbox.
  • Saved view — a new top-level Saved item in the left nav opening a /saved route that lists your bookmarked messages newest-first, with click-to-jump-back-to-context (opens the thread when the saved message is a reply).

Design notes / decision to sanity-check

Bookmarks are stored as a private, per-user NIP-78 app-data list (kind:30078, d-tag "bookmarks"), encrypted to self via nip44 — a direct clone of the existing channel-stars/mutes/sort sync engine (desktop/src/features/sidebar/lib/channelStarsSync.ts). Consequences:

  • No relay/backend changekind:30078 is already accepted; this is a pure desktop/ change.
  • Private by default — only the author can decrypt their saved list (matches Slack "saved items" semantics). The only plaintext tags are the d/t markers; no per-message data leaks.
  • Single-query Saved view — one authors:[self] #d:bookmarks fetch, no cross-channel fan-out.
  • Payload is a replaceable list with per-key last-write-wins merge; un-bookmarking is a tombstone (bookmarked:false) so removals propagate across devices.

One thing for maintainers to weigh in on: buzz-core/src/kind.rs reserves KIND_STREAM_MESSAGE_BOOKMARKED = 40005 ("a stream message bookmarked by a user"), which may be the intended home for this. I went with the 30078 private-list approach because it's private, cross-channel, needs no relay change, and reuses a proven client pattern — but 40005 is channel-scoped and would need relay handling + a cross-channel query for the Saved view. Happy to pivot to 40005 if that's the preferred direction.

Related issue

None found (searched issues/PRs for "bookmark" / "save for later").

Testing

All desktop gates green locally:

  • tsc --noEmit ✓ · biome check ✓ · pnpm check:px-text
  • pnpm test3778/3778 pass, including 9 new unit tests for the parse/merge/tombstone/thread-root logic (bookmarksStorage.test.mjs)
  • vite build

Also went through an independent code review pass; fixed two items it surfaced before opening: a no-op remote-sync path that could re-render the whole timeline (now content-stable via useStableSet + a split actions/list context), and jump-to-context missing thread context for saved replies (now captured as threadRootId).

Not yet exercised: a full native Tauri run against a live relay. UI screenshots to follow.

Adds a "Save for later" bookmark toggle to the message hover toolbar
(between react and reply) and a top-level "Saved" left-nav view listing
bookmarked messages with jump-to-context.

Bookmarks are stored as a private, per-user NIP-78 (kind 30078) app-data
list with d-tag "bookmarks", encrypted to self via nip44 — mirroring the
existing channel-stars sync engine. No relay/backend change required.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Signed-off-by: Ognjen Divljak <ogilud@gmail.com>
@divljak
divljak requested a review from a team as a code owner July 30, 2026 09:30
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant