diff --git a/src/Lifecycle.ts b/src/Lifecycle.ts index bd45dd1c4b3..6c2fbec8219 100644 --- a/src/Lifecycle.ts +++ b/src/Lifecycle.ts @@ -130,7 +130,7 @@ export async function loadSession(opts: ILoadSessionOpts = {}): Promise enableGuest = false; } - if (enableGuest && fragmentQueryParams.guest_user_id && fragmentQueryParams.guest_access_token) { + if (enableGuest && guestHsUrl && fragmentQueryParams.guest_user_id && fragmentQueryParams.guest_access_token) { logger.log("Using guest access credentials"); return doSetLoggedIn( { @@ -150,7 +150,7 @@ export async function loadSession(opts: ILoadSessionOpts = {}): Promise return true; } - if (enableGuest) { + if (enableGuest && guestHsUrl) { return registerAsGuest(guestHsUrl, guestIsUrl, defaultDeviceDisplayName); } @@ -174,7 +174,7 @@ export async function loadSession(opts: ILoadSessionOpts = {}): Promise * session is for a guest user, if an owner exists. If there is no stored session, * return [null, null]. */ -export async function getStoredSessionOwner(): Promise<[string, boolean]> { +export async function getStoredSessionOwner(): Promise<[string, boolean] | [null, null]> { const { hsUrl, userId, hasAccessToken, isGuest } = await getStoredSessionVars(); return hsUrl && userId && hasAccessToken ? [userId, isGuest] : [null, null]; } @@ -259,7 +259,7 @@ export function attemptTokenLogin( }); } -export function handleInvalidStoreError(e: InvalidStoreError): Promise { +export function handleInvalidStoreError(e: InvalidStoreError): Promise | void { if (e.reason === InvalidStoreError.TOGGLED_LAZY_LOADING) { return Promise.resolve() .then(() => { @@ -292,7 +292,7 @@ export function handleInvalidStoreError(e: InvalidStoreError): Promise { } } -function registerAsGuest(hsUrl: string, isUrl: string, defaultDeviceDisplayName: string): Promise { +function registerAsGuest(hsUrl: string, isUrl?: string, defaultDeviceDisplayName?: string): Promise { logger.log(`Doing guest login on ${hsUrl}`); // create a temporary MatrixClient to do the login @@ -346,14 +346,14 @@ export interface IStoredSession { export async function getStoredSessionVars(): Promise { const hsUrl = localStorage.getItem(HOMESERVER_URL_KEY); const isUrl = localStorage.getItem(ID_SERVER_URL_KEY); - let accessToken; + let accessToken: string | undefined; try { accessToken = await StorageManager.idbLoad("account", "mx_access_token"); } catch (e) { logger.error("StorageManager.idbLoad failed for account:mx_access_token", e); } if (!accessToken) { - accessToken = localStorage.getItem("mx_access_token"); + accessToken = localStorage.getItem("mx_access_token") ?? undefined; if (accessToken) { try { // try to migrate access token to IndexedDB if we can @@ -370,7 +370,7 @@ export async function getStoredSessionVars(): Promise { const userId = localStorage.getItem("mx_user_id"); const deviceId = localStorage.getItem("mx_device_id"); - let isGuest; + let isGuest: boolean; if (localStorage.getItem("mx_is_guest") !== null) { isGuest = localStorage.getItem("mx_is_guest") === "true"; } else { @@ -447,7 +447,7 @@ export async function restoreFromLocalStorage(opts?: { ignoreGuest?: boolean }): } let decryptedAccessToken = accessToken; - const pickleKey = await PlatformPeg.get().getPickleKey(userId, deviceId); + const pickleKey = await PlatformPeg.get()?.getPickleKey(userId, deviceId); if (pickleKey) { logger.log("Got pickle key"); if (typeof accessToken !== "string") { @@ -471,7 +471,7 @@ export async function restoreFromLocalStorage(opts?: { ignoreGuest?: boolean }): homeserverUrl: hsUrl, identityServerUrl: isUrl, guest: isGuest, - pickleKey: pickleKey, + pickleKey: pickleKey ?? undefined, freshLogin: freshLogin, }, false, @@ -561,7 +561,8 @@ export async function hydrateSession(credentials: IMatrixClientCreds): Promise { - return new Promise((resolve) => { - Modal.createDialog(StorageEvictedDialog, { - onFinished: resolve, - }); - }); +async function showStorageEvictedDialog(): Promise { + const { finished } = Modal.createDialog(StorageEvictedDialog); + const [ok] = await finished; + return !!ok; } // Note: Babel 6 requires the `transform-builtin-extend` plugin for this to satisfy @@ -675,7 +674,7 @@ async function persistCredentials(credentials: IMatrixClientCreds): Promise { // Just throwing an error here is going to be very unhelpful // if you're trying to log out because your server's down and @@ -870,7 +869,7 @@ export async function onLoggedOut(): Promise { logger.log("Redirecting to external provider to finish logout"); // XXX: Defer this so that it doesn't race with MatrixChat unmounting the world by going to /#/login window.setTimeout(() => { - window.location.href = SdkConfig.get().logout_redirect_url; + window.location.href = SdkConfig.get().logout_redirect_url!; }, 100); } // Do this last to prevent racing `stopMatrixClient` and `on_logged_out` with MatrixChat handling Session.logged_out diff --git a/src/MatrixClientPeg.ts b/src/MatrixClientPeg.ts index 1194b5daa94..3aae805faeb 100644 --- a/src/MatrixClientPeg.ts +++ b/src/MatrixClientPeg.ts @@ -160,7 +160,7 @@ class MatrixClientPegClass implements IMatrixClientPeg { } public currentUserIsJustRegistered(): boolean { - return this.matrixClient && this.matrixClient.credentials.userId === this.justRegisteredUserId; + return !!this.matrixClient && this.matrixClient.credentials.userId === this.justRegisteredUserId; } public userRegisteredWithinLastHours(hours: number): boolean { diff --git a/src/components/views/messages/EditHistoryMessage.tsx b/src/components/views/messages/EditHistoryMessage.tsx index ca8a8599fd9..421673d7711 100644 --- a/src/components/views/messages/EditHistoryMessage.tsx +++ b/src/components/views/messages/EditHistoryMessage.tsx @@ -79,7 +79,7 @@ export default class EditHistoryMessage extends React.PureComponent { - await cli.redactEvent(event.getRoomId()!, event.getId()); + await cli.redactEvent(event.getRoomId()!, event.getId()!); }, }, "mx_Dialog_confirmredact", diff --git a/src/components/views/messages/ReactionsRowButton.tsx b/src/components/views/messages/ReactionsRowButton.tsx index 284b7138212..3b165b9a61f 100644 --- a/src/components/views/messages/ReactionsRowButton.tsx +++ b/src/components/views/messages/ReactionsRowButton.tsx @@ -56,7 +56,7 @@ export default class ReactionsRowButton extends React.PureComponent { const { mxEvent, myReactionEvent, content } = this.props; if (myReactionEvent) { - this.context.redactEvent(mxEvent.getRoomId()!, myReactionEvent.getId()); + this.context.redactEvent(mxEvent.getRoomId()!, myReactionEvent.getId()!); } else { this.context.sendEvent(mxEvent.getRoomId()!, "m.reaction", { "m.relates_to": { diff --git a/src/components/views/settings/account/PhoneNumbers.tsx b/src/components/views/settings/account/PhoneNumbers.tsx index 305a198bccd..ac8e2ab244b 100644 --- a/src/components/views/settings/account/PhoneNumbers.tsx +++ b/src/components/views/settings/account/PhoneNumbers.tsx @@ -206,7 +206,7 @@ export default class PhoneNumbers extends React.Component { const address = this.state.verifyMsisdn; this.state.addTask ?.haveMsisdnToken(token) - .then(([finished]) => { + .then(([finished] = []) => { let newPhoneNumber = this.state.newPhoneNumber; if (finished) { const msisdns = [...this.props.msisdns, { address, medium: ThreepidMedium.Phone }]; diff --git a/src/createRoom.ts b/src/createRoom.ts index 0898609b895..2b5831498be 100644 --- a/src/createRoom.ts +++ b/src/createRoom.ts @@ -320,7 +320,7 @@ export default async function createRoom(opts: IOpts): Promise { return SpaceStore.instance.addRoomToSpace( opts.parentSpace, roomId, - [client.getDomain()], + [client.getDomain()!], opts.suggested, ); } diff --git a/src/stores/AsyncStoreWithClient.ts b/src/stores/AsyncStoreWithClient.ts index 84ff7da317f..00569209216 100644 --- a/src/stores/AsyncStoreWithClient.ts +++ b/src/stores/AsyncStoreWithClient.ts @@ -30,7 +30,7 @@ export abstract class AsyncStoreWithClient extends AsyncStore< // Create an anonymous class to avoid code duplication const asyncStore = this; // eslint-disable-line @typescript-eslint/no-this-alias this.readyStore = new (class extends ReadyWatchingStore { - public get mxClient(): MatrixClient { + public get mxClient(): MatrixClient | null { return this.matrixClient; } @@ -48,7 +48,7 @@ export abstract class AsyncStoreWithClient extends AsyncStore< await this.readyStore.start(); } - public get matrixClient(): MatrixClient { + public get matrixClient(): MatrixClient | null { return this.readyStore.mxClient; } diff --git a/src/stores/AutoRageshakeStore.ts b/src/stores/AutoRageshakeStore.ts index 7f68b295eef..b09205841cf 100644 --- a/src/stores/AutoRageshakeStore.ts +++ b/src/stores/AutoRageshakeStore.ts @@ -135,7 +135,7 @@ export default class AutoRageshakeStore extends AsyncStoreWithClient { ...eventInfo, recipient_rageshake: rageshakeURL, }; - this.matrixClient.sendToDevice( + this.matrixClient?.sendToDevice( AUTO_RS_REQUEST, new Map([["messageContent.user_id", new Map([[messageContent.device_id, messageContent]])]]), ); diff --git a/src/stores/BreadcrumbsStore.ts b/src/stores/BreadcrumbsStore.ts index cff80f52514..333d9c3409b 100644 --- a/src/stores/BreadcrumbsStore.ts +++ b/src/stores/BreadcrumbsStore.ts @@ -21,7 +21,7 @@ import { ClientEvent } from "matrix-js-sdk/src/client"; import SettingsStore from "../settings/SettingsStore"; import { AsyncStoreWithClient } from "./AsyncStoreWithClient"; import defaultDispatcher from "../dispatcher/dispatcher"; -import { arrayHasDiff } from "../utils/arrays"; +import { arrayHasDiff, filterBoolean } from "../utils/arrays"; import { SettingLevel } from "../settings/SettingLevel"; import { Action } from "../dispatcher/actions"; import { SettingUpdatedPayload } from "../dispatcher/payloads/SettingUpdatedPayload"; @@ -75,7 +75,7 @@ export class BreadcrumbsStore extends AsyncStoreWithClient { public get meetsRoomRequirement(): boolean { if (SettingsStore.getValue("feature_breadcrumbs_v2")) return true; const msc3946ProcessDynamicPredecessor = SettingsStore.getValue("feature_dynamic_room_predecessors"); - return this.matrixClient?.getVisibleRooms(msc3946ProcessDynamicPredecessor).length >= 20; + return !!this.matrixClient && this.matrixClient.getVisibleRooms(msc3946ProcessDynamicPredecessor).length >= 20; } protected async onAction(payload: SettingUpdatedPayload | ViewRoomPayload | JoinRoomPayload): Promise { @@ -107,13 +107,17 @@ export class BreadcrumbsStore extends AsyncStoreWithClient { await this.updateRooms(); await this.updateState({ enabled: SettingsStore.getValue("breadcrumbs", null) }); - this.matrixClient.on(RoomEvent.MyMembership, this.onMyMembership); - this.matrixClient.on(ClientEvent.Room, this.onRoom); + if (this.matrixClient) { + this.matrixClient.on(RoomEvent.MyMembership, this.onMyMembership); + this.matrixClient.on(ClientEvent.Room, this.onRoom); + } } protected async onNotReady(): Promise { - this.matrixClient.removeListener(RoomEvent.MyMembership, this.onMyMembership); - this.matrixClient.removeListener(ClientEvent.Room, this.onRoom); + if (this.matrixClient) { + this.matrixClient.removeListener(RoomEvent.MyMembership, this.onMyMembership); + this.matrixClient.removeListener(ClientEvent.Room, this.onRoom); + } } private onMyMembership = async (room: Room): Promise => { @@ -137,7 +141,7 @@ export class BreadcrumbsStore extends AsyncStoreWithClient { let roomIds = SettingsStore.getValue("breadcrumb_rooms"); if (!roomIds || roomIds.length === 0) roomIds = []; - const rooms = roomIds.map((r) => this.matrixClient.getRoom(r)).filter((r) => !!r); + const rooms = filterBoolean(roomIds.map((r) => this.matrixClient?.getRoom(r))); const currentRooms = this.state.rooms || []; if (!arrayHasDiff(rooms, currentRooms)) return; // no change (probably echo) await this.updateState({ rooms }); @@ -150,8 +154,8 @@ export class BreadcrumbsStore extends AsyncStoreWithClient { // If the room is upgraded, use that room instead. We'll also splice out // any children of the room. - const history = this.matrixClient.getRoomUpgradeHistory(room.roomId, false, msc3946ProcessDynamicPredecessor); - if (history.length > 1) { + const history = this.matrixClient?.getRoomUpgradeHistory(room.roomId, false, msc3946ProcessDynamicPredecessor); + if (history && history.length > 1) { room = history[history.length - 1]; // Last room is most recent in history // Take out any room that isn't the most recent room diff --git a/src/stores/CallStore.ts b/src/stores/CallStore.ts index 8a2e6a9a571..68bc2fe94df 100644 --- a/src/stores/CallStore.ts +++ b/src/stores/CallStore.ts @@ -53,6 +53,7 @@ export class CallStore extends AsyncStoreWithClient<{}> { } protected async onReady(): Promise { + if (!this.matrixClient) return; // We assume that the calls present in a room are a function of room // widgets and group calls, so we initialize the room map here and then // update it whenever those change @@ -90,9 +91,11 @@ export class CallStore extends AsyncStoreWithClient<{}> { this.calls.clear(); this._activeCalls.clear(); - this.matrixClient.off(GroupCallEventHandlerEvent.Incoming, this.onGroupCall); - this.matrixClient.off(GroupCallEventHandlerEvent.Outgoing, this.onGroupCall); - this.matrixClient.off(GroupCallEventHandlerEvent.Ended, this.onGroupCall); + if (this.matrixClient) { + this.matrixClient.off(GroupCallEventHandlerEvent.Incoming, this.onGroupCall); + this.matrixClient.off(GroupCallEventHandlerEvent.Outgoing, this.onGroupCall); + this.matrixClient.off(GroupCallEventHandlerEvent.Ended, this.onGroupCall); + } WidgetStore.instance.off(UPDATE_EVENT, this.onWidgets); } @@ -174,6 +177,7 @@ export class CallStore extends AsyncStoreWithClient<{}> { } private onWidgets = (roomId: string | null): void => { + if (!this.matrixClient) return; if (roomId === null) { // This store happened to start before the widget store was done // loading all rooms, so we need to initialize each room again diff --git a/src/stores/OwnBeaconStore.ts b/src/stores/OwnBeaconStore.ts index 458cca7454c..db9e57f46c2 100644 --- a/src/stores/OwnBeaconStore.ts +++ b/src/stores/OwnBeaconStore.ts @@ -142,11 +142,13 @@ export class OwnBeaconStore extends AsyncStoreWithClient { } protected async onNotReady(): Promise { - this.matrixClient.removeListener(BeaconEvent.LivenessChange, this.onBeaconLiveness); - this.matrixClient.removeListener(BeaconEvent.New, this.onNewBeacon); - this.matrixClient.removeListener(BeaconEvent.Update, this.onUpdateBeacon); - this.matrixClient.removeListener(BeaconEvent.Destroy, this.onDestroyBeacon); - this.matrixClient.removeListener(RoomStateEvent.Members, this.onRoomStateMembers); + if (this.matrixClient) { + this.matrixClient.removeListener(BeaconEvent.LivenessChange, this.onBeaconLiveness); + this.matrixClient.removeListener(BeaconEvent.New, this.onNewBeacon); + this.matrixClient.removeListener(BeaconEvent.Update, this.onUpdateBeacon); + this.matrixClient.removeListener(BeaconEvent.Destroy, this.onDestroyBeacon); + this.matrixClient.removeListener(RoomStateEvent.Members, this.onRoomStateMembers); + } SettingsStore.unwatchSetting(this.dynamicWatcherRef ?? ""); this.clearBeacons(); @@ -164,11 +166,13 @@ export class OwnBeaconStore extends AsyncStoreWithClient { } protected async onReady(): Promise { - this.matrixClient.on(BeaconEvent.LivenessChange, this.onBeaconLiveness); - this.matrixClient.on(BeaconEvent.New, this.onNewBeacon); - this.matrixClient.on(BeaconEvent.Update, this.onUpdateBeacon); - this.matrixClient.on(BeaconEvent.Destroy, this.onDestroyBeacon); - this.matrixClient.on(RoomStateEvent.Members, this.onRoomStateMembers); + if (this.matrixClient) { + this.matrixClient.on(BeaconEvent.LivenessChange, this.onBeaconLiveness); + this.matrixClient.on(BeaconEvent.New, this.onNewBeacon); + this.matrixClient.on(BeaconEvent.Update, this.onUpdateBeacon); + this.matrixClient.on(BeaconEvent.Destroy, this.onDestroyBeacon); + this.matrixClient.on(RoomStateEvent.Members, this.onRoomStateMembers); + } this.dynamicWatcherRef = SettingsStore.watchSetting( "feature_dynamic_room_predecessors", null, @@ -200,7 +204,8 @@ export class OwnBeaconStore extends AsyncStoreWithClient { * Then consider it to have an error */ public beaconHasLocationPublishError = (beaconId: string): boolean => { - return this.beaconLocationPublishErrorCounts.get(beaconId) >= BAIL_AFTER_CONSECUTIVE_ERROR_COUNT; + const counts = this.beaconLocationPublishErrorCounts.get(beaconId); + return counts !== undefined && counts >= BAIL_AFTER_CONSECUTIVE_ERROR_COUNT; }; public resetLocationPublishError = (beaconId: string): void => { @@ -246,7 +251,7 @@ export class OwnBeaconStore extends AsyncStoreWithClient { */ private onNewBeacon = (_event: MatrixEvent, beacon: Beacon): void => { - if (!isOwnBeacon(beacon, this.matrixClient.getUserId()!)) { + if (!this.matrixClient || !isOwnBeacon(beacon, this.matrixClient.getUserId()!)) { return; } this.addBeacon(beacon); @@ -257,7 +262,7 @@ export class OwnBeaconStore extends AsyncStoreWithClient { * This will be called when a beacon is replaced */ private onUpdateBeacon = (_event: MatrixEvent, beacon: Beacon): void => { - if (!isOwnBeacon(beacon, this.matrixClient.getUserId()!)) { + if (!this.matrixClient || !isOwnBeacon(beacon, this.matrixClient.getUserId()!)) { return; } @@ -296,7 +301,11 @@ export class OwnBeaconStore extends AsyncStoreWithClient { */ private onRoomStateMembers = (_event: MatrixEvent, roomState: RoomState, member: RoomMember): void => { // no beacons for this room, ignore - if (!this.beaconsByRoomId.has(roomState.roomId) || member.userId !== this.matrixClient.getUserId()) { + if ( + !this.matrixClient || + !this.beaconsByRoomId.has(roomState.roomId) || + member.userId !== this.matrixClient.getUserId() + ) { return; } @@ -332,7 +341,8 @@ export class OwnBeaconStore extends AsyncStoreWithClient { }; private initialiseBeaconState = (): void => { - const userId = this.matrixClient.getUserId()!; + if (!this.matrixClient) return; + const userId = this.matrixClient.getSafeUserId(); const visibleRooms = this.matrixClient.getVisibleRooms( SettingsStore.getValue("feature_dynamic_room_predecessors"), ); diff --git a/src/stores/OwnProfileStore.ts b/src/stores/OwnProfileStore.ts index 07671192f13..a3563380534 100644 --- a/src/stores/OwnProfileStore.ts +++ b/src/stores/OwnProfileStore.ts @@ -112,7 +112,8 @@ export class OwnProfileStore extends AsyncStoreWithClient { } protected async onReady(): Promise { - const myUserId = this.matrixClient.getUserId()!; + if (!this.matrixClient) return; + const myUserId = this.matrixClient.getSafeUserId(); this.monitoredUser = this.matrixClient.getUser(myUserId); if (this.monitoredUser) { this.monitoredUser.on(UserEvent.DisplayName, this.onProfileUpdate); @@ -132,9 +133,10 @@ export class OwnProfileStore extends AsyncStoreWithClient { private onProfileUpdate = throttle( async (): Promise => { + if (!this.matrixClient) return; // We specifically do not use the User object we stored for profile info as it // could easily be wrong (such as per-room instead of global profile). - const profileInfo = await this.matrixClient.getProfileInfo(this.matrixClient.getUserId()!); + const profileInfo = await this.matrixClient.getProfileInfo(this.matrixClient.getSafeUserId()); if (profileInfo.displayname) { window.localStorage.setItem(KEY_DISPLAY_NAME, profileInfo.displayname); } else { diff --git a/src/stores/WidgetStore.ts b/src/stores/WidgetStore.ts index 35668b48123..2dd2ca9bdc4 100644 --- a/src/stores/WidgetStore.ts +++ b/src/stores/WidgetStore.ts @@ -27,7 +27,6 @@ import defaultDispatcher from "../dispatcher/dispatcher"; import WidgetEchoStore from "../stores/WidgetEchoStore"; import ActiveWidgetStore from "../stores/ActiveWidgetStore"; import WidgetUtils from "../utils/WidgetUtils"; -import { WidgetType } from "../widgets/WidgetType"; import { UPDATE_EVENT } from "./AsyncStore"; interface IState {} @@ -74,6 +73,7 @@ export default class WidgetStore extends AsyncStoreWithClient { } protected async onReady(): Promise { + if (!this.matrixClient) return; this.matrixClient.on(ClientEvent.Room, this.onRoom); this.matrixClient.on(RoomStateEvent.Events, this.onRoomStateEvents); this.matrixClient.getRooms().forEach((room: Room) => { @@ -83,8 +83,10 @@ export default class WidgetStore extends AsyncStoreWithClient { } protected async onNotReady(): Promise { - this.matrixClient.off(ClientEvent.Room, this.onRoom); - this.matrixClient.off(RoomStateEvent.Events, this.onRoomStateEvents); + if (this.matrixClient) { + this.matrixClient.off(ClientEvent.Room, this.onRoom); + this.matrixClient.off(RoomStateEvent.Events, this.onRoomStateEvents); + } this.widgetMap = new Map(); this.roomMap = new Map(); await this.reset({}); @@ -95,9 +97,9 @@ export default class WidgetStore extends AsyncStoreWithClient { return; } - private onWidgetEchoStoreUpdate = (roomId: string, widgetId: string): void => { + private onWidgetEchoStoreUpdate = (roomId: string): void => { this.initRoom(roomId); - this.loadRoomWidgets(this.matrixClient.getRoom(roomId)); + this.loadRoomWidgets(this.matrixClient?.getRoom(roomId) ?? null); this.emit(UPDATE_EVENT, roomId); }; @@ -174,7 +176,7 @@ export default class WidgetStore extends AsyncStoreWithClient { if (ev.getType() !== "im.vector.modular.widgets") return; // TODO: Support m.widget too const roomId = ev.getRoomId()!; this.initRoom(roomId); - this.loadRoomWidgets(this.matrixClient.getRoom(roomId)); + this.loadRoomWidgets(this.matrixClient?.getRoom(roomId) ?? null); this.emit(UPDATE_EVENT, roomId); }; @@ -207,24 +209,6 @@ export default class WidgetStore extends AsyncStoreWithClient { roomApps.widgets = roomApps.widgets.filter((app) => !(app.id === widgetId && app.roomId === roomId)); } } - - public doesRoomHaveConference(room: Room): boolean { - const roomInfo = this.getRoom(room.roomId); - if (!roomInfo) return false; - - const currentWidgets = roomInfo.widgets.filter((w) => WidgetType.JITSI.matches(w.type)); - const hasPendingWidgets = WidgetEchoStore.roomHasPendingWidgetsOfType(room.roomId, [], WidgetType.JITSI); - return currentWidgets.length > 0 || hasPendingWidgets; - } - - public isJoinedToConferenceIn(room: Room): boolean { - const roomInfo = this.getRoom(room.roomId); - if (!roomInfo) return false; - - // A persistent conference widget indicates that we're participating - const widgets = roomInfo.widgets.filter((w) => WidgetType.JITSI.matches(w.type)); - return widgets.some((w) => ActiveWidgetStore.instance.getWidgetPersistence(w.id, room.roomId)); - } } window.mxWidgetStore = WidgetStore.instance; diff --git a/src/stores/local-echo/RoomEchoChamber.ts b/src/stores/local-echo/RoomEchoChamber.ts index 62ebad48b30..15a3affdda4 100644 --- a/src/stores/local-echo/RoomEchoChamber.ts +++ b/src/stores/local-echo/RoomEchoChamber.ts @@ -47,6 +47,7 @@ export class RoomEchoChamber extends GenericEchoChamber { + if (!this.matrixClient) return; if (event.getType() === EventType.PushRules) { const currentVolume = this.properties.get(CachedRoomKey.NotificationVolume); const newVolume = getRoomNotifsState(this.matrixClient, this.context.room.roomId); diff --git a/src/stores/notifications/RoomNotificationStateStore.ts b/src/stores/notifications/RoomNotificationStateStore.ts index b0cafc0064d..66cb0e7c673 100644 --- a/src/stores/notifications/RoomNotificationStateStore.ts +++ b/src/stores/notifications/RoomNotificationStateStore.ts @@ -119,6 +119,7 @@ export class RoomNotificationStateStore extends AsyncStoreWithClient { * @internal public for test */ public emitUpdateIfStateChanged = (state: SyncState, forceEmit: boolean): void => { + if (!this.matrixClient) return; // Only count visible rooms to not torment the user with notification counts in rooms they can't see. // This will include highlights from the previous version of the room internally const msc3946ProcessDynamicPredecessor = SettingsStore.getValue("feature_dynamic_room_predecessors"); @@ -149,7 +150,7 @@ export class RoomNotificationStateStore extends AsyncStoreWithClient { }; protected async onReady(): Promise { - this.matrixClient.on(ClientEvent.Sync, this.onSync); + this.matrixClient?.on(ClientEvent.Sync, this.onSync); } protected async onNotReady(): Promise { diff --git a/src/stores/notifications/SpaceNotificationState.ts b/src/stores/notifications/SpaceNotificationState.ts index f97f9189144..28d29ae2296 100644 --- a/src/stores/notifications/SpaceNotificationState.ts +++ b/src/stores/notifications/SpaceNotificationState.ts @@ -77,7 +77,8 @@ export class SpaceNotificationState extends NotificationState { this._count = 0; this._color = NotificationColor.None; for (const [roomId, state] of Object.entries(this.states)) { - const roomTags = RoomListStore.instance.getTagsForRoom(this.rooms.find((r) => r.roomId === roomId)); + const room = this.rooms.find((r) => r.roomId === roomId); + const roomTags = room ? RoomListStore.instance.getTagsForRoom(room) : []; // We ignore unreads in LowPriority rooms, see https://github.com/vector-im/element-web/issues/16836 if (roomTags.includes(DefaultTagID.LowPriority) && state.color === NotificationColor.Bold) continue; diff --git a/src/stores/room-list/MessagePreviewStore.ts b/src/stores/room-list/MessagePreviewStore.ts index 5bd927282fd..f8af88aed8d 100644 --- a/src/stores/room-list/MessagePreviewStore.ts +++ b/src/stores/room-list/MessagePreviewStore.ts @@ -165,7 +165,7 @@ export class MessagePreviewStore extends AsyncStoreWithClient { const event = events[i]; - await this.matrixClient.decryptEventIfNeeded(event); + await this.matrixClient?.decryptEventIfNeeded(event); const previewDef = PREVIEWS[event.getType()]; if (!previewDef) continue; diff --git a/src/stores/room-list/RoomListStore.ts b/src/stores/room-list/RoomListStore.ts index 630ccb46d71..cb7ce972a86 100644 --- a/src/stores/room-list/RoomListStore.ts +++ b/src/stores/room-list/RoomListStore.ts @@ -189,8 +189,7 @@ export class RoomListStoreClass extends AsyncStoreWithClient implements protected async onDispatchAsync(payload: ActionPayload): Promise { // Everything here requires a MatrixClient or some sort of logical readiness. - const logicallyReady = this.matrixClient && this.initialListsGenerated; - if (!logicallyReady) return; + if (!this.matrixClient || !this.initialListsGenerated) return; if (!this.algorithm) { // This shouldn't happen because `initialListsGenerated` implies we have an algorithm. @@ -229,7 +228,7 @@ export class RoomListStoreClass extends AsyncStoreWithClient implements eventPayload.event.getType() === EventType.RoomTombstone && eventPayload.event.getStateKey() === "" ) { - const newRoom = this.matrixClient.getRoom(eventPayload.event.getContent()["replacement_room"]); + const newRoom = this.matrixClient?.getRoom(eventPayload.event.getContent()["replacement_room"]); if (newRoom) { // If we have the new room, then the new room check will have seen the predecessor // and did the required updates, so do nothing here. @@ -243,7 +242,7 @@ export class RoomListStoreClass extends AsyncStoreWithClient implements logger.warn(`Live timeline event ${eventPayload.event.getId()} received without associated room`); logger.warn(`Queuing failed room update for retry as a result.`); window.setTimeout(async (): Promise => { - const updatedRoom = this.matrixClient.getRoom(roomId); + const updatedRoom = this.matrixClient?.getRoom(roomId); if (updatedRoom) { await tryUpdate(updatedRoom); @@ -307,7 +306,7 @@ export class RoomListStoreClass extends AsyncStoreWithClient implements const roomState: RoomState = membershipPayload.room.currentState; const predecessor = roomState.findPredecessor(this.msc3946ProcessDynamicPredecessor); if (predecessor) { - const prevRoom = this.matrixClient.getRoom(predecessor.roomId); + const prevRoom = this.matrixClient?.getRoom(predecessor.roomId); if (prevRoom) { const isSticky = this.algorithm.stickyRoom === prevRoom; if (isSticky) { diff --git a/src/stores/room-list/SlidingRoomListStore.ts b/src/stores/room-list/SlidingRoomListStore.ts index cc4de473b84..0d615207aed 100644 --- a/src/stores/room-list/SlidingRoomListStore.ts +++ b/src/stores/room-list/SlidingRoomListStore.ts @@ -252,7 +252,7 @@ export class SlidingRoomListStoreClass extends AsyncStoreWithClient impl // now set the rooms const rooms: Room[] = []; orderedRoomIds.forEach((roomId) => { - const room = this.matrixClient.getRoom(roomId); + const room = this.matrixClient?.getRoom(roomId); if (!room) { return; } diff --git a/src/stores/spaces/SpaceStore.ts b/src/stores/spaces/SpaceStore.ts index a068db483a8..06dfa66971f 100644 --- a/src/stores/spaces/SpaceStore.ts +++ b/src/stores/spaces/SpaceStore.ts @@ -178,7 +178,7 @@ export class SpaceStoreClass extends AsyncStoreWithClient { public get activeSpaceRoom(): Room | null { if (isMetaSpace(this._activeSpace)) return null; - return this.matrixClient?.getRoom(this._activeSpace); + return this.matrixClient?.getRoom(this._activeSpace) ?? null; } public get suggestedRooms(): ISuggestedRoom[] { @@ -290,7 +290,7 @@ export class SpaceStoreClass extends AsyncStoreWithClient { SpaceStore.instance.traverseSpace( space, (roomId) => { - this.matrixClient.getRoom(roomId)?.loadMembersIfNeeded(); + this.matrixClient?.getRoom(roomId)?.loadMembersIfNeeded(); }, false, ); @@ -324,7 +324,7 @@ export class SpaceStoreClass extends AsyncStoreWithClient { .filter((roomInfo) => { return ( roomInfo.room_type !== RoomType.Space && - this.matrixClient.getRoom(roomInfo.room_id)?.getMyMembership() !== "join" + this.matrixClient?.getRoom(roomInfo.room_id)?.getMyMembership() !== "join" ); }) .map((roomInfo) => ({ @@ -396,7 +396,7 @@ export class SpaceStoreClass extends AsyncStoreWithClient { // only respect the relationship if the sender has sufficient permissions in the parent to set // child relations, as per MSC1772. // https://github.com/matrix-org/matrix-doc/blob/main/proposals/1772-groups-as-rooms.md#relationship-between-rooms-and-spaces - const parent = this.matrixClient.getRoom(ev.getStateKey()); + const parent = this.matrixClient?.getRoom(ev.getStateKey()); const relation = parent?.currentState.getStateEvents(EventType.SpaceChild, roomId); if ( !parent?.currentState.maySendStateEvent(EventType.SpaceChild, userId) || @@ -877,7 +877,7 @@ export class SpaceStoreClass extends AsyncStoreWithClient { private switchSpaceIfNeeded = (roomId = SdkContextClass.instance.roomViewStore.getRoomId()): void => { if (!roomId) return; - if (!this.isRoomInSpace(this.activeSpace, roomId) && !this.matrixClient.getRoom(roomId)?.isSpaceRoom()) { + if (!this.isRoomInSpace(this.activeSpace, roomId) && !this.matrixClient?.getRoom(roomId)?.isSpaceRoom()) { this.switchToRelatedSpace(roomId); } }; @@ -972,7 +972,7 @@ export class SpaceStoreClass extends AsyncStoreWithClient { } private onRoomState = (ev: MatrixEvent): void => { - const room = this.matrixClient.getRoom(ev.getRoomId()); + const room = this.matrixClient?.getRoom(ev.getRoomId()); if (!room) return; @@ -1022,7 +1022,7 @@ export class SpaceStoreClass extends AsyncStoreWithClient { // listening for m.room.member events in onRoomState above doesn't work as the Member object isn't updated by then private onRoomStateMembers = (ev: MatrixEvent): void => { - const room = this.matrixClient.getRoom(ev.getRoomId()); + const room = this.matrixClient?.getRoom(ev.getRoomId()); const userId = ev.getStateKey()!; if ( @@ -1135,6 +1135,7 @@ export class SpaceStoreClass extends AsyncStoreWithClient { } protected async onReady(): Promise { + if (!this.matrixClient) return; this.matrixClient.on(ClientEvent.Room, this.onRoom); this.matrixClient.on(RoomEvent.MyMembership, this.onRoom); this.matrixClient.on(RoomEvent.AccountData, this.onRoomAccountData); @@ -1350,7 +1351,7 @@ export class SpaceStoreClass extends AsyncStoreWithClient { private async setRootSpaceOrder(space: Room, order: string): Promise { this.spaceOrderLocalEchoMap.set(space.roomId, order); try { - await this.matrixClient.setRoomAccountData(space.roomId, EventType.SpaceOrder, { order }); + await this.matrixClient?.setRoomAccountData(space.roomId, EventType.SpaceOrder, { order }); } catch (e) { logger.warn("Failed to set root space order", e); if (this.spaceOrderLocalEchoMap.get(space.roomId) === order) { diff --git a/test/components/views/beacon/RoomLiveShareWarning-test.tsx b/test/components/views/beacon/RoomLiveShareWarning-test.tsx index 3db674bc7fe..1432bcc818f 100644 --- a/test/components/views/beacon/RoomLiveShareWarning-test.tsx +++ b/test/components/views/beacon/RoomLiveShareWarning-test.tsx @@ -42,6 +42,7 @@ describe("", () => { const mockClient = getMockClientWithEventEmitter({ getVisibleRooms: jest.fn().mockReturnValue([]), getUserId: jest.fn().mockReturnValue(aliceId), + getSafeUserId: jest.fn().mockReturnValue(aliceId), unstable_setLiveBeacon: jest.fn().mockResolvedValue({ event_id: "1" }), sendEvent: jest.fn(), isGuest: jest.fn().mockReturnValue(false), diff --git a/test/components/views/settings/tabs/room/NotificationSettingsTab-test.tsx b/test/components/views/settings/tabs/room/NotificationSettingsTab-test.tsx index 1b9a393e616..6e5a3e69825 100644 --- a/test/components/views/settings/tabs/room/NotificationSettingsTab-test.tsx +++ b/test/components/views/settings/tabs/room/NotificationSettingsTab-test.tsx @@ -42,7 +42,7 @@ describe("NotificatinSettingsTab", () => { const room = mkStubRoom(roomId, "test room", cli); roomProps = EchoChamber.forRoom(room); - NotificationSettingsTab.contextType = React.createContext(cli); + NotificationSettingsTab.contextType = React.createContext(cli); }); it("should prevent »Settings« link click from bubbling up to radio buttons", async () => { diff --git a/test/stores/OwnBeaconStore-test.ts b/test/stores/OwnBeaconStore-test.ts index 937b0259748..d49e77008aa 100644 --- a/test/stores/OwnBeaconStore-test.ts +++ b/test/stores/OwnBeaconStore-test.ts @@ -59,6 +59,7 @@ describe("OwnBeaconStore", () => { const bobId = "@bob:server.org"; const mockClient = getMockClientWithEventEmitter({ getUserId: jest.fn().mockReturnValue(aliceId), + getSafeUserId: jest.fn().mockReturnValue(aliceId), getVisibleRooms: jest.fn().mockReturnValue([]), unstable_setLiveBeacon: jest.fn().mockResolvedValue({ event_id: "1" }), sendEvent: jest.fn().mockResolvedValue({ event_id: "1" }),