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:
@@ -18,6 +18,7 @@ import { RoomType, StateEvent } from '../../../types/matrix/room';
|
||||
import { SequenceCard } from '../../components/sequence-card';
|
||||
import { getRoomCreatorsForRoomId } from '../../hooks/useRoomCreators';
|
||||
import { getRoomPermissionsAPI } from '../../hooks/useRoomPermissions';
|
||||
import { PaarrotSubRoomsContent } from '../../hooks/useRoomSubRooms';
|
||||
|
||||
type SpaceHierarchyProps = {
|
||||
summary: IHierarchyRoom | undefined;
|
||||
@@ -99,7 +100,36 @@ export const SpaceHierarchy = forwardRef<HTMLDivElement, SpaceHierarchyProps>(
|
||||
onSpacesFound(Array.from(subspaces.values()));
|
||||
}, [subspaces, onSpacesFound]);
|
||||
|
||||
let childItems = roomItems?.filter((i) => !subspaces.has(i.roomId));
|
||||
// Build a global set of all sub-room IDs by checking ALL joined rooms
|
||||
// This ensures sub-rooms are hidden even if their parent isn't in this space
|
||||
const globalSubRoomIds = useMemo(() => {
|
||||
const subRoomIds = new Set<string>();
|
||||
mx.getRooms().forEach((room) => {
|
||||
const subRoomsEvent = room.currentState.getStateEvents(StateEvent.PaarrotSubRooms, '');
|
||||
const content = subRoomsEvent?.getContent<PaarrotSubRoomsContent>();
|
||||
content?.children?.forEach((childId: string) => subRoomIds.add(childId));
|
||||
});
|
||||
return subRoomIds;
|
||||
}, [mx, allJoinedRooms]);
|
||||
|
||||
// Build a map of parent room -> sub-room IDs for nested rendering (only for rooms in this space)
|
||||
const subRoomsMap = useMemo(() => {
|
||||
const parentToSubRooms = new Map<string, string[]>();
|
||||
roomItems?.forEach((item) => {
|
||||
const room = mx.getRoom(item.roomId);
|
||||
if (room) {
|
||||
const subRoomsEvent = room.currentState.getStateEvents(StateEvent.PaarrotSubRooms, '');
|
||||
const content = subRoomsEvent?.getContent<PaarrotSubRoomsContent>();
|
||||
const children = content?.children ?? [];
|
||||
if (children.length > 0) {
|
||||
parentToSubRooms.set(item.roomId, children);
|
||||
}
|
||||
}
|
||||
});
|
||||
return parentToSubRooms;
|
||||
}, [mx, roomItems]);
|
||||
|
||||
let childItems = roomItems?.filter((i) => !subspaces.has(i.roomId) && !globalSubRoomIds.has(i.roomId));
|
||||
if (!spacePermissions?.stateEvent(StateEvent.SpaceChild, mx.getSafeUserId())) {
|
||||
// hide unknown rooms for normal user
|
||||
childItems = childItems?.filter((i) => {
|
||||
@@ -166,40 +196,89 @@ export const SpaceHierarchy = forwardRef<HTMLDivElement, SpaceHierarchyProps>(
|
||||
draggingItem?.roomId === roomItem.roomId &&
|
||||
draggingItem.parentId === roomItem.parentId;
|
||||
|
||||
// Get sub-rooms for this room
|
||||
const subRoomIds = subRoomsMap.get(roomItem.roomId) ?? [];
|
||||
|
||||
return (
|
||||
<RoomItemCard
|
||||
key={roomItem.roomId}
|
||||
item={roomItem}
|
||||
loading={fetching}
|
||||
error={error}
|
||||
summary={roomSummary}
|
||||
dm={mDirects.has(roomItem.roomId)}
|
||||
onOpen={onOpenRoom}
|
||||
getRoom={getRoom}
|
||||
canReorder={
|
||||
!!spacePermissions?.stateEvent(StateEvent.SpaceChild, mx.getSafeUserId()) &&
|
||||
!disabledReorder
|
||||
}
|
||||
options={
|
||||
<HierarchyItemMenu
|
||||
item={roomItem}
|
||||
powerLevels={roomPowerLevels}
|
||||
joined={allJoinedRooms.has(roomItem.roomId)}
|
||||
canEditChild={
|
||||
!!spacePermissions?.stateEvent(StateEvent.SpaceChild, mx.getSafeUserId())
|
||||
}
|
||||
/>
|
||||
}
|
||||
after={
|
||||
<AfterItemDropTarget
|
||||
item={roomItem}
|
||||
nextRoomId={nextRoomId}
|
||||
canDrop={canDrop}
|
||||
/>
|
||||
}
|
||||
data-dragging={roomDragging}
|
||||
onDragging={onDragging}
|
||||
/>
|
||||
<React.Fragment key={roomItem.roomId}>
|
||||
<RoomItemCard
|
||||
item={roomItem}
|
||||
loading={fetching}
|
||||
error={error}
|
||||
summary={roomSummary}
|
||||
dm={mDirects.has(roomItem.roomId)}
|
||||
onOpen={onOpenRoom}
|
||||
getRoom={getRoom}
|
||||
canReorder={
|
||||
!!spacePermissions?.stateEvent(StateEvent.SpaceChild, mx.getSafeUserId()) &&
|
||||
!disabledReorder
|
||||
}
|
||||
options={
|
||||
<HierarchyItemMenu
|
||||
item={roomItem}
|
||||
powerLevels={roomPowerLevels}
|
||||
joined={allJoinedRooms.has(roomItem.roomId)}
|
||||
canEditChild={
|
||||
!!spacePermissions?.stateEvent(StateEvent.SpaceChild, mx.getSafeUserId())
|
||||
}
|
||||
/>
|
||||
}
|
||||
after={
|
||||
<AfterItemDropTarget
|
||||
item={roomItem}
|
||||
nextRoomId={nextRoomId}
|
||||
canDrop={canDrop}
|
||||
/>
|
||||
}
|
||||
data-dragging={roomDragging}
|
||||
onDragging={onDragging}
|
||||
/>
|
||||
{/* Render sub-rooms nested under this room */}
|
||||
{subRoomIds.length > 0 && (
|
||||
<Box direction="Column" gap="100" style={{ marginLeft: config.space.S400 }}>
|
||||
{subRoomIds.map((subRoomId) => {
|
||||
const subRoom = mx.getRoom(subRoomId);
|
||||
const subRoomSummary = rooms.get(subRoomId);
|
||||
const subRoomPowerLevels = roomsPowerLevels.get(subRoomId) ?? {};
|
||||
const subRoomItem: HierarchyItemRoom = {
|
||||
roomId: subRoomId,
|
||||
parentId: roomItem.roomId,
|
||||
content: {},
|
||||
};
|
||||
const subRoomDragging =
|
||||
draggingItem?.roomId === subRoomId &&
|
||||
draggingItem.parentId === roomItem.roomId;
|
||||
|
||||
// Skip sub-rooms the user hasn't joined yet if we have no summary
|
||||
if (!subRoom && !subRoomSummary) return null;
|
||||
|
||||
return (
|
||||
<RoomItemCard
|
||||
key={subRoomId}
|
||||
item={subRoomItem}
|
||||
loading={fetching}
|
||||
error={error}
|
||||
summary={subRoomSummary}
|
||||
dm={mDirects.has(subRoomId)}
|
||||
onOpen={onOpenRoom}
|
||||
getRoom={getRoom}
|
||||
canReorder={false}
|
||||
options={
|
||||
<HierarchyItemMenu
|
||||
item={subRoomItem}
|
||||
powerLevels={subRoomPowerLevels}
|
||||
joined={allJoinedRooms.has(subRoomId)}
|
||||
canEditChild={false}
|
||||
/>
|
||||
}
|
||||
data-dragging={subRoomDragging}
|
||||
onDragging={onDragging}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</Box>
|
||||
)}
|
||||
</React.Fragment>
|
||||
);
|
||||
})}
|
||||
</Box>
|
||||
|
||||
Reference in New Issue
Block a user