Revert most changes to Space.tsx

This commit is contained in:
Gimle Larpes 2025-06-27 12:38:13 +02:00 committed by GitHub
parent eaf70fb79a
commit efc77ceb44
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -26,7 +26,6 @@ import { useVirtualizer } from '@tanstack/react-virtual';
import { JoinRule, Room } from 'matrix-js-sdk'; import { JoinRule, Room } from 'matrix-js-sdk';
import { RoomJoinRulesEventContent } from 'matrix-js-sdk/lib/types'; import { RoomJoinRulesEventContent } from 'matrix-js-sdk/lib/types';
import FocusTrap from 'focus-trap-react'; import FocusTrap from 'focus-trap-react';
import { logger } from 'matrix-js-sdk/lib/logger';
import { useMatrixClient } from '../../../hooks/useMatrixClient'; import { useMatrixClient } from '../../../hooks/useMatrixClient';
import { mDirectAtom } from '../../../state/mDirectList'; import { mDirectAtom } from '../../../state/mDirectList';
import { import {
@ -46,8 +45,7 @@ import {
import { useSpace } from '../../../hooks/useSpace'; import { useSpace } from '../../../hooks/useSpace';
import { VirtualTile } from '../../../components/virtualizer'; import { VirtualTile } from '../../../components/virtualizer';
import { RoomNavCategoryButton, RoomNavItem } from '../../../features/room-nav'; import { RoomNavCategoryButton, RoomNavItem } from '../../../features/room-nav';
// Using the original name for clarity when generating space category IDs import { makeNavCategoryId } from '../../../state/closedNavCategories';
import { makeNavCategoryId as makeSpaceNavCategoryId } from '../../../state/closedNavCategories';
import { roomToUnreadAtom } from '../../../state/room/roomToUnread'; import { roomToUnreadAtom } from '../../../state/room/roomToUnread';
import { useCategoryHandler } from '../../../hooks/useCategoryHandler'; import { useCategoryHandler } from '../../../hooks/useCategoryHandler';
import { useNavToActivePathMapper } from '../../../hooks/useNavToActivePathMapper'; import { useNavToActivePathMapper } from '../../../hooks/useNavToActivePathMapper';
@ -79,118 +77,6 @@ import {
import { useOpenSpaceSettings } from '../../../state/hooks/spaceSettings'; import { useOpenSpaceSettings } from '../../../state/hooks/spaceSettings';
import { useRoomNavigate } from '../../../hooks/useRoomNavigate'; import { useRoomNavigate } from '../../../hooks/useRoomNavigate';
import { CallNavStatus } from '../../../features/room-nav/RoomCallNavStatus'; import { CallNavStatus } from '../../../features/room-nav/RoomCallNavStatus';
import { getStateEvents } from '../../../utils/room';
import { RoomNavUser } from '../../../features/room-nav/RoomNavUser';
import { useStateEvents } from '../../../hooks/useStateEvents';
/**
* Processes the raw hierarchy from useSpaceJoinedHierarchy into a flat list
* suitable for the virtualizer, including collapsible headers for text/voice rooms.
* Removes the top-level "Rooms" category header.
*
* @param hierarchy - The raw hierarchy data (array of { roomId: string }).
* @param mx - The Matrix client instance.
* @param spaceRoomId - The ID of the root space being viewed.
* @param closedCategories - The Set of currently closed category IDs.
* @returns An array of processed items for rendering.
*/
const processHierarchyForVirtualizer = (
hierarchy: { roomId: string }[],
mx: ReturnType<typeof useMatrixClient>,
spaceRoomId: string,
closedCategories: Set<string>
): Array<{ type: string; key: string; [key: string]: any }> => {
const processed: Array<{ type: string; key: string; [key: string]: any }> = [];
let currentCategoryRooms = { text: [], voice: [], users: [] };
let currentParentId: string = spaceRoomId;
const addCollectedRoomsToProcessed = (parentId: string) => {
const textCategoryId = `${parentId}_text_rooms`;
const voiceCategoryId = `${parentId}_call_rooms`;
const isTextClosed = closedCategories.has(textCategoryId);
const isCallClosed = closedCategories.has(voiceCategoryId);
if (currentCategoryRooms.text.length > 0) {
processed.push({
type: 'room_header',
title: 'Text Rooms',
categoryId: textCategoryId,
key: `${parentId}-text-header`,
});
if (!isTextClosed) {
currentCategoryRooms.text.forEach((room) =>
processed.push({ type: 'room', room, key: room.roomId })
);
}
}
if (currentCategoryRooms.voice.length > 0) {
processed.push({
type: 'room_header',
title: 'Call Rooms',
categoryId: voiceCategoryId,
key: `${parentId}-voice-header`,
});
if (!isCallClosed) {
currentCategoryRooms.voice.forEach((room) => {
processed.push({ type: 'room', room, key: room.roomId });
currentCategoryRooms.users.forEach((entry) => {
if (entry.room.roomId === room.roomId) {
processed.push(entry);
}
});
});
}
}
currentCategoryRooms = { text: [], voice: [], users: [] };
};
hierarchy.forEach((item) => {
const room = mx.getRoom(item.roomId);
if (!room) {
logger.warn(`processHierarchyForVirtualizer: Room not found for ID ${item.roomId}`);
}
if (room.isSpaceRoom()) {
addCollectedRoomsToProcessed(currentParentId);
currentParentId = room.roomId;
if (room.roomId !== spaceRoomId) {
const spaceCategoryId = makeSpaceNavCategoryId(spaceRoomId, room.roomId);
processed.push({
type: 'category',
room,
categoryId: spaceCategoryId,
key: room.roomId,
});
}
} else if (room.isCallRoom()) {
currentCategoryRooms.voice.push(room);
getStateEvents(room, 'org.matrix.msc3401.call.member').forEach(({ event }) => {
if (Object.entries(event?.content).length !== 0) {
if (event.origin_server_ts + event.content.expires > Date.now()) {
currentCategoryRooms.users.push({
type: 'user',
sender: event.sender,
key: event.event_id,
room,
});
}
}
});
} else if (!room.isCallRoom()) {
currentCategoryRooms.text.push(room);
} else {
logger.warn(`processHierarchyForVirtualizer: Room ${room.roomId} is neither text nor voice.`);
currentCategoryRooms.text.push(room);
}
});
addCollectedRoomsToProcessed(currentParentId);
return processed;
};
type SpaceMenuProps = { type SpaceMenuProps = {
room: Room; room: Room;
@ -203,7 +89,7 @@ const SpaceMenu = forwardRef<HTMLDivElement, SpaceMenuProps>(({ room, requestClo
const roomToParents = useAtomValue(roomToParentsAtom); const roomToParents = useAtomValue(roomToParentsAtom);
const powerLevels = usePowerLevels(room); const powerLevels = usePowerLevels(room);
const { getPowerLevel, canDoAction } = usePowerLevelsAPI(powerLevels); const { getPowerLevel, canDoAction } = usePowerLevelsAPI(powerLevels);
const canInvite = canDoAction('invite', mx.getUserId() ?? ''); const canInvite = canDoAction('invite', getPowerLevel(mx.getUserId() ?? ''));
const openSpaceSettings = useOpenSpaceSettings(); const openSpaceSettings = useOpenSpaceSettings();
const { navigateRoom } = useRoomNavigate(); const { navigateRoom } = useRoomNavigate();
@ -354,6 +240,7 @@ function SpaceHeader() {
return cords; return cords;
}); });
}; };
return ( return (
<> <>
<PageNavHeader> <PageNavHeader>
@ -430,8 +317,7 @@ export function Space() {
getRoom, getRoom,
useCallback( useCallback(
(parentId, roomId) => { (parentId, roomId) => {
const parentSpaceCategoryId = makeSpaceNavCategoryId(space.roomId, parentId); if (!closedCategories.has(makeNavCategoryId(space.roomId, parentId))) {
if (!closedCategories.has(parentSpaceCategoryId)) {
return false; return false;
} }
const showRoomAnyway = roomToUnread.has(roomId) || roomId === selectedRoomId; const showRoomAnyway = roomToUnread.has(roomId) || roomId === selectedRoomId;
@ -439,31 +325,14 @@ export function Space() {
}, },
[space.roomId, closedCategories, roomToUnread, selectedRoomId] [space.roomId, closedCategories, roomToUnread, selectedRoomId]
), ),
useCallback( useCallback(
(subCategoryId) => closedCategories.has(makeSpaceNavCategoryId(space.roomId, subCategoryId)), (sId) => closedCategories.has(makeNavCategoryId(space.roomId, sId)),
[closedCategories, space.roomId] [closedCategories, space.roomId]
) )
); );
const callRooms = useMemo(
() =>
hierarchy
.map((item) => mx.getRoom(item.roomId))
.filter((room): room is Room => !!room && room.isCallRoom()),
[hierarchy, mx]
);
const updateTrigger = useStateEvents(callRooms, StateEvent.GroupCallMemberPrefix);
const processedHierarchy = useMemo(
() => processHierarchyForVirtualizer(hierarchy, mx, space.roomId, closedCategories),
// eslint-disable-next-line react-hooks/exhaustive-deps
[hierarchy, mx, space.roomId, closedCategories, updateTrigger]
);
const virtualizer = useVirtualizer({ const virtualizer = useVirtualizer({
count: processedHierarchy.length, count: hierarchy.length,
getScrollElement: () => scrollRef.current, getScrollElement: () => scrollRef.current,
estimateSize: () => 32, estimateSize: () => 32,
overscan: 10, overscan: 10,
@ -477,12 +346,9 @@ export function Space() {
getSpaceRoomPath(spaceIdOrAlias, getCanonicalAliasOrRoomId(mx, roomId)); getSpaceRoomPath(spaceIdOrAlias, getCanonicalAliasOrRoomId(mx, roomId));
return ( return (
<PageNav style={{ display: 'flex', flexDirection: 'column', height: '100%' }}> <PageNav>
<SpaceHeader /> <SpaceHeader />
<PageNavContent <PageNavContent scrollRef={scrollRef}>
scrollRef={scrollRef}
style={{ flexGrow: 1, overflowY: 'auto', overflowX: 'hidden' }}
>
<Box direction="Column" gap="300"> <Box direction="Column" gap="300">
<NavCategory> <NavCategory>
<NavItem variant="Background" radii="400" aria-selected={lobbySelected}> <NavItem variant="Background" radii="400" aria-selected={lobbySelected}>
@ -518,91 +384,56 @@ export function Space() {
</NavLink> </NavLink>
</NavItem> </NavItem>
</NavCategory> </NavCategory>
</Box>
<NavCategory <NavCategory
style={{ style={{
height: `${virtualizer.getTotalSize()}px`, height: virtualizer.getTotalSize(),
width: '100%',
position: 'relative', position: 'relative',
}} }}
> >
{virtualizer.getVirtualItems().map((vItem) => { {virtualizer.getVirtualItems().map((vItem) => {
const item = processedHierarchy[vItem.index]; const { roomId } = hierarchy[vItem.index] ?? {};
if (!item) return null; const room = mx.getRoom(roomId);
const renderContent = () => { if (!room) return null;
switch (item.type) {
case 'category': { if (room.isSpaceRoom()) {
const { room, categoryId } = item; const categoryId = makeNavCategoryId(space.roomId, roomId);
const { name } = room;
const paddingTop = config?.space?.S400 ?? '1rem';
return ( return (
<div style={{ paddingTop }}> <VirtualTile
virtualItem={vItem}
key={vItem.index}
ref={virtualizer.measureElement}
>
<div style={{ paddingTop: vItem.index === 0 ? undefined : config.space.S400 }}>
<NavCategoryHeader> <NavCategoryHeader>
<RoomNavCategoryButton <RoomNavCategoryButton
data-category-id={categoryId} data-category-id={categoryId}
onClick={handleCategoryClick} onClick={handleCategoryClick}
closed={closedCategories.has(categoryId)} closed={closedCategories.has(categoryId)}
> >
{name} {roomId === space.roomId ? 'Rooms' : room?.name}
</RoomNavCategoryButton> </RoomNavCategoryButton>
</NavCategoryHeader> </NavCategoryHeader>
</div> </div>
</VirtualTile>
); );
} }
case 'room_header': {
const { title, categoryId } = item;
return (
<Box>
<NavCategoryHeader variant="Subtle">
<RoomNavCategoryButton
data-category-id={categoryId}
onClick={handleCategoryClick}
closed={closedCategories.has(categoryId)}
>
{title}
</RoomNavCategoryButton>
</NavCategoryHeader>
</Box>
);
}
case 'room': {
const { room } = item;
return (
<Box>
<RoomNavItem
room={room}
selected={selectedRoomId === room.roomId}
showAvatar={mDirects.has(room.roomId)}
direct={mDirects.has(room.roomId)}
linkPath={getToLink(room.roomId)}
notificationMode={getRoomNotificationMode(
notificationPreferences,
room.roomId
)}
/>
</Box>
);
}
case 'user': {
const { sender, room } = item;
return (
<Box style={{ paddingLeft: config.space.S200 }}>
<RoomNavUser room={room} space={space} sender={sender} />
</Box>
);
}
default:
return null;
}
};
return ( return (
<VirtualTile virtualItem={vItem} key={item.key} ref={virtualizer.measureElement}> <VirtualTile virtualItem={vItem} key={vItem.index} ref={virtualizer.measureElement}>
{renderContent()} <RoomNavItem
room={room}
selected={selectedRoomId === roomId}
showAvatar={mDirects.has(roomId)}
direct={mDirects.has(roomId)}
linkPath={getToLink(roomId)}
notificationMode={getRoomNotificationMode(notificationPreferences, room.roomId)}
/>
</VirtualTile> </VirtualTile>
); );
})} })}
</NavCategory> </NavCategory>
</Box>
</PageNavContent> </PageNavContent>
<CallNavStatus /> <CallNavStatus />
</PageNav> </PageNav>