vojo/src/app/pages/client/space/RoomProvider.tsx

74 lines
3.1 KiB
TypeScript

import React, { ReactNode } from 'react';
import { Room } from 'matrix-js-sdk';
import { Navigate, useParams } from 'react-router-dom';
import { useAtom, useAtomValue } from 'jotai';
import { useSelectedRoom } from '../../../hooks/router/useSelectedRoom';
import { IsOneOnOneProvider, RoomProvider } from '../../../hooks/useRoom';
import { useMatrixClient } from '../../../hooks/useMatrixClient';
import { JoinBeforeNavigate } from '../../../features/join-before-navigate';
import { useSpace } from '../../../hooks/useSpace';
import { getAllParents, getSpaceChildren } from '../../../utils/room';
import { roomToParentsAtom } from '../../../state/room/roomToParents';
import { allRoomsAtom } from '../../../state/room-list/roomList';
import { useSearchParamsViaServers } from '../../../hooks/router/useSearchParamsViaServers';
import { settingsAtom } from '../../../state/settings';
import { useSetting } from '../../../state/hooks/settings';
import { useIsOneOnOneRoom } from '../../../hooks/useIsOneOnOneRoom';
// Inner provider hosts the reactive 1:1 subscription. Mirrors
// direct/RoomProvider.tsx — the hook can't run before the early-return
// guards, so we split the component once we know the room exists.
function ResolvedRoomProvider({ room, children }: { room: Room; children: ReactNode }) {
const isOneOnOne = useIsOneOnOneRoom(room);
return (
<RoomProvider key={room.roomId} value={room}>
<IsOneOnOneProvider value={isOneOnOne}>{children}</IsOneOnOneProvider>
</RoomProvider>
);
}
export function SpaceRouteRoomProvider({ children }: { children: ReactNode }) {
const mx = useMatrixClient();
const space = useSpace();
const [developerTools] = useSetting(settingsAtom, 'developerTools');
const [roomToParents, setRoomToParents] = useAtom(roomToParentsAtom);
const allRooms = useAtomValue(allRoomsAtom);
const { roomIdOrAlias, eventId } = useParams();
const viaServers = useSearchParamsViaServers();
const roomId = useSelectedRoom();
const room = mx.getRoom(roomId);
if (!room || !allRooms.includes(room.roomId)) {
// room is not joined
if (!roomIdOrAlias) return <Navigate to="/direct" replace />;
return (
<JoinBeforeNavigate roomIdOrAlias={roomIdOrAlias} eventId={eventId} viaServers={viaServers} />
);
}
if (developerTools && room.isSpaceRoom() && room.roomId === space.roomId) {
// Allow viewing the space's own timeline. `isOneOnOneRoom` already guards
// spaces (it returns false for `isSpace(room)`), so the value is stable
// here regardless of member count.
return <ResolvedRoomProvider room={room}>{children}</ResolvedRoomProvider>;
}
if (!getAllParents(roomToParents, room.roomId).has(space.roomId)) {
if (getSpaceChildren(space).includes(room.roomId)) {
// fill missing roomToParent mapping
setRoomToParents({
type: 'PUT',
parent: space.roomId,
children: [room.roomId],
});
}
if (!roomIdOrAlias) return <Navigate to="/direct" replace />;
return (
<JoinBeforeNavigate roomIdOrAlias={roomIdOrAlias} eventId={eventId} viaServers={viaServers} />
);
}
return <ResolvedRoomProvider room={room}>{children}</ResolvedRoomProvider>;
}