import React, { useState, useCallback } from 'react'; import { Box, Button, Text, Icon, Icons, config, IconButton, Chip, Line, } from 'folds'; import { useAtomValue } from 'jotai'; import { useMatrixClient } from '../../../hooks/useMatrixClient'; import { useRoom } from '../../../hooks/useRoom'; import { useRoomSubRooms, PaarrotSubRoomsContent } from '../../../hooks/useRoomSubRooms'; import { useAllJoinedRoomsSet, useGetRoom } from '../../../hooks/useGetRoom'; import { AsyncStatus, useAsyncCallback } from '../../../hooks/useAsyncCallback'; import { SettingTile } from '../../../components/setting-tile'; import { StateEvent } from '../../../../types/matrix/room'; import { usePowerLevels } from '../../../hooks/usePowerLevels'; import { useRoomCreators } from '../../../hooks/useRoomCreators'; import { useRoomPermissions } from '../../../hooks/useRoomPermissions'; import { roomToParentsAtom } from '../../../state/room/roomToParents'; import { isSpace } from '../../../utils/room'; function SubRoomItem({ roomId, onRemove, canEdit }: { roomId: string; onRemove: (roomId: string) => void; canEdit: boolean }) { const allJoinedRooms = useAllJoinedRoomsSet(); const getRoom = useGetRoom(allJoinedRooms); const room = getRoom(roomId); if (!room) { return ( Unknown Room ({roomId}) {canEdit && ( onRemove(roomId)} > )} ); } return ( {room.name} {canEdit && ( onRemove(roomId)} > )} ); } export function SubRooms() { const mx = useMatrixClient(); const room = useRoom(); const subRooms = useRoomSubRooms(room); const [addRoomId, setAddRoomId] = useState(''); const roomToParents = useAtomValue(roomToParentsAtom); const powerLevels = usePowerLevels(room); const creators = useRoomCreators(room); const permissions = useRoomPermissions(creators, powerLevels); const canEdit = permissions.stateEvent(StateEvent.PaarrotSubRooms, mx.getSafeUserId()); const [updateState, updateSubRooms] = useAsyncCallback( useCallback( async (newSubRooms: string[]) => { const content: PaarrotSubRoomsContent = { children: newSubRooms, }; // Custom state event type - need to cast // eslint-disable-next-line @typescript-eslint/no-explicit-any await mx.sendStateEvent(room.roomId, StateEvent.PaarrotSubRooms as any, content, ''); }, [mx, room] ) ); const handleRemoveSubRoom = useCallback( (roomId: string) => { const newSubRooms = subRooms.filter(id => id !== roomId); updateSubRooms(newSubRooms); }, [subRooms, updateSubRooms] ); const handleAddRoom = useCallback(async () => { const trimmedId = addRoomId.trim(); if (!trimmedId) return; // Try to resolve room - if it's already joined, use the actual room ID const targetRoom = mx.getRoom(trimmedId); const roomIdToAdd = targetRoom ? targetRoom.roomId : trimmedId; // Allow adding any room ID (even if not joined) if (!subRooms.includes(roomIdToAdd)) { const newSubRooms = [...subRooms, roomIdToAdd]; await updateSubRooms(newSubRooms); // Also add sub-room to this room's parent space(s) for joining/permissions // The UI will hide it from the top-level display and only show it nested const parentSpaces = roomToParents.get(room.roomId); if (parentSpaces) { const spaceIds = Array.from(parentSpaces); await Promise.all( spaceIds.map(async (parentSpaceId) => { const parentSpaceRoom = mx.getRoom(parentSpaceId); // Only add to actual spaces (not other sub-room parents) if (parentSpaceRoom && isSpace(parentSpaceRoom)) { try { const spaceServerName = parentSpaceId.split(':')[1] || ''; const spaceViaServers = spaceServerName ? [spaceServerName] : []; // Add as m.space.child to the space await mx.sendStateEvent( parentSpaceId, // eslint-disable-next-line @typescript-eslint/no-explicit-any StateEvent.SpaceChild as any, { via: spaceViaServers }, roomIdToAdd ); } catch (spaceError) { // eslint-disable-next-line no-console console.warn(`Failed to add sub-room to space ${parentSpaceId}:`, spaceError); } } }) ); } setAddRoomId(''); } }, [mx, addRoomId, subRooms, updateSubRooms, room.roomId, roomToParents]); return ( Sub-Rooms Sub-rooms are nested rooms that appear indented under this room in the room list. They work like sub-spaces but for regular rooms. Current Sub-Rooms setAddRoomId(e.target.value)} onKeyDown={(e) => { if (e.key === 'Enter') { handleAddRoom(); } }} style={{ flexGrow: 1, padding: `${config.space.S200} ${config.space.S300}`, borderRadius: config.radii.R400, border: '1px solid var(--border-surface)', backgroundColor: 'var(--background-surface)', color: 'var(--text-primary)', fontSize: config.fontSize.T300, }} /> {!canEdit && ( You need power level 50 or higher to manage sub-rooms. )} {subRooms.length === 0 ? ( No sub-rooms added yet.
Add a room to nest it under this room.
) : ( {subRooms.map((roomId) => ( ))} )} {updateState.status === AsyncStatus.Error && ( Failed to update sub-rooms: {String((updateState.error as Error)?.message || updateState.error)} )}
); }