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();