feat: P1 features — voice speed, private receipts, room filter, favorites, invite link, poll creation
P1-5: Voice message playback speed toggle (0.75×/1×/1.5×/2×) in AudioContent.tsx P1-10: Private read receipts toggle in Privacy settings; wired to notifications.ts P1-3: Room filter input on Home tab and DMs tab (client-side, clears on tab switch) P1-8: Favorite rooms via m.favourite tag — Favorites section in Home sidebar, star/unstar in right-click menu P1-9: Room invite link + QR code in room settings (Share Room tile, api.qrserver.com QR) P1-6: Poll creation modal in composer (PollCreator.tsx, sends m.poll.start) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -8,6 +8,7 @@ import {
|
||||
Icon,
|
||||
IconButton,
|
||||
Icons,
|
||||
Input,
|
||||
Menu,
|
||||
MenuItem,
|
||||
PopOut,
|
||||
@@ -181,6 +182,7 @@ export function Direct() {
|
||||
const scrollRef = useRef<HTMLDivElement>(null);
|
||||
const directs = useDirectRooms();
|
||||
const notificationPreferences = useRoomsNotificationPreferencesContext();
|
||||
const [filterQuery, setFilterQuery] = useState('');
|
||||
const roomsWithUnreadSet = useAtomValue(
|
||||
useMemo(
|
||||
() =>
|
||||
@@ -216,8 +218,14 @@ export function Direct() {
|
||||
return items;
|
||||
}, [mx, directs, closedCategories, roomsWithUnreadSet, selectedRoomId]);
|
||||
|
||||
const filteredDirects = useMemo(() => {
|
||||
if (!filterQuery.trim()) return sortedDirects;
|
||||
const q = filterQuery.toLowerCase();
|
||||
return sortedDirects.filter((rId) => (mx.getRoom(rId)?.name ?? '').toLowerCase().includes(q));
|
||||
}, [mx, sortedDirects, filterQuery]);
|
||||
|
||||
const virtualizer = useVirtualizer({
|
||||
count: sortedDirects.length,
|
||||
count: filteredDirects.length,
|
||||
getScrollElement: () => scrollRef.current,
|
||||
estimateSize: () => 38,
|
||||
overscan: 10,
|
||||
@@ -253,6 +261,34 @@ export function Direct() {
|
||||
</NavButton>
|
||||
</NavItem>
|
||||
</NavCategory>
|
||||
<NavCategory>
|
||||
<Box style={{ padding: `0 ${config.space.S200}`, paddingBottom: config.space.S100 }}>
|
||||
<Input
|
||||
value={filterQuery}
|
||||
onChange={(e: React.ChangeEvent<HTMLInputElement>) =>
|
||||
setFilterQuery(e.target.value)
|
||||
}
|
||||
placeholder="Filter DMs…"
|
||||
variant="Surface"
|
||||
size="300"
|
||||
radii="300"
|
||||
after={
|
||||
filterQuery ? (
|
||||
<IconButton
|
||||
onClick={() => setFilterQuery('')}
|
||||
size="300"
|
||||
radii="300"
|
||||
variant="Background"
|
||||
fill="None"
|
||||
aria-label="Clear filter"
|
||||
>
|
||||
<Icon size="50" src={Icons.Cross} />
|
||||
</IconButton>
|
||||
) : undefined
|
||||
}
|
||||
/>
|
||||
</Box>
|
||||
</NavCategory>
|
||||
<NavCategory>
|
||||
<NavCategoryHeader>
|
||||
<RoomNavCategoryButton
|
||||
@@ -270,7 +306,7 @@ export function Direct() {
|
||||
}}
|
||||
>
|
||||
{virtualizer.getVirtualItems().map((vItem) => {
|
||||
const roomId = sortedDirects[vItem.index];
|
||||
const roomId = filteredDirects[vItem.index];
|
||||
const room = mx.getRoom(roomId);
|
||||
if (!room) return null;
|
||||
const selected = selectedRoomId === roomId;
|
||||
|
||||
Reference in New Issue
Block a user