diff --git a/src/renderer/src/components/BufferList.tsx b/src/renderer/src/components/BufferList.tsx index b462e3d..dab11cf 100644 --- a/src/renderer/src/components/BufferList.tsx +++ b/src/renderer/src/components/BufferList.tsx @@ -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 ? ( 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} @@ -1062,9 +1063,9 @@ function BufferRow({ arrive is worse than either on its own. */} {live ? ( LIVE - ) : buffer.unread > 0 && (!muted || buffer.highlight) ? ( + ) : waitingCount(buffer) > 0 && (!muted || buffer.highlight) ? ( - {buffer.unread > 99 ? '99+' : buffer.unread} + {waitingCount(buffer) > 99 ? '99+' : waitingCount(buffer)} ) : ( // Left unread on purpose, with nothing new in it. A dot rather than diff --git a/src/renderer/src/components/QuickSwitcher.tsx b/src/renderer/src/components/QuickSwitcher.tsx index 8cadf39..f765ee2 100644 --- a/src/renderer/src/components/QuickSwitcher.tsx +++ b/src/renderer/src/components/QuickSwitcher.tsx @@ -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 { {hit.title} {hit.where} - {b.unread > 0 && ( + {waitingCount(b) > 0 && ( - {b.unread > 99 ? '99+' : b.unread} + {waitingCount(b) > 99 ? '99+' : waitingCount(b)} )} diff --git a/src/renderer/src/lib/groups.test.ts b/src/renderer/src/lib/groups.test.ts index c8e5160..5966a08 100644 --- a/src/renderer/src/lib/groups.test.ts +++ b/src/renderer/src/lib/groups.test.ts @@ -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 => + ({ 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) + }) +}) diff --git a/src/renderer/src/lib/groups.ts b/src/renderer/src/lib/groups.ts index afe007a..2e25346 100644 --- a/src/renderer/src/lib/groups.ts +++ b/src/renderer/src/lib/groups.ts @@ -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, diff --git a/src/renderer/src/lib/traycount.ts b/src/renderer/src/lib/traycount.ts index 89547c9..174c520 100644 --- a/src/renderer/src/lib/traycount.ts +++ b/src/renderer/src/lib/traycount.ts @@ -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