diff --git a/src/LegacyCallHandler.tsx b/src/LegacyCallHandler.tsx index 7abdb236ae..ba766853e0 100644 --- a/src/LegacyCallHandler.tsx +++ b/src/LegacyCallHandler.tsx @@ -18,7 +18,7 @@ limitations under the License. */ import React from "react"; -import { MatrixError, RuleId, TweakName, SyncState } from "matrix-js-sdk/src/matrix"; +import { MatrixError, RuleId, TweakName, SyncState, MatrixClient } from "matrix-js-sdk/src/matrix"; import { CallError, CallErrorCode, @@ -169,6 +169,7 @@ export default class LegacyCallHandler extends EventEmitter { private assertedIdentityNativeUsers = new Map(); private silencedCalls = new Set(); // callIds + private matrixClient?: MatrixClient; public static get instance(): LegacyCallHandler { if (!window.mxLegacyCallHandler) { @@ -190,7 +191,7 @@ export default class LegacyCallHandler extends EventEmitter { if (this.shouldObeyAssertedfIdentity()) { const nativeUser = this.assertedIdentityNativeUsers.get(call.callId); if (nativeUser) { - const room = findDMForUser(MatrixClientPeg.safeGet(), nativeUser); + const room = findDMForUser(this.matrixClient!, nativeUser); if (room) return room.roomId; } } @@ -198,7 +199,7 @@ export default class LegacyCallHandler extends EventEmitter { return VoipUserMapper.sharedInstance().nativeRoomForVirtualRoom(call.roomId) ?? call.roomId ?? null; } - public start(): void { + public start(client: MatrixClient): void { // add empty handlers for media actions, otherwise the media keys // end up causing the audio elements with our ring/ringback etc // audio clips in to play. @@ -211,8 +212,9 @@ export default class LegacyCallHandler extends EventEmitter { navigator.mediaSession.setActionHandler("nexttrack", function () {}); } + this.matrixClient = client; if (SettingsStore.getValue(UIFeature.Voip)) { - MatrixClientPeg.safeGet().on(CallEventHandlerEvent.Incoming, this.onCallIncoming); + this.matrixClient.on(CallEventHandlerEvent.Incoming, this.onCallIncoming); } this.checkProtocols(CHECK_PROTOCOLS_ATTEMPTS); @@ -269,8 +271,8 @@ export default class LegacyCallHandler extends EventEmitter { } public isForcedSilent(): boolean { - const cli = MatrixClientPeg.safeGet(); - return localNotificationsAreSilenced(cli); + const cli = this.matrixClient; + return !cli || localNotificationsAreSilenced(cli); } public silenceCall(callId?: string): void { @@ -308,8 +310,9 @@ export default class LegacyCallHandler extends EventEmitter { } private async checkProtocols(maxTries: number): Promise { + if (!this.matrixClient) return; try { - const protocols = await MatrixClientPeg.safeGet().getThirdpartyProtocols(); + const protocols = await this.matrixClient.getThirdpartyProtocols(); if (protocols[PROTOCOL_PSTN] !== undefined) { this.supportsPstnProtocol = Boolean(protocols[PROTOCOL_PSTN]); @@ -356,7 +359,7 @@ export default class LegacyCallHandler extends EventEmitter { public async pstnLookup(phoneNumber: string): Promise { try { - return await MatrixClientPeg.safeGet().getThirdpartyUser( + return await this.matrixClient!.getThirdpartyUser( this.pstnSupportPrefixed ? PROTOCOL_PSTN_PREFIXED : PROTOCOL_PSTN, { "m.id.phone": phoneNumber, @@ -370,7 +373,7 @@ export default class LegacyCallHandler extends EventEmitter { public async sipVirtualLookup(nativeMxid: string): Promise { try { - return await MatrixClientPeg.safeGet().getThirdpartyUser(PROTOCOL_SIP_VIRTUAL, { + return await this.matrixClient!.getThirdpartyUser(PROTOCOL_SIP_VIRTUAL, { native_mxid: nativeMxid, }); } catch (e) { @@ -381,7 +384,7 @@ export default class LegacyCallHandler extends EventEmitter { public async sipNativeLookup(virtualMxid: string): Promise { try { - return await MatrixClientPeg.safeGet().getThirdpartyUser(PROTOCOL_SIP_NATIVE, { + return await this.matrixClient!.getThirdpartyUser(PROTOCOL_SIP_NATIVE, { virtual_mxid: virtualMxid, }); } catch (e) { @@ -413,9 +416,9 @@ export default class LegacyCallHandler extends EventEmitter { // get ready to send encrypted events in the room, so if the user does answer // the call, we'll be ready to send. NB. This is the protocol-level room ID not // the mapped one: that's where we'll send the events. - const cli = MatrixClientPeg.safeGet(); - const room = cli.getRoom(call.roomId); - if (room) cli.prepareToEncrypt(room); + const cli = this.matrixClient; + const room = cli?.getRoom(call.roomId); + if (room) cli?.prepareToEncrypt(room); }; public getCallById(callId: string): MatrixCall | null { @@ -452,7 +455,7 @@ export default class LegacyCallHandler extends EventEmitter { } public getAllActiveCallsForPip(roomId: string): MatrixCall[] { - const room = MatrixClientPeg.safeGet().getRoom(roomId); + const room = this.matrixClient?.getRoom(roomId); if (room && WidgetLayoutStore.instance.hasMaximisedWidget(room)) { // This checks if there is space for the call view in the aux panel // If there is no space any call should be displayed in PiP @@ -559,7 +562,7 @@ export default class LegacyCallHandler extends EventEmitter { } if ( - MatrixClientPeg.safeGet().getTurnServers().length === 0 && + this.matrixClient?.getTurnServers().length === 0 && SettingsStore.getValue("fallbackICEServerAllowed") === null ) { this.showICEFallbackPrompt(); @@ -627,7 +630,7 @@ export default class LegacyCallHandler extends EventEmitter { // this if we want the actual, native room to exist (which we do). This is why it's // important to only obey asserted identity in trusted environments, since anyone you're // on a call with can cause you to send a room invite to someone. - await ensureDMExists(MatrixClientPeg.safeGet(), newNativeAssertedIdentity); + await ensureDMExists(this.matrixClient!, newNativeAssertedIdentity); const newMappedRoomId = this.roomIdForCall(call); logger.log(`Old room ID: ${mappedRoomId}, new room ID: ${newMappedRoomId}`); @@ -667,9 +670,7 @@ export default class LegacyCallHandler extends EventEmitter { switch (newState) { case CallState.Ringing: { - const incomingCallPushRule = new PushProcessor(MatrixClientPeg.safeGet()).getPushRuleById( - RuleId.IncomingCall, - ); + const incomingCallPushRule = new PushProcessor(this.matrixClient!).getPushRuleById(RuleId.IncomingCall); const pushRuleEnabled = incomingCallPushRule?.enabled; // actions can be either Tweaks | PushRuleActionName, ie an object or a string type enum // and we want to only run this check on the Tweaks @@ -814,7 +815,7 @@ export default class LegacyCallHandler extends EventEmitter { } private showICEFallbackPrompt(): void { - const cli = MatrixClientPeg.safeGet(); + const cli = this.matrixClient; Modal.createDialog( QuestionDialog, { @@ -824,7 +825,7 @@ export default class LegacyCallHandler extends EventEmitter {

{_t( "voip|misconfigured_server_description", - { homeserverDomain: cli.getDomain() }, + { homeserverDomain: cli!.getDomain() }, { code: (sub: string) => {sub} }, )}

@@ -841,7 +842,7 @@ export default class LegacyCallHandler extends EventEmitter { cancelButton: _t("action|ok"), onFinished: (allow) => { SettingsStore.setValue("fallbackICEServerAllowed", null, SettingLevel.DEVICE, allow); - cli.setFallbackICEServerAllowed(!!allow); + cli!.setFallbackICEServerAllowed(!!allow); }, }, undefined, @@ -882,7 +883,7 @@ export default class LegacyCallHandler extends EventEmitter { } private async placeMatrixCall(roomId: string, type: CallType, transferee?: MatrixCall): Promise { - const cli = MatrixClientPeg.safeGet(); + const cli = this.matrixClient; const mappedRoomId = (await VoipUserMapper.sharedInstance().getOrCreateVirtualRoomForRoom(roomId)) || roomId; logger.debug("Mapped real room " + roomId + " to room ID " + mappedRoomId); @@ -892,15 +893,15 @@ export default class LegacyCallHandler extends EventEmitter { // in this queue, and since we're about to place a new call, they can only be events from // previous calls that are probably stale by now, so just cancel them. if (mappedRoomId !== roomId) { - const mappedRoom = cli.getRoom(mappedRoomId); + const mappedRoom = cli!.getRoom(mappedRoomId); if (mappedRoom?.getPendingEvents().length) { Resend.cancelUnsentEvents(mappedRoom); } } - const timeUntilTurnCresExpire = cli.getTurnServersExpiry() - Date.now(); + const timeUntilTurnCresExpire = cli!.getTurnServersExpiry() - Date.now(); logger.log("Current turn creds expire in " + timeUntilTurnCresExpire + " ms"); - const call = cli.createCall(mappedRoomId)!; + const call = cli!.createCall(mappedRoomId)!; try { this.addCallForRoom(roomId, call); @@ -929,7 +930,7 @@ export default class LegacyCallHandler extends EventEmitter { } public async placeCall(roomId: string, type: CallType, transferee?: MatrixCall): Promise { - const cli = MatrixClientPeg.safeGet(); + const cli = this.matrixClient; // Pause current broadcast, if any SdkContextClass.instance.voiceBroadcastPlaybacksStore.getCurrent()?.pause(); @@ -946,7 +947,7 @@ export default class LegacyCallHandler extends EventEmitter { } // if the runtime env doesn't do VoIP, whine. - if (!cli.supportsVoip()) { + if (!cli!.supportsVoip()) { Modal.createDialog(ErrorDialog, { title: _t("voip|unsupported"), description: _t("voip|unsupported_browser"), @@ -954,7 +955,7 @@ export default class LegacyCallHandler extends EventEmitter { return; } - if (cli.getSyncState() === SyncState.Error) { + if (cli!.getSyncState() === SyncState.Error) { Modal.createDialog(ErrorDialog, { title: _t("voip|connection_lost"), description: _t("voip|connection_lost_description"), @@ -971,7 +972,7 @@ export default class LegacyCallHandler extends EventEmitter { return; } - const room = cli.getRoom(roomId); + const room = cli!.getRoom(roomId); if (!room) { logger.error(`Room ${roomId} does not exist.`); return; @@ -1072,7 +1073,7 @@ export default class LegacyCallHandler extends EventEmitter { nativeUserId = userId; } - const roomId = await ensureDMExists(MatrixClientPeg.safeGet(), nativeUserId); + const roomId = await ensureDMExists(this.matrixClient!, nativeUserId); if (!roomId) { throw new Error("Failed to ensure DM exists for dialing number"); } @@ -1112,7 +1113,7 @@ export default class LegacyCallHandler extends EventEmitter { public async startTransferToMatrixID(call: MatrixCall, destination: string, consultFirst: boolean): Promise { if (consultFirst) { - const dmRoomId = await ensureDMExists(MatrixClientPeg.safeGet(), destination); + const dmRoomId = await ensureDMExists(this.matrixClient!, destination); if (!dmRoomId) { logger.log("Failed to transfer call, could not ensure dm exists"); Modal.createDialog(ErrorDialog, { @@ -1171,7 +1172,7 @@ export default class LegacyCallHandler extends EventEmitter { } private async placeJitsiCall(roomId: string, type: CallType): Promise { - const client = MatrixClientPeg.safeGet(); + const client = this.matrixClient; logger.info(`Place conference call in ${roomId}`); dis.dispatch({ action: "appsDrawer", show: true }); @@ -1180,7 +1181,7 @@ export default class LegacyCallHandler extends EventEmitter { const widget = WidgetStore.instance.getApps(roomId).find((app) => WidgetType.JITSI.matches(app.type)); if (widget) { // If there already is a Jitsi widget, pin it - const room = client.getRoom(roomId); + const room = client!.getRoom(roomId); if (isNotNull(room)) { WidgetLayoutStore.instance.moveToContainer(room, widget, Container.Top); } @@ -1188,7 +1189,7 @@ export default class LegacyCallHandler extends EventEmitter { } try { - await WidgetUtils.addJitsiWidget(client, roomId, type, "Jitsi", false); + await WidgetUtils.addJitsiWidget(client!, roomId, type, "Jitsi", false); logger.log("Jitsi widget added"); } catch (e) { if (e instanceof MatrixError && e.errcode === "M_FORBIDDEN") { diff --git a/src/Lifecycle.ts b/src/Lifecycle.ts index f851ddddf6..66a308d090 100644 --- a/src/Lifecycle.ts +++ b/src/Lifecycle.ts @@ -995,12 +995,12 @@ async function startMatrixClient( ToastStore.sharedInstance().reset(); DialogOpener.instance.prepare(client); - Notifier.start(); + Notifier.start(client); UserActivity.sharedInstance().start(); DMRoomMap.makeShared(client).start(); - IntegrationManagers.sharedInstance().startWatching(); - ActiveWidgetStore.instance.start(); - LegacyCallHandler.instance.start(); + IntegrationManagers.sharedInstance().startWatching(client); + ActiveWidgetStore.instance.start(client); + LegacyCallHandler.instance.start(client); // Start Mjolnir even though we haven't checked the feature flag yet. Starting // the thing just wastes CPU cycles, but should result in no actual functionality diff --git a/src/Notifier.ts b/src/Notifier.ts index bdd2d6aeb1..2b91acb3ec 100644 --- a/src/Notifier.ts +++ b/src/Notifier.ts @@ -29,11 +29,11 @@ import { IRoomTimelineData, M_LOCATION, EventType, + MatrixClient, } from "matrix-js-sdk/src/matrix"; import { logger } from "matrix-js-sdk/src/logger"; import { PermissionChanged as PermissionChangedEvent } from "@matrix-org/analytics-events/types/typescript/PermissionChanged"; -import { MatrixClientPeg } from "./MatrixClientPeg"; import { PosthogAnalytics } from "./PosthogAnalytics"; import SdkConfig from "./SdkConfig"; import PlatformPeg from "./PlatformPeg"; @@ -74,7 +74,7 @@ Override both the content body and the TextForEvent handler for specific msgtype This is useful when the content body contains fallback text that would explain that the client can't handle a particular type of tile. */ -const msgTypeHandlers: Record string | null> = { +const msgTypeHandlers: Record string | null> = { [MsgType.KeyVerificationRequest]: (event: MatrixEvent) => { const name = (event.sender || {}).name; return _t("notifier|m.key.verification.request", { name }); @@ -85,7 +85,7 @@ const msgTypeHandlers: Record string | null> = { [M_LOCATION.altName]: (event: MatrixEvent) => { return TextForEvent.textForLocationEvent(event)(); }, - [MsgType.Audio]: (event: MatrixEvent): string | null => { + [MsgType.Audio]: (event: MatrixEvent, client: MatrixClient): string | null => { if (event.getContent()?.[VoiceBroadcastChunkEventType]) { if (event.getContent()?.[VoiceBroadcastChunkEventType]?.sequence === 1) { // Show a notification for the first broadcast chunk. @@ -97,7 +97,7 @@ const msgTypeHandlers: Record string | null> = { return null; } - return TextForEvent.textForEvent(event, MatrixClientPeg.safeGet()); + return TextForEvent.textForEvent(event, client); }, }; @@ -111,20 +111,21 @@ class NotifierClass { private toolbarHidden?: boolean; private isSyncing?: boolean; + private matrixClient?: MatrixClient; public notificationMessageForEvent(ev: MatrixEvent): string | null { const msgType = ev.getContent().msgtype; if (msgType && msgTypeHandlers.hasOwnProperty(msgType)) { - return msgTypeHandlers[msgType](ev); + return msgTypeHandlers[msgType](ev, this.matrixClient!); } - return TextForEvent.textForEvent(ev, MatrixClientPeg.safeGet()); + return TextForEvent.textForEvent(ev, this.matrixClient!); } // XXX: exported for tests public displayPopupNotification(ev: MatrixEvent, room: Room): void { const plaf = PlatformPeg.get(); - const cli = MatrixClientPeg.safeGet(); - if (!plaf) { + const cli = this.matrixClient; + if (!plaf || !cli) { return; } if (!plaf.supportsNotifications() || !plaf.maySendNotifications()) { @@ -221,8 +222,8 @@ class NotifierClass { // XXX: Exported for tests public async playAudioNotification(ev: MatrixEvent, room: Room): Promise { - const cli = MatrixClientPeg.safeGet(); - if (localNotificationsAreSilenced(cli)) { + const cli = this.matrixClient; + if (!cli || localNotificationsAreSilenced(cli)) { return; } @@ -251,23 +252,24 @@ class NotifierClass { } } - public start(): void { - const cli = MatrixClientPeg.safeGet(); - cli.on(RoomEvent.Timeline, this.onEvent); - cli.on(RoomEvent.Receipt, this.onRoomReceipt); - cli.on(MatrixEventEvent.Decrypted, this.onEventDecrypted); - cli.on(ClientEvent.Sync, this.onSyncStateChange); + public start(client: MatrixClient): void { + this.matrixClient = client; + this.matrixClient.on(RoomEvent.Timeline, this.onEvent); + this.matrixClient.on(RoomEvent.Receipt, this.onRoomReceipt); + this.matrixClient.on(MatrixEventEvent.Decrypted, this.onEventDecrypted); + this.matrixClient.on(ClientEvent.Sync, this.onSyncStateChange); this.toolbarHidden = false; this.isSyncing = false; } public stop(): void { - if (MatrixClientPeg.get()) { - MatrixClientPeg.get()!.removeListener(RoomEvent.Timeline, this.onEvent); - MatrixClientPeg.get()!.removeListener(RoomEvent.Receipt, this.onRoomReceipt); - MatrixClientPeg.get()!.removeListener(MatrixEventEvent.Decrypted, this.onEventDecrypted); - MatrixClientPeg.get()!.removeListener(ClientEvent.Sync, this.onSyncStateChange); + if (this.matrixClient) { + this.matrixClient.removeListener(RoomEvent.Timeline, this.onEvent); + this.matrixClient.removeListener(RoomEvent.Receipt, this.onRoomReceipt); + this.matrixClient.removeListener(MatrixEventEvent.Decrypted, this.onEventDecrypted); + this.matrixClient.removeListener(ClientEvent.Sync, this.onSyncStateChange); } + this.matrixClient = undefined; this.isSyncing = false; } @@ -370,11 +372,10 @@ class NotifierClass { } public shouldShowPrompt(): boolean { - const client = MatrixClientPeg.get(); - if (!client) { + if (!this.matrixClient) { return false; } - const isGuest = client.isGuest(); + const isGuest = this.matrixClient.isGuest(); return ( !isGuest && this.supportsDesktopNotifications() && @@ -403,7 +404,7 @@ class NotifierClass { // wait for first non-cached sync to complete if (![SyncState.Stopped, SyncState.Error].includes(state) && !data?.fromCache) { - createLocalNotificationSettingsIfNeeded(MatrixClientPeg.safeGet()); + createLocalNotificationSettingsIfNeeded(this.matrixClient!); } }; @@ -417,10 +418,10 @@ class NotifierClass { if (removed) return; // only notify for new events, not removed ones if (!data.liveEvent || !!toStartOfTimeline) return; // only notify for new things, not old. if (!this.isSyncing) return; // don't alert for any messages initially - if (ev.getSender() === MatrixClientPeg.safeGet().getUserId()) return; + if (ev.getSender() === this.matrixClient?.getUserId()) return; if (data.timeline.getTimelineSet().threadListType !== null) return; // Ignore events on the thread list generated timelines - MatrixClientPeg.safeGet().decryptEventIfNeeded(ev); + this.matrixClient?.decryptEventIfNeeded(ev); // If it's an encrypted event and the type is still 'm.room.encrypted', // it hasn't yet been decrypted, so wait until it is. @@ -478,14 +479,14 @@ class NotifierClass { roomId = nativeRoomId; } } - const room = MatrixClientPeg.safeGet().getRoom(roomId); + const room = this.matrixClient?.getRoom(roomId); if (!room) { // e.g we are in the process of joining a room. // Seen in the Playwright lazy-loading test. return; } - const actions = MatrixClientPeg.safeGet().getPushActionsForEvent(ev); + const actions = this.matrixClient?.getPushActionsForEvent(ev); if (actions?.notify) { this.performCustomEventHandling(ev); diff --git a/src/integrations/IntegrationManagers.ts b/src/integrations/IntegrationManagers.ts index 0ec7d99185..f0a52a8638 100644 --- a/src/integrations/IntegrationManagers.ts +++ b/src/integrations/IntegrationManagers.ts @@ -25,7 +25,6 @@ import { IntegrationManagerInstance, Kind } from "./IntegrationManagerInstance"; import IntegrationsImpossibleDialog from "../components/views/dialogs/IntegrationsImpossibleDialog"; import IntegrationsDisabledDialog from "../components/views/dialogs/IntegrationsDisabledDialog"; import WidgetUtils from "../utils/WidgetUtils"; -import { MatrixClientPeg } from "../MatrixClientPeg"; const KIND_PREFERENCE = [ // Ordered: first is most preferred, last is least preferred. @@ -52,9 +51,9 @@ export class IntegrationManagers { this.compileManagers(); } - public startWatching(): void { + public startWatching(client: MatrixClient): void { this.stopWatching(); - this.client = MatrixClientPeg.safeGet(); + this.client = client; this.client.on(ClientEvent.AccountData, this.onAccountData); this.client.on(ClientEvent.ClientWellKnown, this.setupHomeserverManagers); this.compileManagers(); diff --git a/src/stores/ActiveWidgetStore.ts b/src/stores/ActiveWidgetStore.ts index 214f2aa673..92ba482625 100644 --- a/src/stores/ActiveWidgetStore.ts +++ b/src/stores/ActiveWidgetStore.ts @@ -15,9 +15,8 @@ limitations under the License. */ import EventEmitter from "events"; -import { MatrixEvent, RoomStateEvent, RoomState } from "matrix-js-sdk/src/matrix"; +import { MatrixEvent, RoomStateEvent, RoomState, MatrixClient } from "matrix-js-sdk/src/matrix"; -import { MatrixClientPeg } from "../MatrixClientPeg"; import WidgetUtils from "../utils/WidgetUtils"; import { WidgetMessagingStore } from "./widgets/WidgetMessagingStore"; @@ -41,6 +40,7 @@ export default class ActiveWidgetStore extends EventEmitter { private persistentWidgetId: string | null = null; private persistentRoomId: string | null = null; private dockedWidgetsByUid = new Map(); + private matrixClient?: MatrixClient; public static get instance(): ActiveWidgetStore { if (!ActiveWidgetStore.internalInstance) { @@ -49,12 +49,13 @@ export default class ActiveWidgetStore extends EventEmitter { return ActiveWidgetStore.internalInstance; } - public start(): void { - MatrixClientPeg.safeGet().on(RoomStateEvent.Events, this.onRoomStateEvents); + public start(client: MatrixClient): void { + this.matrixClient = client; + this.matrixClient.on(RoomStateEvent.Events, this.onRoomStateEvents); } public stop(): void { - MatrixClientPeg.get()?.removeListener(RoomStateEvent.Events, this.onRoomStateEvents); + this.matrixClient?.removeListener(RoomStateEvent.Events, this.onRoomStateEvents); } private onRoomStateEvents = (ev: MatrixEvent, { roomId }: RoomState): void => {