From 9b5b05314832ab0d57901b7755819b65835ddced Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Wed, 21 Jun 2023 17:29:44 +0100 Subject: [PATCH] Use MatrixClientPeg::safeGet for strict typing (#10989) --- src/IdentityAuthClient.tsx | 4 +- src/LegacyCallHandler.tsx | 48 ++++++++++--------- src/Lifecycle.ts | 12 ++--- src/MatrixClientPeg.ts | 4 +- src/MediaDeviceHandler.ts | 10 ++-- src/Notifier.ts | 35 +++++++------- src/Presence.ts | 4 +- src/ScalarAuthClient.ts | 6 +-- src/ScalarMessaging.ts | 6 +-- src/SecurityManager.ts | 10 ++-- src/Unread.ts | 2 +- src/VoipUserMapper.ts | 21 ++++---- .../security/CreateKeyBackupDialog.tsx | 9 ++-- .../security/CreateSecretStorageDialog.tsx | 27 +++++------ .../security/NewRecoveryMethodDialog.tsx | 4 +- src/audio/PlaybackQueue.ts | 2 +- src/autocomplete/NotifProvider.tsx | 4 +- src/autocomplete/RoomProvider.tsx | 2 +- src/autocomplete/SpaceProvider.tsx | 2 +- src/autocomplete/UserProvider.tsx | 14 +++--- src/components/structures/MessagePanel.tsx | 13 +++-- src/components/structures/RoomSearchView.tsx | 2 +- src/components/structures/TimelinePanel.tsx | 3 +- .../views/dialogs/ConfirmRedactDialog.tsx | 2 +- .../ManualDeviceKeyVerificationDialog.tsx | 2 +- .../views/right_panel/VerificationPanel.tsx | 4 +- src/components/views/rooms/EventTile.tsx | 2 +- .../views/rooms/SearchResultTile.tsx | 13 ++++- .../views/settings/CrossSigningPanel.tsx | 6 +-- .../tabs/user/MjolnirUserSettingsTab.tsx | 8 ++-- .../tabs/user/SecurityUserSettingsTab.tsx | 20 ++++---- .../views/toasts/VerificationRequestToast.tsx | 8 ++-- src/customisations/Media.ts | 2 +- src/events/EventTileFactory.tsx | 17 ++++--- src/indexing/EventIndex.ts | 20 ++++---- src/integrations/IntegrationManagers.ts | 2 +- src/linkify-matrix.ts | 2 +- src/mjolnir/BanList.ts | 6 +-- src/mjolnir/Mjolnir.ts | 7 ++- src/modules/ProxiedModuleApi.ts | 2 +- src/sentry.ts | 2 +- src/settings/SettingsStore.ts | 2 +- .../controllers/NotificationControllers.ts | 2 +- src/toasts/DesktopNotificationsToast.ts | 2 +- src/toasts/IncomingCallToast.tsx | 2 +- src/toasts/IncomingLegacyCallToast.tsx | 2 +- src/toasts/UnverifiedSessionToast.tsx | 2 +- src/utils/EventRenderingUtils.ts | 2 +- src/utils/exportUtils/HtmlExport.tsx | 5 +- src/utils/exportUtils/JSONExport.ts | 2 +- src/utils/exportUtils/PlainTextExport.ts | 2 +- .../hooks/useVoiceBroadcastPlayback.ts | 2 +- .../hooks/useVoiceBroadcastRecording.tsx | 2 +- src/widgets/CapabilityText.tsx | 2 +- src/widgets/Jitsi.ts | 2 +- src/widgets/ManagedHybrid.ts | 4 +- test/Unread-test.ts | 4 +- .../structures/MessagePanel-test.tsx | 12 +++-- .../security/CreateKeyBackupDialog-test.tsx | 4 +- .../views/rooms/RoomPreviewBar-test.tsx | 4 +- 60 files changed, 225 insertions(+), 203 deletions(-) diff --git a/src/IdentityAuthClient.tsx b/src/IdentityAuthClient.tsx index 28e88a49f7..c27640c6c4 100644 --- a/src/IdentityAuthClient.tsx +++ b/src/IdentityAuthClient.tsx @@ -59,7 +59,7 @@ export default class IdentityAuthClient { } private get matrixClient(): MatrixClient { - return this.tempClient ? this.tempClient : MatrixClientPeg.get(); + return this.tempClient ? this.tempClient : MatrixClientPeg.safeGet(); } private writeToken(): void { @@ -176,7 +176,7 @@ export default class IdentityAuthClient { } public async registerForToken(check = true): Promise { - const hsOpenIdToken = await MatrixClientPeg.get().getOpenIdToken(); + const hsOpenIdToken = await MatrixClientPeg.safeGet().getOpenIdToken(); // XXX: The spec is `token`, but we used `access_token` for a Sydent release. const { access_token: accessToken, token } = await this.matrixClient.registerWithIdentityServer(hsOpenIdToken); const identityAccessToken = token ? token : accessToken; diff --git a/src/LegacyCallHandler.tsx b/src/LegacyCallHandler.tsx index 08636e5011..1414fc2893 100644 --- a/src/LegacyCallHandler.tsx +++ b/src/LegacyCallHandler.tsx @@ -192,7 +192,7 @@ export default class LegacyCallHandler extends EventEmitter { if (this.shouldObeyAssertedfIdentity()) { const nativeUser = this.assertedIdentityNativeUsers.get(call.callId); if (nativeUser) { - const room = findDMForUser(MatrixClientPeg.get(), nativeUser); + const room = findDMForUser(MatrixClientPeg.safeGet(), nativeUser); if (room) return room.roomId; } } @@ -214,7 +214,7 @@ export default class LegacyCallHandler extends EventEmitter { } if (SettingsStore.getValue(UIFeature.Voip)) { - MatrixClientPeg.get().on(CallEventHandlerEvent.Incoming, this.onCallIncoming); + MatrixClientPeg.safeGet().on(CallEventHandlerEvent.Incoming, this.onCallIncoming); } this.checkProtocols(CHECK_PROTOCOLS_ATTEMPTS); @@ -271,7 +271,7 @@ export default class LegacyCallHandler extends EventEmitter { } public isForcedSilent(): boolean { - const cli = MatrixClientPeg.get(); + const cli = MatrixClientPeg.safeGet(); return localNotificationsAreSilenced(cli); } @@ -311,7 +311,7 @@ export default class LegacyCallHandler extends EventEmitter { private async checkProtocols(maxTries: number): Promise { try { - const protocols = await MatrixClientPeg.get().getThirdpartyProtocols(); + const protocols = await MatrixClientPeg.safeGet().getThirdpartyProtocols(); if (protocols[PROTOCOL_PSTN] !== undefined) { this.supportsPstnProtocol = Boolean(protocols[PROTOCOL_PSTN]); @@ -358,7 +358,7 @@ export default class LegacyCallHandler extends EventEmitter { public async pstnLookup(phoneNumber: string): Promise { try { - return await MatrixClientPeg.get().getThirdpartyUser( + return await MatrixClientPeg.safeGet().getThirdpartyUser( this.pstnSupportPrefixed ? PROTOCOL_PSTN_PREFIXED : PROTOCOL_PSTN, { "m.id.phone": phoneNumber, @@ -372,7 +372,7 @@ export default class LegacyCallHandler extends EventEmitter { public async sipVirtualLookup(nativeMxid: string): Promise { try { - return await MatrixClientPeg.get().getThirdpartyUser(PROTOCOL_SIP_VIRTUAL, { + return await MatrixClientPeg.safeGet().getThirdpartyUser(PROTOCOL_SIP_VIRTUAL, { native_mxid: nativeMxid, }); } catch (e) { @@ -383,7 +383,7 @@ export default class LegacyCallHandler extends EventEmitter { public async sipNativeLookup(virtualMxid: string): Promise { try { - return await MatrixClientPeg.get().getThirdpartyUser(PROTOCOL_SIP_NATIVE, { + return await MatrixClientPeg.safeGet().getThirdpartyUser(PROTOCOL_SIP_NATIVE, { virtual_mxid: virtualMxid, }); } catch (e) { @@ -394,7 +394,7 @@ export default class LegacyCallHandler extends EventEmitter { private onCallIncoming = (call: MatrixCall): void => { // if the runtime env doesn't do VoIP, stop here. - if (!MatrixClientPeg.get().supportsVoip()) { + if (!MatrixClientPeg.get()?.supportsVoip()) { return; } @@ -415,7 +415,7 @@ 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.get(); + const cli = MatrixClientPeg.safeGet(); const room = cli.getRoom(call.roomId); if (room) cli.prepareToEncrypt(room); }; @@ -463,7 +463,7 @@ export default class LegacyCallHandler extends EventEmitter { } public getAllActiveCallsForPip(roomId: string): MatrixCall[] { - const room = MatrixClientPeg.get().getRoom(roomId); + const room = MatrixClientPeg.safeGet().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 @@ -570,7 +570,7 @@ export default class LegacyCallHandler extends EventEmitter { } if ( - MatrixClientPeg.get().getTurnServers().length === 0 && + MatrixClientPeg.safeGet().getTurnServers().length === 0 && SettingsStore.getValue("fallbackICEServerAllowed") === null ) { this.showICEFallbackPrompt(); @@ -638,7 +638,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.get(), newNativeAssertedIdentity); + await ensureDMExists(MatrixClientPeg.safeGet(), newNativeAssertedIdentity); const newMappedRoomId = this.roomIdForCall(call); logger.log(`Old room ID: ${mappedRoomId}, new room ID: ${newMappedRoomId}`); @@ -678,7 +678,7 @@ export default class LegacyCallHandler extends EventEmitter { switch (newState) { case CallState.Ringing: { - const incomingCallPushRule = new PushProcessor(MatrixClientPeg.get()).getPushRuleById( + const incomingCallPushRule = new PushProcessor(MatrixClientPeg.safeGet()).getPushRuleById( RuleId.IncomingCall, ); const pushRuleEnabled = incomingCallPushRule?.enabled; @@ -825,7 +825,7 @@ export default class LegacyCallHandler extends EventEmitter { } private showICEFallbackPrompt(): void { - const cli = MatrixClientPeg.get(); + const cli = MatrixClientPeg.safeGet(); Modal.createDialog( QuestionDialog, { @@ -907,6 +907,7 @@ export default class LegacyCallHandler extends EventEmitter { } private async placeMatrixCall(roomId: string, type: CallType, transferee?: MatrixCall): Promise { + const cli = MatrixClientPeg.safeGet(); const mappedRoomId = (await VoipUserMapper.sharedInstance().getOrCreateVirtualRoomForRoom(roomId)) || roomId; logger.debug("Mapped real room " + roomId + " to room ID " + mappedRoomId); @@ -916,15 +917,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 = MatrixClientPeg.get().getRoom(mappedRoomId); + const mappedRoom = cli.getRoom(mappedRoomId); if (mappedRoom?.getPendingEvents().length) { Resend.cancelUnsentEvents(mappedRoom); } } - const timeUntilTurnCresExpire = MatrixClientPeg.get().getTurnServersExpiry() - Date.now(); + const timeUntilTurnCresExpire = cli.getTurnServersExpiry() - Date.now(); logger.log("Current turn creds expire in " + timeUntilTurnCresExpire + " ms"); - const call = MatrixClientPeg.get().createCall(mappedRoomId)!; + const call = cli.createCall(mappedRoomId)!; try { this.addCallForRoom(roomId, call); @@ -953,6 +954,7 @@ export default class LegacyCallHandler extends EventEmitter { } public async placeCall(roomId: string, type: CallType, transferee?: MatrixCall): Promise { + const cli = MatrixClientPeg.safeGet(); // Pause current broadcast, if any SdkContextClass.instance.voiceBroadcastPlaybacksStore.getCurrent()?.pause(); @@ -969,7 +971,7 @@ export default class LegacyCallHandler extends EventEmitter { } // if the runtime env doesn't do VoIP, whine. - if (!MatrixClientPeg.get().supportsVoip()) { + if (!cli.supportsVoip()) { Modal.createDialog(ErrorDialog, { title: _t("Calls are unsupported"), description: _t("You cannot place calls in this browser."), @@ -977,7 +979,7 @@ export default class LegacyCallHandler extends EventEmitter { return; } - if (MatrixClientPeg.get().getSyncState() === SyncState.Error) { + if (cli.getSyncState() === SyncState.Error) { Modal.createDialog(ErrorDialog, { title: _t("Connectivity to the server has been lost"), description: _t("You cannot place calls without a connection to the server."), @@ -994,7 +996,7 @@ export default class LegacyCallHandler extends EventEmitter { return; } - const room = MatrixClientPeg.get().getRoom(roomId); + const room = cli.getRoom(roomId); if (!room) { logger.error(`Room ${roomId} does not exist.`); return; @@ -1095,7 +1097,7 @@ export default class LegacyCallHandler extends EventEmitter { nativeUserId = userId; } - const roomId = await ensureDMExists(MatrixClientPeg.get(), nativeUserId); + const roomId = await ensureDMExists(MatrixClientPeg.safeGet(), nativeUserId); if (!roomId) { throw new Error("Failed to ensure DM exists for dialing number"); } @@ -1135,7 +1137,7 @@ export default class LegacyCallHandler extends EventEmitter { public async startTransferToMatrixID(call: MatrixCall, destination: string, consultFirst: boolean): Promise { if (consultFirst) { - const dmRoomId = await ensureDMExists(MatrixClientPeg.get(), destination); + const dmRoomId = await ensureDMExists(MatrixClientPeg.safeGet(), destination); if (!dmRoomId) { logger.log("Failed to transfer call, could not ensure dm exists"); Modal.createDialog(ErrorDialog, { @@ -1194,7 +1196,7 @@ export default class LegacyCallHandler extends EventEmitter { } private async placeJitsiCall(roomId: string, type: CallType): Promise { - const client = MatrixClientPeg.get(); + const client = MatrixClientPeg.safeGet(); logger.info(`Place conference call in ${roomId}`); dis.dispatch({ action: "appsDrawer", show: true }); diff --git a/src/Lifecycle.ts b/src/Lifecycle.ts index 6f49a0a0b6..a1d00d86ad 100644 --- a/src/Lifecycle.ts +++ b/src/Lifecycle.ts @@ -278,7 +278,7 @@ export function handleInvalidStoreError(e: InvalidStoreError): Promise | v } }) .then(() => { - return MatrixClientPeg.get().store.deleteAllData(); + return MatrixClientPeg.safeGet().store.deleteAllData(); }) .then(() => { PlatformPeg.get()?.reload(); @@ -541,8 +541,8 @@ export async function setLoggedIn(credentials: IMatrixClientCreds): Promise { - const oldUserId = MatrixClientPeg.get().getUserId(); - const oldDeviceId = MatrixClientPeg.get().getDeviceId(); + const oldUserId = MatrixClientPeg.safeGet().getUserId(); + const oldDeviceId = MatrixClientPeg.safeGet().getDeviceId(); stopMatrixClient(); // unsets MatrixClientPeg.get() localStorage.removeItem("mx_soft_logout"); @@ -603,7 +603,7 @@ async function doSetLoggedIn(credentials: IMatrixClientCreds, clearStorageEnable } MatrixClientPeg.replaceUsingCreds(credentials); - const client = MatrixClientPeg.get(); + const client = MatrixClientPeg.safeGet(); setSentryUser(credentials.userId); @@ -724,7 +724,7 @@ export function logout(): void { PosthogAnalytics.instance.logout(); - if (MatrixClientPeg.get().isGuest()) { + if (MatrixClientPeg.get()!.isGuest()) { // logout doesn't work for guest sessions // Also we sometimes want to re-log in a guest session if we abort the login. // defer until next tick because it calls a synchronous dispatch, and we are likely here from a dispatch. @@ -733,7 +733,7 @@ export function logout(): void { } _isLoggingOut = true; - const client = MatrixClientPeg.get(); + const client = MatrixClientPeg.get()!; PlatformPeg.get()?.destroyPickleKey(client.getSafeUserId(), client.getDeviceId() ?? ""); client.logout(true).then(onLoggedOut, (err) => { // Just throwing an error here is going to be very unhelpful diff --git a/src/MatrixClientPeg.ts b/src/MatrixClientPeg.ts index 979f679f46..fabed4f61c 100644 --- a/src/MatrixClientPeg.ts +++ b/src/MatrixClientPeg.ts @@ -73,7 +73,7 @@ export interface IMatrixClientPeg { */ getHomeserverName(): string | null; - get(): MatrixClient; + get(): MatrixClient | null; safeGet(): MatrixClient; unset(): void; assign(): Promise; @@ -142,7 +142,7 @@ class MatrixClientPegClass implements IMatrixClientPeg { // used if we tear it down & recreate it with a different store private currentClientCreds: IMatrixClientCreds | null = null; - public get(): MatrixClient { + public get(): MatrixClient | null { return this.matrixClient; } diff --git a/src/MediaDeviceHandler.ts b/src/MediaDeviceHandler.ts index 7e66fbc3df..f194b5e204 100644 --- a/src/MediaDeviceHandler.ts +++ b/src/MediaDeviceHandler.ts @@ -99,14 +99,14 @@ export default class MediaDeviceHandler extends EventEmitter { const audioDeviceId = SettingsStore.getValue("webrtc_audioinput"); const videoDeviceId = SettingsStore.getValue("webrtc_videoinput"); - await MatrixClientPeg.get().getMediaHandler().setAudioInput(audioDeviceId); - await MatrixClientPeg.get().getMediaHandler().setVideoInput(videoDeviceId); + await MatrixClientPeg.safeGet().getMediaHandler().setAudioInput(audioDeviceId); + await MatrixClientPeg.safeGet().getMediaHandler().setVideoInput(videoDeviceId); await MediaDeviceHandler.updateAudioSettings(); } private static async updateAudioSettings(): Promise { - await MatrixClientPeg.get().getMediaHandler().setAudioSettings({ + await MatrixClientPeg.safeGet().getMediaHandler().setAudioSettings({ autoGainControl: MediaDeviceHandler.getAudioAutoGainControl(), echoCancellation: MediaDeviceHandler.getAudioEchoCancellation(), noiseSuppression: MediaDeviceHandler.getAudioNoiseSuppression(), @@ -125,7 +125,7 @@ export default class MediaDeviceHandler extends EventEmitter { */ public async setAudioInput(deviceId: string): Promise { SettingsStore.setValue("webrtc_audioinput", null, SettingLevel.DEVICE, deviceId); - return MatrixClientPeg.get().getMediaHandler().setAudioInput(deviceId); + return MatrixClientPeg.safeGet().getMediaHandler().setAudioInput(deviceId); } /** @@ -135,7 +135,7 @@ export default class MediaDeviceHandler extends EventEmitter { */ public async setVideoInput(deviceId: string): Promise { SettingsStore.setValue("webrtc_videoinput", null, SettingLevel.DEVICE, deviceId); - return MatrixClientPeg.get().getMediaHandler().setVideoInput(deviceId); + return MatrixClientPeg.safeGet().getMediaHandler().setVideoInput(deviceId); } public async setDevice(deviceId: string, kind: MediaDeviceKindEnum): Promise { diff --git a/src/Notifier.ts b/src/Notifier.ts index 4a36338729..fed04dd68d 100644 --- a/src/Notifier.ts +++ b/src/Notifier.ts @@ -91,7 +91,7 @@ const msgTypeHandlers: Record string | null> = { return null; } - return TextForEvent.textForEvent(event, MatrixClientPeg.get()); + return TextForEvent.textForEvent(event, MatrixClientPeg.safeGet()); }, }; @@ -111,13 +111,13 @@ class NotifierClass { if (msgType && msgTypeHandlers.hasOwnProperty(msgType)) { return msgTypeHandlers[msgType](ev); } - return TextForEvent.textForEvent(ev, MatrixClientPeg.get()); + return TextForEvent.textForEvent(ev, MatrixClientPeg.safeGet()); } // XXX: exported for tests public displayPopupNotification(ev: MatrixEvent, room: Room): void { const plaf = PlatformPeg.get(); - const cli = MatrixClientPeg.get(); + const cli = MatrixClientPeg.safeGet(); if (!plaf) { return; } @@ -215,7 +215,7 @@ class NotifierClass { // XXX: Exported for tests public async playAudioNotification(ev: MatrixEvent, room: Room): Promise { - const cli = MatrixClientPeg.get(); + const cli = MatrixClientPeg.safeGet(); if (localNotificationsAreSilenced(cli)) { return; } @@ -246,20 +246,21 @@ class NotifierClass { } public start(): void { - MatrixClientPeg.get().on(RoomEvent.Timeline, this.onEvent); - MatrixClientPeg.get().on(RoomEvent.Receipt, this.onRoomReceipt); - MatrixClientPeg.get().on(MatrixEventEvent.Decrypted, this.onEventDecrypted); - MatrixClientPeg.get().on(ClientEvent.Sync, this.onSyncStateChange); + 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); 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); + 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); } this.isSyncing = false; } @@ -400,7 +401,7 @@ class NotifierClass { // wait for first non-cached sync to complete if (![SyncState.Stopped, SyncState.Error].includes(state) && !data?.fromCache) { - createLocalNotificationSettingsIfNeeded(MatrixClientPeg.get()); + createLocalNotificationSettingsIfNeeded(MatrixClientPeg.safeGet()); } }; @@ -413,9 +414,9 @@ class NotifierClass { ): void => { if (!data.liveEvent) return; // only notify for new things, not old. if (!this.isSyncing) return; // don't alert for any messages initially - if (ev.getSender() === MatrixClientPeg.get().getUserId()) return; + if (ev.getSender() === MatrixClientPeg.safeGet().getUserId()) return; - MatrixClientPeg.get().decryptEventIfNeeded(ev); + MatrixClientPeg.safeGet().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. @@ -473,14 +474,14 @@ class NotifierClass { roomId = nativeRoomId; } } - const room = MatrixClientPeg.get().getRoom(roomId); + const room = MatrixClientPeg.safeGet().getRoom(roomId); if (!room) { // e.g we are in the process of joining a room. // Seen in the cypress lazy-loading test. return; } - const actions = MatrixClientPeg.get().getPushActionsForEvent(ev); + const actions = MatrixClientPeg.safeGet().getPushActionsForEvent(ev); if (actions?.notify) { this.performCustomEventHandling(ev); diff --git a/src/Presence.ts b/src/Presence.ts index 02d2ef0e7e..a8ba8b40c4 100644 --- a/src/Presence.ts +++ b/src/Presence.ts @@ -97,12 +97,12 @@ class Presence { const oldState = this.state; this.state = newState; - if (MatrixClientPeg.get().isGuest()) { + if (MatrixClientPeg.safeGet().isGuest()) { return; // don't try to set presence when a guest; it won't work. } try { - await MatrixClientPeg.get().setPresence({ presence: this.state }); + await MatrixClientPeg.safeGet().setPresence({ presence: this.state }); logger.info("Presence:", newState); } catch (err) { logger.error("Failed to set presence:", err); diff --git a/src/ScalarAuthClient.ts b/src/ScalarAuthClient.ts index ca181ebb8f..07dc478876 100644 --- a/src/ScalarAuthClient.ts +++ b/src/ScalarAuthClient.ts @@ -131,7 +131,7 @@ export default class ScalarAuthClient { private checkToken(token: string): Promise { return this.getAccountName(token) .then((userId) => { - const me = MatrixClientPeg.get().getUserId(); + const me = MatrixClientPeg.safeGet().getUserId(); if (userId !== me) { throw new Error("Scalar token is owned by someone else: " + me); } @@ -157,7 +157,7 @@ export default class ScalarAuthClient { const parsedImRestUrl = parseUrl(this.apiUrl); parsedImRestUrl.pathname = ""; return startTermsFlow( - MatrixClientPeg.get(), + MatrixClientPeg.safeGet(), [new Service(SERVICE_TYPES.IM, parsedImRestUrl.toString(), token)], this.termsInteractionCallback, ).then(() => { @@ -171,7 +171,7 @@ export default class ScalarAuthClient { public registerForToken(): Promise { // Get openid bearer token from the HS as the first part of our dance - return MatrixClientPeg.get() + return MatrixClientPeg.safeGet() .getOpenIdToken() .then((tokenObject) => { // Now we can send that to scalar and exchange it for a scalar token diff --git a/src/ScalarMessaging.ts b/src/ScalarMessaging.ts index fa0849bf30..b3a0c5640b 100644 --- a/src/ScalarMessaging.ts +++ b/src/ScalarMessaging.ts @@ -411,7 +411,7 @@ function kickUser(event: MessageEvent, roomId: string, userId: string): voi } function setWidget(event: MessageEvent, roomId: string | null): void { - const client = MatrixClientPeg.get(); + const client = MatrixClientPeg.safeGet(); const widgetId = event.data.widget_id; let widgetType = event.data.type; const widgetUrl = event.data.url; @@ -535,7 +535,7 @@ function getRoomEncState(event: MessageEvent, roomId: string): void { sendError(event, _t("This room is not recognised.")); return; } - const roomIsEncrypted = MatrixClientPeg.get().isRoomEncrypted(roomId); + const roomIsEncrypted = MatrixClientPeg.safeGet().isRoomEncrypted(roomId); sendResponse(event, roomIsEncrypted); } @@ -715,7 +715,7 @@ function returnStateEvent(event: MessageEvent, roomId: string, eventType: s async function getOpenIdToken(event: MessageEvent): Promise { try { - const tokenObject = await MatrixClientPeg.get().getOpenIdToken(); + const tokenObject = await MatrixClientPeg.safeGet().getOpenIdToken(); sendResponse(event, tokenObject); } catch (ex) { logger.warn("Unable to fetch openId token.", ex); diff --git a/src/SecurityManager.ts b/src/SecurityManager.ts index c1a59bce74..52c5a8f852 100644 --- a/src/SecurityManager.ts +++ b/src/SecurityManager.ts @@ -99,7 +99,7 @@ async function getSecretStorageKey({ }: { keys: Record; }): Promise<[string, Uint8Array]> { - const cli = MatrixClientPeg.get(); + const cli = MatrixClientPeg.safeGet(); let keyId = await cli.getDefaultSecretStorageKeyId(); let keyInfo!: ISecretStorageKeyInfo; if (keyId) { @@ -127,7 +127,7 @@ async function getSecretStorageKey({ } if (dehydrationCache.key) { - if (await MatrixClientPeg.get().checkSecretStorageKey(dehydrationCache.key, keyInfo)) { + if (await MatrixClientPeg.safeGet().checkSecretStorageKey(dehydrationCache.key, keyInfo)) { cacheSecretStorageKey(keyId, keyInfo, dehydrationCache.key); return [keyId, dehydrationCache.key]; } @@ -152,7 +152,7 @@ async function getSecretStorageKey({ keyInfo, checkPrivateKey: async (input: KeyParams): Promise => { const key = await inputToKey(input); - return MatrixClientPeg.get().checkSecretStorageKey(key, keyInfo); + return MatrixClientPeg.safeGet().checkSecretStorageKey(key, keyInfo); }, }, /* className= */ undefined, @@ -244,7 +244,7 @@ async function onSecretRequested( deviceTrust: DeviceTrustLevel, ): Promise { logger.log("onSecretRequested", userId, deviceId, requestId, name, deviceTrust); - const client = MatrixClientPeg.get(); + const client = MatrixClientPeg.safeGet(); if (userId !== client.getUserId()) { return; } @@ -324,9 +324,9 @@ export async function promptForBackupPassphrase(): Promise { * @param {bool} [forceReset] Reset secret storage even if it's already set up */ export async function accessSecretStorage(func = async (): Promise => {}, forceReset = false): Promise { - const cli = MatrixClientPeg.get(); secretStorageBeingAccessed = true; try { + const cli = MatrixClientPeg.safeGet(); if (!(await cli.hasSecretStorageKey()) || forceReset) { // This dialog calls bootstrap itself after guiding the user through // passphrase creation. diff --git a/src/Unread.ts b/src/Unread.ts index 3b1826b3a6..03a816c680 100644 --- a/src/Unread.ts +++ b/src/Unread.ts @@ -52,7 +52,7 @@ export function eventTriggersUnreadCount(client: MatrixClient, ev: MatrixEvent): } if (ev.isRedacted()) return false; - return haveRendererForEvent(ev, false /* hidden messages should never trigger unread counts anyways */); + return haveRendererForEvent(ev, client, false /* hidden messages should never trigger unread counts anyways */); } export function doesRoomHaveUnreadMessages(room: Room): boolean { diff --git a/src/VoipUserMapper.ts b/src/VoipUserMapper.ts index 0214ab9cbe..d1f5b5817c 100644 --- a/src/VoipUserMapper.ts +++ b/src/VoipUserMapper.ts @@ -58,8 +58,9 @@ export default class VoipUserMapper { const virtualUser = await this.getVirtualUserForRoom(roomId); if (!virtualUser) return null; - const virtualRoomId = await ensureVirtualRoomExists(MatrixClientPeg.get(), virtualUser, roomId); - MatrixClientPeg.get().setRoomAccountData(virtualRoomId!, VIRTUAL_ROOM_EVENT_TYPE, { + const cli = MatrixClientPeg.safeGet(); + const virtualRoomId = await ensureVirtualRoomExists(cli, virtualUser, roomId); + cli.setRoomAccountData(virtualRoomId!, VIRTUAL_ROOM_EVENT_TYPE, { native_room: roomId, }); @@ -76,7 +77,7 @@ export default class VoipUserMapper { const virtualUser = await this.getVirtualUserForRoom(roomId); if (!virtualUser) return undefined; - return findDMForUser(MatrixClientPeg.get(), virtualUser); + return findDMForUser(MatrixClientPeg.safeGet(), virtualUser); } public nativeRoomForVirtualRoom(roomId: string): string | null { @@ -88,12 +89,13 @@ export default class VoipUserMapper { return cachedNativeRoomId; } - const virtualRoom = MatrixClientPeg.get().getRoom(roomId); + const cli = MatrixClientPeg.safeGet(); + const virtualRoom = cli.getRoom(roomId); if (!virtualRoom) return null; const virtualRoomEvent = virtualRoom.getAccountData(VIRTUAL_ROOM_EVENT_TYPE); if (!virtualRoomEvent || !virtualRoomEvent.getContent()) return null; const nativeRoomID = virtualRoomEvent.getContent()["native_room"]; - const nativeRoom = MatrixClientPeg.get().getRoom(nativeRoomID); + const nativeRoom = cli.getRoom(nativeRoomID); if (!nativeRoom || nativeRoom.getMyMembership() !== "join") return null; return nativeRoomID; @@ -112,7 +114,7 @@ export default class VoipUserMapper { if (!roomCreateEvent || !roomCreateEvent.getContent()) return false; // we only look at this for rooms we created (so inviters can't just cause rooms // to be invisible) - if (roomCreateEvent.getSender() !== MatrixClientPeg.get().getUserId()) return false; + if (roomCreateEvent.getSender() !== MatrixClientPeg.safeGet().getUserId()) return false; const claimedNativeRoomId = roomCreateEvent.getContent()[VIRTUAL_ROOM_EVENT_TYPE]; return Boolean(claimedNativeRoomId); } @@ -132,19 +134,20 @@ export default class VoipUserMapper { } if (result[0].fields.is_virtual) { + const cli = MatrixClientPeg.safeGet(); const nativeUser = result[0].userid; - const nativeRoom = findDMForUser(MatrixClientPeg.get(), nativeUser); + const nativeRoom = findDMForUser(cli, nativeUser); if (nativeRoom) { // It's a virtual room with a matching native room, so set the room account data. This // will make sure we know where how to map calls and also allow us know not to display // it in the future. - MatrixClientPeg.get().setRoomAccountData(invitedRoom.roomId, VIRTUAL_ROOM_EVENT_TYPE, { + cli.setRoomAccountData(invitedRoom.roomId, VIRTUAL_ROOM_EVENT_TYPE, { native_room: nativeRoom.roomId, }); // also auto-join the virtual room if we have a matching native room // (possibly we should only join if we've also joined the native room, then we'd also have // to make sure we joined virtual rooms on joining a native one) - MatrixClientPeg.get().joinRoom(invitedRoom.roomId); + cli.joinRoom(invitedRoom.roomId); // also put this room in the virtual room ID cache so isVirtualRoom return the right answer // in however long it takes for the echo of setAccountData to come down the sync diff --git a/src/async-components/views/dialogs/security/CreateKeyBackupDialog.tsx b/src/async-components/views/dialogs/security/CreateKeyBackupDialog.tsx index 822e9fd46b..d07c86daa1 100644 --- a/src/async-components/views/dialogs/security/CreateKeyBackupDialog.tsx +++ b/src/async-components/views/dialogs/security/CreateKeyBackupDialog.tsx @@ -72,14 +72,15 @@ export default class CreateKeyBackupDialog extends React.PureComponent => { - info = await MatrixClientPeg.get().prepareKeyBackupVersion(null /* random key */, { + info = await cli.prepareKeyBackupVersion(null /* random key */, { secureSecretStorage: true, }); - info = await MatrixClientPeg.get().createKeyBackupVersion(info); + info = await cli.createKeyBackupVersion(info); }); - await MatrixClientPeg.get().scheduleAllGroupSessionsForBackup(); + await cli.scheduleAllGroupSessionsForBackup(); this.setState({ phase: Phase.Done, }); @@ -90,7 +91,7 @@ export default class CreateKeyBackupDialog extends React.PureComponent { try { - const backupInfo = await MatrixClientPeg.get().getKeyBackupVersion(); + const cli = MatrixClientPeg.safeGet(); + const backupInfo = await cli.getKeyBackupVersion(); const backupSigStatus = // we may not have started crypto yet, in which case we definitely don't trust the backup - backupInfo && MatrixClientPeg.get().isCryptoEnabled() - ? await MatrixClientPeg.get().isKeyBackupTrusted(backupInfo) - : null; + backupInfo && cli.isCryptoEnabled() ? await cli.isKeyBackupTrusted(backupInfo) : null; const { forceReset } = this.props; const phase = backupInfo && !forceReset ? Phase.Migrate : Phase.ChooseKeyPassphrase; @@ -204,7 +203,7 @@ export default class CreateSecretStorageDialog extends React.PureComponent { try { - await MatrixClientPeg.get().uploadDeviceSigningKeys(undefined, {} as CrossSigningKeys); + await MatrixClientPeg.safeGet().uploadDeviceSigningKeys(undefined, {} as CrossSigningKeys); // We should never get here: the server should always require // UI auth to upload device signing keys. If we do, we upload // no keys which would be a no-op. @@ -235,7 +234,7 @@ export default class CreateSecretStorageDialog extends React.PureComponent => { if (this.state.passPhraseKeySelected === SecureBackupSetupMethod.Key) { - this.recoveryKey = await MatrixClientPeg.get().createRecoveryKeyFromPassphrase(); + this.recoveryKey = await MatrixClientPeg.safeGet().createRecoveryKeyFromPassphrase(); this.setState({ copied: false, downloaded: false, @@ -286,11 +285,11 @@ export default class CreateSecretStorageDialog extends React.PureComponent

); - let content; - if (MatrixClientPeg.get().getKeyBackupEnabled()) { + let content: JSX.Element | undefined; + if (MatrixClientPeg.safeGet().getKeyBackupEnabled()) { content = (
{newMethodDetected} diff --git a/src/audio/PlaybackQueue.ts b/src/audio/PlaybackQueue.ts index 6ae899704b..c9b61cc2f6 100644 --- a/src/audio/PlaybackQueue.ts +++ b/src/audio/PlaybackQueue.ts @@ -64,7 +64,7 @@ export class PlaybackQueue { } public static forRoom(roomId: string): PlaybackQueue { - const cli = MatrixClientPeg.get(); + const cli = MatrixClientPeg.safeGet(); const room = cli.getRoom(roomId); if (!room) throw new Error("Unknown room"); if (PlaybackQueue.queues.has(room.roomId)) { diff --git a/src/autocomplete/NotifProvider.tsx b/src/autocomplete/NotifProvider.tsx index d4a4793ab5..51373a7403 100644 --- a/src/autocomplete/NotifProvider.tsx +++ b/src/autocomplete/NotifProvider.tsx @@ -38,9 +38,9 @@ export default class NotifProvider extends AutocompleteProvider { force = false, limit = -1, ): Promise { - const client = MatrixClientPeg.get(); + const client = MatrixClientPeg.safeGet(); - if (!this.room.currentState.mayTriggerNotifOfType("room", client.credentials.userId!)) return []; + if (!this.room.currentState.mayTriggerNotifOfType("room", client.getSafeUserId())) return []; const { command, range } = this.getCurrentCommand(query, selection, force); if ( diff --git a/src/autocomplete/RoomProvider.tsx b/src/autocomplete/RoomProvider.tsx index 48f1582be0..a28777e65d 100644 --- a/src/autocomplete/RoomProvider.tsx +++ b/src/autocomplete/RoomProvider.tsx @@ -65,7 +65,7 @@ export default class RoomProvider extends AutocompleteProvider { } protected getRooms(): Room[] { - const cli = MatrixClientPeg.get(); + const cli = MatrixClientPeg.safeGet(); // filter out spaces here as they get their own autocomplete provider return cli diff --git a/src/autocomplete/SpaceProvider.tsx b/src/autocomplete/SpaceProvider.tsx index 3f6bd30e3e..4c6b8a3259 100644 --- a/src/autocomplete/SpaceProvider.tsx +++ b/src/autocomplete/SpaceProvider.tsx @@ -24,7 +24,7 @@ import RoomProvider from "./RoomProvider"; export default class SpaceProvider extends RoomProvider { protected getRooms(): Room[] { - return MatrixClientPeg.get() + return MatrixClientPeg.safeGet() .getVisibleRooms(SettingsStore.getValue("feature_dynamic_room_predecessors")) .filter((r) => r.isSpaceRoom()); } diff --git a/src/autocomplete/UserProvider.tsx b/src/autocomplete/UserProvider.tsx index 3d4c542c48..51a9806408 100644 --- a/src/autocomplete/UserProvider.tsx +++ b/src/autocomplete/UserProvider.tsx @@ -60,15 +60,13 @@ export default class UserProvider extends AutocompleteProvider { shouldMatchWordsOnly: false, }); - MatrixClientPeg.get().on(RoomEvent.Timeline, this.onRoomTimeline); - MatrixClientPeg.get().on(RoomStateEvent.Update, this.onRoomStateUpdate); + MatrixClientPeg.safeGet().on(RoomEvent.Timeline, this.onRoomTimeline); + MatrixClientPeg.safeGet().on(RoomStateEvent.Update, this.onRoomStateUpdate); } public destroy(): void { - if (MatrixClientPeg.get()) { - MatrixClientPeg.get().removeListener(RoomEvent.Timeline, this.onRoomTimeline); - MatrixClientPeg.get().removeListener(RoomStateEvent.Update, this.onRoomStateUpdate); - } + MatrixClientPeg.get()?.removeListener(RoomEvent.Timeline, this.onRoomTimeline); + MatrixClientPeg.get()?.removeListener(RoomStateEvent.Update, this.onRoomStateUpdate); } private onRoomTimeline = ( @@ -155,7 +153,7 @@ export default class UserProvider extends AutocompleteProvider { lastSpoken[event.getSender()!] = event.getTs(); } - const currentUserId = MatrixClientPeg.get().credentials.userId; + const currentUserId = MatrixClientPeg.safeGet().credentials.userId; this.users = this.room.getJoinedMembers().filter(({ userId }) => userId !== currentUserId); this.users = this.users.concat(this.room.getMembersWithMembership("invite")); @@ -167,7 +165,7 @@ export default class UserProvider extends AutocompleteProvider { public onUserSpoke(user: RoomMember | null): void { if (!this.users) return; if (!user) return; - if (user.userId === MatrixClientPeg.get().credentials.userId) return; + if (user.userId === MatrixClientPeg.safeGet().getSafeUserId()) return; // Move the user that spoke to the front of the array this.users.splice( diff --git a/src/components/structures/MessagePanel.tsx b/src/components/structures/MessagePanel.tsx index 687ab1f56a..479e6d6aa9 100644 --- a/src/components/structures/MessagePanel.tsx +++ b/src/components/structures/MessagePanel.tsx @@ -25,6 +25,7 @@ import { RoomStateEvent } from "matrix-js-sdk/src/models/room-state"; import { M_BEACON_INFO } from "matrix-js-sdk/src/@types/beacon"; import { isSupportedReceiptType } from "matrix-js-sdk/src/utils"; import { Optional } from "matrix-events-sdk"; +import { MatrixClient } from "matrix-js-sdk/src/matrix"; import shouldHideEvent from "../../shouldHideEvent"; import { wantsDateSeparator } from "../../DateUtils"; @@ -73,6 +74,7 @@ const groupedStateEvents = [ export function shouldFormContinuation( prevEvent: MatrixEvent | null, mxEvent: MatrixEvent, + matrixClient: MatrixClient, showHiddenEvents: boolean, timelineRenderingType?: TimelineRenderingType, ): boolean { @@ -110,7 +112,7 @@ export function shouldFormContinuation( } // if we don't have tile for previous event then it was shown by showHiddenEvents and has no SenderProfile - if (!haveRendererForEvent(prevEvent, showHiddenEvents)) return false; + if (!haveRendererForEvent(prevEvent, matrixClient, showHiddenEvents)) return false; return true; } @@ -481,7 +483,7 @@ export default class MessagePanel extends React.Component { return true; } - if (!haveRendererForEvent(mxEv, this.showHiddenEvents)) { + if (!haveRendererForEvent(mxEv, MatrixClientPeg.safeGet(), this.showHiddenEvents)) { return false; // no tile = no show } @@ -736,6 +738,7 @@ export default class MessagePanel extends React.Component { ret.push(dateSeparator); } + const cli = MatrixClientPeg.safeGet(); let lastInSection = true; if (nextEventWithTile) { const nextEv = nextEventWithTile; @@ -743,14 +746,14 @@ export default class MessagePanel extends React.Component { lastInSection = willWantDateSeparator || mxEv.getSender() !== nextEv.getSender() || - getEventDisplayInfo(MatrixClientPeg.safeGet(), nextEv, this.showHiddenEvents).isInfoMessage || - !shouldFormContinuation(mxEv, nextEv, this.showHiddenEvents, this.context.timelineRenderingType); + getEventDisplayInfo(cli, nextEv, this.showHiddenEvents).isInfoMessage || + !shouldFormContinuation(mxEv, nextEv, cli, this.showHiddenEvents, this.context.timelineRenderingType); } // is this a continuation of the previous message? const continuation = !wantsDateSeparator && - shouldFormContinuation(prevEvent, mxEv, this.showHiddenEvents, this.context.timelineRenderingType); + shouldFormContinuation(prevEvent, mxEv, cli, this.showHiddenEvents, this.context.timelineRenderingType); const eventId = mxEv.getId()!; const highlight = eventId === this.props.highlightedEventId; diff --git a/src/components/structures/RoomSearchView.tsx b/src/components/structures/RoomSearchView.tsx index 83a63387d5..d0f2c362be 100644 --- a/src/components/structures/RoomSearchView.tsx +++ b/src/components/structures/RoomSearchView.tsx @@ -237,7 +237,7 @@ export const RoomSearchView = forwardRef( continue; } - if (!haveRendererForEvent(mxEv, roomContext.showHiddenEvents)) { + if (!haveRendererForEvent(mxEv, client, roomContext.showHiddenEvents)) { // XXX: can this ever happen? It will make the result count // not match the displayed count. continue; diff --git a/src/components/structures/TimelinePanel.tsx b/src/components/structures/TimelinePanel.tsx index 103e6d844b..fc9b29d69f 100644 --- a/src/components/structures/TimelinePanel.tsx +++ b/src/components/structures/TimelinePanel.tsx @@ -1965,7 +1965,8 @@ class TimelinePanel extends React.Component { !!ev.status || // local echo (ignoreOwn && ev.getSender() === myUserId); // own message const isWithoutTile = - !haveRendererForEvent(ev, this.context?.showHiddenEvents) || shouldHideEvent(ev, this.context); + !haveRendererForEvent(ev, MatrixClientPeg.safeGet(), this.context?.showHiddenEvents) || + shouldHideEvent(ev, this.context); if (isWithoutTile || !node) { // don't start counting if the event should be ignored, diff --git a/src/components/views/dialogs/ConfirmRedactDialog.tsx b/src/components/views/dialogs/ConfirmRedactDialog.tsx index 9c1fa2b181..0cf77871c0 100644 --- a/src/components/views/dialogs/ConfirmRedactDialog.tsx +++ b/src/components/views/dialogs/ConfirmRedactDialog.tsx @@ -75,7 +75,7 @@ export function createRedactEventDialog({ onFinished: async (proceed, reason): Promise => { if (!proceed) return; - const cli = MatrixClientPeg.get(); + const cli = MatrixClientPeg.safeGet(); const withRelTypes: Pick = {}; // redact related events if this is a voice broadcast started event and diff --git a/src/components/views/dialogs/ManualDeviceKeyVerificationDialog.tsx b/src/components/views/dialogs/ManualDeviceKeyVerificationDialog.tsx index d81d6e76ca..0946f2dbb9 100644 --- a/src/components/views/dialogs/ManualDeviceKeyVerificationDialog.tsx +++ b/src/components/views/dialogs/ManualDeviceKeyVerificationDialog.tsx @@ -37,7 +37,7 @@ export function ManualDeviceKeyVerificationDialog({ device, onFinished, }: IManualDeviceKeyVerificationDialogProps): JSX.Element { - const mxClient = MatrixClientPeg.get(); + const mxClient = MatrixClientPeg.safeGet(); const onLegacyFinished = useCallback( (confirm: boolean) => { diff --git a/src/components/views/right_panel/VerificationPanel.tsx b/src/components/views/right_panel/VerificationPanel.tsx index 6782c967a2..0abee5e81f 100644 --- a/src/components/views/right_panel/VerificationPanel.tsx +++ b/src/components/views/right_panel/VerificationPanel.tsx @@ -203,9 +203,9 @@ export default class VerificationPanel extends React.PureComponent let replyChain: JSX.Element | undefined; if ( - haveRendererForEvent(this.props.mxEvent, this.context.showHiddenEvents) && + haveRendererForEvent(this.props.mxEvent, MatrixClientPeg.safeGet(), this.context.showHiddenEvents) && shouldDisplayReply(this.props.mxEvent) ) { replyChain = ( diff --git a/src/components/views/rooms/SearchResultTile.tsx b/src/components/views/rooms/SearchResultTile.tsx index a4b369150e..c7366c6c14 100644 --- a/src/components/views/rooms/SearchResultTile.tsx +++ b/src/components/views/rooms/SearchResultTile.tsx @@ -27,6 +27,7 @@ import { shouldFormContinuation } from "../../structures/MessagePanel"; import { wantsDateSeparator } from "../../../DateUtils"; import LegacyCallEventGrouper, { buildLegacyCallEventGroupers } from "../../structures/LegacyCallEventGrouper"; import { haveRendererForEvent } from "../../../events/EventTileFactory"; +import { MatrixClientPeg } from "../../../MatrixClientPeg"; interface IProps { // a list of strings to be highlighted in the results @@ -69,6 +70,7 @@ export default class SearchResultTile extends React.Component { const isTwelveHour = SettingsStore.getValue("showTwelveHourTimestamps"); const alwaysShowTimestamps = SettingsStore.getValue("alwaysShowTimestamps"); + const cli = MatrixClientPeg.safeGet(); for (let j = 0; j < timeline.length; j++) { const mxEv = timeline[j]; let highlights: string[] | undefined; @@ -77,14 +79,20 @@ export default class SearchResultTile extends React.Component { highlights = this.props.searchHighlights; } - if (haveRendererForEvent(mxEv, this.context?.showHiddenEvents)) { + if (haveRendererForEvent(mxEv, cli, this.context?.showHiddenEvents)) { // do we need a date separator since the last event? const prevEv = timeline[j - 1]; // is this a continuation of the previous message? const continuation = prevEv && !wantsDateSeparator(prevEv.getDate() || undefined, mxEv.getDate() || undefined) && - shouldFormContinuation(prevEv, mxEv, this.context?.showHiddenEvents, TimelineRenderingType.Search); + shouldFormContinuation( + prevEv, + mxEv, + cli, + this.context?.showHiddenEvents, + TimelineRenderingType.Search, + ); let lastInSection = true; const nextEv = timeline[j + 1]; @@ -99,6 +107,7 @@ export default class SearchResultTile extends React.Component { !shouldFormContinuation( mxEv, nextEv, + cli, this.context?.showHiddenEvents, TimelineRenderingType.Search, ); diff --git a/src/components/views/settings/CrossSigningPanel.tsx b/src/components/views/settings/CrossSigningPanel.tsx index f5b893e4ea..ed53bd274c 100644 --- a/src/components/views/settings/CrossSigningPanel.tsx +++ b/src/components/views/settings/CrossSigningPanel.tsx @@ -51,7 +51,7 @@ export default class CrossSigningPanel extends React.PureComponent<{}, IState> { } public componentDidMount(): void { - const cli = MatrixClientPeg.get(); + const cli = MatrixClientPeg.safeGet(); cli.on(ClientEvent.AccountData, this.onAccountData); cli.on(CryptoEvent.UserTrustStatusChanged, this.onStatusChanged); cli.on(CryptoEvent.KeysChanged, this.onStatusChanged); @@ -89,7 +89,7 @@ export default class CrossSigningPanel extends React.PureComponent<{}, IState> { }; private async getUpdatedStatus(): Promise { - const cli = MatrixClientPeg.get(); + const cli = MatrixClientPeg.safeGet(); const crypto = cli.getCrypto(); if (!crypto) return; @@ -127,7 +127,7 @@ export default class CrossSigningPanel extends React.PureComponent<{}, IState> { private bootstrapCrossSigning = async ({ forceReset = false }): Promise => { this.setState({ error: undefined }); try { - const cli = MatrixClientPeg.get(); + const cli = MatrixClientPeg.safeGet(); await cli.bootstrapCrossSigning({ authUploadDeviceSigningKeys: async (makeRequest): Promise => { const { finished } = Modal.createDialog(InteractiveAuthDialog, { diff --git a/src/components/views/settings/tabs/user/MjolnirUserSettingsTab.tsx b/src/components/views/settings/tabs/user/MjolnirUserSettingsTab.tsx index 150db726a7..92981407fb 100644 --- a/src/components/views/settings/tabs/user/MjolnirUserSettingsTab.tsx +++ b/src/components/views/settings/tabs/user/MjolnirUserSettingsTab.tsx @@ -89,7 +89,7 @@ export default class MjolnirUserSettingsTab extends React.Component<{}, IState> this.setState({ busy: true }); try { - const room = await MatrixClientPeg.get().joinRoom(this.state.newList); + const room = await MatrixClientPeg.safeGet().joinRoom(this.state.newList); await Mjolnir.sharedInstance().subscribeToList(room.roomId); this.setState({ newList: "" }); // this will also cause the new rule to be rendered } catch (e) { @@ -125,7 +125,7 @@ export default class MjolnirUserSettingsTab extends React.Component<{}, IState> this.setState({ busy: true }); try { await Mjolnir.sharedInstance().unsubscribeFromList(list.roomId); - await MatrixClientPeg.get().leave(list.roomId); + await MatrixClientPeg.safeGet().leave(list.roomId); } catch (e) { logger.error(e); @@ -139,7 +139,7 @@ export default class MjolnirUserSettingsTab extends React.Component<{}, IState> } private viewListRules(list: BanList): void { - const room = MatrixClientPeg.get().getRoom(list.roomId); + const room = MatrixClientPeg.safeGet().getRoom(list.roomId); const name = room ? room.name : list.roomId; const renderRules = (rules: ListRule[]): JSX.Element => { @@ -210,7 +210,7 @@ export default class MjolnirUserSettingsTab extends React.Component<{}, IState> const tiles: JSX.Element[] = []; for (const list of lists) { - const room = MatrixClientPeg.get().getRoom(list.roomId); + const room = MatrixClientPeg.safeGet().getRoom(list.roomId); const name = room ? ( {room.name} ({list.roomId}) diff --git a/src/components/views/settings/tabs/user/SecurityUserSettingsTab.tsx b/src/components/views/settings/tabs/user/SecurityUserSettingsTab.tsx index c902577b61..e77627323e 100644 --- a/src/components/views/settings/tabs/user/SecurityUserSettingsTab.tsx +++ b/src/components/views/settings/tabs/user/SecurityUserSettingsTab.tsx @@ -93,7 +93,7 @@ export default class SecurityUserSettingsTab extends React.Component room.roomId)); this.state = { - ignoredUserIds: MatrixClientPeg.get().getIgnoredUsers(), + ignoredUserIds: MatrixClientPeg.safeGet().getIgnoredUsers(), waitingUnignored: [], managingInvites: false, invitedRoomIds, @@ -102,7 +102,7 @@ export default class SecurityUserSettingsTab extends React.Component { if (action === "ignore_state_changed") { - const ignoredUserIds = MatrixClientPeg.get().getIgnoredUsers(); + const ignoredUserIds = MatrixClientPeg.safeGet().getIgnoredUsers(); const newWaitingUnignored = this.state.waitingUnignored.filter((e) => ignoredUserIds.includes(e)); this.setState({ ignoredUserIds, waitingUnignored: newWaitingUnignored }); } @@ -110,15 +110,15 @@ export default class SecurityUserSettingsTab extends React.Component this.setState({ versions })); } public componentWillUnmount(): void { if (this.dispatcherRef) dis.unregister(this.dispatcherRef); - MatrixClientPeg.get().removeListener(RoomEvent.MyMembership, this.onMyMembership); + MatrixClientPeg.safeGet().removeListener(RoomEvent.MyMembership, this.onMyMembership); } private onMyMembership = (room: Room, membership: string): void => { @@ -159,15 +159,15 @@ export default class SecurityUserSettingsTab extends React.Component ({ waitingUnignored: [...waitingUnignored, userId] })); - MatrixClientPeg.get().setIgnoredUsers(currentlyIgnoredUserIds); + MatrixClientPeg.safeGet().setIgnoredUsers(currentlyIgnoredUserIds); } }; private getInvitedRooms = (): Room[] => { - return MatrixClientPeg.get() + return MatrixClientPeg.safeGet() .getRooms() .filter((r) => { - return r.hasMembershipState(MatrixClientPeg.get().getUserId()!, "invite"); + return r.hasMembershipState(MatrixClientPeg.safeGet().getUserId()!, "invite"); }); }; @@ -180,7 +180,7 @@ export default class SecurityUserSettingsTab extends React.Component {_t( diff --git a/src/components/views/toasts/VerificationRequestToast.tsx b/src/components/views/toasts/VerificationRequestToast.tsx index 9f2aa89312..0e9a7781f6 100644 --- a/src/components/views/toasts/VerificationRequestToast.tsx +++ b/src/components/views/toasts/VerificationRequestToast.tsx @@ -75,7 +75,7 @@ export default class VerificationRequestToast extends React.PureComponent({ @@ -165,12 +165,12 @@ export default class VerificationRequestToast extends React.PureComponent { - cli = cli ?? MatrixClientPeg.get(); // because param defaults don't do the correct thing + cli = cli ?? MatrixClientPeg.safeGet(); // because param defaults don't do the correct thing const factory = pickFactory(props.mxEvent, cli, showHiddenEvents); if (!factory) return undefined; @@ -378,7 +378,7 @@ export function renderReplyTile( showHiddenEvents: boolean, cli?: MatrixClient, ): Optional { - cli = cli ?? MatrixClientPeg.get(); // because param defaults don't do the correct thing + cli = cli ?? MatrixClientPeg.safeGet(); // because param defaults don't do the correct thing const factory = pickFactory(props.mxEvent, cli, showHiddenEvents); if (!factory) return undefined; @@ -426,7 +426,11 @@ export function isMessageEvent(ev: MatrixEvent): boolean { ); } -export function haveRendererForEvent(mxEvent: MatrixEvent, showHiddenEvents: boolean): boolean { +export function haveRendererForEvent( + mxEvent: MatrixEvent, + matrixClient: MatrixClient, + showHiddenEvents: boolean, +): boolean { // Only show "Message deleted" tile for plain message events, encrypted events, // and state events as they'll likely still contain enough keys to be relevant. if (mxEvent.isRedacted() && !mxEvent.isEncrypted() && !isMessageEvent(mxEvent) && !mxEvent.isState()) { @@ -436,14 +440,13 @@ export function haveRendererForEvent(mxEvent: MatrixEvent, showHiddenEvents: boo // No tile for replacement events since they update the original tile if (mxEvent.isRelation(RelationType.Replace)) return false; - const cli = MatrixClientPeg.get(); - const handler = pickFactory(mxEvent, cli, showHiddenEvents); + const handler = pickFactory(mxEvent, matrixClient, showHiddenEvents); if (!handler) return false; if (handler === TextualEventFactory) { - return hasText(mxEvent, cli, showHiddenEvents); + return hasText(mxEvent, matrixClient, showHiddenEvents); } else if (handler === STATE_EVENT_TILE_TYPES.get(EventType.RoomCreate)) { const dynamicPredecessorsEnabled = SettingsStore.getValue("feature_dynamic_room_predecessors"); - const predecessor = cli.getRoom(mxEvent.getRoomId())?.findPredecessor(dynamicPredecessorsEnabled); + const predecessor = matrixClient.getRoom(mxEvent.getRoomId())?.findPredecessor(dynamicPredecessorsEnabled); return Boolean(predecessor); } else if ( ElementCall.CALL_EVENT_TYPE.names.some((eventType) => handler === STATE_EVENT_TILE_TYPES.get(eventType)) diff --git a/src/indexing/EventIndex.ts b/src/indexing/EventIndex.ts index 2abecef074..5a24b2ae36 100644 --- a/src/indexing/EventIndex.ts +++ b/src/indexing/EventIndex.ts @@ -69,7 +69,7 @@ export default class EventIndex extends EventEmitter { * Register event listeners that are necessary for the event index to work. */ public registerListeners(): void { - const client = MatrixClientPeg.get(); + const client = MatrixClientPeg.safeGet(); client.on(ClientEvent.Sync, this.onSync); client.on(RoomEvent.Timeline, this.onRoomTimeline); @@ -96,7 +96,7 @@ export default class EventIndex extends EventEmitter { public async addInitialCheckpoints(): Promise { const indexManager = PlatformPeg.get()?.getEventIndexingManager(); if (!indexManager) return; - const client = MatrixClientPeg.get(); + const client = MatrixClientPeg.safeGet(); const rooms = client.getRooms(); const isRoomEncrypted = (room: Room): boolean => { @@ -200,7 +200,7 @@ export default class EventIndex extends EventEmitter { ): Promise => { if (!room) return; // notification timeline, we'll get this event again with a room specific timeline - const client = MatrixClientPeg.get(); + const client = MatrixClientPeg.safeGet(); // We only index encrypted rooms locally. if (!client.isRoomEncrypted(ev.getRoomId()!)) return; @@ -220,7 +220,7 @@ export default class EventIndex extends EventEmitter { }; private onRoomStateEvent = async (ev: MatrixEvent, state: RoomState): Promise => { - if (!MatrixClientPeg.get().isRoomEncrypted(state.roomId)) return; + if (!MatrixClientPeg.safeGet().isRoomEncrypted(state.roomId)) return; if (ev.getType() === EventType.RoomEncryption && !(await this.isRoomIndexed(state.roomId))) { logger.log("EventIndex: Adding a checkpoint for a newly encrypted room", state.roomId); @@ -254,7 +254,7 @@ export default class EventIndex extends EventEmitter { */ private onTimelineReset = async (room: Room | undefined): Promise => { if (!room) return; - if (!MatrixClientPeg.get().isRoomEncrypted(room.roomId)) return; + if (!MatrixClientPeg.safeGet().isRoomEncrypted(room.roomId)) return; logger.log("EventIndex: Adding a checkpoint because of a limited timeline", room.roomId); @@ -364,7 +364,7 @@ export default class EventIndex extends EventEmitter { private async addRoomCheckpoint(roomId: string, fullCrawl = false): Promise { const indexManager = PlatformPeg.get()?.getEventIndexingManager(); if (!indexManager) return; - const client = MatrixClientPeg.get(); + const client = MatrixClientPeg.safeGet(); const room = client.getRoom(roomId); if (!room) return; @@ -410,7 +410,7 @@ export default class EventIndex extends EventEmitter { private async crawlerFunc(): Promise { let cancelled = false; - const client = MatrixClientPeg.get(); + const client = MatrixClientPeg.safeGet(); const indexManager = PlatformPeg.get()?.getEventIndexingManager(); if (!indexManager) return; @@ -707,7 +707,7 @@ export default class EventIndex extends EventEmitter { fromEvent?: string, direction: string = EventTimeline.BACKWARDS, ): Promise { - const client = MatrixClientPeg.get(); + const client = MatrixClientPeg.safeGet(); const indexManager = PlatformPeg.get()?.getEventIndexingManager(); if (!indexManager) return []; @@ -942,7 +942,7 @@ export default class EventIndex extends EventEmitter { return null; } - const client = MatrixClientPeg.get(); + const client = MatrixClientPeg.safeGet(); if (this.currentCheckpoint !== null) { return client.getRoom(this.currentCheckpoint.roomId); @@ -966,7 +966,7 @@ export default class EventIndex extends EventEmitter { crawlingRooms.add(this.currentCheckpoint.roomId); } - const client = MatrixClientPeg.get(); + const client = MatrixClientPeg.safeGet(); const rooms = client.getRooms(); const isRoomEncrypted = (room: Room): boolean => { diff --git a/src/integrations/IntegrationManagers.ts b/src/integrations/IntegrationManagers.ts index 38e5bcb96f..3464bbc20e 100644 --- a/src/integrations/IntegrationManagers.ts +++ b/src/integrations/IntegrationManagers.ts @@ -55,7 +55,7 @@ export class IntegrationManagers { public startWatching(): void { this.stopWatching(); - this.client = MatrixClientPeg.get(); + this.client = MatrixClientPeg.safeGet(); this.client.on(ClientEvent.AccountData, this.onAccountData); this.client.on(ClientEvent.ClientWellKnown, this.setupHomeserverManagers); this.compileManagers(); diff --git a/src/linkify-matrix.ts b/src/linkify-matrix.ts index d753bc21d7..085143f198 100644 --- a/src/linkify-matrix.ts +++ b/src/linkify-matrix.ts @@ -197,7 +197,7 @@ export const options: Opts = { case Type.RoomAlias: case Type.UserId: default: { - return tryTransformEntityToPermalink(MatrixClientPeg.get(), href) ?? ""; + return tryTransformEntityToPermalink(MatrixClientPeg.safeGet(), href) ?? ""; } } }, diff --git a/src/mjolnir/BanList.ts b/src/mjolnir/BanList.ts index 7eec5d3b26..0d82a4c080 100644 --- a/src/mjolnir/BanList.ts +++ b/src/mjolnir/BanList.ts @@ -70,7 +70,7 @@ export class BanList { public async banEntity(kind: string, entity: string, reason: string): Promise { const type = ruleTypeToStable(kind); if (!type) return; // unknown rule type - await MatrixClientPeg.get().sendStateEvent( + await MatrixClientPeg.safeGet().sendStateEvent( this._roomId, type, { @@ -87,7 +87,7 @@ export class BanList { const type = ruleTypeToStable(kind); if (!type) return; // unknown rule type // Empty state event is effectively deleting it. - await MatrixClientPeg.get().sendStateEvent(this._roomId, type, {}, "rule:" + entity); + await MatrixClientPeg.safeGet().sendStateEvent(this._roomId, type, {}, "rule:" + entity); this._rules = this._rules.filter((r) => { if (r.kind !== ruleTypeToStable(kind)) return true; if (r.entity !== entity) return true; @@ -98,7 +98,7 @@ export class BanList { public updateList(): void { this._rules = []; - const room = MatrixClientPeg.get().getRoom(this._roomId); + const room = MatrixClientPeg.safeGet().getRoom(this._roomId); if (!room) return; for (const eventType of ALL_RULE_TYPES) { diff --git a/src/mjolnir/Mjolnir.ts b/src/mjolnir/Mjolnir.ts index 6df52f808a..303db61f8b 100644 --- a/src/mjolnir/Mjolnir.ts +++ b/src/mjolnir/Mjolnir.ts @@ -67,7 +67,7 @@ export class Mjolnir { public setup(): void { if (!MatrixClientPeg.get()) return; this.updateLists(SettingsStore.getValue("mjolnirRooms")); - MatrixClientPeg.get().on(RoomStateEvent.Events, this.onEvent); + MatrixClientPeg.get()!.on(RoomStateEvent.Events, this.onEvent); } public stop(): void { @@ -81,14 +81,13 @@ export class Mjolnir { this.dispatcherRef = null; } - if (!MatrixClientPeg.get()) return; - MatrixClientPeg.get().removeListener(RoomStateEvent.Events, this.onEvent); + MatrixClientPeg.get()?.removeListener(RoomStateEvent.Events, this.onEvent); } public async getOrCreatePersonalList(): Promise { let personalRoomId = SettingsStore.getValue("mjolnirPersonalRoom"); if (!personalRoomId) { - const resp = await MatrixClientPeg.get().createRoom({ + const resp = await MatrixClientPeg.safeGet().createRoom({ name: _t("My Ban List"), topic: _t("This is your list of users/servers you have blocked - don't leave the room!"), preset: Preset.PrivateChat, diff --git a/src/modules/ProxiedModuleApi.ts b/src/modules/ProxiedModuleApi.ts index 5d93c67eec..e66d3cbe10 100644 --- a/src/modules/ProxiedModuleApi.ts +++ b/src/modules/ProxiedModuleApi.ts @@ -189,7 +189,7 @@ export class ProxiedModuleApi implements ModuleApi { roomId = getCachedRoomIDForAlias(parts.roomIdOrAlias); if (!roomId) { // alias resolution failed - const result = await MatrixClientPeg.get().getRoomIdForAlias(parts.roomIdOrAlias); + const result = await MatrixClientPeg.safeGet().getRoomIdForAlias(parts.roomIdOrAlias); roomId = result.room_id; if (!servers) servers = result.servers; // use provided servers first, if available } diff --git a/src/sentry.ts b/src/sentry.ts index 61098bf59d..909a21ed1c 100644 --- a/src/sentry.ts +++ b/src/sentry.ts @@ -165,7 +165,7 @@ function getDeviceContext(client: MatrixClient): DeviceContext { } async function getContexts(): Promise { - const client = MatrixClientPeg.get(); + const client = MatrixClientPeg.safeGet(); return { user: getUserContext(client), crypto: await getCryptoContext(client), diff --git a/src/settings/SettingsStore.ts b/src/settings/SettingsStore.ts index a51faa2cee..7169494f4c 100644 --- a/src/settings/SettingsStore.ts +++ b/src/settings/SettingsStore.ts @@ -611,7 +611,7 @@ export default class SettingsStore { } private static migrateHiddenReadReceipts(): void { - if (MatrixClientPeg.get().isGuest()) return; // not worth it + if (MatrixClientPeg.safeGet().isGuest()) return; // not worth it // We wait for the first sync to ensure that the user's existing account data has loaded, as otherwise // getValue() for an account-level setting like sendReadReceipts will return `null`. diff --git a/src/settings/controllers/NotificationControllers.ts b/src/settings/controllers/NotificationControllers.ts index aae9b16ba2..1827ab98ec 100644 --- a/src/settings/controllers/NotificationControllers.ts +++ b/src/settings/controllers/NotificationControllers.ts @@ -28,7 +28,7 @@ import { SettingLevel } from "../SettingLevel"; // default action on this rule is dont_notify, but it could be something else export function isPushNotifyDisabled(): boolean { // Return the value of the master push rule as a default - const processor = new PushProcessor(MatrixClientPeg.get()); + const processor = new PushProcessor(MatrixClientPeg.safeGet()); const masterRule = processor.getPushRuleById(".m.rule.master"); if (!masterRule) { diff --git a/src/toasts/DesktopNotificationsToast.ts b/src/toasts/DesktopNotificationsToast.ts index 856c6d9dd6..73f5ff3c15 100644 --- a/src/toasts/DesktopNotificationsToast.ts +++ b/src/toasts/DesktopNotificationsToast.ts @@ -23,7 +23,7 @@ import { getLocalNotificationAccountDataEventType } from "../utils/notifications const onAccept = (): void => { Notifier.setEnabled(true); - const cli = MatrixClientPeg.get(); + const cli = MatrixClientPeg.safeGet(); const eventType = getLocalNotificationAccountDataEventType(cli.deviceId!); cli.setAccountData(eventType, { is_silenced: false, diff --git a/src/toasts/IncomingCallToast.tsx b/src/toasts/IncomingCallToast.tsx index 72df4fd122..1dd7a0343c 100644 --- a/src/toasts/IncomingCallToast.tsx +++ b/src/toasts/IncomingCallToast.tsx @@ -67,7 +67,7 @@ interface Props { export function IncomingCallToast({ callEvent }: Props): JSX.Element { const roomId = callEvent.getRoomId()!; - const room = MatrixClientPeg.get().getRoom(roomId) ?? undefined; + const room = MatrixClientPeg.safeGet().getRoom(roomId) ?? undefined; const call = useCall(roomId); const dismissToast = useCallback((): void => { diff --git a/src/toasts/IncomingLegacyCallToast.tsx b/src/toasts/IncomingLegacyCallToast.tsx index 33a904afe7..7464f806f1 100644 --- a/src/toasts/IncomingLegacyCallToast.tsx +++ b/src/toasts/IncomingLegacyCallToast.tsx @@ -92,7 +92,7 @@ export default class IncomingLegacyCallToast extends React.Component => { - const cli = MatrixClientPeg.get(); + const cli = MatrixClientPeg.safeGet(); const onAccept = (): void => { DeviceListener.sharedInstance().dismissUnverifiedSessions([deviceId]); diff --git a/src/utils/EventRenderingUtils.ts b/src/utils/EventRenderingUtils.ts index eda5a14320..ebbd688c9c 100644 --- a/src/utils/EventRenderingUtils.ts +++ b/src/utils/EventRenderingUtils.ts @@ -101,7 +101,7 @@ export function getEventDisplayInfo( // source tile when there's no regular tile for an event and also for // replace relations (which otherwise would display as a confusing // duplicate of the thing they are replacing). - if (hideEvent || !haveRendererForEvent(mxEvent, showHiddenEvents)) { + if (hideEvent || !haveRendererForEvent(mxEvent, matrixClient, showHiddenEvents)) { // forcefully ask for a factory for a hidden event (hidden event setting is checked internally) factory = pickFactory(mxEvent, matrixClient, showHiddenEvents, true); if (factory === JSONEventFactory) { diff --git a/src/utils/exportUtils/HtmlExport.tsx b/src/utils/exportUtils/HtmlExport.tsx index 0dd315819a..7863ae01b1 100644 --- a/src/utils/exportUtils/HtmlExport.tsx +++ b/src/utils/exportUtils/HtmlExport.tsx @@ -429,11 +429,12 @@ export default class HTMLExporter extends Exporter { true, ); if (this.cancelled) return this.cleanUp(); - if (!haveRendererForEvent(event, false)) continue; + if (!haveRendererForEvent(event, this.room.client, false)) continue; content += this.needsDateSeparator(event, prevEvent) ? this.getDateSeparator(event) : ""; const shouldBeJoined = - !this.needsDateSeparator(event, prevEvent) && shouldFormContinuation(prevEvent, event, false); + !this.needsDateSeparator(event, prevEvent) && + shouldFormContinuation(prevEvent, event, this.room.client, false); const body = await this.createMessageBody(event, shouldBeJoined); this.totalSize += Buffer.byteLength(body); content += body; diff --git a/src/utils/exportUtils/JSONExport.ts b/src/utils/exportUtils/JSONExport.ts index 17546790c7..a78f11caa4 100644 --- a/src/utils/exportUtils/JSONExport.ts +++ b/src/utils/exportUtils/JSONExport.ts @@ -93,7 +93,7 @@ export default class JSONExporter extends Exporter { true, ); if (this.cancelled) return this.cleanUp(); - if (!haveRendererForEvent(event, false)) continue; + if (!haveRendererForEvent(event, this.room.client, false)) continue; this.messages.push(await this.getJSONString(event)); } return this.createJSONString(); diff --git a/src/utils/exportUtils/PlainTextExport.ts b/src/utils/exportUtils/PlainTextExport.ts index a6e509fc94..504ebc442b 100644 --- a/src/utils/exportUtils/PlainTextExport.ts +++ b/src/utils/exportUtils/PlainTextExport.ts @@ -120,7 +120,7 @@ export default class PlainTextExporter extends Exporter { true, ); if (this.cancelled) return this.cleanUp(); - if (!haveRendererForEvent(event, false)) continue; + if (!haveRendererForEvent(event, this.room.client, false)) continue; const textForEvent = await this.plainTextForEvent(event); content += textForEvent && `${new Date(event.getTs()).toLocaleString()} - ${textForEvent}\n`; } diff --git a/src/voice-broadcast/hooks/useVoiceBroadcastPlayback.ts b/src/voice-broadcast/hooks/useVoiceBroadcastPlayback.ts index 1e4c02fe00..bbf4600f25 100644 --- a/src/voice-broadcast/hooks/useVoiceBroadcastPlayback.ts +++ b/src/voice-broadcast/hooks/useVoiceBroadcastPlayback.ts @@ -41,7 +41,7 @@ export const useVoiceBroadcastPlayback = ( toggle(): void; room: Room; } => { - const client = MatrixClientPeg.get(); + const client = MatrixClientPeg.safeGet(); const room = client.getRoom(playback.infoEvent.getRoomId()); if (!room) { diff --git a/src/voice-broadcast/hooks/useVoiceBroadcastRecording.tsx b/src/voice-broadcast/hooks/useVoiceBroadcastRecording.tsx index f9df59ae41..fe6d00a996 100644 --- a/src/voice-broadcast/hooks/useVoiceBroadcastRecording.tsx +++ b/src/voice-broadcast/hooks/useVoiceBroadcastRecording.tsx @@ -58,7 +58,7 @@ export const useVoiceBroadcastRecording = ( stopRecording(): void; toggleRecording(): void; } => { - const client = MatrixClientPeg.get(); + const client = MatrixClientPeg.safeGet(); const roomId = recording.infoEvent.getRoomId(); const room = client.getRoom(roomId); diff --git a/src/widgets/CapabilityText.tsx b/src/widgets/CapabilityText.tsx index d0dffd399e..bbb0ebfb96 100644 --- a/src/widgets/CapabilityText.tsx +++ b/src/widgets/CapabilityText.tsx @@ -149,7 +149,7 @@ export class CapabilityText { return { primary: _t("The above, but in any room you are joined or invited to as well") }; } else { const roomId = getTimelineRoomIDFromCapability(capability); - const room = MatrixClientPeg.get().getRoom(roomId); + const room = MatrixClientPeg.safeGet().getRoom(roomId); return { primary: _t( "The above, but in as well", diff --git a/src/widgets/Jitsi.ts b/src/widgets/Jitsi.ts index af250e91bf..eecab5ab67 100644 --- a/src/widgets/Jitsi.ts +++ b/src/widgets/Jitsi.ts @@ -62,7 +62,7 @@ export class Jitsi { } public start(): void { - const cli = MatrixClientPeg.get(); + const cli = MatrixClientPeg.safeGet(); cli.on(ClientEvent.ClientWellKnown, this.update); // call update initially in case we missed the first WellKnown.client event and for if no well-known present this.update(cli.getClientWellKnown()); diff --git a/src/widgets/ManagedHybrid.ts b/src/widgets/ManagedHybrid.ts index 21c432aa3b..e171a31af7 100644 --- a/src/widgets/ManagedHybrid.ts +++ b/src/widgets/ManagedHybrid.ts @@ -43,7 +43,7 @@ function getWidgetBuildUrl(roomId: string): string | undefined { return SdkConfig.get().widget_build_url; } - const wellKnown = getCallBehaviourWellKnown(MatrixClientPeg.get()); + const wellKnown = getCallBehaviourWellKnown(MatrixClientPeg.safeGet()); if (isDm && wellKnown?.ignore_dm) { return undefined; } @@ -56,7 +56,7 @@ export function isManagedHybridWidgetEnabled(roomId: string): boolean { } export async function addManagedHybridWidget(roomId: string): Promise { - const cli = MatrixClientPeg.get(); + const cli = MatrixClientPeg.safeGet(); const room = cli.getRoom(roomId); if (!room) { return; diff --git a/test/Unread-test.ts b/test/Unread-test.ts index dc9c0f7a6b..ca67257357 100644 --- a/test/Unread-test.ts +++ b/test/Unread-test.ts @@ -81,13 +81,13 @@ describe("Unread", () => { it("returns false for an event without a renderer", () => { mocked(haveRendererForEvent).mockReturnValue(false); expect(eventTriggersUnreadCount(client, alicesMessage)).toBe(false); - expect(haveRendererForEvent).toHaveBeenCalledWith(alicesMessage, false); + expect(haveRendererForEvent).toHaveBeenCalledWith(alicesMessage, client, false); }); it("returns true for an event with a renderer", () => { mocked(haveRendererForEvent).mockReturnValue(true); expect(eventTriggersUnreadCount(client, alicesMessage)).toBe(true); - expect(haveRendererForEvent).toHaveBeenCalledWith(alicesMessage, false); + expect(haveRendererForEvent).toHaveBeenCalledWith(alicesMessage, client, false); }); it("returns false for beacon locations", () => { diff --git a/test/components/structures/MessagePanel-test.tsx b/test/components/structures/MessagePanel-test.tsx index ec7382f5e7..01671fdb01 100644 --- a/test/components/structures/MessagePanel-test.tsx +++ b/test/components/structures/MessagePanel-test.tsx @@ -29,6 +29,7 @@ import RoomContext, { TimelineRenderingType } from "../../../src/contexts/RoomCo import DMRoomMap from "../../../src/utils/DMRoomMap"; import * as TestUtilsMatrix from "../../test-utils"; import { + createTestClient, getMockClientWithEventEmitter, makeBeaconInfoEvent, mockClientMethodsEvents, @@ -773,16 +774,17 @@ describe("shouldFormContinuation", () => { msg: "And here's another message in the main timeline after the thread root", }); - expect(shouldFormContinuation(message1, message2, false)).toEqual(true); - expect(shouldFormContinuation(message2, threadRoot, false)).toEqual(true); - expect(shouldFormContinuation(threadRoot, message3, false)).toEqual(true); + const client = createTestClient(); + expect(shouldFormContinuation(message1, message2, client, false)).toEqual(true); + expect(shouldFormContinuation(message2, threadRoot, client, false)).toEqual(true); + expect(shouldFormContinuation(threadRoot, message3, client, false)).toEqual(true); const thread = { length: 1, replyToEvent: {}, } as unknown as Thread; jest.spyOn(threadRoot, "getThread").mockReturnValue(thread); - expect(shouldFormContinuation(message2, threadRoot, false)).toEqual(false); - expect(shouldFormContinuation(threadRoot, message3, false)).toEqual(false); + expect(shouldFormContinuation(message2, threadRoot, client, false)).toEqual(false); + expect(shouldFormContinuation(threadRoot, message3, client, false)).toEqual(false); }); }); diff --git a/test/components/views/dialogs/security/CreateKeyBackupDialog-test.tsx b/test/components/views/dialogs/security/CreateKeyBackupDialog-test.tsx index e7a91f3715..b0d461b8f8 100644 --- a/test/components/views/dialogs/security/CreateKeyBackupDialog-test.tsx +++ b/test/components/views/dialogs/security/CreateKeyBackupDialog-test.tsx @@ -28,7 +28,7 @@ jest.mock("../../../../../src/SecurityManager", () => ({ describe("CreateKeyBackupDialog", () => { beforeEach(() => { - MatrixClientPeg.get = () => createTestClient(); + MatrixClientPeg.safeGet = MatrixClientPeg.get = () => createTestClient(); }); it("should display the spinner when creating backup", () => { @@ -42,7 +42,7 @@ describe("CreateKeyBackupDialog", () => { it("should display the error message when backup creation failed", async () => { const matrixClient = createTestClient(); mocked(matrixClient.scheduleAllGroupSessionsForBackup).mockRejectedValue("my error"); - MatrixClientPeg.get = () => matrixClient; + MatrixClientPeg.safeGet = MatrixClientPeg.get = () => matrixClient; const { asFragment } = render(); diff --git a/test/components/views/rooms/RoomPreviewBar-test.tsx b/test/components/views/rooms/RoomPreviewBar-test.tsx index e39c5fc6f6..cc418bcb0a 100644 --- a/test/components/views/rooms/RoomPreviewBar-test.tsx +++ b/test/components/views/rooms/RoomPreviewBar-test.tsx @@ -95,8 +95,8 @@ describe("", () => { beforeEach(() => { stubClient(); - MatrixClientPeg.get().getUserId = jest.fn().mockReturnValue(userId); - MatrixClientPeg.get().getSafeUserId = jest.fn().mockReturnValue(userId); + MatrixClientPeg.get()!.getUserId = jest.fn().mockReturnValue(userId); + MatrixClientPeg.get()!.getSafeUserId = jest.fn().mockReturnValue(userId); MatrixClientPeg.safeGet().getUserId = jest.fn().mockReturnValue(userId); MatrixClientPeg.safeGet().getSafeUserId = jest.fn().mockReturnValue(userId); });