Remove pointless comments, helper funcs, and clean up formatting

This commit is contained in:
Gigiaj 2025-04-16 20:52:29 -05:00
parent 0b6a7c752d
commit 9d352de995

View file

@ -10,7 +10,6 @@ import { useAtom, useAtomValue } from 'jotai';
import { import {
Avatar, Avatar,
Box, Box,
Button,
Icon, Icon,
IconButton, IconButton,
Icons, Icons,
@ -22,11 +21,13 @@ import {
Text, Text,
config, config,
toRem, toRem,
} from 'folds'; // Assuming 'folds' is your UI library } from 'folds';
import { useVirtualizer } from '@tanstack/react-virtual'; 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 { WidgetApiToWidgetAction } from 'matrix-widget-api';
import { useMatrixClient } from '../../../hooks/useMatrixClient'; import { useMatrixClient } from '../../../hooks/useMatrixClient';
import { mDirectAtom } from '../../../state/mDirectList'; import { mDirectAtom } from '../../../state/mDirectList';
import { import {
@ -46,6 +47,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 as makeSpaceNavCategoryId } 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';
@ -76,26 +78,12 @@ import {
useRoomsNotificationPreferencesContext, useRoomsNotificationPreferencesContext,
} from '../../../hooks/useRoomsNotificationPreferences'; } from '../../../hooks/useRoomsNotificationPreferences';
import { useOpenSpaceSettings } from '../../../state/hooks/spaceSettings'; import { useOpenSpaceSettings } from '../../../state/hooks/spaceSettings';
import { useCallState } from '../CallProvider'; // Assuming path import { useCallState } from '../CallProvider';
import { WidgetApiToWidgetAction } from 'matrix-widget-api';
import { logger } from 'matrix-js-sdk/lib/logger';
// --- Helper Functions ---
// Determine if a room is a voice room (assuming Room object has this method)
const isVoiceRoom = (room: Room): boolean => room.isCallRoom?.() ?? false;
// Determine if a room is a text room
const isTextRoom = (room: Room): boolean => !isVoiceRoom(room);
// Helper function to generate unique category IDs for channel type headers
const makeChannelTypeId = (parentId: string, type: 'text' | 'voice'): string => {
return `${parentId}_${type}_channels`;
};
/** /**
* Processes the raw hierarchy from useSpaceJoinedHierarchy into a flat list * Processes the raw hierarchy from useSpaceJoinedHierarchy into a flat list
* suitable for the virtualizer, including collapsible headers for text/voice channels. * suitable for the virtualizer, including collapsible headers for text/voice channels.
* Removes the top-level "Channels" category header. * Removes the top-level "Rooms" category header.
* *
* @param hierarchy - The raw hierarchy data (array of { roomId: string }). * @param hierarchy - The raw hierarchy data (array of { roomId: string }).
* @param mx - The Matrix client instance. * @param mx - The Matrix client instance.
@ -111,25 +99,21 @@ const processHierarchyForVirtualizer = (
): Array<{ type: string; key: string; [key: string]: any }> => { ): Array<{ type: string; key: string; [key: string]: any }> => {
const processed: Array<{ type: string; key: string; [key: string]: any }> = []; const processed: Array<{ type: string; key: string; [key: string]: any }> = [];
let currentCategoryRooms = { text: [], voice: [] }; let currentCategoryRooms = { text: [], voice: [] };
// Start with the root space as the initial parent context
let currentParentId: string = spaceRoomId; let currentParentId: string = spaceRoomId;
// Function to add collected text/voice rooms under their respective headers
const addCollectedRoomsToProcessed = (parentId: string) => { const addCollectedRoomsToProcessed = (parentId: string) => {
const textCategoryId = makeChannelTypeId(parentId, 'text'); const textCategoryId = `${parentId}_text_rooms`;
const voiceCategoryId = makeChannelTypeId(parentId, 'voice'); const voiceCategoryId = `${parentId}_call_rooms`;
const isTextClosed = closedCategories.has(textCategoryId); const isTextClosed = closedCategories.has(textCategoryId);
const isVoiceClosed = closedCategories.has(voiceCategoryId); const isCallClosed = closedCategories.has(voiceCategoryId);
// Add Text Channels Header and Rooms (if any exist)
if (currentCategoryRooms.text.length > 0) { if (currentCategoryRooms.text.length > 0) {
processed.push({ processed.push({
type: 'channel_header', // Use specific type for collapsible channel headers type: 'channel_header',
title: 'Text Channels', title: 'Text Rooms',
categoryId: textCategoryId, // ID used for collapse state categoryId: textCategoryId,
key: `${parentId}-text-header`, key: `${parentId}-text-header`,
}); });
// Only add room items if this category is not closed
if (!isTextClosed) { if (!isTextClosed) {
currentCategoryRooms.text.forEach((room) => currentCategoryRooms.text.forEach((room) =>
processed.push({ type: 'room', room, key: room.roomId }) processed.push({ type: 'room', room, key: room.roomId })
@ -137,76 +121,55 @@ const processHierarchyForVirtualizer = (
} }
} }
// Add Voice Channels Header and Rooms (if any exist)
if (currentCategoryRooms.voice.length > 0) { if (currentCategoryRooms.voice.length > 0) {
processed.push({ processed.push({
type: 'channel_header', // Use specific type type: 'channel_header',
title: 'Voice Channels', title: 'Call Rooms',
categoryId: voiceCategoryId, // ID used for collapse state categoryId: voiceCategoryId,
key: `${parentId}-voice-header`, key: `${parentId}-voice-header`,
}); });
// Only add room items if this category is not closed if (!isCallClosed) {
if (!isVoiceClosed) {
currentCategoryRooms.voice.forEach((room) => currentCategoryRooms.voice.forEach((room) =>
processed.push({ type: 'room', room, key: room.roomId }) processed.push({ type: 'room', room, key: room.roomId })
); );
} }
} }
// Reset collected rooms for the next category/space
currentCategoryRooms = { text: [], voice: [] }; currentCategoryRooms = { text: [], voice: [] };
}; };
// Iterate through the raw hierarchy provided by the hook
hierarchy.forEach((item) => { hierarchy.forEach((item) => {
const room = mx.getRoom(item.roomId); const room = mx.getRoom(item.roomId);
if (!room) { if (!room) {
logger.warn(`processHierarchyForVirtualizer: Room not found for ID ${item.roomId}`); logger.warn(`processHierarchyForVirtualizer: Room not found for ID ${item.roomId}`);
return; // Skip if room data isn't available
} }
if (room.isSpaceRoom()) { if (room.isSpaceRoom()) {
// When encountering a new space, first process the rooms collected under the *previous* parent
addCollectedRoomsToProcessed(currentParentId); addCollectedRoomsToProcessed(currentParentId);
// Now, set the current parent context to this new space
currentParentId = room.roomId; currentParentId = room.roomId;
// Add the space category item itself to the processed list,
// *UNLESS* it's the root space (we want to skip the top-level "Channels" header)
if (room.roomId !== spaceRoomId) { if (room.roomId !== spaceRoomId) {
const spaceCategoryId = makeSpaceNavCategoryId(spaceRoomId, room.roomId); // Use original ID generator for spaces const spaceCategoryId = makeSpaceNavCategoryId(spaceRoomId, room.roomId);
processed.push({ processed.push({
type: 'category', // Type for main space categories type: 'category',
room, room,
categoryId: spaceCategoryId, // ID for this space's collapse state categoryId: spaceCategoryId,
key: room.roomId, key: room.roomId,
}); });
} }
// Note: We assume the `hierarchy` list is already filtered based on closed *space* categories. } else if (room.isCallRoom()) {
currentCategoryRooms.voice.push(room);
} else if (!room.isCallRoom()) {
currentCategoryRooms.text.push(room);
} else { } else {
// This is a regular room (not a space). Add it to the appropriate list (text/voice) logger.warn(`processHierarchyForVirtualizer: Room ${room.roomId} is neither text nor voice.`);
// for the *current* parent space. currentCategoryRooms.text.push(room);
if (isVoiceRoom(room)) {
currentCategoryRooms.voice.push(room);
} else if (isTextRoom(room)) {
currentCategoryRooms.text.push(room);
} else {
// Fallback or handle unexpected room types if necessary
logger.warn(
`processHierarchyForVirtualizer: Room ${room.roomId} is neither text nor voice.`
);
currentCategoryRooms.text.push(room); // Default to text for now
}
} }
}); });
// After iterating through all items, process any remaining rooms collected under the last parent
addCollectedRoomsToProcessed(currentParentId); addCollectedRoomsToProcessed(currentParentId);
return processed; return processed;
}; };
// --- Space Menu Component (Remains Unchanged) ---
type SpaceMenuProps = { type SpaceMenuProps = {
room: Room; room: Room;
requestClose: () => void; requestClose: () => void;
@ -333,7 +296,6 @@ const SpaceMenu = forwardRef<HTMLDivElement, SpaceMenuProps>(({ room, requestClo
); );
}); });
// --- Space Header Component (Remains Unchanged) ---
function SpaceHeader() { function SpaceHeader() {
const space = useSpace(); const space = useSpace();
const spaceName = useRoomName(space); const spaceName = useRoomName(space);
@ -395,14 +357,13 @@ function SpaceHeader() {
); );
} }
// --- Fixed Bottom Nav Area Component (Remains Unchanged) ---
function FixedBottomNavArea() { function FixedBottomNavArea() {
const { sendWidgetAction, activeCallRoomId } = useCallState(); const { sendWidgetAction, activeCallRoomId } = useCallState();
const mx = useMatrixClient(); const mx = useMatrixClient();
const userName = mx.getUser(mx.getUserId() ?? '')?.displayName ?? mx.getUserId() ?? 'User'; const userName = mx.getUser(mx.getUserId() ?? '')?.displayName ?? mx.getUserId() ?? 'User';
const handleSendMessageClick = () => { const handleSendMessageClick = () => {
const action = 'my.custom.action'; // Replace with your actual action const action = 'my.custom.action';
const data = { message: `Hello from ${userName}!` }; const data = { message: `Hello from ${userName}!` };
logger.debug(`FixedBottomNavArea: Sending action '${action}'`); logger.debug(`FixedBottomNavArea: Sending action '${action}'`);
sendWidgetAction(action, data) sendWidgetAction(action, data)
@ -412,7 +373,7 @@ function FixedBottomNavArea() {
const handleToggleMuteClick = () => { const handleToggleMuteClick = () => {
const action = WidgetApiToWidgetAction.SetAudioInputMuted; const action = WidgetApiToWidgetAction.SetAudioInputMuted;
const data = {}; // Sending empty data might imply toggle for some widgets const data = {};
logger.debug(`FixedBottomNavArea: Sending action '${action}'`); logger.debug(`FixedBottomNavArea: Sending action '${action}'`);
sendWidgetAction(action, data) sendWidgetAction(action, data)
.then(() => logger.info(`FixedBottomNavArea: Action '${action}' sent.`)) .then(() => logger.info(`FixedBottomNavArea: Action '${action}' sent.`))
@ -425,7 +386,7 @@ function FixedBottomNavArea() {
direction="Column" direction="Column"
gap="200" gap="200"
padding="300" padding="300"
style={{ flexShrink: 0, borderTop: `1px solid ${config?.color?.LineStrong ?? '#ccc'}` }} // Use theme color if possible style={{ flexShrink: 0, borderTop: `1px solid ${config?.color?.LineStrong ?? '#ccc'}` }}
> >
<Text size="T200" color="Muted" align="Center"> <Text size="T200" color="Muted" align="Center">
No active call No active call
@ -436,7 +397,7 @@ function FixedBottomNavArea() {
return ( return (
<Text size="T200" color="Muted" align="Center"> <Text size="T200" color="Muted" align="Center">
{mx.getRoom(activeCallRoomId)?.normalizedName} {}
</Text> </Text>
); );
} /* } /*
@ -457,10 +418,9 @@ function FixedBottomNavArea() {
</Box> </Box>
*/ */
// --- Main Space Component (Updated Rendering Logic) ---
export function Space() { export function Space() {
const mx = useMatrixClient(); const mx = useMatrixClient();
const space = useSpace(); // The current top-level space being viewed const space = useSpace();
useNavToActivePathMapper(space.roomId); useNavToActivePathMapper(space.roomId);
const spaceIdOrAlias = getCanonicalAliasOrRoomId(mx, space.roomId); const spaceIdOrAlias = getCanonicalAliasOrRoomId(mx, space.roomId);
const scrollRef = useRef<HTMLDivElement>(null); const scrollRef = useRef<HTMLDivElement>(null);
@ -474,10 +434,8 @@ export function Space() {
const lobbySelected = useSpaceLobbySelected(spaceIdOrAlias); const lobbySelected = useSpaceLobbySelected(spaceIdOrAlias);
const searchSelected = useSpaceSearchSelected(spaceIdOrAlias); const searchSelected = useSpaceSearchSelected(spaceIdOrAlias);
// State for managing collapsed categories (includes spaces and channel types)
const [closedCategories, setClosedCategories] = useAtom(useClosedNavCategoriesAtom()); const [closedCategories, setClosedCategories] = useAtom(useClosedNavCategoriesAtom());
// Memoized callback to get room objects
const getRoom = useCallback( const getRoom = useCallback(
(rId: string): Room | undefined => { (rId: string): Room | undefined => {
if (allJoinedRooms.has(rId)) { if (allJoinedRooms.has(rId)) {
@ -488,75 +446,53 @@ export function Space() {
[mx, allJoinedRooms] [mx, allJoinedRooms]
); );
// Fetch the raw hierarchy using the hook
// Note: The filtering callbacks passed here primarily affect *which* rooms/spaces
// are included in the raw list *before* processing.
const hierarchy = useSpaceJoinedHierarchy( const hierarchy = useSpaceJoinedHierarchy(
space.roomId, space.roomId,
getRoom, getRoom,
// isRoomHidden callback: Hides room if parent space category is closed, unless room is unread/selected.
useCallback( useCallback(
(parentId, roomId) => { (parentId, roomId) => {
// Generate the category ID for the parent *space*
const parentSpaceCategoryId = makeSpaceNavCategoryId(space.roomId, parentId); const parentSpaceCategoryId = makeSpaceNavCategoryId(space.roomId, parentId);
// If the parent space category is not closed, the room is not hidden by this rule.
if (!closedCategories.has(parentSpaceCategoryId)) { if (!closedCategories.has(parentSpaceCategoryId)) {
return false; return false;
} }
// Parent space is closed. Hide the room unless it's unread or currently selected.
const showRoomAnyway = roomToUnread.has(roomId) || roomId === selectedRoomId; const showRoomAnyway = roomToUnread.has(roomId) || roomId === selectedRoomId;
return !showRoomAnyway; // Return true to hide, false to show return !showRoomAnyway;
}, },
[space.roomId, closedCategories, roomToUnread, selectedRoomId] // Dependencies [space.roomId, closedCategories, roomToUnread, selectedRoomId]
), ),
// isSubCategoryClosed callback: Checks if a *space* subcategory is closed.
useCallback( useCallback(
(subCategoryId) => closedCategories.has(makeSpaceNavCategoryId(space.roomId, subCategoryId)), (subCategoryId) => closedCategories.has(makeSpaceNavCategoryId(space.roomId, subCategoryId)),
[closedCategories, space.roomId] // Dependencies [closedCategories, space.roomId]
) )
); );
// Process the raw hierarchy into a list with collapsible channel headers
const processedHierarchy = useMemo( const processedHierarchy = useMemo(
() => () => processHierarchyForVirtualizer(hierarchy, mx, space.roomId, closedCategories),
processHierarchyForVirtualizer( [hierarchy, mx, space.roomId, closedCategories]
hierarchy,
mx,
space.roomId,
closedCategories // Pass closed state to the processing function
),
[hierarchy, mx, space.roomId, closedCategories] // Dependencies for memoization
); );
// Setup the virtualizer with the processed list
const virtualizer = useVirtualizer({ const virtualizer = useVirtualizer({
count: processedHierarchy.length, count: processedHierarchy.length,
getScrollElement: () => scrollRef.current, getScrollElement: () => scrollRef.current,
estimateSize: () => 32, // Adjust based on average item height estimateSize: () => 32,
overscan: 10, // Render items slightly outside the viewport overscan: 10,
}); });
// Click handler for toggling category collapse state (works for spaces and channel types)
const handleCategoryClick = useCategoryHandler(setClosedCategories, (categoryId) => const handleCategoryClick = useCategoryHandler(setClosedCategories, (categoryId) =>
closedCategories.has(categoryId) closedCategories.has(categoryId)
); );
// Function to generate navigation links for rooms
const getToLink = (roomId: string) => const getToLink = (roomId: string) =>
getSpaceRoomPath(spaceIdOrAlias, getCanonicalAliasOrRoomId(mx, roomId)); getSpaceRoomPath(spaceIdOrAlias, getCanonicalAliasOrRoomId(mx, roomId));
// --- Render ---
return ( return (
<PageNav style={{ display: 'flex', flexDirection: 'column', height: '100%' }}> <PageNav style={{ display: 'flex', flexDirection: 'column', height: '100%' }}>
{/* Fixed Header */}
<SpaceHeader /> <SpaceHeader />
{/* Scrollable Content Area */}
<PageNavContent <PageNavContent
scrollRef={scrollRef} scrollRef={scrollRef}
style={{ flexGrow: 1, overflowY: 'auto', overflowX: 'hidden' }} style={{ flexGrow: 1, overflowY: 'auto', overflowX: 'hidden' }}
> >
{/* Static Top Links (Lobby, Search) */}
<Box direction="Column" gap="300" paddingBottom="400"> <Box direction="Column" gap="300" paddingBottom="400">
<NavCategory> <NavCategory>
<NavItem variant="Background" radii="400" aria-selected={lobbySelected}> <NavItem variant="Background" radii="400" aria-selected={lobbySelected}>
@ -593,38 +529,29 @@ export function Space() {
</NavItem> </NavItem>
</NavCategory> </NavCategory>
</Box> </Box>
{/* Virtualized List Area */}
<NavCategory <NavCategory
style={{ style={{
height: `${virtualizer.getTotalSize()}px`, // Set height for virtualizer scroll calculations height: `${virtualizer.getTotalSize()}px`,
width: '100%', width: '100%',
position: 'relative', // Needed for absolute positioning of virtual items position: 'relative',
}} }}
> >
{virtualizer.getVirtualItems().map((vItem) => { {virtualizer.getVirtualItems().map((vItem) => {
const item = processedHierarchy[vItem.index]; const item = processedHierarchy[vItem.index];
if (!item) return null; // Should not happen with correct processing if (!item) return null;
// --- Render Logic based on Item Type ---
const renderContent = () => { const renderContent = () => {
switch (item.type) { switch (item.type) {
// Render a main space category header (for nested spaces)
case 'category': { case 'category': {
// item has: room, categoryId, key
const { room, categoryId } = item; const { room, categoryId } = item;
// Determine name: Use the room name for nested spaces
const name = room.name; const name = room.name;
// Add padding above subsequent categories
// Removed index === 0 check as root category is gone
const paddingTop = config?.space?.S400 ?? '1rem'; const paddingTop = config?.space?.S400 ?? '1rem';
return ( return (
<div style={{ paddingTop: paddingTop }}> <div style={{ paddingTop: paddingTop }}>
<NavCategoryHeader> <NavCategoryHeader>
<RoomNavCategoryButton <RoomNavCategoryButton
data-category-id={categoryId} // ID for collapse state data-category-id={categoryId}
onClick={handleCategoryClick} // Toggle collapse onClick={handleCategoryClick}
closed={closedCategories.has(categoryId)} // Pass closed state closed={closedCategories.has(categoryId)}
> >
{name} {name}
</RoomNavCategoryButton> </RoomNavCategoryButton>
@ -632,21 +559,16 @@ export function Space() {
</div> </div>
); );
} }
// Render a collapsible header for Text or Voice channels
case 'channel_header': { case 'channel_header': {
// item has: title, categoryId, key
const { title, categoryId } = item; const { title, categoryId } = item;
return ( return (
// Add indentation and padding for visual hierarchy
<Box paddingLeft="400" paddingTop="200" paddingBottom="100"> <Box paddingLeft="400" paddingTop="200" paddingBottom="100">
<NavCategoryHeader variant="Subtle"> <NavCategoryHeader variant="Subtle">
{' '}
{/* Use subtle variant if available */}
<RoomNavCategoryButton <RoomNavCategoryButton
data-category-id={categoryId} // ID for collapse state data-category-id={categoryId}
onClick={handleCategoryClick} // Toggle collapse onClick={handleCategoryClick}
closed={closedCategories.has(categoryId)} // Pass closed state closed={closedCategories.has(categoryId)}
isSubCategory // Optional prop for styling tweaks isSubCategory
> >
{title} {title}
</RoomNavCategoryButton> </RoomNavCategoryButton>
@ -654,12 +576,9 @@ export function Space() {
</Box> </Box>
); );
} }
// Render a regular room item (text or voice channel)
case 'room': { case 'room': {
// item has: room, key
const { room } = item; const { room } = item;
return ( return (
// Add indentation for rooms under headers
<Box paddingLeft="500"> <Box paddingLeft="500">
<RoomNavItem <RoomNavItem
room={room} room={room}
@ -676,13 +595,11 @@ export function Space() {
); );
} }
default: default:
// Log error for unexpected item types
logger.error('Unknown item type in virtualized list:', item); logger.error('Unknown item type in virtualized list:', item);
return null; return null;
} }
}; };
// Render the virtual tile wrapper with the content
return ( return (
<VirtualTile virtualItem={vItem} key={item.key} ref={virtualizer.measureElement}> <VirtualTile virtualItem={vItem} key={item.key} ref={virtualizer.measureElement}>
{renderContent()} {renderContent()}
@ -691,8 +608,6 @@ export function Space() {
})} })}
</NavCategory> </NavCategory>
</PageNavContent> </PageNavContent>
{/* Fixed Bottom Section (Remains Unchanged) */}
<FixedBottomNavArea /> <FixedBottomNavArea />
</PageNav> </PageNav>
); );