fix: restore rooms and DMs after React Router 7 upgrade

Stop double-encoding Matrix IDs in pathUtils, decode route params on read, fix millify CJS import, and read space children from currentState so channels and joins work again.
This commit is contained in:
2026-07-09 02:35:04 +10:00
parent e058165830
commit b4258278d8
16 changed files with 110 additions and 51 deletions

View File

@@ -1,6 +1,7 @@
import { ReactNode, useCallback } from 'react'; import { ReactNode, useCallback } from 'react';
import { matchPath, useLocation, useNavigate } from 'react-router-dom'; import { matchPath, useLocation, useNavigate } from 'react-router-dom';
import { import {
decodeRouteParam,
getDirectPath, getDirectPath,
getExplorePath, getExplorePath,
getHomePath, getHomePath,
@@ -52,7 +53,10 @@ export function BackRouteHandler({ children }: BackRouteHandlerProps) {
location.pathname location.pathname
); );
if (spaceMatch?.params.spaceIdOrAlias) { if (spaceMatch?.params.spaceIdOrAlias) {
navigate(getSpacePath(spaceMatch.params.spaceIdOrAlias)); const spaceIdOrAlias = decodeRouteParam(spaceMatch.params.spaceIdOrAlias);
if (spaceIdOrAlias) {
navigate(getSpacePath(spaceIdOrAlias));
}
return; return;
} }
if ( if (

View File

@@ -1,5 +1,6 @@
import React, { useCallback, useEffect } from 'react'; import React, { useCallback, useEffect } from 'react';
import { Box, Line } from 'folds'; import { Box, Line } from 'folds';
import { decodeRouteParam } from '../../pages/pathUtils';
import { useParams } from 'react-router-dom'; import { useParams } from 'react-router-dom';
import { isKeyHotkey } from 'is-hotkey'; import { isKeyHotkey } from 'is-hotkey';
import { useSetAtom } from 'jotai'; import { useSetAtom } from 'jotai';
@@ -19,7 +20,8 @@ import { isForum } from '../../utils/room';
import { ForumRoomView } from './ForumRoomView'; import { ForumRoomView } from './ForumRoomView';
export function Room() { export function Room() {
const { eventId } = useParams(); const { eventId: rawEventId } = useParams();
const eventId = decodeRouteParam(rawEventId);
const room = useRoom(); const room = useRoom();
const mx = useMatrixClient(); const mx = useMatrixClient();
const setActiveRoomId = useSetAtom(activeRoomIdAtom); const setActiveRoomId = useSetAtom(activeRoomIdAtom);

View File

@@ -1,5 +1,5 @@
import { useMatch, useParams } from 'react-router-dom'; import { useMatch, useParams } from 'react-router-dom';
import { getExploreFeaturedPath, getExplorePath } from '../../pages/pathUtils'; import { decodeRouteParam, getExploreFeaturedPath, getExplorePath } from '../../pages/pathUtils';
export const useExploreSelected = (): boolean => { export const useExploreSelected = (): boolean => {
const match = useMatch({ const match = useMatch({
@@ -24,5 +24,5 @@ export const useExploreFeaturedSelected = (): boolean => {
export const useExploreServer = (): string | undefined => { export const useExploreServer = (): string | undefined => {
const { server } = useParams(); const { server } = useParams();
return server; return decodeRouteParam(server);
}; };

View File

@@ -1,11 +1,13 @@
import { useParams } from 'react-router-dom'; import { useParams } from 'react-router-dom';
import { decodeRouteParam } from '../../pages/pathUtils';
import { getCanonicalAliasRoomId, isRoomAlias } from '../../utils/matrix'; import { getCanonicalAliasRoomId, isRoomAlias } from '../../utils/matrix';
import { useMatrixClient } from '../useMatrixClient'; import { useMatrixClient } from '../useMatrixClient';
export const useSelectedRoom = (): string | undefined => { export const useSelectedRoom = (): string | undefined => {
const mx = useMatrixClient(); const mx = useMatrixClient();
const { roomIdOrAlias } = useParams(); const { roomIdOrAlias: rawRoomIdOrAlias } = useParams();
const roomIdOrAlias = decodeRouteParam(rawRoomIdOrAlias);
const roomId = const roomId =
roomIdOrAlias && isRoomAlias(roomIdOrAlias) roomIdOrAlias && isRoomAlias(roomIdOrAlias)
? getCanonicalAliasRoomId(mx, roomIdOrAlias) ? getCanonicalAliasRoomId(mx, roomIdOrAlias)

View File

@@ -1,11 +1,13 @@
import { useLocation, useParams } from 'react-router-dom'; import { useLocation, useParams } from 'react-router-dom';
import { decodeRouteParam } from '../../pages/pathUtils';
import { getCanonicalAliasRoomId, isRoomAlias } from '../../utils/matrix'; import { getCanonicalAliasRoomId, isRoomAlias } from '../../utils/matrix';
import { useMatrixClient } from '../useMatrixClient'; import { useMatrixClient } from '../useMatrixClient';
export const useSelectedSpace = (): string | undefined => { export const useSelectedSpace = (): string | undefined => {
const mx = useMatrixClient(); const mx = useMatrixClient();
const { spaceIdOrAlias } = useParams(); const { spaceIdOrAlias: rawSpaceIdOrAlias } = useParams();
const spaceIdOrAlias = decodeRouteParam(rawSpaceIdOrAlias);
const spaceId = const spaceId =
spaceIdOrAlias && isRoomAlias(spaceIdOrAlias) spaceIdOrAlias && isRoomAlias(spaceIdOrAlias)

View File

@@ -1,15 +1,17 @@
import { useMemo } from 'react'; import { useMemo } from 'react';
import { useMatrixClient } from './useMatrixClient'; import { useMatrixClient } from './useMatrixClient';
import { decodeRouteParam } from '../pages/pathUtils';
import { getCanonicalAliasRoomId, isRoomAlias } from '../utils/matrix'; import { getCanonicalAliasRoomId, isRoomAlias } from '../utils/matrix';
export const useJoinedRoomId = (allRooms: string[], roomIdOrAlias: string): string | undefined => { export const useJoinedRoomId = (allRooms: string[], roomIdOrAlias: string): string | undefined => {
const mx = useMatrixClient(); const mx = useMatrixClient();
const joinedRoomId = useMemo(() => { const joinedRoomId = useMemo(() => {
const roomId = isRoomAlias(roomIdOrAlias) const decoded = decodeRouteParam(roomIdOrAlias) ?? roomIdOrAlias;
? getCanonicalAliasRoomId(mx, roomIdOrAlias) const roomId = isRoomAlias(decoded)
: roomIdOrAlias; ? getCanonicalAliasRoomId(mx, decoded)
: decoded;
if (roomId && allRooms.includes(roomId)) return roomId; if (roomId && allRooms.includes(roomId)) return roomId;
return undefined; return undefined;

View File

@@ -96,10 +96,11 @@ export function AuthLayout() {
// if server is mismatches with path server, update path // if server is mismatches with path server, update path
useEffect(() => { useEffect(() => {
if (!urlEncodedServer || tryDecodeURIComponent(urlEncodedServer) !== server) { const decodedUrlServer = urlEncodedServer ? tryDecodeURIComponent(urlEncodedServer) : undefined;
if (!urlEncodedServer || decodedUrlServer !== server) {
navigate( navigate(
generatePath(currentAuthPath(location.pathname), { generatePath(currentAuthPath(location.pathname), {
server: encodeURIComponent(server), server,
}) + location.search, }) + location.search,
{ replace: true } { replace: true }
); );
@@ -114,7 +115,7 @@ export function AuthLayout() {
return; return;
} }
navigate( navigate(
generatePath(currentAuthPath(location.pathname), { server: encodeURIComponent(newServer) }) + location.search generatePath(currentAuthPath(location.pathname), { server: newServer }) + location.search
); );
}, },
[navigate, location, discoveryState, server, discoverServer] [navigate, location, discoveryState, server, discoverServer]

View File

@@ -1,5 +1,6 @@
import React, { ReactNode } from 'react'; import React, { ReactNode } from 'react';
import { useParams } from 'react-router-dom'; import { useParams } from 'react-router-dom';
import { decodeRouteParam } from '../../pathUtils';
import { useSelectedRoom } from '../../../hooks/router/useSelectedRoom'; import { useSelectedRoom } from '../../../hooks/router/useSelectedRoom';
import { IsDirectRoomProvider, RoomProvider } from '../../../hooks/useRoom'; import { IsDirectRoomProvider, RoomProvider } from '../../../hooks/useRoom';
import { useMatrixClient } from '../../../hooks/useMatrixClient'; import { useMatrixClient } from '../../../hooks/useMatrixClient';
@@ -10,12 +11,19 @@ export function DirectRouteRoomProvider({ children }: { children: ReactNode }) {
const mx = useMatrixClient(); const mx = useMatrixClient();
const rooms = useDirectRooms(); const rooms = useDirectRooms();
const { roomIdOrAlias, eventId } = useParams(); const { roomIdOrAlias: rawRoomIdOrAlias, eventId: rawEventId } = useParams();
const roomIdOrAlias = decodeRouteParam(rawRoomIdOrAlias);
const eventId = decodeRouteParam(rawEventId);
const roomId = useSelectedRoom(); const roomId = useSelectedRoom();
const room = mx.getRoom(roomId); const room = mx.getRoom(roomId);
if (!room || !rooms.includes(room.roomId)) { if (!room || !rooms.includes(room.roomId)) {
return <JoinBeforeNavigate roomIdOrAlias={roomIdOrAlias!} eventId={eventId} />; return (
<JoinBeforeNavigate
roomIdOrAlias={roomIdOrAlias ?? rawRoomIdOrAlias!}
eventId={eventId}
/>
);
} }
return ( return (

View File

@@ -20,7 +20,7 @@ import { useMatrixClient } from '../../../hooks/useMatrixClient';
import { RoomTopicViewer } from '../../../components/room-topic-viewer'; import { RoomTopicViewer } from '../../../components/room-topic-viewer';
import { RoomCard, RoomCardBase, RoomCardGrid } from '../../../components/room-card'; import { RoomCard, RoomCardBase, RoomCardGrid } from '../../../components/room-card';
import { ExploreServerPathSearchParams } from '../../paths'; import { ExploreServerPathSearchParams } from '../../paths';
import { getExploreServerPath, withSearchParam } from '../../pathUtils'; import { getExploreServerPath, decodeRouteParam, withSearchParam } from '../../pathUtils';
import * as css from './style.css'; import * as css from './style.css';
import { allRoomsAtom } from '../../../state/room-list/roomList'; import { allRoomsAtom } from '../../../state/room-list/roomList';
import { useRoomNavigate } from '../../../hooks/useRoomNavigate'; import { useRoomNavigate } from '../../../hooks/useRoomNavigate';
@@ -324,7 +324,8 @@ function LimitButton({ limit, onLimitChange }: LimitButtonProps) {
} }
export function PublicRooms() { export function PublicRooms() {
const { server } = useParams(); const { server: rawServer } = useParams();
const server = decodeRouteParam(rawServer);
const mx = useMatrixClient(); const mx = useMatrixClient();
const userId = mx.getUserId(); const userId = mx.getUserId();
const userServer = userId && getMxIdServer(userId); const userServer = userId && getMxIdServer(userId);

View File

@@ -1,5 +1,6 @@
import React, { ReactNode } from 'react'; import React, { ReactNode } from 'react';
import { useParams } from 'react-router-dom'; import { useParams } from 'react-router-dom';
import { decodeRouteParam } from '../../pathUtils';
import { useSelectedRoom } from '../../../hooks/router/useSelectedRoom'; import { useSelectedRoom } from '../../../hooks/router/useSelectedRoom';
import { IsDirectRoomProvider, RoomProvider } from '../../../hooks/useRoom'; import { IsDirectRoomProvider, RoomProvider } from '../../../hooks/useRoom';
import { useMatrixClient } from '../../../hooks/useMatrixClient'; import { useMatrixClient } from '../../../hooks/useMatrixClient';
@@ -11,7 +12,9 @@ export function HomeRouteRoomProvider({ children }: { children: ReactNode }) {
const mx = useMatrixClient(); const mx = useMatrixClient();
const rooms = useHomeRooms(); const rooms = useHomeRooms();
const { roomIdOrAlias, eventId } = useParams(); const { roomIdOrAlias: rawRoomIdOrAlias, eventId: rawEventId } = useParams();
const roomIdOrAlias = decodeRouteParam(rawRoomIdOrAlias);
const eventId = decodeRouteParam(rawEventId);
const viaServers = useSearchParamsViaServers(); const viaServers = useSearchParamsViaServers();
const roomId = useSelectedRoom(); const roomId = useSelectedRoom();
const room = mx.getRoom(roomId); const room = mx.getRoom(roomId);
@@ -19,7 +22,7 @@ export function HomeRouteRoomProvider({ children }: { children: ReactNode }) {
if (!room || !rooms.includes(room.roomId)) { if (!room || !rooms.includes(room.roomId)) {
return ( return (
<JoinBeforeNavigate <JoinBeforeNavigate
roomIdOrAlias={roomIdOrAlias!} roomIdOrAlias={roomIdOrAlias ?? rawRoomIdOrAlias!}
eventId={eventId} eventId={eventId}
viaServers={viaServers} viaServers={viaServers}
/> />

View File

@@ -1,6 +1,7 @@
import React, { ReactNode } from 'react'; import React, { ReactNode } from 'react';
import { useParams } from 'react-router-dom'; import { useParams } from 'react-router-dom';
import { useAtom, useAtomValue } from 'jotai'; import { useAtom, useAtomValue } from 'jotai';
import { decodeRouteParam } from '../../pathUtils';
import { useSelectedRoom } from '../../../hooks/router/useSelectedRoom'; import { useSelectedRoom } from '../../../hooks/router/useSelectedRoom';
import { IsDirectRoomProvider, RoomProvider } from '../../../hooks/useRoom'; import { IsDirectRoomProvider, RoomProvider } from '../../../hooks/useRoom';
import { useMatrixClient } from '../../../hooks/useMatrixClient'; import { useMatrixClient } from '../../../hooks/useMatrixClient';
@@ -22,7 +23,9 @@ export function SpaceRouteRoomProvider({ children }: { children: ReactNode }) {
const mDirects = useAtomValue(mDirectAtom); const mDirects = useAtomValue(mDirectAtom);
const allRooms = useAtomValue(allRoomsAtom); const allRooms = useAtomValue(allRoomsAtom);
const { roomIdOrAlias, eventId } = useParams(); const { roomIdOrAlias: rawRoomIdOrAlias, eventId: rawEventId } = useParams();
const roomIdOrAlias = decodeRouteParam(rawRoomIdOrAlias);
const eventId = decodeRouteParam(rawEventId);
const viaServers = useSearchParamsViaServers(); const viaServers = useSearchParamsViaServers();
const roomId = useSelectedRoom(); const roomId = useSelectedRoom();
const room = mx.getRoom(roomId); const room = mx.getRoom(roomId);
@@ -31,7 +34,7 @@ export function SpaceRouteRoomProvider({ children }: { children: ReactNode }) {
// room is not joined // room is not joined
return ( return (
<JoinBeforeNavigate <JoinBeforeNavigate
roomIdOrAlias={roomIdOrAlias!} roomIdOrAlias={roomIdOrAlias ?? rawRoomIdOrAlias!}
eventId={eventId} eventId={eventId}
viaServers={viaServers} viaServers={viaServers}
/> />
@@ -59,7 +62,7 @@ export function SpaceRouteRoomProvider({ children }: { children: ReactNode }) {
return ( return (
<JoinBeforeNavigate <JoinBeforeNavigate
roomIdOrAlias={roomIdOrAlias!} roomIdOrAlias={roomIdOrAlias ?? rawRoomIdOrAlias!}
eventId={eventId} eventId={eventId}
viaServers={viaServers} viaServers={viaServers}
/> />

View File

@@ -324,7 +324,7 @@ export function Space() {
const virtualizer = useVirtualizer({ const virtualizer = useVirtualizer({
count: flattenedHierarchy.length, count: flattenedHierarchy.length,
getScrollElement: () => scrollRef.current, getScrollElement: () => scrollRef.current,
estimateSize: () => 0, estimateSize: () => 32,
overscan: 10, overscan: 10,
}); });

View File

@@ -1,5 +1,6 @@
import React, { ReactNode } from 'react'; import React, { ReactNode } from 'react';
import { useParams } from 'react-router-dom'; import { useParams } from 'react-router-dom';
import { decodeRouteParam } from '../../pathUtils';
import { useMatrixClient } from '../../../hooks/useMatrixClient'; import { useMatrixClient } from '../../../hooks/useMatrixClient';
import { useAutoJoinOnSpaceVisit } from '../../../hooks/useAutoJoinSpaceRooms'; import { useAutoJoinOnSpaceVisit } from '../../../hooks/useAutoJoinSpaceRooms';
import { useSpaces } from '../../../state/hooks/roomList'; import { useSpaces } from '../../../state/hooks/roomList';
@@ -16,7 +17,8 @@ export function RouteSpaceProvider({ children }: RouteSpaceProviderProps) {
const mx = useMatrixClient(); const mx = useMatrixClient();
const joinedSpaces = useSpaces(mx, allRoomsAtom); const joinedSpaces = useSpaces(mx, allRoomsAtom);
const { spaceIdOrAlias } = useParams(); const { spaceIdOrAlias: rawSpaceIdOrAlias } = useParams();
const spaceIdOrAlias = decodeRouteParam(rawSpaceIdOrAlias);
const viaServers = useSearchParamsViaServers(); const viaServers = useSearchParamsViaServers();
const selectedSpaceId = useSelectedSpace(); const selectedSpaceId = useSelectedSpace();
@@ -25,7 +27,12 @@ export function RouteSpaceProvider({ children }: RouteSpaceProviderProps) {
useAutoJoinOnSpaceVisit(space ?? undefined); useAutoJoinOnSpaceVisit(space ?? undefined);
if (!space || !joinedSpaces.includes(space.roomId)) { if (!space || !joinedSpaces.includes(space.roomId)) {
return <JoinBeforeNavigate roomIdOrAlias={spaceIdOrAlias ?? ''} viaServers={viaServers} />; return (
<JoinBeforeNavigate
roomIdOrAlias={spaceIdOrAlias ?? rawSpaceIdOrAlias ?? ''}
viaServers={viaServers}
/>
);
} }
return ( return (

View File

@@ -28,6 +28,7 @@ import {
CREATE_PATH, CREATE_PATH,
} from './paths'; } from './paths';
import { trimLeadingSlash, trimTrailingSlash } from '../utils/common'; import { trimLeadingSlash, trimTrailingSlash } from '../utils/common';
import { tryDecodeURIComponent } from '../utils/dom';
import { HashRouterConfig } from '../hooks/useClientConfig'; import { HashRouterConfig } from '../hooks/useClientConfig';
import type { MatrixClient } from 'matrix-js-sdk'; import type { MatrixClient } from 'matrix-js-sdk';
import { getCanonicalAliasOrRoomId } from '../utils/matrix'; import { getCanonicalAliasOrRoomId } from '../utils/matrix';
@@ -46,6 +47,18 @@ export const withSearchParam = <T extends Record<string, string>>(
export const encodeSearchParamValueArray = (ids: string[]): string => ids.join(','); export const encodeSearchParamValueArray = (ids: string[]): string => ids.join(',');
export const decodeSearchParamValueArray = (idsParam: string): string[] => idsParam.split(','); export const decodeSearchParamValueArray = (idsParam: string): string[] => idsParam.split(',');
/** Decode route params (React Router 7 encodes in generatePath — do not pre-encode). */
export const decodeRouteParam = (param: string | undefined): string | undefined => {
if (!param) return param;
let decoded = param;
let next = tryDecodeURIComponent(decoded);
while (next !== decoded) {
decoded = next;
next = tryDecodeURIComponent(decoded);
}
return decoded;
};
export const getOriginBaseUrl = (hashRouterConfig?: HashRouterConfig): string => { export const getOriginBaseUrl = (hashRouterConfig?: HashRouterConfig): string => {
const baseUrl = `${trimTrailingSlash(window.location.origin)}${import.meta.env.BASE_URL}`; const baseUrl = `${trimTrailingSlash(window.location.origin)}${import.meta.env.BASE_URL}`;
@@ -79,17 +92,17 @@ export const getAppPathFromHref = (baseUrl: string, href: string): string => {
export const getRootPath = (): string => ROOT_PATH; export const getRootPath = (): string => ROOT_PATH;
export const getLoginPath = (server?: string): string => { export const getLoginPath = (server?: string): string => {
const params = server ? { server: encodeURIComponent(server) } : undefined; const params = server ? { server } : undefined;
return generatePath(LOGIN_PATH, params); return generatePath(LOGIN_PATH, params);
}; };
export const getRegisterPath = (server?: string): string => { export const getRegisterPath = (server?: string): string => {
const params = server ? { server: encodeURIComponent(server) } : undefined; const params = server ? { server } : undefined;
return generatePath(REGISTER_PATH, params); return generatePath(REGISTER_PATH, params);
}; };
export const getResetPasswordPath = (server?: string): string => { export const getResetPasswordPath = (server?: string): string => {
const params = server ? { server: encodeURIComponent(server) } : undefined; const params = server ? { server } : undefined;
return generatePath(RESET_PASSWORD_PATH, params); return generatePath(RESET_PASSWORD_PATH, params);
}; };
@@ -99,8 +112,8 @@ export const getHomeJoinPath = (): string => HOME_JOIN_PATH;
export const getHomeSearchPath = (): string => HOME_SEARCH_PATH; export const getHomeSearchPath = (): string => HOME_SEARCH_PATH;
export const getHomeRoomPath = (roomIdOrAlias: string, eventId?: string): string => { export const getHomeRoomPath = (roomIdOrAlias: string, eventId?: string): string => {
const params = { const params = {
roomIdOrAlias: encodeURIComponent(roomIdOrAlias), roomIdOrAlias,
eventId: eventId ? encodeURIComponent(eventId) : null, eventId: eventId ?? null,
}; };
return generatePath(HOME_ROOM_PATH, params); return generatePath(HOME_ROOM_PATH, params);
@@ -110,8 +123,8 @@ export const getDirectPath = (): string => DIRECT_PATH;
export const getDirectCreatePath = (): string => DIRECT_CREATE_PATH; export const getDirectCreatePath = (): string => DIRECT_CREATE_PATH;
export const getDirectRoomPath = (roomIdOrAlias: string, eventId?: string): string => { export const getDirectRoomPath = (roomIdOrAlias: string, eventId?: string): string => {
const params = { const params = {
roomIdOrAlias: encodeURIComponent(roomIdOrAlias), roomIdOrAlias,
eventId: eventId ? encodeURIComponent(eventId) : null, eventId: eventId ?? null,
}; };
return generatePath(DIRECT_ROOM_PATH, params); return generatePath(DIRECT_ROOM_PATH, params);
@@ -121,20 +134,20 @@ export const getDirectInvitesPath = (): string => DIRECT_INVITES_PATH;
export const getSpacePath = (spaceIdOrAlias: string): string => { export const getSpacePath = (spaceIdOrAlias: string): string => {
const params = { const params = {
spaceIdOrAlias: encodeURIComponent(spaceIdOrAlias), spaceIdOrAlias,
}; };
return generatePath(SPACE_PATH, params); return generatePath(SPACE_PATH, params);
}; };
export const getSpaceLobbyPath = (spaceIdOrAlias: string): string => { export const getSpaceLobbyPath = (spaceIdOrAlias: string): string => {
const params = { const params = {
spaceIdOrAlias: encodeURIComponent(spaceIdOrAlias), spaceIdOrAlias,
}; };
return generatePath(SPACE_LOBBY_PATH, params); return generatePath(SPACE_LOBBY_PATH, params);
}; };
export const getSpaceFeedPath = (spaceIdOrAlias: string): string => { export const getSpaceFeedPath = (spaceIdOrAlias: string): string => {
const params = { const params = {
spaceIdOrAlias: encodeURIComponent(spaceIdOrAlias), spaceIdOrAlias,
}; };
return generatePath(SPACE_FEED_PATH, params); return generatePath(SPACE_FEED_PATH, params);
}; };
@@ -149,7 +162,7 @@ export const getSpaceDefaultPath = (mx: MatrixClient, spaceId: string): string =
}; };
export const getSpaceSearchPath = (spaceIdOrAlias: string): string => { export const getSpaceSearchPath = (spaceIdOrAlias: string): string => {
const params = { const params = {
spaceIdOrAlias: encodeURIComponent(spaceIdOrAlias), spaceIdOrAlias,
}; };
return generatePath(SPACE_SEARCH_PATH, params); return generatePath(SPACE_SEARCH_PATH, params);
}; };
@@ -159,9 +172,9 @@ export const getSpaceRoomPath = (
eventId?: string eventId?: string
): string => { ): string => {
const params = { const params = {
spaceIdOrAlias: encodeURIComponent(spaceIdOrAlias), spaceIdOrAlias,
roomIdOrAlias: encodeURIComponent(roomIdOrAlias), roomIdOrAlias,
eventId: eventId ? encodeURIComponent(eventId) : null, eventId: eventId ?? null,
}; };
return generatePath(SPACE_ROOM_PATH, params); return generatePath(SPACE_ROOM_PATH, params);
@@ -171,7 +184,7 @@ export const getExplorePath = (): string => EXPLORE_PATH;
export const getExploreFeaturedPath = (): string => EXPLORE_FEATURED_PATH; export const getExploreFeaturedPath = (): string => EXPLORE_FEATURED_PATH;
export const getExploreServerPath = (server: string): string => { export const getExploreServerPath = (server: string): string => {
const params = { const params = {
server: encodeURIComponent(server), server,
}; };
return generatePath(EXPLORE_SERVER_PATH, params); return generatePath(EXPLORE_SERVER_PATH, params);
}; };

View File

@@ -1,8 +1,8 @@
import millifyPlugin from 'millify'; import { millify as millifyFn } from 'millify';
import { MillifyOptions } from 'millify/dist/options'; import type { MillifyOptions } from 'millify/dist/options';
export const millify = (count: number, options?: Partial<MillifyOptions>): string => export const millify = (count: number, options?: Partial<MillifyOptions>): string =>
millifyPlugin(count, { millifyFn(count, {
precision: 1, precision: 1,
locales: [], locales: [],
...options, ...options,

View File

@@ -34,12 +34,20 @@ export const getStateEvent = (
room: Room, room: Room,
eventType: StateEvent, eventType: StateEvent,
stateKey = '' stateKey = ''
): MatrixEvent | undefined => ): MatrixEvent | undefined => {
room.getLiveTimeline().getState(EventTimeline.FORWARDS)?.getStateEvents(eventType, stateKey) ?? const fromCurrent = room.currentState?.getStateEvents(eventType, stateKey);
undefined; if (fromCurrent) return fromCurrent;
return (
room.getLiveTimeline().getState(EventTimeline.FORWARDS)?.getStateEvents(eventType, stateKey) ??
undefined
);
};
export const getStateEvents = (room: Room, eventType: StateEvent): MatrixEvent[] => export const getStateEvents = (room: Room, eventType: StateEvent): MatrixEvent[] => {
room.getLiveTimeline().getState(EventTimeline.FORWARDS)?.getStateEvents(eventType) ?? []; const fromCurrent = room.currentState?.getStateEvents(eventType);
if (fromCurrent && fromCurrent.length > 0) return fromCurrent;
return room.getLiveTimeline().getState(EventTimeline.FORWARDS)?.getStateEvents(eventType) ?? [];
};
export const getAccountData = ( export const getAccountData = (
mx: MatrixClient, mx: MatrixClient,
@@ -170,10 +178,13 @@ export const isUnsupportedRoom = (room: Room | null): boolean => {
}; };
export function isValidChild(mEvent: MatrixEvent): boolean { export function isValidChild(mEvent: MatrixEvent): boolean {
return ( if (mEvent.getType() !== StateEvent.SpaceChild) return false;
mEvent.getType() === StateEvent.SpaceChild && const stateKey = mEvent.getStateKey();
Array.isArray(mEvent.getContent<{ via: string[] }>().via) if (!stateKey || !stateKey.startsWith('!')) return false;
); const { via } = mEvent.getContent<{ via?: string[] }>();
// via is optional on m.space.child; only reject malformed values
if (via !== undefined && !Array.isArray(via)) return false;
return true;
} }
export const getAllParents = (roomToParents: RoomToParents, roomId: string): Set<string> => { export const getAllParents = (roomToParents: RoomToParents, roomId: string): Set<string> => {