diff --git a/frontend/src/components/Sidebar.tsx b/frontend/src/components/Sidebar.tsx index add9e02..48e70da 100644 --- a/frontend/src/components/Sidebar.tsx +++ b/frontend/src/components/Sidebar.tsx @@ -265,6 +265,12 @@ export function Sidebar({ const sortContactsByOrder = useCallback( (items: Contact[], order: SortOrder) => [...items].sort((a, b) => { + // Unread DM contacts always float to the top + const unreadA = unreadCounts[getStateKey('contact', a.public_key)] || 0; + const unreadB = unreadCounts[getStateKey('contact', b.public_key)] || 0; + if (unreadA > 0 && unreadB === 0) return -1; + if (unreadA === 0 && unreadB > 0) return 1; + if (order === 'recent') { const timeA = getContactRecentTime(a); const timeB = getContactRecentTime(b); @@ -274,7 +280,7 @@ export function Sidebar({ } return (a.name || a.public_key).localeCompare(b.name || b.public_key); }), - [getContactRecentTime] + [getContactRecentTime, unreadCounts] ); const sortRepeatersByOrder = useCallback( diff --git a/frontend/src/test/sidebar.test.tsx b/frontend/src/test/sidebar.test.tsx index 57bd7f4..66b5f4e 100644 --- a/frontend/src/test/sidebar.test.tsx +++ b/frontend/src/test/sidebar.test.tsx @@ -513,6 +513,42 @@ describe('Sidebar section summaries', () => { expect(contactRows).toEqual(['DM Recent', 'Advert Only', 'No Recency']); }); + it('floats contacts with unread DMs above read contacts regardless of recency', () => { + const publicChannel = makeChannel(PUBLIC_CHANNEL_KEY, 'Public'); + const readRecent = makeContact('11'.repeat(32), 'Read Recent', 1, { last_advert: 500 }); + const unreadOld = makeContact('22'.repeat(32), 'Unread Old', 1, { last_advert: 100 }); + + render( + + ); + + const contactRows = screen + .getAllByText(/^(Read Recent|Unread Old)$/) + .map((node) => node.textContent) + .filter((text): text is string => Boolean(text)); + + // Unread Old has unread DMs so it floats above Read Recent despite older recency + expect(contactRows).toEqual(['Unread Old', 'Read Recent']); + }); + it('sorts repeaters by heard recency even when message times disagree', () => { const publicChannel = makeChannel(PUBLIC_CHANNEL_KEY, 'Public'); const staleMessageRelay = makeContact(