Repository navigation
feat(chat): show the unread count in the chat row badge - #1553
Merged
Merged
Conversation
The row's unread badge was an 8dp dot that ignored its count. It now renders the shared Badge in the indicator colour with the count in the background colour, 18dp tall, as in node 10329:8245. The badge's start padding is gone so the gap to the timestamp is the title row's 4dp.
The pill drew 5dp of horizontal padding on a Pixel 10, where grid.x1 scales with width and staticGrid.x1 is 5dp, and came out 20dp tall because Avenir's 12sp line is about 16dp and doesn't shrink for a smaller line height. Badge takes contentPadding and an optional height; UnreadBadge passes 4dp horizontal and a fixed 18dp. Defaults are unchanged for the tab bar and ChatNode.
The row badge showed "1" on every unread chat on a device: unreadCount returned 0 or 1, and only the previews passed it anything larger. Each message carries the server's unreadSeq, a running count of unread-eligible messages, so the count is the newest message's stamp less the stamp on the message the READ pointer names. The feed reads that stamp from the message table when it builds, and the paged feed per row. With no pointer, the newest stamp is the count. The count is null, drawn as the old plain dot, when it can't be known: the pointer's message isn't stored (after a database rebuild), or the newest stamp is at or below the pointer's. The tab's unread total still counts those chats. This matches iOS's Conversation.unreadCount.
When the message a chat's READ pointer names isn't in the message table, as after a database rebuild, the row showed a dot with no count. The feed now fetches that message with GetMessage (view mode FULL) and counts from its unreadSeq, so every unread row gets an exact number. Fetched stamps live in ChatState for the session and are never written to chat_messages: a lone message there reads as part of the transcript. A stored stamp wins over a fetched one. Only unread chats with a READ pointer fetch, one request per chat and message at a time. NOT_FOUND isn't asked for again this session and keeps the dot; any other failure is retried on the next feed build. DENIED and NOT_FOUND log at info. This matches iOS, where the fetch runs per visible row. Here it runs from the feed build, for every chat that needs one.
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.
The chat list's unread badge was an 8dp dot:
UnreadBadgetook acountand never drew it. It now shows the number, as in node 10329:8245, and the number is the real count:unreadCountreturned 0 or 1, so every unread chat would have read "1".UnreadBadgerenders the sharedBadgefromui/components, the one the Chats tab already uses, withindicatoras the fill andbackgroundas the text colour. The digits are tracked in by 0.06em. One digit is an 18dp circle; "15" measures 21.7×18dp at 412dp width, against the design's 22×18.BadgetakescontentPaddingand an optionalheight, with defaults that keep the tab bar andChatNodeas they were. The unread pill needs both:grid.x1scales with window width andstaticGrid.x1is 5dp, so neither gives the design's 4dp. Avenir's 12sp line is about 16dp and doesn't shrink for a smallerlineHeight, so 2dp of vertical padding lands at 20dp; the pill sets 18dp directly.ChatRowTrailingdrops the badge's start padding, so the gap to the timestamp is the title row's 4dp spacing rather than 8dp.unreadCount(FeedProjection) subtracts theunreadSeqstamp on the message the READ pointer names from the newest message's stamp; with no pointer, the newest stamp is the count. The feed reads that stamp fromchat_messageswhen it builds (ChatState.readStamps), and the paged feed per row. When the pointer's message isn't stored, the feed fetches it withGetMessageand keeps its stamp in memory for the session (ChatState.fetchedReadStamps), never inchat_messages. The count is null, drawn as the old plain dot, until that lands, when the server returns NOT_FOUND, or when the newest stamp is at or below the pointer's. The tab's unread total still counts a null. Same rules as iOS'sConversation.unreadCount.TipChatRowgets a preview with a read row and rows at 2 and 15 unread.TipChatRowScreenshotTestadds a 15-unread row.The timestamp keeps its
captionstyle and still turnsindicatorblue when a chat has unread messages. Counts over 100 read "101+",Badge's existing behaviour and the same as the tab bar.