Skip to content

fix(unreads): remove phantom dot badges when server reports zero unreads or you sent the latest message#883

Open
Just-Insane wants to merge 5 commits into
SableClient:devfrom
Just-Insane:fix/phantom-unreads
Open

fix(unreads): remove phantom dot badges when server reports zero unreads or you sent the latest message#883
Just-Insane wants to merge 5 commits into
SableClient:devfrom
Just-Insane:fix/phantom-unreads

Conversation

@Just-Insane
Copy link
Copy Markdown
Contributor

@Just-Insane Just-Insane commented May 19, 2026

Summary

Eliminates two sources of phantom unread dot badges in the room list:

  1. Server reports zero unreads — if the server sends a m.room.summary or m.fully_read marker that clears the unread count to zero, the client was still showing a dot badge. Now the dot is suppressed when the server-reported count is zero.

  2. You sent the latest message — rooms where the authenticated user sent the most recent message were incorrectly showing a dot badge. The dot is now suppressed in this case since you clearly read your own message.

Changes

  • src/app/utils/room.ts: Two targeted fixes to the unread-dot logic.

Testing

  1. Join a room with unread messages — verify dot appears
  2. Open the room and read all messages — verify dot disappears
  3. Send a message — verify dot does not appear on your own room
  4. Have another user send a message — verify dot re-appears correctly

Type of change

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • This change requires a documentation update

Checklist:

  • My code follows the style guidelines of this project
  • I have performed a self-review of my own code
  • I have commented my code, particularly in hard-to-understand areas
  • I have made corresponding changes to the documentation
  • My changes generate no new warnings

AI disclosure:

  • Partially AI assisted (clarify which code was AI assisted and briefly explain what it does).
  • Fully AI generated (explain what all the generated code does in moderate detail).

[DRAFT — reword in your own words]: The two suppression conditions in room.ts were drafted with AI assistance. The first skips the dot badge when the server-reported unread count is zero, guarding against stale local state after the server has acknowledged all reads. The second skips it when the most recent event sender matches the local user ID, since a message you sent yourself can't be unread.

The no-readUpToId path in getUnreadInfo returned { total: 1 } (a phantom dot)
whenever the local SDK had no read receipt cached AND there was activity from
others in the live timeline - even when the server's notification count was 0.

A server count of 0 means the server has the user's receipt and knows the room
is fully read; the local cache is just stale (common after iOS cold-start, sync
gap, or rooms outside the active sliding-sync subscription window). Trusting
the synthetic count over the server's authoritative 0 caused phantom unread
dots on both DMs and Rooms that the user had already read.

Fix: remove the hasActivity + synthetic total:1 fallback entirely. Rooms with
no local receipt but non-zero server counts already fall through to the final
return statement (with DM force-highlight applied if needed), so real counts
are still displayed correctly. Rooms where both server and local cache agree
there is nothing unread now return 0, eliminating the phantom dots.
@Just-Insane Just-Insane marked this pull request as ready for review May 19, 2026 23:15
@Just-Insane Just-Insane requested review from 7w1 and hazre as code owners May 19, 2026 23:15
Copilot AI review requested due to automatic review settings May 19, 2026 23:15

This comment was marked as spam.

Restrict the phantom-unread suppression to events in NOTIFICATION_EVENT_TYPES
(m.room.message, m.room.encrypted, m.sticker, etc.) so that reactions,
membership changes, and other non-message events no longer incorrectly zero
the badge.
Guard the phantom-unread suppression path with SUPPRESSABLE_SENT_EVENT_TYPES
(m.room.message, m.room.encrypted, m.sticker) so that state events such as
m.room.create and reactions do not incorrectly clear notification badges.

Also pass room and userId to isNotificationEvent for consistent filtering.

Addresses Copilot review comment on SableClient#883.
Just-Insane added a commit to Just-Insane/Sable that referenced this pull request May 20, 2026
- fix(phantom-unreads): restrict badge suppression to SUPPRESSABLE_SENT_EVENT_TYPES
  (m.room.message / m.room.encrypted / m.sticker) and pass room+userId to
  isNotificationEvent to prevent state events from clearing badges (SableClient#883)

- fix(dm-list-group-avatars): add GroupAvatarRowHideText/GroupAvatarMiniHideText
  CSS classes (32px/18px) so composite DM avatars scale properly in icon-only
  sidebar mode instead of leaving 14px minis in a 32px container (SableClient#816)

- fix(media-cache): catch downloadMedia errors in openMediaInNewTab so a failed
  network response does not become an unhandled promise rejection (SableClient#870)

- fix(media-cache): restore touchCacheEntry LRU tracking in useBlobCache.ts that
  was dropped during merge; cache hits now update the access timestamp so
  frequently-used entries are not prematurely evicted (SableClient#870)

- fix(media-cache): restore SVG_BLOB_CACHE_MAX eviction cap and non-SVG URL
  fast-path in AvatarImage.useProcessedAvatarSrc that were dropped during merge;
  prevents unbounded memory growth and avoids a redundant fetch for every
  .png/.jpg/.gif/.webp avatar (SableClient#870)
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.

2 participants