Files
cinny/src/app/hooks/useRoomNavigate.ts
litruv 99a294791e Open DMs during sync catch-up instead of blocking on parent maps.
Prefer the direct route for m.direct rooms and allow joined space children before roomToParents finishes backfilling.
2026-07-24 16:52:34 +10:00

108 lines
3.6 KiB
TypeScript

import { useCallback } from 'react';
import { NavigateOptions, useLocation, useNavigate } from 'react-router-dom';
import { useAtomValue } from 'jotai';
import { getCanonicalAliasOrRoomId } from '../utils/matrix';
import {
getDirectRoomPath,
getHomeRoomPath,
getSpacePath,
getSpaceRoomPath,
withRoomEventId,
} from '../pages/pathUtils';
import { useMatrixClient } from './useMatrixClient';
import { getOrphanParents, guessPerfectParent, isSpace } from '../utils/room';
import { roomToParentsAtom } from '../state/room/roomToParents';
import { mDirectAtom } from '../state/mDirectList';
import { useSelectedRoom } from './router/useSelectedRoom';
import { useSelectedSpace } from './router/useSelectedSpace';
import { settingsAtom } from '../state/settings';
import { useSetting } from '../state/hooks/settings';
export const useRoomNavigate = () => {
const navigate = useNavigate();
const location = useLocation();
const mx = useMatrixClient();
const roomToParents = useAtomValue(roomToParentsAtom);
const mDirects = useAtomValue(mDirectAtom);
const spaceSelectedId = useSelectedSpace();
const selectedRoomId = useSelectedRoom();
const [developerTools] = useSetting(settingsAtom, 'developerTools');
const navigateSpace = useCallback(
(roomId: string) => {
const roomIdOrAlias = getCanonicalAliasOrRoomId(mx, roomId);
navigate(getSpacePath(roomIdOrAlias));
},
[mx, navigate]
);
const navigateRoom = useCallback(
(roomId: string, eventId?: string, opts?: NavigateOptions) => {
// Already viewing this room — keep the current home/direct/space URL and only
// change the event id. Otherwise guessPerfectParent can yank the sidebar to
// wherever the room "belongs".
if (selectedRoomId === roomId) {
navigate(withRoomEventId(location.pathname, eventId), opts);
return;
}
const room = mx.getRoom(roomId);
const roomIdOrAlias = getCanonicalAliasOrRoomId(mx, roomId);
const openSpaceTimeline = developerTools && spaceSelectedId === roomId;
if (room && isSpace(room) && !openSpaceTimeline) {
navigate(getSpacePath(roomIdOrAlias), opts);
return;
}
const orphanParents = openSpaceTimeline ? [roomId] : getOrphanParents(roomToParents, roomId);
// Prefer Direct for m.direct rooms unless we are already browsing a parent space
// of this room (keep space chrome). Opening via space while roomToParents is still
// catching up used to hit JoinBeforeNavigate even though the DM was listed.
if (mDirects.has(roomId)) {
const stayInSelectedSpace =
Boolean(spaceSelectedId) && orphanParents.includes(spaceSelectedId!);
if (!stayInSelectedSpace) {
navigate(getDirectRoomPath(roomIdOrAlias, eventId), opts);
return;
}
}
if (orphanParents.length > 0) {
let parentSpace: string;
if (spaceSelectedId && orphanParents.includes(spaceSelectedId)) {
parentSpace = spaceSelectedId;
} else {
parentSpace = guessPerfectParent(mx, roomId, orphanParents) ?? orphanParents[0];
}
const pSpaceIdOrAlias = getCanonicalAliasOrRoomId(mx, parentSpace);
navigate(
getSpaceRoomPath(pSpaceIdOrAlias, openSpaceTimeline ? roomId : roomIdOrAlias, eventId),
opts
);
return;
}
navigate(getHomeRoomPath(roomIdOrAlias, eventId), opts);
},
[
mx,
navigate,
location.pathname,
selectedRoomId,
spaceSelectedId,
roomToParents,
mDirects,
developerTools,
]
);
return {
navigateSpace,
navigateRoom,
};
};