The number beside a conversation is what is addressed to you: mentions in a channel, each unread message in a direct message - the bold name says there is activity, a count of it does not

Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
This commit is contained in:
2026-10-10 13:21:56 -04:00
co-authored by Claude Sonnet 5.5
parent 336335a8a3
commit 987f31cf77
5 changed files with 36 additions and 9 deletions
+6 -5
View File
@@ -19,6 +19,7 @@ import {
MENTIONS_GROUP_ID,
PINNED_GROUP_ID,
pinnedGroup,
waitingCount,
visibleGroups,
type RailGroup
} from '../lib/groups'
@@ -593,12 +594,12 @@ export function BufferList(): JSX.Element {
// What is waiting in what is folded away, which is
// the thing worth knowing about a closed heading;
// how many channels it has is the fallback.
const waiting = section.buffers.filter((b) => b.unread > 0 && (!isEffectivelyMuted(b) || b.highlight))
const unread = waiting.reduce((n, b) => n + b.unread, 0)
const waiting = section.buffers.filter((b) => waitingCount(b) > 0 && (!isEffectivelyMuted(b) || b.highlight))
const unread = waiting.reduce((n, b) => n + waitingCount(b), 0)
return unread > 0 ? (
<span
className={classes('unread-badge category-count', waiting.some((b) => b.highlight) && 'highlight')}
title={`${unread} unread in ${waiting.length} ${waiting.length === 1 ? 'channel' : 'channels'}`}
title={`${unread} ${unread === 1 ? 'mention' : 'mentions'} in ${waiting.length} ${waiting.length === 1 ? 'channel' : 'channels'}`}
>
{unread > 99 ? '99+' : unread}
</span>
@@ -1062,9 +1063,9 @@ function BufferRow({
arrive is worse than either on its own. */}
{live ? (
<span className="live-badge">LIVE</span>
) : buffer.unread > 0 && (!muted || buffer.highlight) ? (
) : waitingCount(buffer) > 0 && (!muted || buffer.highlight) ? (
<span className={classes('unread-badge', buffer.highlight && 'highlight')}>
{buffer.unread > 99 ? '99+' : buffer.unread}
{waitingCount(buffer) > 99 ? '99+' : waitingCount(buffer)}
</span>
) : (
// Left unread on purpose, with nothing new in it. A dot rather than
@@ -3,6 +3,7 @@ import { createPortal } from 'react-dom'
import { Avatar } from './Avatar'
import { Icon } from './Icon'
import { useChat, useStore } from '../state/hooks'
import { waitingCount } from '../lib/groups'
import { rankBuffers, whereText } from '../lib/switcher'
import { bufferKindGlyph, classes } from '../lib/util'
@@ -122,9 +123,9 @@ export function QuickSwitcher(): JSX.Element | null {
</span>
<span className="switcher-title ellipsis">{hit.title}</span>
<span className="switcher-where muted small ellipsis">{hit.where}</span>
{b.unread > 0 && (
{waitingCount(b) > 0 && (
<span className={classes('unread-badge', b.highlight && 'highlight')}>
{b.unread > 99 ? '99+' : b.unread}
{waitingCount(b) > 99 ? '99+' : waitingCount(b)}
</span>
)}
</button>
+13
View File
@@ -10,6 +10,7 @@ import {
removeFromFolders,
reorder,
reorderFolder,
waitingCount,
type RailFolder,
type RailGroup
} from './groups'
@@ -124,3 +125,15 @@ describe('compareInBand', () => {
expect([dm('old', 1), dm('new', 9)].sort(compareInBand).map((c) => c.name)).toEqual(['new', 'old'])
})
})
describe('waitingCount', () => {
const buffer = (over: Partial<BufferEntry>): BufferEntry =>
({ id: 'a|#x', kind: 'channel', unread: 97, mentions: 0, highlight: false, ...over }) as BufferEntry
it('is the mentions in a channel, not everything said in it', () => {
expect(waitingCount(buffer({}))).toBe(0)
expect(waitingCount(buffer({ mentions: 2, highlight: true }))).toBe(2)
})
it('is each unread message in a direct message, which are all for you', () => {
expect(waitingCount(buffer({ kind: 'dm', unread: 4 }))).toBe(4)
})
})
+12
View File
@@ -95,6 +95,18 @@ export function isDirectMessage(buffer: BufferEntry): boolean {
return buffer.kind === 'dm'
}
/**
* The number to show beside a conversation: what is addressed to you.
*
* Mentions, not everything said - a bold name already says there is activity,
* and a count of it is the noise the rail and tray were rid of. A direct
* message is the exception that is the same rule: every line in one is
* addressed to you, so it is counted by each unread message.
*/
export function waitingCount(buffer: BufferEntry): number {
return isDirectMessage(buffer) ? buffer.unread : buffer.mentions
}
export function pinnedGroup(): RailGroup {
return {
id: PINNED_GROUP_ID,
+2 -2
View File
@@ -1,4 +1,4 @@
import { countsTowardRail, isDirectMessage } from './groups'
import { countsTowardRail, isDirectMessage, waitingCount } from './groups'
import type { BufferEntry } from '../state/store'
/** What the tray is told is waiting. */
@@ -32,7 +32,7 @@ export function trayCount(buffers: BufferEntry[], muted: string[], hidden: strin
let latest: BufferEntry | null = null
for (const b of buffers) {
if (!countsTowardRail(b, muted, hidden, mutedGroups)) continue
const waiting = isDirectMessage(b) ? b.unread : b.mentions
const waiting = waitingCount(b)
if (waiting <= 0) continue
if (isDirectMessage(b)) dms += waiting
else mentions += waiting