You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Deferred finding from the adversarial review of the #3197 late-joiner backfill fix. Depends on #3197.
Problem
The server-side mentions feed now bounds @channel rows to members who had already joined when the relay admitted the event (cm.joined_at <= e.received_at, cf5fd18). The desktop catch-up path has no equivalent bound:
useUnreadChannels.ts fetches per-channel history with since = readAt + 1, and a channel with no read state — which is exactly the just-joined case — uses since: 0, pulling the full retained timeline.
Classification runs each fetched event through shouldNotifyForEvent / isHighPriorityEventForUser, whose channelNotifyEscalates treats every["notify","channel"] marker as mention-tier. It has no join-time input.
Net effect: a member who joins a channel gets mention-tier sidebar/app badges for @channel events sent before they joined — events the (now-correct) server feed deliberately excludes. Badge with no corresponding Inbox row.
@here is only marginally exposed (its 120s observation-time freshness window bounds retroactivity), but the same join-time question applies to an @here sent moments before the reader joined.
Fix directions
Join-time bound in the pure predicate (mirrors the server): extend ChannelNotifyContext (desktop/src/features/notifications/lib/channelNotifyEscalation.ts) with an optional joinedAtSeconds, and return false when event.created_at < joinedAtSeconds. Callers in the catch-up path pass the reader's own joinedAt (available on ChannelMember, desktop/src/shared/api/types.ts) — the cost is plumbing per-channel self-membership into useUnreadChannels, which today only holds membership sets. Note the client only has the client-authored created_at, not the relay's received_at, so the bound is skew-tolerant rather than exact — same trade-off class the server fix documents.
Feed-driven badges: treat the bounded mentions feed (which desktop now requests — see feat: @channel and @here channel-wide mentions (NIP-CM) #3197) as the authority for @channel mention-tier in catch-up, and stop escalating on the raw marker for historical fetches. Live-subscription escalation stays as-is (a live event is by definition post-join).
Option 1 is the smaller change; option 2 removes the client/server divergence class entirely.
Deferred finding from the adversarial review of the #3197 late-joiner backfill fix. Depends on #3197.
Problem
The server-side mentions feed now bounds
@channelrows to members who had already joined when the relay admitted the event (cm.joined_at <= e.received_at, cf5fd18). The desktop catch-up path has no equivalent bound:useUnreadChannels.tsfetches per-channel history withsince = readAt + 1, and a channel with no read state — which is exactly the just-joined case — usessince: 0, pulling the full retained timeline.shouldNotifyForEvent/isHighPriorityEventForUser, whosechannelNotifyEscalatestreats every["notify","channel"]marker as mention-tier. It has no join-time input.Net effect: a member who joins a channel gets mention-tier sidebar/app badges for
@channelevents sent before they joined — events the (now-correct) server feed deliberately excludes. Badge with no corresponding Inbox row.@hereis only marginally exposed (its 120s observation-time freshness window bounds retroactivity), but the same join-time question applies to an@heresent moments before the reader joined.Fix directions
ChannelNotifyContext(desktop/src/features/notifications/lib/channelNotifyEscalation.ts) with an optionaljoinedAtSeconds, and returnfalsewhenevent.created_at < joinedAtSeconds. Callers in the catch-up path pass the reader's ownjoinedAt(available onChannelMember,desktop/src/shared/api/types.ts) — the cost is plumbing per-channel self-membership intouseUnreadChannels, which today only holds membership sets. Note the client only has the client-authoredcreated_at, not the relay'sreceived_at, so the bound is skew-tolerant rather than exact — same trade-off class the server fix documents.@channelmention-tier in catch-up, and stop escalating on the raw marker for historical fetches. Live-subscription escalation stays as-is (a live event is by definition post-join).Option 1 is the smaller change; option 2 removes the client/server divergence class entirely.
Related
@channel/@here(NIP-CM); server-side join bound in cf5fd18#ponly