forked from element-hq/element-web
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathleave-behaviour.ts
More file actions
190 lines (173 loc) · 7.26 KB
/
Copy pathleave-behaviour.ts
File metadata and controls
190 lines (173 loc) · 7.26 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
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
/*
Copyright 2024 New Vector Ltd.
Copyright 2022 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 { sleep } from "matrix-js-sdk/src/utils";
import React, { type ReactNode } from "react";
import { EventStatus, MatrixEventEvent, type Room, type MatrixClient, MatrixError } from "matrix-js-sdk/src/matrix";
import Modal, { type IHandle } from "../Modal";
import Spinner from "../components/views/elements/Spinner";
import { _t } from "../languageHandler";
import ErrorDialog from "../components/views/dialogs/ErrorDialog";
import { isMetaSpace } from "../stores/spaces";
import SpaceStore from "../stores/spaces/SpaceStore";
import dis from "../dispatcher/dispatcher";
import { type ViewRoomPayload } from "../dispatcher/payloads/ViewRoomPayload";
import { Action } from "../dispatcher/actions";
import { type ViewHomePagePayload } from "../dispatcher/payloads/ViewHomePagePayload";
import LeaveSpaceDialog from "../components/views/dialogs/LeaveSpaceDialog";
import { type AfterLeaveRoomPayload } from "../dispatcher/payloads/AfterLeaveRoomPayload";
import { bulkSpaceBehaviour } from "./space";
import { SdkContextClass } from "../contexts/SDKContext";
import SettingsStore from "../settings/SettingsStore";
export async function leaveRoomBehaviour(
matrixClient: MatrixClient,
roomId: string,
retry = true,
spinner = true,
): Promise<void> {
let spinnerModal: IHandle<any> | undefined;
if (spinner) {
spinnerModal = Modal.createDialog(Spinner, undefined, "mx_Dialog_spinner");
}
let leavingAllVersions = true;
const history = matrixClient.getRoomUpgradeHistory(
roomId,
false,
SettingsStore.getValue("feature_dynamic_room_predecessors"),
);
if (history && history.length > 0) {
const currentRoom = history[history.length - 1];
if (currentRoom.roomId !== roomId) {
// The user is trying to leave an older version of the room. Let them through
// without making them leave the current version of the room.
leavingAllVersions = false;
}
}
const room = matrixClient.getRoom(roomId);
// should not encounter this
if (!room) {
throw new Error(`Expected to find room for id ${roomId}`);
}
// await any queued messages being sent so that they do not fail
await Promise.all(
room
.getPendingEvents()
.filter((ev) => {
return [EventStatus.QUEUED, EventStatus.ENCRYPTING, EventStatus.SENDING].includes(ev.status!);
})
.map(
(ev) =>
new Promise<void>((resolve, reject) => {
const handler = (): void => {
if (ev.status === EventStatus.NOT_SENT) {
spinnerModal?.close();
reject(ev.error);
}
if (!ev.status || ev.status === EventStatus.SENT) {
ev.off(MatrixEventEvent.Status, handler);
resolve();
}
};
ev.on(MatrixEventEvent.Status, handler);
}),
),
);
let results: { [roomId: string]: Error | MatrixError | null } = {};
if (!leavingAllVersions) {
try {
await matrixClient.leave(roomId);
} catch (e) {
if (e instanceof MatrixError) {
const message = e.data.error || _t("room|leave_unexpected_error");
results[roomId] = Object.assign(new Error(message), { errcode: e.data.errcode, data: e.data });
} else if (e instanceof Error) {
results[roomId] = e;
} else {
results[roomId] = new Error("Failed to leave room for unknown causes");
}
}
} else {
results = await matrixClient.leaveRoomChain(roomId, retry);
}
if (retry) {
const limitExceededError = Object.values(results).find(
(e) => (e as MatrixError)?.errcode === "M_LIMIT_EXCEEDED",
) as MatrixError;
if (limitExceededError) {
await sleep(limitExceededError.data.retry_after_ms ?? 100);
return leaveRoomBehaviour(matrixClient, roomId, false, false);
}
}
spinnerModal?.close();
const errors = Object.entries(results).filter((r) => !!r[1]);
if (errors.length > 0) {
const messages: ReactNode[] = [];
for (const roomErr of errors) {
const err = roomErr[1] as MatrixError; // [0] is the roomId
let message = _t("room|leave_unexpected_error");
if (err?.errcode && err.message) {
if (err.errcode === "M_CANNOT_LEAVE_SERVER_NOTICE_ROOM") {
Modal.createDialog(ErrorDialog, {
title: _t("room|leave_server_notices_title"),
description: _t("room|leave_server_notices_description"),
});
return;
}
message = results[roomId]!.message;
}
messages.push(message, React.createElement("BR")); // createElement to avoid using a tsx file in utils
}
Modal.createDialog(ErrorDialog, {
title: _t("room|leave_error_title"),
description: messages,
});
return;
}
if (SdkContextClass.instance.roomViewStore.getRoomId() === roomId) {
// We were viewing the room that was just left. In order to avoid
// accidentally viewing the next room in the list and clearing its
// notifications, switch to a neutral ground such as the home page or
// space landing page.
if (isMetaSpace(SpaceStore.instance.activeSpace)) {
dis.dispatch<ViewHomePagePayload>({ action: Action.ViewHomePage });
} else if (SpaceStore.instance.activeSpace === roomId) {
// View the parent space, if there is one
const parent = SpaceStore.instance.getCanonicalParent(roomId);
if (parent !== null) {
dis.dispatch<ViewRoomPayload>({
action: Action.ViewRoom,
room_id: parent.roomId,
metricsTrigger: undefined, // other
});
} else {
dis.dispatch<ViewHomePagePayload>({ action: Action.ViewHomePage });
}
} else {
dis.dispatch<ViewRoomPayload>({
action: Action.ViewRoom,
room_id: SpaceStore.instance.activeSpace,
metricsTrigger: undefined, // other
});
}
}
}
export const leaveSpace = (space: Room): void => {
const { finished } = Modal.createDialog(
LeaveSpaceDialog,
{
space,
},
"mx_LeaveSpaceDialog_wrapper",
);
finished.then(async ([leave, rooms]) => {
if (!leave) return;
await bulkSpaceBehaviour(space, rooms!, (room) => leaveRoomBehaviour(space.client, room.roomId));
dis.dispatch<AfterLeaveRoomPayload>({
action: Action.AfterLeaveRoom,
room_id: space.roomId,
});
});
};