sync channel sort preferences across clients#1556
Merged
Merged
Conversation
wesbillman
approved these changes
Jul 6, 2026
wesbillman
left a comment
Collaborator
There was a problem hiding this comment.
LGTM — approve. CI fully green (all checks pass/skip, incl. both E2E Integration shards + relay e2e). This is a disciplined, near line-for-line mirror of the existing channelSectionsSync pattern, which is exactly the right move here.
What's good
- Faithful pattern replication: manager class, NIP-44 encrypt-to-self, kind 30078 + d-tag, debounced whole-blob LWW,
destroyedflag with both abort guards (post-fetch and pre-publish), monotoniccreated_at = max(now, lastRemote+1)— all consistent withchannelSectionsSync.tson main. No invented conventions. - The workspace-switch regression tests are real: the in-flight
doPublishabort test holdsfetchEventsopen with a manual-release promise, fires the timer, destroys, then releases — that exercises the actual cross-relay leak vector, not a sequential approximation. - Seed-publish adaptation is correct:
Object.keys(local.groups).length > 0is the right non-default check for a map-shaped store (sections usessections.length > 0). KIND_CHANNEL_SORTdoc-comment update keeps the d-tag inventory inkinds.tsaccurate.
Minor notes (none blocking)
- Stacked PR — merge order matters. Base is
tho/channel-sort(#1505, still open); don't merge this one first. - Whole-blob LWW is a shade lossy for this shape. The sort store is a per-group keyed map (
Record<groupKey, mode>) — structurally identical to mutes/stars, which use per-keymergeStores. Two clients changing different groups within the same debounce window will drop one change under whole-blob LWW. The PR body's low-frequency argument is convincing for sort toggles — but if a "my sort reset" ghost report ever shows up, per-key merge is the known fix. - Fourth copy of the same manager. sections, mutes, stars, and now sort each carry a near-identical ~225-line sync manager + test scaffolding. Matching the convention rather than refactoring mid-flight is the right call for this PR, but a generic NIP-78 sync-manager factory is now worth a queued follow-up — the next preference blob shouldn't be copy #5.
Reviewed by Pinky (Buzz agent) on behalf of Wes.
04d0a72 to
2fe7f2c
Compare
The per-group Recent/A-Z sort preference was desktop-only localStorage, so it silently diverged between devices. It now syncs as encrypted NIP-78 app data (kind 30078, d-tag "channel-sort") following the same pattern as channel sections: NIP-44 encrypted-to-self content, debounced publishes, read-before-write, and whole-blob last-write-wins. localStorage stays as the instant-paint/offline cache; when no remote blob exists yet, non-default local prefs seed-publish. The existing orphaned-section pruning still applies on write, and user-facing sort behavior is unchanged. Co-authored-by: Taylor Ho <taylorkmho@gmail.com> Signed-off-by: Taylor Ho <taylorkmho@gmail.com>
865e96f to
c4bc3b0
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Category: improvement
User Impact: Users' per-group channel sort choices now follow them across desktop clients on the same relay.
Problem: The channel sort controls from #1505 persisted only in the desktop local cache, unlike nearby sidebar preferences such as sections, stars, and mutes that sync through the relay. That meant another desktop client could show the same channel groups but lose the user's chosen Recent / A–Z ordering.
Solution: This adds encrypted NIP-78 relay sync for the existing per-group sort map using the same local-cache, startup fetch, live subscribe, reconnect, and debounced publish pattern as channel sections. The UI and default behavior stay unchanged; localStorage remains the instant/offline cache while the relay blob becomes the cross-client source of truth.
File changes
desktop/src/features/sidebar/lib/channelSortSync.ts
Adds a sync manager for channel sort preferences using kind
30078, d-tagchannel-sort, encrypted-to-self content, debounced whole-blob LWW writes, and workspace-switch destroy guards.desktop/src/features/sidebar/lib/useChannelSortPreference.ts
Wires the existing sort preference hook into startup remote fetch, live subscription updates, reconnect refresh, pending publish flush, and seed-publishing of non-default local preferences when no remote blob exists.
desktop/src/features/sidebar/lib/channelSortSync.test.mjs
Adds regression coverage for destroy/cancel behavior so pending or in-flight publishes cannot leak preferences to the wrong relay during workspace switches.
desktop/src/shared/constants/kinds.ts
Documents
channel-sortalongside the other NIP-78 d-tags and adds a namedKIND_CHANNEL_SORTconstant for the shared30078kind.Reproduction Steps
Validation
tsc— cleanbiome— clean