Skip to content
This repository was archived by the owner on Sep 11, 2024. It is now read-only.

Commit 2a3c483

Browse files
authored
Merge pull request #6919 from matrix-org/t3chguy/fix/19146
2 parents 1f7d07a + 2483f1d commit 2a3c483

File tree

5 files changed

+188
-70
lines changed

5 files changed

+188
-70
lines changed

res/css/views/dialogs/_RoomUpgradeWarningDialog.scss

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
Copyright 2019 The Matrix.org Foundation C.I.C.
2+
Copyright 2019 - 2021 The Matrix.org Foundation C.I.C.
33
44
Licensed under the Apache License, Version 2.0 (the "License");
55
you may not use this file except in compliance with the License.
@@ -17,6 +17,22 @@ limitations under the License.
1717
.mx_RoomUpgradeWarningDialog {
1818
max-width: 38vw;
1919
width: 38vw;
20+
21+
.mx_RoomUpgradeWarningDialog_progress {
22+
.mx_ProgressBar {
23+
height: 8px;
24+
width: 100%;
25+
26+
@mixin ProgressBarBorderRadius 8px;
27+
}
28+
29+
.mx_RoomUpgradeWarningDialog_progressText {
30+
margin-top: 8px;
31+
font-size: $font-15px;
32+
line-height: $font-24px;
33+
color: $primary-content;
34+
}
35+
}
2036
}
2137

2238
.mx_RoomUpgradeWarningDialog .mx_SettingsFlag {

src/components/views/dialogs/RoomUpgradeWarningDialog.tsx

Lines changed: 45 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -28,15 +28,25 @@ import { IDialogProps } from "./IDialogProps";
2828
import BugReportDialog from './BugReportDialog';
2929
import BaseDialog from "./BaseDialog";
3030
import DialogButtons from "../elements/DialogButtons";
31+
import ProgressBar from "../elements/ProgressBar";
32+
33+
export interface IFinishedOpts {
34+
continue: boolean;
35+
invite: boolean;
36+
}
3137

3238
interface IProps extends IDialogProps {
3339
roomId: string;
3440
targetVersion: string;
3541
description?: ReactNode;
42+
doUpgrade?(opts: IFinishedOpts, fn: (progressText: string, progress: number, total: number) => void): Promise<void>;
3643
}
3744

3845
interface IState {
3946
inviteUsersToNewRoom: boolean;
47+
progressText?: string;
48+
progress?: number;
49+
total?: number;
4050
}
4151

4252
@replaceableComponent("views.dialogs.RoomUpgradeWarningDialog")
@@ -50,15 +60,30 @@ export default class RoomUpgradeWarningDialog extends React.Component<IProps, IS
5060
const room = MatrixClientPeg.get().getRoom(this.props.roomId);
5161
const joinRules = room?.currentState.getStateEvents(EventType.RoomJoinRules, "");
5262
this.isPrivate = joinRules?.getContent()['join_rule'] !== JoinRule.Public ?? true;
53-
this.currentVersion = room?.getVersion() || "1";
63+
this.currentVersion = room?.getVersion();
5464

5565
this.state = {
5666
inviteUsersToNewRoom: true,
5767
};
5868
}
5969

70+
private onProgressCallback = (progressText: string, progress: number, total: number): void => {
71+
this.setState({ progressText, progress, total });
72+
};
73+
6074
private onContinue = () => {
61-
this.props.onFinished({ continue: true, invite: this.isPrivate && this.state.inviteUsersToNewRoom });
75+
const opts = {
76+
continue: true,
77+
invite: this.isPrivate && this.state.inviteUsersToNewRoom,
78+
};
79+
80+
if (this.props.doUpgrade) {
81+
this.props.doUpgrade(opts, this.onProgressCallback).then(() => {
82+
this.props.onFinished(opts);
83+
});
84+
} else {
85+
this.props.onFinished(opts);
86+
}
6287
};
6388

6489
private onCancel = () => {
@@ -118,6 +143,23 @@ export default class RoomUpgradeWarningDialog extends React.Component<IProps, IS
118143
);
119144
}
120145

