Skip to content

feat(desktop): sync channel sections across devices via Nostr#792

Open
wpfleger96 wants to merge 3 commits into
worktree-wpfleger+channel-sectionsfrom
wpfleger/channel-sections-sync
Open

feat(desktop): sync channel sections across devices via Nostr#792
wpfleger96 wants to merge 3 commits into
worktree-wpfleger+channel-sectionsfrom
wpfleger/channel-sections-sync

Conversation

@wpfleger96
Copy link
Copy Markdown
Collaborator

@wpfleger96 wpfleger96 commented May 29, 2026

Stacked on #789 (must merge first). #789 adds the channel sections UI with localStorage persistence; this PR adds Nostr event sync so sections propagate across devices.

Summary

  • New channelSectionsSync.ts module encapsulating all relay interaction: fetch, publish (debounced ~2s), live subscription, and NIP-44 encrypt/decrypt. Uses kind 30078 (NIP-78 app-specific data) with d-tag "channel-sections", same pattern as ReadStateManager
  • Modified useChannelSections.ts to fetch from relay on mount, reconcile with localStorage (remote wins if newer, local migrates to relay if no remote event exists), subscribe to live updates from other devices, re-fetch on reconnect, and publish after every mutation
  • localStorage retained as local cache for instant rendering on startup; the sync layer is additive
  • Last-write-wins conflict resolution with monotonic timestamps (Math.max(now, lastSeenRemoteTimestamp + 1)) to handle clock skew, plus NIP-01 event ID tie-breaking for equal timestamps
  • Cancels pending debounced publishes when applying remote updates to prevent stale state from overwriting newer remote data
  • Tracks pending publishes for retry on relay reconnect when offline mutations failed to sync
  • Validates event.pubkey before decrypting relay events, matching ReadStateManager's guard
  • Extracts shared parseChannelSectionPayload from channelSectionsStorage.ts (eliminates duplicated validation) and swapSectionOrder into channelSectionsHelpers.ts
  • Wires resetSyncState() into resetWorkspaceState() in useWorkspaceInit.ts per the project's module-singleton reset convention
  • KIND_CHANNEL_SECTIONS constant in kinds.ts with comment explaining shared kind 30078 value
  • 27 unit tests covering storage validation, orphan cleanup, localStorage round-trip, and section reorder boundary conditions

@wpfleger96 wpfleger96 requested a review from a team as a code owner May 29, 2026 18:25
@wpfleger96 wpfleger96 marked this pull request as draft May 29, 2026 18:35
@wpfleger96 wpfleger96 force-pushed the wpfleger/channel-sections-sync branch from 18ae736 to 6fdfbd5 Compare May 29, 2026 20:01
@wpfleger96 wpfleger96 force-pushed the worktree-wpfleger+channel-sections branch from 881b951 to f081fbf Compare May 29, 2026 22:31
Channel sections were localStorage-only, meaning they didn't sync
across devices. Adds a NIP-78 (kind 30078) event-based sync layer
so sections created, renamed, deleted, or reordered on any device
propagate to all others. localStorage is retained as a local cache
for instant rendering on startup.
Cancel stale debounce when applying remote updates to prevent
publishing overwritten state. Track pending publishes for retry on
reconnect. Add pubkey validation before decrypting relay events.
Extract applyRemote reconcile helper and swapSectionOrder to
eliminate duplication. Fix createSection to construct section before
setStore so return value is deterministic. Add NIP-01 event ID
tie-breaking. Wire resetSyncState into resetWorkspaceState.
Extract swapSectionOrder to channelSectionsHelpers.ts for testability.
27 tests covering parseChannelSectionPayload validation,
stripOrphanedAssignments identity semantics, localStorage round-trip,
and swapSectionOrder boundary conditions.
@wpfleger96 wpfleger96 force-pushed the wpfleger/channel-sections-sync branch from 02d06b9 to 2b5e69e Compare May 29, 2026 22:32
@wpfleger96
Copy link
Copy Markdown
Collaborator Author

wpfleger96 commented May 29, 2026

Channel section changes sync across devices in realtime via Nostr

sprout_channel_sections_sync_to_mobile

@wpfleger96 wpfleger96 marked this pull request as ready for review May 29, 2026 22:47
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