Skip to content

bug: chat mute, first-DM badge, tab focus stealing, tournament chat entry - #607

Merged
lukepolo merged 8 commits into
mainfrom
bug/chat-unread-focus
Sep 29, 2026
Merged

lukepolo merged 8 commits into
mainfrom
bug/chat-unread-focus

Conversation

@lukepolo

@lukepolo lukepolo commented Sep 28, 2026 •

Copy link
Copy Markdown
Contributor

Fixes chat unread badges and focus, and adds a Chat Room button to the tournament page.

  • The chat ping now respects the notification-sound setting. playNotificationSound never checked isEnabled.
  • DMs from a new contact now badge, and every DM in a burst counts. direct:incoming counts unless that conversation is on screen, and message ids are deduped against the room's lobby:chat.
  • Rooms the app opens on its own (lobby, match, organizer, tournament) no longer steal the active tab.
  • ChatPanel's first mount opens the room that was asked for (Message button, stored room), not tabs[0]. Auto-selecting while hidden no longer clears a badge.
  • Closing a tab falls back to its neighbour on the rail, not the next tab in insertion order.
  • The tournament page gets a Chat Room button beside the tabs, with the room's unread count. On phones it shrinks to the icon and pill.

Merge/deploy: standalone.

Tests: mute, first-DM badge, DM burst, dedupe, focus stealing, ChatPanel first mount, hidden-panel badge and closeTab order each fail without the fix. The chat button layout has class-level assertions. Manual QA: at 360px and 1280px on a tournament with a chat room, check the button sits on the tab row and the tabs stay left-aligned. Based on DEAFCS f14aeb3, e64ac63, 9106650

…ntry

The chat notification sound now honours the sound setting like the
other sounds. A direct message from someone not yet on the rail badges
its new tab with 1 unless that conversation is on screen; an existing
tab is still counted by its room's live handler.

Lobby, match, organizer and tournament rooms that the session opens on
its own no longer take the active tab, so the restore step that undid
it is gone. ChatPanel's first mount opens the room already requested
(a Message button, the stored room) instead of the first room.

The tournament page gets a Chat Room entry for tournaments in the chat
hub's list, with the room's unread count, which opens the hub on that
room.
… the tablist

ChatPanel's auto-select also runs while the panel is mounted but
hidden, and it cleared the selected room's unread -- which wiped the
new first-DM badge whenever the panel had no room yet. Only a click
clears a badge now; the on-screen watcher still clears it once the room
is actually visible.

The on-screen check in direct:incoming could never match a tab that
does not exist yet, so it is gone. The tournament Chat Room button
moves beside the TabsList instead of inside role=tablist.
The Chat Room entry is now a ghost Button beside the TabsList, as the
match page's More button is, with the tab triggers' typography and a
MessageSquare h-4 icon, instead of a hand-built tab strip. From md up
the row no longer wraps, so the tab box shrinks and wraps its own
triggers rather than pushing the button onto a line of its own; on a
phone it still drops below. The unread pill takes the inline tab-count
geometry in red and pops in and out the way the hub badges do, with the
motion switched off under reduced motion.
@lukepolo

Copy link
Copy Markdown
Contributor Author

Design-consistency follow-up (style: match 5stack design conventions for the chat room button):

  • The tournament Chat Room entry is now a <Button variant="ghost" size="sm"> beside the TabsList, following the match page's More button, instead of a hand-built tab strip. It uses the tactical tab triggers' typography (text-[0.7rem] font-semibold uppercase tracking-[0.14em]) and a MessageSquare icon at h-4.
  • The unread pill uses the inline tab-count geometry (h-4 min-w-[1rem] text-[0.6rem] font-bold) in the unread red. It still caps at 100+, and it now pops in and out with the hub badges' transition and AnimatedStat. Reduced motion switches the pop off.
  • From md up the tab row no longer wraps. The tab box shrinks and wraps its own triggers, so the button stays beside it. On a phone the button still drops below.

ChatPanel changes are logic only, so nothing visual to adjust there. yarn test passes (47 files, 291 tests).

closeTab picked the next active tab by insertion order, so closing the
active room could land on a tab far from it on the rail. The rail's sort
(lobby, matches, channels by name, conversations by position) moves out
of ChatPanel into orderChatTabs, and closeTab now falls back to the room
after the closed one in that order, else the one before it.
flex-1 on the TabsList, with its base justify-center, centred the tabs
across the whole row, which is every tournament without a chat room, and
slid the triggers under the underline whenever the button's width
changed. The list now sizes to its tabs and starts at the left, and the
Chat Room button drops 0.2rem to sit level with the tab labels.
direct:incoming only counted the message that created a conversation's
tab. A second or third DM that landed before the new tab's room join
reached neither path: the tab already existed, and the client wasn't in
the room yet to get lobby:chat. The badge showed 1 until a reload.

direct:incoming now counts for new and existing tabs alike, unless that
conversation is on screen, using the same on-screen check as the room
handler (isChatTabOnScreen). Both paths pass the message id to
incrementUnread, and each badge remembers the ids it has counted, so a
message that arrives both ways is counted once. That memory is capped
at 200 ids and dropped when the badge is cleared or the tab closes. A
non-zero recount from the server keeps it.
@lukepolo
lukepolo merged commit dcb3ca9 into main Sep 29, 2026
2 checks passed
@lukepolo
lukepolo deleted the bug/chat-unread-focus branch September 29, 2026 01:00
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.

1 participant