146+
let footer: JSX.Element;
147+
if (this.state.progressText) {
148+
footer = <span className="mx_RoomUpgradeWarningDialog_progress">
149+
<ProgressBar value={this.state.progress} max={this.state.total} />
150+
<div className="mx_RoomUpgradeWarningDialog_progressText">
151+
{ this.state.progressText }
152+
</div>
153+
</span>;
154+
} else {
155+
footer = <DialogButtons
156+
primaryButton={_t("Upgrade")}
157+
onPrimaryButtonClick={this.onContinue}
158+
cancelButton={_t("Cancel")}
159+
onCancel={this.onCancel}
160+
/>;
161+
}
162+
121163
return (
122164
<BaseDialog
123165
className='mx_RoomUpgradeWarningDialog'
@@ -154,12 +196,7 @@ export default class RoomUpgradeWarningDialog extends React.Component<IProps, IS
154196
</p>
155197
{ inviteToggle }
156198
</div>
157-
<DialogButtons
158-
primaryButton={_t("Upgrade")}
159-
onPrimaryButtonClick={this.onContinue}
160-
cancelButton={_t("Cancel")}
161-
onCancel={this.onCancel}
162-
/>
199+
{ footer }
163200
</BaseDialog>
164201
);
165202
}

src/components/views/settings/JoinRuleSettings.tsx

