feat(desktop): thread-aware notifications with mutable follow/mute controls#761
Open
wpfleger96 wants to merge 7 commits into
Open
feat(desktop): thread-aware notifications with mutable follow/mute controls#761wpfleger96 wants to merge 7 commits into
wpfleger96 wants to merge 7 commits into
Conversation
b033b6e to
b203463
Compare
Collaborator
Author
…tion All channel messages — including thread replies from threads the user has no involvement in — triggered equal badge/bounce/toast noise. Adds a client-side notification filter so only top-level messages, broadcast replies, and thread replies in participated/followed threads fire notifications. Participation is detected from the user's own replies via the existing catch-up REQ and a new onSelfChannelMessage live path. Also adds a "Follow thread" / "Unfollow thread" action to the message action bar in the thread panel, backed by a localStorage-persisted per- pubkey follow set (500-entry LRU cap, v1 key with cross-device sync deferred to a future NIP-RS extension).
Thread authors weren't auto-notified when someone replied to their post because top-level messages have no NIP-10 root tag. Added p-tag check to shouldNotifyForEvent so replies that include the author's pubkey in a p tag (Nostr convention) trigger notifications. Also persisted participatedRootIds to localStorage so participation survives restarts, fixed writeToStorage silently swallowing quota errors, extracted shared isBroadcastReply helper, deduplicated EMPTY_SET, added follow-thread action to main timeline thread roots, and added cross-tab storage sync.
…ctivity feed Thread authors weren't notified about replies because buildReplyTags adds the replier's pubkey, not the root author's. Rather than fix p-tags, track authored root IDs client-side (same pattern as participatedRootIds) and check them in shouldNotifyForEvent. The "Follow thread" button only checked explicit follows, showing "Follow thread" even when the user was already notified via authorship or participation. Exposed a combined isNotifiedForThread predicate and show a disabled "Following" indicator for auto-notified threads. Thread reply notifications only manifested as channel unread badges with no way to identify which thread triggered them. Split the notification path so thread replies route to a new Home activity feed instead of channel badges, making them visible in the Activity tab.
…b filter Thread replies were routed exclusively to the activity feed, losing channel unread badges and dock bounce. The if/else in useLiveChannelUpdates now fires onChannelMessage unconditionally, then additionally fires onThreadReplyNotification for thread replies. Users could not opt out of notifications for threads they authored or participated in. A mutedRootIds denylist (localStorage-persisted, identity-scoped) now lets users mute any thread. The UI collapses to a two-state Follow/Unfollow toggle. Mute precedence: p-tag mentions override mute, mute overrides participation/follow/authorship. The "All" tab in Home excluded pure-activity items; it now includes everything.
25b7a98 to
fc8452f
Compare
The badge hook's currentFeedItems included feed.feed.activity, which inflated the seen-set and badge count. Activity items from the relay API are always empty in production (Rust returns Vec::new()), but the E2E mock feed seeds them, breaking two tests. Thread activity items surface via HomeScreen's augmented feed for display only — they should not participate in the badge/seen-set mechanism.
…et seed collectHomeAlertItems was including feed.feed.activity, causing the first-load seed in useFeedDesktopNotifications to write 4 IDs to localStorage instead of the expected 2 (mentions + needsAction only).
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.


Sprout was notifying on every channel message, including thread replies from threads the user has no involvement in. This PR brings notification behavior in line with Slack's model: thread replies only notify when the user has a stake in the thread, and users have full control over opting in or out.
Notification filtering (
shouldNotifyForEvent)Client-side predicate gating notifications before badge/bounce fire. A thread reply triggers a notification only when:
p-tags the current user (explicit @-mention, overrides mute)mutedRootIdsdenylist, AND at least one of:participatedRootIds)authoredRootIds)followedRootIds)Top-level messages and DM notifications are completely unaffected.
Mutable follow/mute controls
Users can follow or unfollow ANY thread, including ones they authored or participated in. The UI is a simple two-state toggle:
followedRootIdsand adds tomutedRootIdsfollowedRootIdsand removes frommutedRootIdsmutedRootIdsis a localStorage-persisted denylist (sprout-thread-muted.v1:{pubkey}, 1000-entry cap) that overrides participation, follow, and authorship signals. Explicit @-mentions (p-tag) override the mute.Thread activity feed
Thread reply notifications populate
threadActivityItems, injected as syntheticFeedItems withcategory: "activity"into the Home feed. The "All" tab includes activity items alongside mentions and needs-action items.Thread activity is persisted to localStorage (
sprout-thread-activity.v1:{pubkey}, 100-item cap) so it survives restarts. Both live events and catch-up REQs populate it. Thread replies also fireonChannelMessagefor channel unread badges and dock bounce.Other details
useThreadFollows-- localStorage-backed follow set per pubkey, 500-entry LRU cap, cross-tabstorageevent syncauthoredRootIds-- tracks top-level messages authored by the user, populated from live events and catch-up REQsonSelfChannelMessageMoreActionsMenushouldNotifyForEventcovering all precedence combinations including mute overrides