feat: Implement sub-room functionality
- Added support for sub-rooms in the SpaceHierarchy component, allowing rooms to have nested child rooms. - Introduced UnjoinedSubRoomItem component to display unjoined sub-rooms with a join button. - Created SubRooms settings page for managing sub-rooms, including adding and removing sub-rooms. - Updated RoomNavItem to include options for creating sub-rooms. - Enhanced room fetching logic to filter out sub-rooms from the main room list. - Added hooks for managing sub-rooms state and permissions. - Updated relevant state management to handle sub-room creation and association with parent rooms.
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import React from 'react';
|
||||
import React, { useCallback } from 'react';
|
||||
import {
|
||||
Box,
|
||||
config,
|
||||
@@ -14,6 +14,7 @@ import {
|
||||
Text,
|
||||
} from 'folds';
|
||||
import FocusTrap from 'focus-trap-react';
|
||||
import { useAtomValue } from 'jotai';
|
||||
import { useAllJoinedRoomsSet, useGetRoom } from '../../hooks/useGetRoom';
|
||||
import { SpaceProvider } from '../../hooks/useSpace';
|
||||
import { CreateRoomForm } from './CreateRoom';
|
||||
@@ -23,17 +24,96 @@ import {
|
||||
} from '../../state/hooks/createRoomModal';
|
||||
import { CreateRoomModalState } from '../../state/createRoomModal';
|
||||
import { stopPropagation } from '../../utils/keyboard';
|
||||
import { useMatrixClient } from '../../hooks/useMatrixClient';
|
||||
import { StateEvent } from '../../../types/matrix/room';
|
||||
import { PaarrotSubRoomsContent } from '../../hooks/useRoomSubRooms';
|
||||
import { roomToParentsAtom } from '../../state/room/roomToParents';
|
||||
import { isSpace } from '../../utils/room';
|
||||
|
||||
type CreateRoomModalProps = {
|
||||
state: CreateRoomModalState;
|
||||
};
|
||||
function CreateRoomModal({ state }: CreateRoomModalProps) {
|
||||
const { spaceId } = state;
|
||||
const { spaceId, parentSubRoomId } = state;
|
||||
const mx = useMatrixClient();
|
||||
const closeDialog = useCloseCreateRoomModal();
|
||||
const roomToParents = useAtomValue(roomToParentsAtom);
|
||||
|
||||
const allJoinedRooms = useAllJoinedRoomsSet();
|
||||
const getRoom = useGetRoom(allJoinedRooms);
|
||||
const space = spaceId ? getRoom(spaceId) : undefined;
|
||||
const parentRoom = parentSubRoomId ? getRoom(parentSubRoomId) : undefined;
|
||||
|
||||
const handleCreate = useCallback(
|
||||
async (newRoomId: string) => {
|
||||
// If creating a sub-room, add it to the parent's sub-rooms and set parent relationship
|
||||
if (parentSubRoomId && parentRoom) {
|
||||
try {
|
||||
// Extract server from parent room ID for via servers
|
||||
const serverName = parentSubRoomId.split(':')[1] || '';
|
||||
const viaServers = serverName ? [serverName] : [];
|
||||
|
||||
// Set m.space.parent on the new room pointing to parent
|
||||
await mx.sendStateEvent(
|
||||
newRoomId,
|
||||
StateEvent.SpaceParent,
|
||||
{
|
||||
canonical: true,
|
||||
via: viaServers,
|
||||
},
|
||||
parentSubRoomId
|
||||
);
|
||||
|
||||
// Add new room to parent's sub-rooms list
|
||||
const stateEvent = parentRoom.currentState.getStateEvents(StateEvent.PaarrotSubRooms, '');
|
||||
const currentContent = stateEvent?.getContent() as PaarrotSubRoomsContent | undefined;
|
||||
const currentChildren = currentContent?.children ?? [];
|
||||
const newContent: PaarrotSubRoomsContent = {
|
||||
children: [...currentChildren, newRoomId],
|
||||
};
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
await mx.sendStateEvent(parentSubRoomId, StateEvent.PaarrotSubRooms as any, newContent, '');
|
||||
|
||||
// Also add sub-room to parent's space(s) for joining/permissions
|
||||
// The UI will hide it from the top-level display and only show it nested
|
||||
const parentSpaces = roomToParents.get(parentSubRoomId);
|
||||
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 },
|
||||
newRoomId
|
||||
);
|
||||
} catch (spaceError) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.warn(`Failed to add sub-room to space ${parentSpaceId}:`, spaceError);
|
||||
}
|
||||
}
|
||||
})
|
||||
);
|
||||
}
|
||||
} catch (error) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.error('Failed to set up sub-room relationship:', error);
|
||||
}
|
||||
}
|
||||
closeDialog();
|
||||
},
|
||||
[mx, parentSubRoomId, parentRoom, roomToParents, closeDialog]
|
||||
);
|
||||
|
||||
const modalTitle = parentSubRoomId ? 'New Sub-Room' : 'New Room';
|
||||
|
||||
return (
|
||||
<SpaceProvider value={space ?? null}>
|
||||
@@ -57,7 +137,7 @@ function CreateRoomModal({ state }: CreateRoomModalProps) {
|
||||
}}
|
||||
>
|
||||
<Box grow="Yes">
|
||||
<Text size="H4">New Room</Text>
|
||||
<Text size="H4">{modalTitle}</Text>
|
||||
</Box>
|
||||
<Box shrink="No">
|
||||
<IconButton size="300" radii="300" onClick={closeDialog}>
|
||||
@@ -74,7 +154,7 @@ function CreateRoomModal({ state }: CreateRoomModalProps) {
|
||||
direction="Column"
|
||||
gap="500"
|
||||
>
|
||||
<CreateRoomForm space={space} onCreate={closeDialog} />
|
||||
<CreateRoomForm space={space} onCreate={handleCreate} />
|
||||
</Box>
|
||||
</Scroll>
|
||||
</Box>
|
||||
|
||||
Reference in New Issue
Block a user