Eliminate the use of MatrixClientPeg in utils (#10910)

This commit is contained in:
Michael Telatynski 2023-05-23 16:24:12 +01:00 committed by GitHub
parent a0c2676c38
commit 30429df948
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
108 changed files with 409 additions and 325 deletions

View file

@ -20,11 +20,10 @@ import { IWidget, IWidgetData } from "matrix-widget-api";
import { Room } from "matrix-js-sdk/src/models/room";
import { MatrixEvent } from "matrix-js-sdk/src/models/event";
import { logger } from "matrix-js-sdk/src/logger";
import { ClientEvent, RoomStateEvent } from "matrix-js-sdk/src/matrix";
import { ClientEvent, MatrixClient, RoomStateEvent } from "matrix-js-sdk/src/matrix";
import { CallType } from "matrix-js-sdk/src/webrtc/call";
import { randomString, randomLowercaseString, randomUppercaseString } from "matrix-js-sdk/src/randomstring";
import { MatrixClientPeg } from "../MatrixClientPeg";
import PlatformPeg from "../PlatformPeg";
import SdkConfig from "../SdkConfig";
import dis from "../dispatcher/dispatcher";
@ -55,19 +54,20 @@ export interface UserWidget extends Omit<IWidgetEvent, "content"> {
}
export default class WidgetUtils {
/* Returns true if user is able to send state events to modify widgets in this room
/**
* Returns true if user is able to send state events to modify widgets in this room
* (Does not apply to non-room-based / user widgets)
* @param client The matrix client of the logged-in user
* @param roomId -- The ID of the room to check
* @return Boolean -- true if the user can modify widgets in this room
* @throws Error -- specifies the error reason
*/
public static canUserModifyWidgets(roomId?: string): boolean {
public static canUserModifyWidgets(client: MatrixClient, roomId?: string): boolean {
if (!roomId) {
logger.warn("No room ID specified");
return false;
}
const client = MatrixClientPeg.get();
if (!client) {
logger.warn("User must be be logged in");
return false;
@ -79,7 +79,7 @@ export default class WidgetUtils {
return false;
}
const me = client.credentials.userId;
const me = client.getUserId();
if (!me) {
logger.warn("Failed to get user ID");
return false;
@ -97,6 +97,7 @@ export default class WidgetUtils {
// TODO: Generify the name of this function. It's not just scalar.
/**
* Returns true if specified url is a scalar URL, typically https://scalar.vector.im/api
* @param matrixClient The matrix client of the logged-in user
* @param {[type]} testUrlString URL to check
* @return {Boolean} True if specified URL is a scalar URL
*/
@ -138,13 +139,14 @@ export default class WidgetUtils {
* ID has been added as a user widget (ie. the accountData event
* arrives) or rejects after a timeout
*
* @param {string} widgetId The ID of the widget to wait for
* @param {boolean} add True to wait for the widget to be added,
* @param client The matrix client of the logged-in user
* @param widgetId The ID of the widget to wait for
* @param add True to wait for the widget to be added,
* false to wait for it to be deleted.
* @returns {Promise} that resolves when the widget is in the
* requested state according to the `add` param
*/
public static waitForUserWidget(widgetId: string, add: boolean): Promise<void> {
public static waitForUserWidget(client: MatrixClient, widgetId: string, add: boolean): Promise<void> {
return new Promise((resolve, reject) => {
// Tests an account data event, returning true if it's in the state
// we're waiting for it to be in
@ -157,25 +159,25 @@ export default class WidgetUtils {
}
}
const startingAccountDataEvent = MatrixClientPeg.get().getAccountData("m.widgets");
const startingAccountDataEvent = client.getAccountData("m.widgets");
if (eventInIntendedState(startingAccountDataEvent)) {
resolve();
return;
}
function onAccountData(ev: MatrixEvent): void {
const currentAccountDataEvent = MatrixClientPeg.get().getAccountData("m.widgets");
const currentAccountDataEvent = client.getAccountData("m.widgets");
if (eventInIntendedState(currentAccountDataEvent)) {
MatrixClientPeg.get().removeListener(ClientEvent.AccountData, onAccountData);
client.removeListener(ClientEvent.AccountData, onAccountData);
clearTimeout(timerId);
resolve();
}
}
const timerId = window.setTimeout(() => {
MatrixClientPeg.get().removeListener(ClientEvent.AccountData, onAccountData);
client.removeListener(ClientEvent.AccountData, onAccountData);
reject(new Error("Timed out waiting for widget ID " + widgetId + " to appear"));
}, WIDGET_WAIT_TIME);
MatrixClientPeg.get().on(ClientEvent.AccountData, onAccountData);
client.on(ClientEvent.AccountData, onAccountData);
});
}
@ -184,6 +186,7 @@ export default class WidgetUtils {
* ID has been added as a room widget in the given room (ie. the
* room state event arrives) or rejects after a timeout
*
* @param client The matrix client of the logged-in user
* @param {string} widgetId The ID of the widget to wait for
* @param {string} roomId The ID of the room to wait for the widget in
* @param {boolean} add True to wait for the widget to be added,
@ -191,7 +194,12 @@ export default class WidgetUtils {
* @returns {Promise} that resolves when the widget is in the
* requested state according to the `add` param
*/
public static waitForRoomWidget(widgetId: string, roomId: string, add: boolean): Promise<void> {
public static waitForRoomWidget(
client: MatrixClient,
widgetId: string,
roomId: string,
add: boolean,
): Promise<void> {
return new Promise((resolve, reject) => {
// Tests a list of state events, returning true if it's in the state
// we're waiting for it to be in
@ -206,7 +214,7 @@ export default class WidgetUtils {
}
}
const room = MatrixClientPeg.get().getRoom(roomId);
const room = client.getRoom(roomId);
// TODO: Enable support for m.widget event type (https://github.com/vector-im/element-web/issues/13111)
const startingWidgetEvents = room?.currentState.getStateEvents("im.vector.modular.widgets");
if (eventsInIntendedState(startingWidgetEvents)) {
@ -221,30 +229,30 @@ export default class WidgetUtils {
const currentWidgetEvents = room?.currentState.getStateEvents("im.vector.modular.widgets");
if (eventsInIntendedState(currentWidgetEvents)) {
MatrixClientPeg.get().removeListener(RoomStateEvent.Events, onRoomStateEvents);
client.removeListener(RoomStateEvent.Events, onRoomStateEvents);
clearTimeout(timerId);
resolve();
}
}
const timerId = window.setTimeout(() => {
MatrixClientPeg.get().removeListener(RoomStateEvent.Events, onRoomStateEvents);
client.removeListener(RoomStateEvent.Events, onRoomStateEvents);
reject(new Error("Timed out waiting for widget ID " + widgetId + " to appear"));
}, WIDGET_WAIT_TIME);
MatrixClientPeg.get().on(RoomStateEvent.Events, onRoomStateEvents);
client.on(RoomStateEvent.Events, onRoomStateEvents);
});
}
public static setUserWidget(
client: MatrixClient,
widgetId: string,
widgetType: WidgetType,
widgetUrl: string,
widgetName: string,
widgetData: IWidgetData,
): Promise<void> {
const client = MatrixClientPeg.get();
// Get the current widgets and clone them before we modify them, otherwise
// we'll modify the content of the old event.
const userWidgets = objectClone(WidgetUtils.getUserWidgets());
const userWidgets = objectClone(WidgetUtils.getUserWidgets(client));
// Delete existing widget with ID
try {
@ -284,7 +292,7 @@ export default class WidgetUtils {
return client
.setAccountData("m.widgets", userWidgets)
.then(() => {
return WidgetUtils.waitForUserWidget(widgetId, addingWidget);
return WidgetUtils.waitForUserWidget(client, widgetId, addingWidget);
})
.then(() => {
dis.dispatch({ action: "user_widget_updated" });
@ -292,6 +300,7 @@ export default class WidgetUtils {
}
public static setRoomWidget(
client: MatrixClient,
roomId: string,
widgetId: string,
widgetType?: WidgetType,
@ -318,20 +327,24 @@ export default class WidgetUtils {
content = {};
}
return WidgetUtils.setRoomWidgetContent(roomId, widgetId, content as IWidget);
return WidgetUtils.setRoomWidgetContent(client, roomId, widgetId, content as IWidget);
}
public static setRoomWidgetContent(roomId: string, widgetId: string, content: IWidget): Promise<void> {
public static setRoomWidgetContent(
client: MatrixClient,
roomId: string,
widgetId: string,
content: IWidget,
): Promise<void> {
const addingWidget = !!content.url;
WidgetEchoStore.setRoomWidgetEcho(roomId, widgetId, content);
const client = MatrixClientPeg.get();
// TODO: Enable support for m.widget event type (https://github.com/vector-im/element-web/issues/13111)
return client
.sendStateEvent(roomId, "im.vector.modular.widgets", content, widgetId)
.then(() => {
return WidgetUtils.waitForRoomWidget(widgetId, roomId, addingWidget);
return WidgetUtils.waitForRoomWidget(client, widgetId, roomId, addingWidget);
})
.finally(() => {
WidgetEchoStore.removeRoomWidgetEcho(roomId, widgetId);
@ -357,10 +370,10 @@ export default class WidgetUtils {
/**
* Get user specific widgets (not linked to a specific room)
* @param client The matrix client of the logged-in user
* @return {object} Event content object containing current / active user widgets
*/
public static getUserWidgets(): Record<string, UserWidget> {
const client = MatrixClientPeg.get();
public static getUserWidgets(client: MatrixClient | undefined): Record<string, UserWidget> {
if (!client) {
throw new Error("User not logged in");
}
@ -373,27 +386,30 @@ export default class WidgetUtils {
/**
* Get user specific widgets (not linked to a specific room) as an array
* @param client The matrix client of the logged-in user
* @return {[object]} Array containing current / active user widgets
*/
public static getUserWidgetsArray(): UserWidget[] {
return Object.values(WidgetUtils.getUserWidgets());
public static getUserWidgetsArray(client: MatrixClient | undefined): UserWidget[] {
return Object.values(WidgetUtils.getUserWidgets(client));
}
/**
* Get active stickerpicker widgets (stickerpickers are user widgets by nature)
* @param client The matrix client of the logged-in user
* @return {[object]} Array containing current / active stickerpicker widgets
*/
public static getStickerpickerWidgets(): UserWidget[] {
const widgets = WidgetUtils.getUserWidgetsArray();
public static getStickerpickerWidgets(client: MatrixClient | undefined): UserWidget[] {
const widgets = WidgetUtils.getUserWidgetsArray(client);
return widgets.filter((widget) => widget.content?.type === "m.stickerpicker");
}
/**
* Get all integration manager widgets for this user.
* @param client The matrix client of the logged-in user
* @returns {Object[]} An array of integration manager user widgets.
*/
public static getIntegrationManagerWidgets(): UserWidget[] {
const widgets = WidgetUtils.getUserWidgetsArray();
public static getIntegrationManagerWidgets(client: MatrixClient | undefined): UserWidget[] {
const widgets = WidgetUtils.getUserWidgetsArray(client);
return widgets.filter((w) => w.content?.type === "m.integration_manager");
}
@ -405,8 +421,7 @@ export default class WidgetUtils {
});
}
public static async removeIntegrationManagerWidgets(): Promise<void> {
const client = MatrixClientPeg.get();
public static async removeIntegrationManagerWidgets(client: MatrixClient | undefined): Promise<void> {
if (!client) {
throw new Error("User not logged in");
}
@ -421,8 +436,14 @@ export default class WidgetUtils {
await client.setAccountData("m.widgets", userWidgets);
}
public static addIntegrationManagerWidget(name: string, uiUrl: string, apiUrl: string): Promise<void> {
public static addIntegrationManagerWidget(
client: MatrixClient,
name: string,
uiUrl: string,
apiUrl: string,
): Promise<void> {
return WidgetUtils.setUserWidget(
client,
"integration_manager_" + new Date().getTime(),
WidgetType.INTEGRATION_MANAGER,
uiUrl,
@ -433,10 +454,10 @@ export default class WidgetUtils {
/**
* Remove all stickerpicker widgets (stickerpickers are user widgets by nature)
* @param client The matrix client of the logged-in user
* @return {Promise} Resolves on account data updated
*/
public static async removeStickerpickerWidgets(): Promise<void> {
const client = MatrixClientPeg.get();
public static async removeStickerpickerWidgets(client: MatrixClient | undefined): Promise<void> {
if (!client) {
throw new Error("User not logged in");
}
@ -452,6 +473,7 @@ export default class WidgetUtils {
}
public static async addJitsiWidget(
client: MatrixClient,
roomId: string,
type: CallType,
name: string,
@ -462,7 +484,7 @@ export default class WidgetUtils {
const auth = (await Jitsi.getInstance().getJitsiAuth()) ?? undefined;
const widgetId = randomString(24); // Must be globally unique
let confId;
let confId: string;
if (auth === "openidtoken-jwt") {
// Create conference ID from room ID
// For compatibility with Jitsi, use base32 without padding.
@ -479,9 +501,9 @@ export default class WidgetUtils {
widgetUrl.search = ""; // Causes the URL class use searchParams instead
widgetUrl.searchParams.set("confId", confId);
await WidgetUtils.setRoomWidget(roomId, widgetId, WidgetType.JITSI, widgetUrl.toString(), name, {
await WidgetUtils.setRoomWidget(client, roomId, widgetId, WidgetType.JITSI, widgetUrl.toString(), name, {
conferenceId: confId,
roomName: oobRoomName ?? MatrixClientPeg.get().getRoom(roomId)?.name,
roomName: oobRoomName ?? client.getRoom(roomId)?.name,
isAudioOnly: type === CallType.Voice,
isVideoChannel,
domain,