Lines changed: 59 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,7 @@ import SpaceStore from "../../../stores/SpaceStore";
2727
import { MatrixClientPeg } from "../../../MatrixClientPeg";
2828
import Modal from "../../../Modal";
2929
import ManageRestrictedJoinRuleDialog from "../dialogs/ManageRestrictedJoinRuleDialog";
30-
import RoomUpgradeWarningDialog from "../dialogs/RoomUpgradeWarningDialog";
31-
import QuestionDialog from "../dialogs/QuestionDialog";
30+
import RoomUpgradeWarningDialog, { IFinishedOpts } from "../dialogs/RoomUpgradeWarningDialog";
3231
import { upgradeRoom } from "../../../utils/RoomUpgrade";
3332
import { arrayHasDiff } from "../../../utils/arrays";
3433
import { useLocalEcho } from "../../../hooks/useLocalEcho";
@@ -210,47 +209,70 @@ const JoinRuleSettings = ({ room, promptUpgrade, onError, beforeChange, closeSet
210209
// Block this action on a room upgrade otherwise it'd make their room unjoinable
211210
const targetVersion = preferredRestrictionVersion;
212211

213-
const modal = Modal.createTrackedDialog('Restricted join rule upgrade', '', RoomUpgradeWarningDialog, {
214-
roomId: room.roomId,
215-
targetVersion,
216-
description: _t("This upgrade will allow members of selected spaces " +
217-
"access to this room without an invite."),
218-
});
219-
220-
const [resp] = await modal.finished;
221-
if (!resp?.continue) return;
222-
212+
let warning: JSX.Element;
223213
const userId = cli.getUserId();
224214
const unableToUpdateSomeParents = Array.from(SpaceStore.instance.getKnownParents(room.roomId))
225215
.some(roomId => !cli.getRoom(roomId)?.currentState.maySendStateEvent(EventType.SpaceChild, userId));
226216
if (unableToUpdateSomeParents) {
227-
const modal = Modal.createTrackedDialog<[boolean]>('Parent relink warning', '', QuestionDialog, {
228-
title: _t("Before you upgrade"),
229-
description: (
230-
<div>{ _t("This room is in some spaces you’re not an admin of. " +
231-
"In those spaces, the old room will still be shown, " +
232-
"but people will be prompted to join the new one.") }</div>
233-
),
234-
hasCancelButton: true,
235-
button: _t("Upgrade anyway"),
236-
danger: true,
237-
});
238-
239-
const [shouldUpgrade] = await modal.finished;
240-
if (!shouldUpgrade) return;
217+
warning = <b>
218+
{ _t("This room is in some spaces you’re not an admin of. " +
219+
"In those spaces, the old room will still be shown, " +
220+
"but people will be prompted to join the new one.") }
221+
</b>;
241222
}
242223

243-
const roomId = await upgradeRoom(room, targetVersion, resp.invite, true, true, true);
244-
closeSettingsFn();
245-
// switch to the new room in the background
246-
dis.dispatch({
247-
action: "view_room",
248-
room_id: roomId,
249-
});
250-
// open new settings on this tab
251-
dis.dispatch({
252-
action: "open_room_settings",
253-
initial_tab_id: ROOM_SECURITY_TAB,
224+
Modal.createTrackedDialog('Restricted join rule upgrade', '', RoomUpgradeWarningDialog, {
225+
roomId: room.roomId,
226+
targetVersion,
227+
description: <>
228+
{ _t("This upgrade will allow members of selected spaces " +
229+
"access to this room without an invite.") }
230+
{ warning }
231+
</>,
232+
doUpgrade: async (
233+
opts: IFinishedOpts,
234+
fn: (progressText: string, progress: number, total: number) => void,
235+
): Promise<void> => {
236+
const roomId = await upgradeRoom(
237+
room,
238+
targetVersion,
239+
opts.invite,
240+
true,
241+
true,
242+
true,
243+
progress => {
244+
const total = 2 + progress.updateSpacesTotal + progress.inviteUsersTotal;
245+
if (!progress.roomUpgraded) {
246+
fn(_t("Upgrading room"), 0, total);
247+
} else if (!progress.roomSynced) {
248+
fn(_t("Loading new room"), 1, total);
249+
} else if (progress.inviteUsersProgress < progress.inviteUsersTotal) {
250+
fn(_t("Sending invites... (%(progress)s out of %(count)s)", {
251+
progress: progress.inviteUsersProgress,
252+
count: progress.inviteUsersTotal,
253+
}), 2 + progress.inviteUsersProgress, total);
254+
} else if (progress.updateSpacesProgress < progress.updateSpacesTotal) {
255+
fn(_t("Updating spaces... (%(progress)s out of %(count)s)", {
256+
progress: progress.updateSpacesProgress,
257+
count: progress.updateSpacesTotal,
258+
}), 2 + progress.inviteUsersProgress + progress.updateSpacesProgress, total);
259+
}
260+
},
261+
);
262+
closeSettingsFn();
263+
264+
// switch to the new room in the background
265+
dis.dispatch({
266+
action: "view_room",
267+
room_id: roomId,
268+
});
269+
270+
// open new settings on this tab
271+
dis.dispatch({
272+
action: "open_room_settings",
273+
initial_tab_id: ROOM_SECURITY_TAB,
274+
});
275+
},
254276
});
255277

256278
return;

src/i18n/strings/en_EN.json

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1167,10 +1167,14 @@
11671167
"Anyone in <spaceName/> can find and join. You can select other spaces too.": "Anyone in <spaceName/> can find and join. You can select other spaces too.",
11681168
"Anyone in a space can find and join. You can select multiple spaces.": "Anyone in a space can find and join. You can select multiple spaces.",
11691169
"Space members": "Space members",
1170-
"This upgrade will allow members of selected spaces access to this room without an invite.": "This upgrade will allow members of selected spaces access to this room without an invite.",
1171-
"Before you upgrade": "Before you upgrade",
11721170
"This room is in some spaces you’re not an admin of. In those spaces, the old room will still be shown, but people will be prompted to join the new one.": "This room is in some spaces you’re not an admin of. In those spaces, the old room will still be shown, but people will be prompted to join the new one.",
1173-
"Upgrade anyway": "Upgrade anyway",
1171+
"This upgrade will allow members of selected spaces access to this room without an invite.": "This upgrade will allow members of selected spaces access to this room without an invite.",
1172+
"Upgrading room": "Upgrading room",
1173+
"Loading new room": "Loading new room",
1174+
"Sending invites... (%(progress)s out of %(count)s)|other": "Sending invites... (%(progress)s out of %(count)s)",
1175+
"Sending invites... (%(progress)s out of %(count)s)|one": "Sending invite...",
1176+
"Updating spaces... (%(progress)s out of %(count)s)|other": "Updating spaces... (%(progress)s out of %(count)s)",
1177+
"Updating spaces... (%(progress)s out of %(count)s)|one": "Updating space...",
11741178
"Message layout": "Message layout",
11751179
"IRC": "IRC",
11761180
"Modern": "Modern",

src/utils/RoomUpgrade.ts

Lines changed: 60 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -18,22 +18,60 @@ import { Room } from "matrix-js-sdk/src/models/room";
1818
import { EventType } from "matrix-js-sdk/src/@types/event";
1919

2020
import { inviteUsersToRoom } from "../RoomInvite";
21-
import Modal from "../Modal";
21+
import Modal, { IHandle } from "../Modal";
2222
import { _t } from "../languageHandler";
2323
import ErrorDialog from "../components/views/dialogs/ErrorDialog";
2424
import SpaceStore from "../stores/SpaceStore";
2525
import Spinner from "../components/views/elements/Spinner";
2626

27+
interface IProgress {
28+
roomUpgraded: boolean;
29+
roomSynced?: boolean;
30+
inviteUsersProgress?: number;
31+
inviteUsersTotal: number;
32+
updateSpacesProgress?: number;
33+
updateSpacesTotal: number;
34+
}
35+
2736
export async function upgradeRoom(
2837
room: Room,
2938
targetVersion: string,
3039
inviteUsers = false,
3140
handleError = true,
3241
updateSpaces = true,
3342
awaitRoom = false,
43+
progressCallback?: (progress: IProgress) => void,
3444
): Promise<string> {
3545
const cli = room.client;
36-
const spinnerModal = Modal.createDialog(Spinner, null, "mx_Dialog_spinner");
46+
let spinnerModal: IHandle<any>;
47+
if (!progressCallback) {
48+
spinnerModal = Modal.createDialog(Spinner, null, "mx_Dialog_spinner");
49+
}
50+
51+
let toInvite: string[];
52+
if (inviteUsers) {
53+
toInvite = [
54+
...room.getMembersWithMembership("join"),
55+
...room.getMembersWithMembership("invite"),
56+
].map(m => m.userId).filter(m => m !== cli.getUserId());
57+
}
58+
59+
let parentsToRelink: Room[];
60+
if (updateSpaces) {
61+
parentsToRelink = Array.from(SpaceStore.instance.getKnownParents(room.roomId))
62+
.map(roomId => cli.getRoom(roomId))
63+
.filter(parent => parent?.currentState.maySendStateEvent(EventType.SpaceChild, cli.getUserId()));
64+
}
65+
66+
const progress: IProgress = {
67+
roomUpgraded: false,
68+
roomSynced: (awaitRoom || inviteUsers) ? false : undefined,
69+
inviteUsersProgress: inviteUsers ? 0 : undefined,
70+
inviteUsersTotal: toInvite.length,
71+
updateSpacesProgress: updateSpaces ? 0 : undefined,
72+
updateSpacesTotal: parentsToRelink.length,
73+
};
74+
progressCallback?.(progress);
3775

3876
let newRoomId: string;
3977
try {
@@ -49,6 +87,9 @@ export async function upgradeRoom(
4987
throw e;
5088
}
5189

90+
progress.roomUpgraded = true;
91+
progressCallback?.(progress);
92+
5293
if (awaitRoom || inviteUsers) {
5394
await new Promise<void>(resolve => {
5495
// already have the room
@@ -67,40 +108,38 @@ export async function upgradeRoom(
67108
};
68109
cli.on("Room", checkForRoomFn);
69110
});
70-
}
71111

72-
if (inviteUsers) {
73-
const toInvite = [
74-
...room.getMembersWithMembership("join"),
75-
...room.getMembersWithMembership("invite"),
76-
].map(m => m.userId).filter(m => m !== cli.getUserId());
112+
progress.roomSynced = true;
113+
progressCallback?.(progress);
114+
}
77115

78-
if (toInvite.length > 0) {
79-
// Errors are handled internally to this function
80-
await inviteUsersToRoom(newRoomId, toInvite);
81-
}
116+
if (toInvite.length > 0) {
117+
// Errors are handled internally to this function
118+
await inviteUsersToRoom(newRoomId, toInvite, () => {
119+
progress.inviteUsersProgress++;
120+
progressCallback?.(progress);
121+
});
82122
}
83123

84-
if (updateSpaces) {
85-
const parents = SpaceStore.instance.getKnownParents(room.roomId);
124+
if (parentsToRelink.length > 0) {
86125
try {
87-
for (const parentId of parents) {
88-
const parent = cli.getRoom(parentId);
89-
if (!parent?.currentState.maySendStateEvent(EventType.SpaceChild, cli.getUserId())) continue;
90-
126+
for (const parent of parentsToRelink) {
91127
const currentEv = parent.currentState.getStateEvents(EventType.SpaceChild, room.roomId);
92-
await cli.sendStateEvent(parentId, EventType.SpaceChild, {
128+
await cli.sendStateEvent(parent.roomId, EventType.SpaceChild, {
93129
...(currentEv?.getContent() || {}), // copy existing attributes like suggested
94130
via: [cli.getDomain()],
95131
}, newRoomId);
96-
await cli.sendStateEvent(parentId, EventType.SpaceChild, {}, room.roomId);
132+
await cli.sendStateEvent(parent.roomId, EventType.SpaceChild, {}, room.roomId);
133+
134+
progress.updateSpacesProgress++;
135+
progressCallback?.(progress);
97136
}
98137
} catch (e) {
99138
// These errors are not critical to the room upgrade itself
100139
console.warn("Failed to update parent spaces during room upgrade", e);
101140
}
102141
}
103142

104-
spinnerModal.close();
143+
spinnerModal?.close();
105144
return newRoomId;
106145
}

0 commit comments

Comments
 (0)