forked from element-hq/element-web
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathRoomUpgrade.ts
More file actions
147 lines (129 loc) · 4.99 KB
/
Copy pathRoomUpgrade.ts
File metadata and controls
147 lines (129 loc) · 4.99 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
/*
Copyright 2024 New Vector Ltd.
Copyright 2021 The Matrix.org Foundation C.I.C.
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
Please see LICENSE files in the repository root for full details.
*/
import { type Room, EventType, ClientEvent, type MatrixClient } from "matrix-js-sdk/src/matrix";
import { KnownMembership } from "matrix-js-sdk/src/types";
import { logger } from "matrix-js-sdk/src/logger";
import { inviteUsersToRoom } from "../RoomInvite";
import Modal, { type IHandle } from "../Modal";
import { _t } from "../languageHandler";
import ErrorDialog from "../components/views/dialogs/ErrorDialog";
import SpaceStore from "../stores/spaces/SpaceStore";
import Spinner from "../components/views/elements/Spinner";
interface IProgress {
roomUpgraded: boolean;
roomSynced?: boolean;
inviteUsersProgress?: number;
inviteUsersTotal: number;
updateSpacesProgress?: number;
updateSpacesTotal: number;
}
export async function awaitRoomDownSync(cli: MatrixClient, roomId: string): Promise<Room> {
const room = cli.getRoom(roomId);
if (room) return room; // already have the room
return new Promise<Room>((resolve) => {
// We have to wait for the js-sdk to give us the room back so
// we can more effectively abuse the MultiInviter behaviour
// which heavily relies on the Room object being available.
const checkForRoomFn = (room: Room): void => {
if (room.roomId !== roomId) return;
resolve(room);
cli.off(ClientEvent.Room, checkForRoomFn);
};
cli.on(ClientEvent.Room, checkForRoomFn);
});
}
export async function upgradeRoom(
room: Room,
targetVersion: string,
inviteUsers = false,
handleError = true,
updateSpaces = true,
awaitRoom = false,
progressCallback?: (progress: IProgress) => void,
): Promise<string> {
const cli = room.client;
let spinnerModal: IHandle<any> | undefined;
if (!progressCallback) {
spinnerModal = Modal.createDialog(Spinner, undefined, "mx_Dialog_spinner");
}
let toInvite: string[] = [];
if (inviteUsers) {
toInvite = [
...room.getMembersWithMembership(KnownMembership.Join),
...room.getMembersWithMembership(KnownMembership.Invite),
]
.map((m) => m.userId)
.filter((m) => m !== cli.getUserId());
}
let parentsToRelink: Room[] = [];
if (updateSpaces) {
parentsToRelink = Array.from(SpaceStore.instance.getKnownParents(room.roomId))
.map((roomId) => cli.getRoom(roomId))
.filter((parent) =>
parent?.currentState.maySendStateEvent(EventType.SpaceChild, cli.getUserId()!),
) as Room[];
}
const progress: IProgress = {
roomUpgraded: false,
roomSynced: awaitRoom || inviteUsers ? false : undefined,
inviteUsersProgress: inviteUsers ? 0 : undefined,
inviteUsersTotal: toInvite.length,
updateSpacesProgress: updateSpaces ? 0 : undefined,
updateSpacesTotal: parentsToRelink.length,
};
progressCallback?.(progress);
let newRoomId: string;
try {
({ replacement_room: newRoomId } = await cli.upgradeRoom(room.roomId, targetVersion));
} catch (e) {
if (!handleError) throw e;
logger.error(e);
Modal.createDialog(ErrorDialog, {
title: _t("room|upgrade_error_title"),
description: _t("room|upgrade_error_description"),
});
throw e;
}
progress.roomUpgraded = true;
progressCallback?.(progress);
if (awaitRoom || inviteUsers) {
await awaitRoomDownSync(room.client, newRoomId);
progress.roomSynced = true;
progressCallback?.(progress);
}
if (toInvite.length > 0) {
// Errors are handled internally to this function
await inviteUsersToRoom(cli, newRoomId, toInvite, () => {
progress.inviteUsersProgress!++;
progressCallback?.(progress);
});
}
if (parentsToRelink.length > 0) {
try {
for (const parent of parentsToRelink) {
const currentEv = parent.currentState.getStateEvents(EventType.SpaceChild, room.roomId);
await cli.sendStateEvent(
parent.roomId,
EventType.SpaceChild,
{
...(currentEv?.getContent() || {}), // copy existing attributes like suggested
via: [cli.getDomain()!],
},
newRoomId,
);
await cli.sendStateEvent(parent.roomId, EventType.SpaceChild, {}, room.roomId);
progress.updateSpacesProgress!++;
progressCallback?.(progress);
}
} catch (e) {
// These errors are not critical to the room upgrade itself
logger.warn("Failed to update parent spaces during room upgrade", e);
}
}
spinnerModal?.close();
return newRoomId;
}