import { roomToParentsAtom } from '../state/room/roomToParents';
import { mDirectAtom } from '../state/mDirectList';
import { useSelectedSpace } from './router/useSelectedSpace';
+import { settingsAtom } from '../state/settings';
+import { useSetting } from '../state/hooks/settings';
export const useRoomNavigate = () => {
const navigate = useNavigate();
const roomToParents = useAtomValue(roomToParentsAtom);
const mDirects = useAtomValue(mDirectAtom);
const spaceSelectedId = useSelectedSpace();
+ const [developerTools] = useSetting(settingsAtom, 'developerTools');
const navigateSpace = useCallback(
(roomId: string) => {
const navigateRoom = useCallback(
(roomId: string, eventId?: string, opts?: NavigateOptions) => {
const roomIdOrAlias = getCanonicalAliasOrRoomId(mx, roomId);
+ const openSpaceTimeline = developerTools && spaceSelectedId === roomId;
- const orphanParents = getOrphanParents(roomToParents, roomId);
+ const orphanParents = openSpaceTimeline ? [roomId] : getOrphanParents(roomToParents, roomId);
if (orphanParents.length > 0) {
const pSpaceIdOrAlias = getCanonicalAliasOrRoomId(
mx,
spaceSelectedId && orphanParents.includes(spaceSelectedId)
? spaceSelectedId
- : orphanParents[0]
+ : orphanParents[0] // TODO: better orphan parent selection.
);
+
+ if (openSpaceTimeline) {
+ navigate(getSpaceRoomPath(pSpaceIdOrAlias, roomId, eventId), opts);
+ return;
+ }
+
navigate(getSpaceRoomPath(pSpaceIdOrAlias, roomIdOrAlias, eventId), opts);
return;
}
navigate(getHomeRoomPath(roomIdOrAlias, eventId), opts);
},
- [mx, navigate, spaceSelectedId, roomToParents, mDirects]
+ [mx, navigate, spaceSelectedId, roomToParents, mDirects, developerTools]
);
return {
const targetSpaceId = target.getAttribute('data-id');
if (!targetSpaceId) return;
+ const spacePath = getSpacePath(getCanonicalAliasOrRoomId(mx, targetSpaceId));
if (screenSize === ScreenSize.Mobile) {
- navigate(getSpacePath(getCanonicalAliasOrRoomId(mx, targetSpaceId)));
+ navigate(spacePath);
return;
}
const activePath = navToActivePath.get(targetSpaceId);
- if (activePath) {
+ if (activePath && activePath.pathname.startsWith(spacePath)) {
navigate(joinPathComponent(activePath));
return;
}
import React, { ReactNode } from 'react';
import { useParams } from 'react-router-dom';
-import { useAtomValue } from 'jotai';
+import { useAtom, useAtomValue } from 'jotai';
import { useSelectedRoom } from '../../../hooks/router/useSelectedRoom';
import { IsDirectRoomProvider, RoomProvider } from '../../../hooks/useRoom';
import { useMatrixClient } from '../../../hooks/useMatrixClient';
import { JoinBeforeNavigate } from '../../../features/join-before-navigate';
import { useSpace } from '../../../hooks/useSpace';
-import { getAllParents } from '../../../utils/room';
+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 { mDirectAtom } from '../../../state/mDirectList';
+import { settingsAtom } from '../../../state/settings';
+import { useSetting } from '../../../state/hooks/settings';
export function SpaceRouteRoomProvider({ children }: { children: ReactNode }) {
const mx = useMatrixClient();
const space = useSpace();
- const roomToParents = useAtomValue(roomToParentsAtom);
+ const [developerTools] = useSetting(settingsAtom, 'developerTools');
+ const [roomToParents, setRoomToParents] = useAtom(roomToParentsAtom);
const mDirects = useAtomValue(mDirectAtom);
const allRooms = useAtomValue(allRoomsAtom);
const roomId = useSelectedRoom();
const room = mx.getRoom(roomId);
- if (
- !room ||
- room.isSpaceRoom() ||
- !allRooms.includes(room.roomId) ||
- !getAllParents(roomToParents, room.roomId).has(space.roomId)
- ) {
+ if (!room || !allRooms.includes(room.roomId)) {
+ // room is not joined
+ return (
+ <JoinBeforeNavigate
+ roomIdOrAlias={roomIdOrAlias!}
+ eventId={eventId}
+ viaServers={viaServers}
+ />
+ );
+ }
+
+ if (developerTools && room.isSpaceRoom() && room.roomId === space.roomId) {
+ // allow to view space timeline
+ return (
+ <RoomProvider key={room.roomId} value={room}>
+ <IsDirectRoomProvider value={mDirects.has(room.roomId)}>{children}</IsDirectRoomProvider>
+ </RoomProvider>
+ );
+ }
+
+ 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],
+ });
+ }
+
return (
<JoinBeforeNavigate
roomIdOrAlias={roomIdOrAlias!}
useRoomsNotificationPreferencesContext,
} from '../../../hooks/useRoomsNotificationPreferences';
import { useOpenSpaceSettings } from '../../../state/hooks/spaceSettings';
+import { useRoomNavigate } from '../../../hooks/useRoomNavigate';
type SpaceMenuProps = {
room: Room;
const SpaceMenu = forwardRef<HTMLDivElement, SpaceMenuProps>(({ room, requestClose }, ref) => {
const mx = useMatrixClient();
const [hideActivity] = useSetting(settingsAtom, 'hideActivity');
+ const [developerTools] = useSetting(settingsAtom, 'developerTools');
const roomToParents = useAtomValue(roomToParentsAtom);
const powerLevels = usePowerLevels(room);
const { getPowerLevel, canDoAction } = usePowerLevelsAPI(powerLevels);
const canInvite = canDoAction('invite', getPowerLevel(mx.getUserId() ?? ''));
const openSpaceSettings = useOpenSpaceSettings();
+ const { navigateRoom } = useRoomNavigate();
const allChild = useSpaceChildren(
allRoomsAtom,
requestClose();
};
+ const handleOpenTimeline = () => {
+ navigateRoom(room.roomId);
+ requestClose();
+ };
+
return (
<Menu ref={ref} style={{ maxWidth: toRem(160), width: '100vw' }}>
<Box direction="Column" gap="100" style={{ padding: config.space.S100 }}>
Space Settings
</Text>
</MenuItem>
+ {developerTools && (
+ <MenuItem
+ onClick={handleOpenTimeline}
+ size="300"
+ after={<Icon size="100" src={Icons.Terminal} />}
+ radii="300"
+ >
+ <Text style={{ flexGrow: 1 }} as="span" size="T300" truncate>
+ Event Timeline
+ </Text>
+ </MenuItem>
+ )}
</Box>
<Line variant="Surface" size="300" />
<Box direction="Column" gap="100" style={{ padding: config.space.S100 }}>
const NAV_TO_ACTIVE_PATH = 'navToActivePath';
+const getStoreKey = (userId: string): string => `${NAV_TO_ACTIVE_PATH}${userId}`;
+
type NavToActivePath = Map<string, Path>;
type NavToActivePathAction =
export type NavToActivePathAtom = WritableAtom<NavToActivePath, [NavToActivePathAction], undefined>;
export const makeNavToActivePathAtom = (userId: string): NavToActivePathAtom => {
- const storeKey = `${NAV_TO_ACTIVE_PATH}${userId}`;
+ const storeKey = getStoreKey(userId);
const baseNavToActivePathAtom = atomWithLocalStorage<NavToActivePath>(
storeKey,
return navToActivePathAtom;
};
+
+export const clearNavToActivePathStore = (userId: string) => {
+ localStorage.removeItem(getStoreKey(userId));
+};
import { createClient, MatrixClient, IndexedDBStore, IndexedDBCryptoStore } from 'matrix-js-sdk';
import { cryptoCallbacks } from './state/secretStorageKeys';
+import { clearNavToActivePathStore } from '../app/state/navToActivePath';
type Session = {
baseUrl: string;
export const clearCacheAndReload = async (mx: MatrixClient) => {
mx.stopClient();
+ clearNavToActivePathStore(mx.getSafeUserId());
await mx.store.deleteAllData();
window.location.reload();
};