diff --git a/frontend/src/components/ChatHeader.tsx b/frontend/src/components/ChatHeader.tsx index 6eea00b..b1720ee 100644 --- a/frontend/src/components/ChatHeader.tsx +++ b/frontend/src/components/ChatHeader.tsx @@ -138,11 +138,11 @@ export function ChatHeader({ {conversation.type === 'channel' ? conversation.id.toLowerCase() : conversation.id} )} - {conversation.type === 'channel' && activeChannel?.flood_scope_override && ( - - Regional override active: {activeChannel.flood_scope_override} - - )} + {conversation.type === 'channel' && activeChannel?.flood_scope_override && ( + + Regional override active: {activeChannel.flood_scope_override} + + )} {conversation.type === 'contact' && (() => { const contact = contacts.find((c) => c.public_key === conversation.id); diff --git a/frontend/src/components/Sidebar.tsx b/frontend/src/components/Sidebar.tsx index f44d8ae..1e6f50a 100644 --- a/frontend/src/components/Sidebar.tsx +++ b/frontend/src/components/Sidebar.tsx @@ -485,6 +485,8 @@ export function Sidebar({ const getSectionUnreadCount = (rows: ConversationRow[]): number => rows.reduce((total, row) => total + row.unreadCount, 0); + const sectionHasMention = (rows: ConversationRow[]): boolean => rows.some((row) => row.isMention); + const favoriteRows = favoriteItems.map((item) => item.type === 'channel' ? buildChannelRow(item.channel, 'fav-chan') @@ -498,6 +500,8 @@ export function Sidebar({ const channelsUnreadCount = getSectionUnreadCount(channelRows); const contactsUnreadCount = getSectionUnreadCount(contactRows); const repeatersUnreadCount = getSectionUnreadCount(repeaterRows); + const favoritesHasMention = sectionHasMention(favoriteRows); + const channelsHasMention = sectionHasMention(channelRows); const toolRows = !query ? [ renderSidebarActionRow({ @@ -575,7 +579,8 @@ export function Sidebar({ collapsed: boolean, onToggle: () => void, showSortToggle = false, - unreadCount = 0 + unreadCount = 0, + highlightUnread = false ) => { const effectiveCollapsed = isSearching ? false : collapsed; @@ -611,7 +616,12 @@ export function Sidebar({ )} {unreadCount > 0 && ( {unreadCount} @@ -701,7 +711,8 @@ export function Sidebar({ favoritesCollapsed, () => setFavoritesCollapsed((prev) => !prev), false, - favoritesUnreadCount + favoritesUnreadCount, + favoritesHasMention )} {(isSearching || !favoritesCollapsed) && favoriteRows.map((row) => renderConversationRow(row))} @@ -716,7 +727,8 @@ export function Sidebar({ channelsCollapsed, () => setChannelsCollapsed((prev) => !prev), true, - channelsUnreadCount + channelsUnreadCount, + channelsHasMention )} {(isSearching || !channelsCollapsed) && channelRows.map((row) => renderConversationRow(row))} @@ -731,7 +743,8 @@ export function Sidebar({ contactsCollapsed, () => setContactsCollapsed((prev) => !prev), true, - contactsUnreadCount + contactsUnreadCount, + contactsUnreadCount > 0 )} {(isSearching || !contactsCollapsed) && contactRows.map((row) => renderConversationRow(row))} diff --git a/frontend/src/test/sidebar.test.tsx b/frontend/src/test/sidebar.test.tsx index 75b19f9..ab01c57 100644 --- a/frontend/src/test/sidebar.test.tsx +++ b/frontend/src/test/sidebar.test.tsx @@ -37,6 +37,7 @@ function makeContact(public_key: string, name: string, type = 1): Contact { function renderSidebar(overrides?: { unreadCounts?: Record; + mentions?: Record; favorites?: Favorite[]; lastMessageTimes?: ConversationTimes; channels?: Channel[]; @@ -67,7 +68,7 @@ function renderSidebar(overrides?: { onNewMessage={vi.fn()} lastMessageTimes={overrides?.lastMessageTimes ?? {}} unreadCounts={unreadCounts} - mentions={{}} + mentions={overrides?.mentions ?? {}} showCracker={false} crackerRunning={false} onToggleCracker={vi.fn()} @@ -102,6 +103,40 @@ describe('Sidebar section summaries', () => { expect(within(getSectionHeaderContainer('Repeaters')).getByText('4')).toBeInTheDocument(); }); + it('turns favorites and channels rollups red when they contain a mention', () => { + renderSidebar({ + mentions: { + [getStateKey('channel', 'BB'.repeat(16))]: true, + [getStateKey('channel', 'CC'.repeat(16))]: true, + }, + }); + + expect(within(getSectionHeaderContainer('Favorites')).getByText('2')).toHaveClass( + 'bg-badge-mention', + 'text-badge-mention-foreground' + ); + expect(within(getSectionHeaderContainer('Channels')).getByText('1')).toHaveClass( + 'bg-badge-mention', + 'text-badge-mention-foreground' + ); + }); + + it('keeps contact row badges normal while the contacts rollup is always red', () => { + const { aliceName } = renderSidebar(); + + expect(within(getSectionHeaderContainer('Contacts')).getByText('3')).toHaveClass( + 'bg-badge-mention', + 'text-badge-mention-foreground' + ); + + const aliceRow = screen.getByText(aliceName).closest('div'); + if (!aliceRow) throw new Error('Missing Alice row'); + expect(within(aliceRow).getByText('3')).toHaveClass( + 'bg-badge-unread/90', + 'text-badge-unread-foreground' + ); + }); + it('expands collapsed sections during search and restores collapse state after clearing search', async () => { const { opsChannel, aliceName } = renderSidebar();