Skip to content

Commit bb1e932

Browse files
authored
Merge branch 'develop' into hs/fix-confirm-recovery-key-multi-press
2 parents fcdab9d + 8fa3d7e commit bb1e932

File tree

12 files changed

+135
-79
lines changed

12 files changed

+135
-79
lines changed

src/RoomAliasCache.ts

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Com
66
Please see LICENSE files in the repository root for full details.
77
*/
88

9+
type CacheResult = { roomId: string; viaServers: string[] };
10+
911
/**
1012
* This is meant to be a cache of room alias to room ID so that moving between
1113
* rooms happens smoothly (for example using browser back / forward buttons).
@@ -16,12 +18,12 @@ Please see LICENSE files in the repository root for full details.
1618
* A similar thing could also be achieved via `pushState` with a state object,
1719
* but keeping it separate like this seems easier in case we do want to extend.
1820
*/
19-
const aliasToIDMap = new Map<string, string>();
21+
const cache = new Map<string, CacheResult>();
2022

21-
export function storeRoomAliasInCache(alias: string, id: string): void {
22-
aliasToIDMap.set(alias, id);
23+
export function storeRoomAliasInCache(alias: string, roomId: string, viaServers: string[]): void {
24+
cache.set(alias, { roomId, viaServers });
2325
}
2426

25-
export function getCachedRoomIDForAlias(alias: string): string | undefined {
26-
return aliasToIDMap.get(alias);
27+
export function getCachedRoomIdForAlias(alias: string): CacheResult | undefined {
28+
return cache.get(alias);
2729
}

src/components/structures/MatrixChat.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ import ThemeController from "../../settings/controllers/ThemeController";
5151
import { startAnyRegistrationFlow } from "../../Registration";
5252
import ResizeNotifier from "../../utils/ResizeNotifier";
5353
import AutoDiscoveryUtils from "../../utils/AutoDiscoveryUtils";
54-
import { makeRoomPermalink } from "../../utils/permalinks/Permalinks";
54+
import { calculateRoomVia, makeRoomPermalink } from "../../utils/permalinks/Permalinks";
5555
import ThemeWatcher, { ThemeWatcherEvent } from "../../settings/watchers/ThemeWatcher";
5656
import { FontWatcher } from "../../settings/watchers/FontWatcher";
5757
import { storeRoomAliasInCache } from "../../RoomAliasCache";
@@ -1026,7 +1026,7 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
10261026
presentedId = theAlias;
10271027
// Store display alias of the presented room in cache to speed future
10281028
// navigation.
1029-
storeRoomAliasInCache(theAlias, room.roomId);
1029+
storeRoomAliasInCache(theAlias, room.roomId, calculateRoomVia(room));
10301030
}
10311031

10321032
// Store this as the ID of the last room accessed. This is so that we can

src/components/views/dialogs/spotlight/SpotlightDialog.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ import { getKeyBindingsManager } from "../../../../KeyBindingsManager";
5353
import { _t } from "../../../../languageHandler";
5454
import { MatrixClientPeg } from "../../../../MatrixClientPeg";
5555
import { PosthogAnalytics } from "../../../../PosthogAnalytics";
56-
import { getCachedRoomIDForAlias } from "../../../../RoomAliasCache";
56+
import { getCachedRoomIdForAlias } from "../../../../RoomAliasCache";
5757
import { showStartChatInviteDialog } from "../../../../RoomInvite";
5858
import { SettingLevel } from "../../../../settings/SettingLevel";
5959
import SettingsStore from "../../../../settings/SettingsStore";
@@ -912,7 +912,7 @@ const SpotlightDialog: React.FC<IProps> = ({ initialText = "", initialFilter = n
912912
if (
913913
trimmedQuery.startsWith("#") &&
914914
trimmedQuery.includes(":") &&
915-
(!getCachedRoomIDForAlias(trimmedQuery) || !cli.getRoom(getCachedRoomIDForAlias(trimmedQuery)))
915+
(!getCachedRoomIdForAlias(trimmedQuery) || !cli.getRoom(getCachedRoomIdForAlias(trimmedQuery)!.roomId))
916916
) {
917917
joinRoomSection = (
918918
<div className="mx_SpotlightDialog_section mx_SpotlightDialog_otherSearches" role="group">

src/modules/Navigation.ts

Lines changed: 17 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -9,33 +9,31 @@ import { type NavigationApi as INavigationApi } from "@element-hq/element-web-mo
99

1010
import { navigateToPermalink } from "../utils/permalinks/navigator.ts";
1111
import { parsePermalink } from "../utils/permalinks/Permalinks.ts";
12-
import { getCachedRoomIDForAlias } from "../RoomAliasCache.ts";
13-
import { MatrixClientPeg } from "../MatrixClientPeg.ts";
1412
import dispatcher from "../dispatcher/dispatcher.ts";
1513
import { Action } from "../dispatcher/actions.ts";
16-
import SettingsStore from "../settings/SettingsStore.ts";
14+
import type { ViewRoomPayload } from "../dispatcher/payloads/ViewRoomPayload.ts";
1715

1816
export class NavigationApi implements INavigationApi {
1917
public async toMatrixToLink(link: string, join = false): Promise<void> {
2018
navigateToPermalink(link);
2119

2220
const parts = parsePermalink(link);
23-
if (parts?.roomIdOrAlias && join) {
24-
let roomId: string | undefined = parts.roomIdOrAlias;
25-
if (roomId.startsWith("#")) {
26-
roomId = getCachedRoomIDForAlias(parts.roomIdOrAlias);
27-
if (!roomId) {
28-
// alias resolution failed
29-
const result = await MatrixClientPeg.safeGet().getRoomIdForAlias(parts.roomIdOrAlias);
30-
roomId = result.room_id;
31-
}
32-
}
33-
34-
if (roomId) {
35-
dispatcher.dispatch({
36-
action: Action.JoinRoom,
37-
canAskToJoin: SettingsStore.getValue("feature_ask_to_join"),
38-
roomId,
21+
if (parts?.roomIdOrAlias) {
22+
if (parts.roomIdOrAlias.startsWith("#")) {
23+
dispatcher.dispatch<ViewRoomPayload>({
24+
action: Action.ViewRoom,
25+
room_alias: parts.roomIdOrAlias,
26+
via_servers: parts.viaServers ?? undefined,
27+
auto_join: join,
28+
metricsTrigger: undefined,
29+
});
30+
} else {
31+
dispatcher.dispatch<ViewRoomPayload>({
32+
action: Action.ViewRoom,
33+
room_id: parts.roomIdOrAlias,
34+
via_servers: parts.viaServers ?? undefined,
35+
auto_join: join,
36+
metricsTrigger: undefined,
3937
});
4038
}
4139
}

src/modules/ProxiedModuleApi.ts

Lines changed: 17 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -28,13 +28,12 @@ import dispatcher from "../dispatcher/dispatcher";
2828
import { navigateToPermalink } from "../utils/permalinks/navigator";
2929
import { parsePermalink } from "../utils/permalinks/Permalinks";
3030
import { MatrixClientPeg } from "../MatrixClientPeg";
31-
import { getCachedRoomIDForAlias } from "../RoomAliasCache";
3231
import { Action } from "../dispatcher/actions";
3332
import { type OverwriteLoginPayload } from "../dispatcher/payloads/OverwriteLoginPayload";
3433
import { type ActionPayload } from "../dispatcher/payloads";
35-
import SettingsStore from "../settings/SettingsStore";
3634
import WidgetStore, { type IApp } from "../stores/WidgetStore";
3735
import { type Container, WidgetLayoutStore } from "../stores/widgets/WidgetLayoutStore";
36+
import type { ViewRoomPayload } from "../dispatcher/payloads/ViewRoomPayload.ts";
3837

3938
/**
4039
* Glue between the `ModuleApi` interface and the react-sdk. Anticipates one instance
@@ -183,28 +182,22 @@ export class ProxiedModuleApi implements ModuleApi {
183182
navigateToPermalink(uri);
184183

185184
const parts = parsePermalink(uri);
186-
if (parts?.roomIdOrAlias && andJoin) {
187-
let roomId: string | undefined = parts.roomIdOrAlias;
188-
let servers = parts.viaServers;
189-
if (roomId.startsWith("#")) {
190-
roomId = getCachedRoomIDForAlias(parts.roomIdOrAlias);
191-
if (!roomId) {
192-
// alias resolution failed
193-
const result = await MatrixClientPeg.safeGet().getRoomIdForAlias(parts.roomIdOrAlias);
194-
roomId = result.room_id;
195-
if (!servers) servers = result.servers; // use provided servers first, if available
196-
}
197-
}
198-
dispatcher.dispatch({
199-
action: Action.ViewRoom,
200-
room_id: roomId,
201-
via_servers: servers,
202-
});
203-
204-
if (andJoin) {
205-
dispatcher.dispatch({
206-
action: Action.JoinRoom,
207-
canAskToJoin: SettingsStore.getValue("feature_ask_to_join"),
185+
if (parts?.roomIdOrAlias) {
186+
if (parts.roomIdOrAlias.startsWith("#")) {
187+
dispatcher.dispatch<ViewRoomPayload>({
188+
action: Action.ViewRoom,
189+
room_alias: parts.roomIdOrAlias,
190+
via_servers: parts.viaServers ?? undefined,
191+
auto_join: andJoin ?? false,
192+
metricsTrigger: undefined,
193+
});
194+
} else {
195+
dispatcher.dispatch<ViewRoomPayload>({
196+
action: Action.ViewRoom,
197+
room_id: parts.roomIdOrAlias,
198+
via_servers: parts.viaServers ?? undefined,
199+
auto_join: andJoin ?? false,
200+
metricsTrigger: undefined,
208201
});
209202
}
210203
}

src/settings/Settings.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ Please see LICENSE files in the repository root for full details.
88
*/
99

1010
import React, { type ReactNode } from "react";
11-
import { UNSTABLE_MSC4133_EXTENDED_PROFILES } from "matrix-js-sdk/src/matrix";
11+
import { STABLE_MSC4133_EXTENDED_PROFILES, UNSTABLE_MSC4133_EXTENDED_PROFILES } from "matrix-js-sdk/src/matrix";
1212

1313
import { type MediaPreviewConfig } from "../@types/media_preview.ts";
1414
// Import i18n.tsx instead of languageHandler to avoid circular deps
@@ -844,7 +844,7 @@ export const SETTINGS: Settings = {
844844
controller: new ServerSupportUnstableFeatureController(
845845
"userTimezonePublish",
846846
defaultWatchManager,
847-
[[UNSTABLE_MSC4133_EXTENDED_PROFILES]],
847+
[[UNSTABLE_MSC4133_EXTENDED_PROFILES], [STABLE_MSC4133_EXTENDED_PROFILES]],
848848
undefined,
849849
_td("labs|extended_profiles_msc_support"),
850850
),

src/stores/RoomViewStore.tsx

Lines changed: 19 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ import { type MatrixDispatcher } from "../dispatcher/dispatcher";
2626
import { MatrixClientPeg } from "../MatrixClientPeg";
2727
import Modal from "../Modal";
2828
import { _t } from "../languageHandler";
29-
import { getCachedRoomIDForAlias, storeRoomAliasInCache } from "../RoomAliasCache";
29+
import { getCachedRoomIdForAlias, storeRoomAliasInCache } from "../RoomAliasCache";
3030
import { Action } from "../dispatcher/actions";
3131
import { retry } from "../utils/promise";
3232
import { TimelineRenderingType } from "../contexts/RoomContext";
@@ -438,17 +438,24 @@ export class RoomViewStore extends EventEmitter {
438438
action: Action.JoinRoom,
439439
roomId: payload.room_id,
440440
metricsTrigger: payload.metricsTrigger as JoinRoomPayload["metricsTrigger"],
441+
canAskToJoin: SettingsStore.getValue("feature_ask_to_join"),
441442
});
442443
}
443444

444445
if (room) {
445446
await setMarkedUnreadState(room, MatrixClientPeg.safeGet(), false);
446447
}
447448
} else if (payload.room_alias) {
449+
let roomId: string;
450+
let viaServers: string[] | undefined;
451+
448452
// Try the room alias to room ID navigation cache first to avoid
449453
// blocking room navigation on the homeserver.
450-
let roomId = getCachedRoomIDForAlias(payload.room_alias);
451-
if (!roomId) {
454+
const cachedResult = getCachedRoomIdForAlias(payload.room_alias);
455+
if (cachedResult) {
456+
roomId = cachedResult.roomId;
457+
viaServers = cachedResult.viaServers;
458+
} else {
452459
// Room alias cache miss, so let's ask the homeserver. Resolve the alias
453460
// and then do a second dispatch with the room ID acquired.
454461
this.setState({
@@ -467,8 +474,9 @@ export class RoomViewStore extends EventEmitter {
467474
});
468475
try {
469476
const result = await MatrixClientPeg.safeGet().getRoomIdForAlias(payload.room_alias);
470-
storeRoomAliasInCache(payload.room_alias, result.room_id);
477+
storeRoomAliasInCache(payload.room_alias, result.room_id, result.servers);
471478
roomId = result.room_id;
479+
viaServers = result.servers;
472480
} catch (err) {
473481
logger.error("RVS failed to get room id for alias: ", err);
474482
this.dis?.dispatch<ViewRoomErrorPayload>({
@@ -485,6 +493,7 @@ export class RoomViewStore extends EventEmitter {
485493
this.dis?.dispatch({
486494
...payload,
487495
room_id: roomId,
496+
via_servers: viaServers,
488497
});
489498
}
490499
}
@@ -509,12 +518,13 @@ export class RoomViewStore extends EventEmitter {
509518
joining: true,
510519
});
511520

512-
// take a copy of roomAlias & roomId as they may change by the time the join is complete
513-
const { roomAlias, roomId } = this.state;
514-
const address = payload.roomId || roomAlias || roomId!;
521+
// take a copy of roomAlias, roomId & viaServers as they may change by the time the join is complete
522+
const { roomAlias, roomId = payload.roomId, viaServers = [] } = this.state;
523+
// prefer the room alias if we have one as it allows joining over federation even with no viaServers
524+
const address = roomAlias || roomId!;
515525

516526
const joinOpts: IJoinRoomOpts = {
517-
viaServers: this.state.viaServers || [],
527+
viaServers,
518528
...(payload.opts ?? {}),
519529
};
520530
if (SettingsStore.getValue("feature_share_history_on_invite")) {
@@ -547,7 +557,7 @@ export class RoomViewStore extends EventEmitter {
547557
canAskToJoin: payload.canAskToJoin,
548558
});
549559

550-
if (payload.canAskToJoin) {
560+
if (payload.canAskToJoin && err instanceof MatrixError && err.httpStatus === 403) {
551561
this.dis?.dispatch({ action: Action.PromptAskToJoin });
552562
}
553563
}

src/stores/spaces/SpaceStore.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ import {
4949
UPDATE_SUGGESTED_ROOMS,
5050
UPDATE_TOP_LEVEL_SPACES,
5151
} from ".";
52-
import { getCachedRoomIDForAlias } from "../../RoomAliasCache";
52+
import { getCachedRoomIdForAlias } from "../../RoomAliasCache";
5353
import { EffectiveMembership, getEffectiveMembership } from "../../utils/membership";
5454
import {
5555
flattenSpaceHierarchyWithCache,
@@ -1249,7 +1249,8 @@ export class SpaceStoreClass extends AsyncStoreWithClient<EmptyObject> {
12491249
let roomId = payload.room_id;
12501250

12511251
if (payload.room_alias && !roomId) {
1252-
roomId = getCachedRoomIDForAlias(payload.room_alias);
1252+
const result = getCachedRoomIdForAlias(payload.room_alias);
1253+
if (result) roomId = result.roomId;
12531254
}
12541255

12551256
if (!roomId) return; // we'll get re-fired with the room ID shortly

test/unit-tests/modules/Navigation-test.ts

Lines changed: 10 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -5,37 +5,35 @@ SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Com
55
Please see LICENSE files in the repository root for full details.
66
*/
77

8-
import { mocked } from "jest-mock";
9-
108
import * as navigator from "../../../src/utils/permalinks/navigator";
119
import { NavigationApi } from "../../../src/modules/Navigation.ts";
12-
import { stubClient } from "../../test-utils";
1310
import defaultDispatcher from "../../../src/dispatcher/dispatcher.ts";
1411

1512
describe("NavigationApi", () => {
1613
const api = new NavigationApi();
1714

1815
describe("toMatrixToLink", () => {
19-
it("should call navigateToPermalink with the correct parameters", async () => {
20-
const link = "https://matrix.to/#/!roomId:server.com";
16+
it.each([
17+
["roomId", "https://matrix.to/#/!roomId:server.com"],
18+
["roomAlias", "https://matrix.to/#/#alias:server.com"],
19+
["user", "https://matrix.to/#/@user:server.com"],
20+
])("should call navigateToPermalink with the correct parameters for %s", async (_type, link) => {
2121
const spy = jest.spyOn(navigator, "navigateToPermalink");
2222

2323
await api.toMatrixToLink(link);
2424
expect(spy).toHaveBeenCalledWith(link);
2525
});
2626

27-
it("should resolve the room alias to a room id when join=true", async () => {
28-
const cli = stubClient();
29-
mocked(cli.getRoomIdForAlias).mockResolvedValue({ room_id: "!roomId:server.com", servers: [] });
30-
31-
const link = "https://matrix.to/#/#alias:server.com";
27+
it("should set auto_join to true when join=true", async () => {
28+
const link = "https://matrix.to/#/#alias:server.com?via=server.com";
3229
const spy = jest.spyOn(defaultDispatcher, "dispatch");
3330

3431
await api.toMatrixToLink(link, true);
3532
expect(spy).toHaveBeenCalledWith(
3633
expect.objectContaining({
37-
action: "join_room",
38-
roomId: "!roomId:server.com",
34+
action: "view_room",
35+
room_alias: "#alias:server.com",
36+
auto_join: true,
3937
}),
4038
);
4139
});

test/unit-tests/modules/ProxiedModuleApi-test.tsx

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import defaultDispatcher from "../../../src/dispatcher/dispatcher";
2424
import { Action } from "../../../src/dispatcher/actions";
2525
import WidgetStore, { type IApp } from "../../../src/stores/WidgetStore";
2626
import { Container, WidgetLayoutStore } from "../../../src/stores/widgets/WidgetLayoutStore";
27+
import * as navigator from "../../../src/utils/permalinks/navigator.ts";
2728

2829
describe("ProxiedApiModule", () => {
2930
afterEach(() => {
@@ -361,4 +362,30 @@ describe("ProxiedApiModule", () => {
361362
expect(WidgetLayoutStore.instance.moveToContainer).toHaveBeenCalledWith(room, app, Container.Top);
362363
});
363364
});
365+
366+
describe("navigatePermalink", () => {
367+
const api = new ProxiedModuleApi();
368+
369+
it("should call navigateToPermalink with the correct parameters", async () => {
370+
const link = "https://matrix.to/#/!roomId:server.com";
371+
const spy = jest.spyOn(navigator, "navigateToPermalink");
372+
373+
await api.navigatePermalink(link);
374+
expect(spy).toHaveBeenCalledWith(link);
375+
});
376+
377+
it("should set auto_join to true when join=true", async () => {
378+
const link = "https://matrix.to/#/#alias:server.com";
379+
const spy = jest.spyOn(defaultDispatcher, "dispatch");
380+
381+
await api.navigatePermalink(link, true);
382+
expect(spy).toHaveBeenCalledWith(
383+
expect.objectContaining({
384+
action: "view_room",
385+
room_alias: "#alias:server.com",
386+
auto_join: true,
387+
}),
388+
);
389+
});
390+
});
364391
});

0 commit comments

Comments
 (0)