fix(sidebar): the room filter hides empty sections and says when nothing matches (#289)
CI / Build & Quality Checks (pull_request) Successful in 3m7s
CI / Trigger Desktop Build (pull_request) Skipped
CI / Docker image build & smoke test (pull_request) Skipped
CI / Secret scan (gitleaks) (pull_request) Successful in 11s
CI / Playwright smoke (e2e) (pull_request) Successful in 15m7s

A filter matching nothing left Favorites / Projects / Rooms / Low
Priority headers with nothing under them. While filtering, sections
without a match are hidden, and with no match at all the list shows
"No rooms match “…”" with a Clear filter button.

Fixes #289

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017PCb59uY3UKqDqt7B6hzd5
This commit is contained in:
Lotus CI
2026-10-17 20:08:56 -04:00
co-authored by Claude Opus 5.5
parent 3878a85339
commit 6ae4d7adc0
+54 -3
View File
@@ -255,6 +255,8 @@ function HomeCustomSection({
return (localName ?? matrixName).toLowerCase().includes(query); return (localName ?? matrixName).toLowerCase().includes(query);
}); });
}, [mx, section.rooms, closed, roomsWithUnreadSet, selectedRoomId, filterQuery]); }, [mx, section.rooms, closed, roomsWithUnreadSet, selectedRoomId, filterQuery]);
// [Gitea #289] While filtering, a section with no matches is hidden.
if (filterQuery.trim() && items.length === 0) return null;
// Sections are user-curated and small, so they render plainly — no // Sections are user-curated and small, so they render plainly — no
// virtualizer per section (a late-mounted one never measured its scroller). // virtualizer per section (a late-mounted one never measured its scroller).
return ( return (
@@ -443,6 +445,26 @@ export function Home() {
}); });
}, [mx, sortedRooms, filterQuery]); }, [mx, sortedRooms, filterQuery]);
// [Gitea #289] While filtering, hide empty sections; with no match at all,
// say so instead of leaving bare headers.
const filtering = filterQuery.trim().length > 0;
const customSectionMatch = useMemo(() => {
if (!filtering) return true;
const query = filterQuery.toLowerCase();
const localNames = getLocalRoomNamesContent(mx);
return customSections.some((section) =>
section.rooms.some((rId) =>
(localNames.rooms[rId] ?? mx.getRoom(rId)?.name ?? '').toLowerCase().includes(query),
),
);
}, [mx, customSections, filtering, filterQuery]);
const noFilterMatch =
filtering &&
!customSectionMatch &&
filteredFavoriteRooms.length === 0 &&
filteredRooms.length === 0 &&
filteredLowPriorityRooms.length === 0;
const favVirtualizer = useVirtualizer({ const favVirtualizer = useVirtualizer({
count: filteredFavoriteRooms.length, count: filteredFavoriteRooms.length,
getScrollElement: () => scrollRef.current, getScrollElement: () => scrollRef.current,
@@ -580,7 +602,7 @@ export function Home() {
/> />
</Box> </Box>
</NavCategory> </NavCategory>
{favoriteRooms.length > 0 && ( {favoriteRooms.length > 0 && (!filtering || filteredFavoriteRooms.length > 0) && (
<NavCategory> <NavCategory>
<NavCategoryHeader> <NavCategoryHeader>
<RoomNavCategoryButton <RoomNavCategoryButton
@@ -629,6 +651,30 @@ export function Home() {
notificationPreferences={notificationPreferences} notificationPreferences={notificationPreferences}
/> />
))} ))}
{noFilterMatch && (
<NavCategory>
<Box
direction="Column"
alignItems="Center"
gap="200"
style={{ padding: config.space.S400 }}
>
<Text size="T300" priority="300" align="Center">
No rooms match &ldquo;{filterQuery.trim()}&rdquo;
</Text>
<Button
size="300"
variant="Secondary"
fill="Soft"
radii="300"
onClick={() => setFilterQuery('')}
>
<Text size="B300">Clear filter</Text>
</Button>
</Box>
</NavCategory>
)}
{(!filtering || filteredRooms.length > 0) && (
<NavCategory> <NavCategory>
<NavCategoryHeader> <NavCategoryHeader>
<RoomNavCategoryButton <RoomNavCategoryButton
@@ -749,7 +795,11 @@ export function Home() {
const selected = selectedRoomId === roomId; const selected = selectedRoomId === roomId;
return ( return (
<VirtualTile virtualItem={vItem} key={roomId} ref={virtualizer.measureElement}> <VirtualTile
virtualItem={vItem}
key={roomId}
ref={virtualizer.measureElement}
>
<RoomNavItem <RoomNavItem
room={room} room={room}
selected={selected} selected={selected}
@@ -764,7 +814,8 @@ export function Home() {
})} })}
</div> </div>
</NavCategory> </NavCategory>
{lowPriorityRooms.length > 0 && ( )}
{lowPriorityRooms.length > 0 && (!filtering || filteredLowPriorityRooms.length > 0) && (
<NavCategory> <NavCategory>
<NavCategoryHeader> <NavCategoryHeader>
<RoomNavCategoryButton <RoomNavCategoryButton