diff --git a/src/ContentMessages.ts b/src/ContentMessages.ts index 41bd0361a2..b1417123b3 100644 --- a/src/ContentMessages.ts +++ b/src/ContentMessages.ts @@ -152,7 +152,7 @@ async function infoForImageFile( // For lesser supported image types, always include the thumbnail even if it is larger if (!ALWAYS_INCLUDE_THUMBNAIL.includes(imageFile.type)) { // we do all sizing checks here because we still rely on thumbnail generation for making a blurhash from. - const sizeDifference = imageFile.size - imageInfo.thumbnail_info.size; + const sizeDifference = imageFile.size - imageInfo.thumbnail_info!.size; if ( // image is small enough already imageFile.size <= IMAGE_SIZE_THRESHOLD_THUMBNAIL || diff --git a/src/DeviceListener.ts b/src/DeviceListener.ts index 039adc27cd..f1b22c2c71 100644 --- a/src/DeviceListener.ts +++ b/src/DeviceListener.ts @@ -227,7 +227,11 @@ export default class DeviceListener { // & cache the result private async getKeyBackupInfo(): Promise { const now = new Date().getTime(); - if (!this.keyBackupInfo || this.keyBackupFetchedAt < now - KEY_BACKUP_POLL_INTERVAL) { + if ( + !this.keyBackupInfo || + !this.keyBackupFetchedAt || + this.keyBackupFetchedAt < now - KEY_BACKUP_POLL_INTERVAL + ) { this.keyBackupInfo = await MatrixClientPeg.get().getKeyBackupVersion(); this.keyBackupFetchedAt = now; } @@ -392,7 +396,7 @@ export default class DeviceListener { private updateClientInformation = async (): Promise => { try { if (this.shouldRecordClientInformation) { - await recordClientInformation(MatrixClientPeg.get(), SdkConfig.get(), PlatformPeg.get()); + await recordClientInformation(MatrixClientPeg.get(), SdkConfig.get(), PlatformPeg.get() ?? undefined); } else { await removeClientInformation(MatrixClientPeg.get()); } diff --git a/src/KeyBindingsDefaults.ts b/src/KeyBindingsDefaults.ts index 271aa04c50..e936ae1de6 100644 --- a/src/KeyBindingsDefaults.ts +++ b/src/KeyBindingsDefaults.ts @@ -23,7 +23,7 @@ import { CATEGORIES, CategoryName, KeyBindingAction } from "./accessibility/Keyb import { getKeyboardShortcuts } from "./accessibility/KeyboardShortcutUtils"; export const getBindingsByCategory = (category: CategoryName): KeyBinding[] => { - return CATEGORIES[category].settingNames.reduce((bindings, action) => { + return CATEGORIES[category].settingNames.reduce((bindings, action) => { const keyCombo = getKeyboardShortcuts()[action]?.default; if (keyCombo) { bindings.push({ action, keyCombo }); diff --git a/src/NodeAnimator.tsx b/src/NodeAnimator.tsx index b8c3f855ac..f793b90a92 100644 --- a/src/NodeAnimator.tsx +++ b/src/NodeAnimator.tsx @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -import React, { ReactInstance } from "react"; +import React, { Key, ReactElement, ReactInstance } from "react"; import ReactDom from "react-dom"; interface IChildProps { @@ -42,7 +42,7 @@ interface IProps { */ export default class NodeAnimator extends React.Component { private nodes: Record = {}; - private children: { [key: string]: React.DetailedReactHTMLElement }; + private children: { [key: string]: ReactElement }; public static defaultProps: Partial = { startStyles: [], }; @@ -72,17 +72,17 @@ export default class NodeAnimator extends React.Component { private updateChildren(newChildren: React.ReactNode): void { const oldChildren = this.children || {}; this.children = {}; - React.Children.toArray(newChildren).forEach((c: any) => { - if (oldChildren[c.key]) { - const old = oldChildren[c.key]; - const oldNode = ReactDom.findDOMNode(this.nodes[old.key]); + React.Children.toArray(newChildren).forEach((c: ReactElement) => { + if (oldChildren[c.key!]) { + const old = oldChildren[c.key!]; + const oldNode = ReactDom.findDOMNode(this.nodes[old.key!]); if (oldNode && (oldNode as HTMLElement).style.left !== c.props.style.left) { this.applyStyles(oldNode as HTMLElement, { left: c.props.style.left }); } // clone the old element with the props (and children) of the new element // so prop updates are still received by the children. - this.children[c.key] = React.cloneElement(old, c.props, c.props.children); + this.children[c.key!] = React.cloneElement(old, c.props, c.props.children); } else { // new element. If we have a startStyle, use that as the style and go through // the enter animations @@ -95,14 +95,14 @@ export default class NodeAnimator extends React.Component { newProps.style = startStyle; } - newProps.ref = (n) => this.collectNode(c.key, n, restingStyle); + newProps.ref = (n) => this.collectNode(c.key!, n, restingStyle); - this.children[c.key] = React.cloneElement(c, newProps); + this.children[c.key!] = React.cloneElement(c, newProps); } }); } - private collectNode(k: string, node: React.ReactInstance, restingStyle: React.CSSProperties): void { + private collectNode(k: Key, node: React.ReactInstance, restingStyle: React.CSSProperties): void { if (node && this.nodes[k] === undefined && this.props.startStyles.length > 0) { const startStyles = this.props.startStyles; const domNode = ReactDom.findDOMNode(node); diff --git a/src/Notifier.ts b/src/Notifier.ts index 4e34083f3f..52983f6fc3 100644 --- a/src/Notifier.ts +++ b/src/Notifier.ts @@ -106,9 +106,10 @@ class NotifierClass { private toolbarHidden?: boolean; private isSyncing?: boolean; - public notificationMessageForEvent(ev: MatrixEvent): string { - if (msgTypeHandlers.hasOwnProperty(ev.getContent().msgtype)) { - return msgTypeHandlers[ev.getContent().msgtype](ev); + public notificationMessageForEvent(ev: MatrixEvent): string | null { + const msgType = ev.getContent().msgtype; + if (msgType && msgTypeHandlers.hasOwnProperty(msgType)) { + return msgTypeHandlers[msgType](ev); } return TextForEvent.textForEvent(ev); } @@ -134,9 +135,9 @@ class NotifierClass { let title; if (!ev.sender || room.name === ev.sender.name) { title = room.name; - // notificationMessageForEvent includes sender, - // but we already have the sender here - if (ev.getContent().body && !msgTypeHandlers.hasOwnProperty(ev.getContent().msgtype)) { + // notificationMessageForEvent includes sender, but we already have the sender here + const msgType = ev.getContent().msgtype; + if (ev.getContent().body && (!msgType || !msgTypeHandlers.hasOwnProperty(msgType))) { msg = ev.getContent().body; } } else if (ev.getType() === "m.room.member") { @@ -145,9 +146,9 @@ class NotifierClass { title = room.name; } else if (ev.sender) { title = ev.sender.name + " (" + room.name + ")"; - // notificationMessageForEvent includes sender, - // but we've just out sender in the title - if (ev.getContent().body && !msgTypeHandlers.hasOwnProperty(ev.getContent().msgtype)) { + // notificationMessageForEvent includes sender, but we've just out sender in the title + const msgType = ev.getContent().msgtype; + if (ev.getContent().body && (!msgType || !msgTypeHandlers.hasOwnProperty(msgType))) { msg = ev.getContent().body; } } @@ -161,7 +162,7 @@ class NotifierClass { avatarUrl = Avatar.avatarUrlForMember(ev.sender, 40, 40, "crop"); } - const notif = plaf.displayNotification(title, msg, avatarUrl, room, ev); + const notif = plaf.displayNotification(title, msg!, avatarUrl, room, ev); // if displayNotification returns non-null, the platform supports // clearing notifications later, so keep track of this. diff --git a/src/Rooms.ts b/src/Rooms.ts index 250f385206..a79204995f 100644 --- a/src/Rooms.ts +++ b/src/Rooms.ts @@ -16,6 +16,7 @@ limitations under the License. import { Room } from "matrix-js-sdk/src/models/room"; import { EventType } from "matrix-js-sdk/src/@types/event"; +import { RoomMember } from "matrix-js-sdk/src/models/room-member"; import { MatrixClientPeg } from "./MatrixClientPeg"; import AliasCustomisations from "./customisations/Alias"; @@ -109,8 +110,8 @@ export async function setDMRoom(roomId: string, userId: string | null): Promise< * @returns {string} User ID of the user that the room is probably a DM with */ function guessDMRoomTargetId(room: Room, myUserId: string): string { - let oldestTs; - let oldestUser; + let oldestTs: number | undefined; + let oldestUser: RoomMember | undefined; // Pick the joined user who's been here longest (and isn't us), for (const user of room.getJoinedMembers()) { diff --git a/src/ScalarMessaging.ts b/src/ScalarMessaging.ts index aebffad18b..9afa10a90a 100644 --- a/src/ScalarMessaging.ts +++ b/src/ScalarMessaging.ts @@ -858,8 +858,7 @@ async function readEvents( const onMessage = function (event: MessageEvent): void { if (!event.origin) { - // stupid chrome - // @ts-ignore + // @ts-ignore - stupid chrome event.origin = event.originalEvent.origin; } @@ -867,10 +866,10 @@ const onMessage = function (event: MessageEvent): void { // This means the URL could contain a path (like /develop) and still be used // to validate event origins, which do not specify paths. // (See https://developer.mozilla.org/en-US/docs/Web/API/Window/postMessage) - let configUrl; + let configUrl: URL | undefined; try { - if (!openManagerUrl) openManagerUrl = IntegrationManagers.sharedInstance().getPrimaryManager().uiUrl; - configUrl = new URL(openManagerUrl); + if (!openManagerUrl) openManagerUrl = IntegrationManagers.sharedInstance().getPrimaryManager()?.uiUrl; + configUrl = new URL(openManagerUrl!); } catch (e) { // No integrations UI URL, ignore silently. return; @@ -987,7 +986,7 @@ const onMessage = function (event: MessageEvent): void { }; let listenerCount = 0; -let openManagerUrl: string | null = null; +let openManagerUrl: string | undefined; export function startListening(): void { if (listenerCount === 0) { diff --git a/src/SecurityManager.ts b/src/SecurityManager.ts index 563aac09ad..34bfc44f07 100644 --- a/src/SecurityManager.ts +++ b/src/SecurityManager.ts @@ -87,9 +87,10 @@ function makeInputToKey(keyInfo: ISecretStorageKeyInfo): (keyParams: KeyParams) return async ({ passphrase, recoveryKey }): Promise => { if (passphrase) { return deriveKey(passphrase, keyInfo.passphrase.salt, keyInfo.passphrase.iterations); - } else { + } else if (recoveryKey) { return decodeRecoveryKey(recoveryKey); } + throw new Error("Invalid input, passphrase or recoveryKey need to be provided"); }; } diff --git a/src/SlashCommands.tsx b/src/SlashCommands.tsx index b1df460f80..0cdd929931 100644 --- a/src/SlashCommands.tsx +++ b/src/SlashCommands.tsx @@ -208,7 +208,10 @@ function successSync(value: any): RunResult { const isCurrentLocalRoom = (): boolean => { const cli = MatrixClientPeg.get(); - const room = cli.getRoom(SdkContextClass.instance.roomViewStore.getRoomId()); + const roomId = SdkContextClass.instance.roomViewStore.getRoomId(); + if (!roomId) return false; + const room = cli.getRoom(roomId); + if (!room) return false; return isLocalRoom(room); }; @@ -873,7 +876,9 @@ export const Commands = [ description: _td("Define the power level of a user"), isEnabled(): boolean { const cli = MatrixClientPeg.get(); - const room = cli.getRoom(SdkContextClass.instance.roomViewStore.getRoomId()); + const roomId = SdkContextClass.instance.roomViewStore.getRoomId(); + if (!roomId) return false; + const room = cli.getRoom(roomId); return ( !!room?.currentState.maySendStateEvent(EventType.RoomPowerLevels, cli.getUserId()!) && !isLocalRoom(room) @@ -897,7 +902,10 @@ export const Commands = [ ); } const member = room.getMember(userId); - if (!member || getEffectiveMembership(member.membership) === EffectiveMembership.Leave) { + if ( + !member?.membership || + getEffectiveMembership(member.membership) === EffectiveMembership.Leave + ) { return reject(newTranslatableError("Could not find user in room")); } const powerLevelEvent = room.currentState.getStateEvents("m.room.power_levels", ""); @@ -916,7 +924,9 @@ export const Commands = [ description: _td("Deops user with given id"), isEnabled(): boolean { const cli = MatrixClientPeg.get(); - const room = cli.getRoom(SdkContextClass.instance.roomViewStore.getRoomId()); + const roomId = SdkContextClass.instance.roomViewStore.getRoomId(); + if (!roomId) return false; + const room = cli.getRoom(roomId); return ( !!room?.currentState.maySendStateEvent(EventType.RoomPowerLevels, cli.getUserId()!) && !isLocalRoom(room) @@ -973,11 +983,12 @@ export const Commands = [ // We use parse5, which doesn't render/create a DOM node. It instead runs // some superfast regex over the text so we don't have to. const embed = parseHtml(widgetUrl); - if (embed && embed.childNodes && embed.childNodes.length === 1) { + if (embed?.childNodes?.length === 1) { const iframe = embed.childNodes[0] as ChildElement; if (iframe.tagName.toLowerCase() === "iframe" && iframe.attrs) { const srcAttr = iframe.attrs.find((a) => a.name === "src"); logger.log("Pulling URL out of iframe (embed code)"); + if (!srcAttr) return reject(newTranslatableError("iframe has no src attribute")); widgetUrl = srcAttr.value; } } @@ -1240,6 +1251,7 @@ export const Commands = [ } const roomId = await ensureDMExists(MatrixClientPeg.get(), userId); + if (!roomId) throw new Error("Failed to ensure DM exists"); dis.dispatch({ action: Action.ViewRoom, @@ -1267,6 +1279,8 @@ export const Commands = [ (async (): Promise => { const cli = MatrixClientPeg.get(); const roomId = await ensureDMExists(cli, userId); + if (!roomId) throw new Error("Failed to ensure DM exists"); + dis.dispatch({ action: Action.ViewRoom, room_id: roomId, @@ -1323,6 +1337,7 @@ export const Commands = [ isEnabled: () => !isCurrentLocalRoom(), runFn: function (roomId, args) { const room = MatrixClientPeg.get().getRoom(roomId); + if (!room) return reject(newTranslatableError("Could not find room")); return success(guessAndSetDMRoom(room, true)); }, renderingTypes: [TimelineRenderingType.Room], @@ -1334,6 +1349,7 @@ export const Commands = [ isEnabled: () => !isCurrentLocalRoom(), runFn: function (roomId, args) { const room = MatrixClientPeg.get().getRoom(roomId); + if (!room) return reject(newTranslatableError("Could not find room")); return success(guessAndSetDMRoom(room, false)); }, renderingTypes: [TimelineRenderingType.Room], diff --git a/src/Terms.ts b/src/Terms.ts index ad4386d7aa..b986a5e83f 100644 --- a/src/Terms.ts +++ b/src/Terms.ts @@ -201,7 +201,7 @@ export async function dialogTermsInteractionCallback( ); const [done, _agreedUrls] = await finished; - if (!done) { + if (!done || !_agreedUrls) { throw new TermsNotSignedError(); } return _agreedUrls; diff --git a/src/audio/PlaybackQueue.ts b/src/audio/PlaybackQueue.ts index 939d76d0a5..b7920316ef 100644 --- a/src/audio/PlaybackQueue.ts +++ b/src/audio/PlaybackQueue.ts @@ -170,7 +170,7 @@ export class PlaybackQueue { // This should cause a Play event, which will re-populate our playback order // and update our current playback ID. // noinspection JSIgnoredPromiseFromCall - instance.play(); + instance?.play(); } } } else { diff --git a/src/createRoom.ts b/src/createRoom.ts index 25c7b10fe2..daf3d56c18 100644 --- a/src/createRoom.ts +++ b/src/createRoom.ts @@ -452,6 +452,7 @@ export async function ensureDMExists(client: MatrixClient, userId: string): Prom } roomId = await createRoom({ encryption, dmUserId: userId, spinner: false, andView: false }); + if (!roomId) return null; await waitForMember(client, roomId, userId); } return roomId; diff --git a/src/hooks/usePublicRoomDirectory.ts b/src/hooks/usePublicRoomDirectory.ts index a14142ebcf..c75cffd825 100644 --- a/src/hooks/usePublicRoomDirectory.ts +++ b/src/hooks/usePublicRoomDirectory.ts @@ -143,8 +143,9 @@ export const usePublicRoomDirectory = (): { let roomServer: string = myHomeserver; if ( - SdkConfig.getObject("room_directory")?.get("servers")?.includes(lsRoomServer) || - SettingsStore.getValue("room_directory_servers")?.includes(lsRoomServer) + lsRoomServer && + (SdkConfig.getObject("room_directory")?.get("servers")?.includes(lsRoomServer) || + SettingsStore.getValue("room_directory_servers")?.includes(lsRoomServer)) ) { roomServer = lsRoomServer!; } diff --git a/src/hooks/useSpaceResults.ts b/src/hooks/useSpaceResults.ts index 8a95190f9c..35a4627b05 100644 --- a/src/hooks/useSpaceResults.ts +++ b/src/hooks/useSpaceResults.ts @@ -22,12 +22,12 @@ import { normalize } from "matrix-js-sdk/src/utils"; import { MatrixClientPeg } from "../MatrixClientPeg"; -export const useSpaceResults = (space?: Room, query?: string): [IHierarchyRoom[], boolean] => { +export const useSpaceResults = (space: Room | undefined, query: string): [IHierarchyRoom[], boolean] => { const [rooms, setRooms] = useState([]); const [hierarchy, setHierarchy] = useState(); const resetHierarchy = useCallback(() => { - setHierarchy(space ? new RoomHierarchy(space, 50) : null); + setHierarchy(space ? new RoomHierarchy(space, 50) : undefined); }, [space]); useEffect(resetHierarchy, [resetHierarchy]); @@ -40,7 +40,7 @@ export const useSpaceResults = (space?: Room, query?: string): [IHierarchyRoom[] while (hierarchy?.canLoadMore && !unmounted && space === hierarchy.root) { await hierarchy.load(); if (hierarchy.canLoadMore) hierarchy.load(); // start next load so that the loading attribute is right - setRooms(hierarchy.rooms); + setRooms(hierarchy.rooms!); } })(); diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index a156b95c96..f73c89a780 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -453,6 +453,7 @@ "Opens the Developer Tools dialog": "Opens the Developer Tools dialog", "Adds a custom widget by URL to the room": "Adds a custom widget by URL to the room", "Please supply a widget URL or embed code": "Please supply a widget URL or embed code", + "iframe has no src attribute": "iframe has no src attribute", "Please supply a https:// or http:// widget URL": "Please supply a https:// or http:// widget URL", "You cannot modify widgets in this room.": "You cannot modify widgets in this room.", "Verifies a user, session, and pubkey tuple": "Verifies a user, session, and pubkey tuple", @@ -478,6 +479,7 @@ "No active call in this room": "No active call in this room", "Takes the call in the current room off hold": "Takes the call in the current room off hold", "Converts the room to a DM": "Converts the room to a DM", + "Could not find room": "Could not find room", "Converts the DM to a room": "Converts the DM to a room", "Displays action": "Displays action", "Someone": "Someone", diff --git a/src/mjolnir/ListRule.ts b/src/mjolnir/ListRule.ts index 9828a0e656..3fc1b00768 100644 --- a/src/mjolnir/ListRule.ts +++ b/src/mjolnir/ListRule.ts @@ -21,7 +21,7 @@ import { MatrixGlob } from "../utils/MatrixGlob"; export const RECOMMENDATION_BAN = "m.ban"; export const RECOMMENDATION_BAN_TYPES = [RECOMMENDATION_BAN, "org.matrix.mjolnir.ban"]; -export function recommendationToStable(recommendation: string, unstable = true): string { +export function recommendationToStable(recommendation: string, unstable = true): string | null { if (RECOMMENDATION_BAN_TYPES.includes(recommendation)) { return unstable ? RECOMMENDATION_BAN_TYPES[RECOMMENDATION_BAN_TYPES.length - 1] : RECOMMENDATION_BAN; } @@ -31,7 +31,7 @@ export function recommendationToStable(recommendation: string, unstable = true): export class ListRule { private _glob: MatrixGlob; private readonly _entity: string; - private readonly _action: string; + private readonly _action: string | null; private readonly _reason: string; private readonly _kind: string; @@ -55,7 +55,7 @@ export class ListRule { return this._kind; } - public get recommendation(): string { + public get recommendation(): string | null { return this._action; } diff --git a/src/modules/ProxiedModuleApi.ts b/src/modules/ProxiedModuleApi.ts index 818a1f9fcd..db3b504c63 100644 --- a/src/modules/ProxiedModuleApi.ts +++ b/src/modules/ProxiedModuleApi.ts @@ -88,7 +88,7 @@ export class ProxiedModuleApi implements ModuleApi { }, "mx_CompoundDialog", ).finished.then(([didOkOrSubmit, model]) => { - resolve({ didOkOrSubmit, model: model as M }); + resolve({ didOkOrSubmit: !!didOkOrSubmit, model: model as M }); }); }); } @@ -102,6 +102,7 @@ export class ProxiedModuleApi implements ModuleApi { displayName?: string, ): Promise { const hsUrl = SdkConfig.get("validated_server_config")?.hsUrl; + if (!hsUrl) throw new Error("Could not get homeserver url"); const client = Matrix.createClient({ baseUrl: hsUrl }); const deviceName = SdkConfig.get("default_device_display_name") || PlatformPeg.get()?.getDefaultDeviceDisplayName(); diff --git a/src/notifications/ContentRules.ts b/src/notifications/ContentRules.ts index 6060033b81..67833245b8 100644 --- a/src/notifications/ContentRules.ts +++ b/src/notifications/ContentRules.ts @@ -107,8 +107,8 @@ export class ContentRules { }; for (const kind in rulesets.global) { - for (let i = 0; i < Object.keys(rulesets.global[kind as keyof PushRuleSet]).length; ++i) { - const r = rulesets.global[kind as keyof PushRuleSet][i] as IAnnotatedPushRule; + for (let i = 0; i < Object.keys(rulesets.global[kind as keyof PushRuleSet]!).length; ++i) { + const r = rulesets.global[kind as keyof PushRuleSet]![i] as IAnnotatedPushRule; // check it's not a default rule if (r.rule_id[0] === "." || kind !== PushRuleKind.ContentSpecific) { diff --git a/src/sentry.ts b/src/sentry.ts index fbb52dbb14..07d1281098 100644 --- a/src/sentry.ts +++ b/src/sentry.ts @@ -89,7 +89,7 @@ async function getStorageContext(): Promise { if (estimate.usageDetails) { const usageDetails: string[] = []; Object.keys(estimate.usageDetails).forEach((k) => { - usageDetails.push(`${k}: ${String(estimate.usageDetails[k])}`); + usageDetails.push(`${k}: ${String(estimate.usageDetails![k])}`); }); result[`storageManager_usage`] = usageDetails.join(", "); } @@ -137,9 +137,9 @@ async function getCryptoContext(client: MatrixClient): Promise { ), cross_signing_key: crossSigning.getId()!, cross_signing_privkey_in_secret_storage: String(!!(await crossSigning.isStoredInSecretStorage(secretStorage))), - cross_signing_master_privkey_cached: String(!!(pkCache && (await pkCache.getCrossSigningKeyCache("master")))), + cross_signing_master_privkey_cached: String(!!(pkCache && (await pkCache.getCrossSigningKeyCache?.("master")))), cross_signing_user_signing_privkey_cached: String( - !!(pkCache && (await pkCache.getCrossSigningKeyCache("user_signing"))), + !!(pkCache && (await pkCache.getCrossSigningKeyCache?.("user_signing"))), ), secret_storage_ready: String(await client.isSecretStorageReady()), secret_storage_key_in_account: String(!!(await secretStorage.hasKey())), @@ -151,7 +151,7 @@ async function getCryptoContext(client: MatrixClient): Promise { function getDeviceContext(client: MatrixClient): DeviceContext { const result: DeviceContext = { - device_id: client?.deviceId, + device_id: client?.deviceId ?? undefined, mx_local_settings: localStorage.getItem("mx_local_settings"), }; diff --git a/src/settings/controllers/ServerSupportUnstableFeatureController.ts b/src/settings/controllers/ServerSupportUnstableFeatureController.ts index e50b673d82..2cbf014f1d 100644 --- a/src/settings/controllers/ServerSupportUnstableFeatureController.ts +++ b/src/settings/controllers/ServerSupportUnstableFeatureController.ts @@ -45,6 +45,7 @@ export default class ServerSupportUnstableFeatureController extends MatrixClient if (!v === this.enabled) return; this.enabled = !v; const level = SettingsStore.firstSupportedLevel(this.settingName); + if (!level) return; const settingValue = SettingsStore.getValue(this.settingName, null); this.watchers.notifyUpdate(this.settingName, null, level, settingValue); } @@ -61,7 +62,7 @@ export default class ServerSupportUnstableFeatureController extends MatrixClient public getValueOverride( level: SettingLevel, - roomId: string, + roomId: string | null, calculatedValue: any, calculatedAtLevel: SettingLevel | null, ): any { diff --git a/src/toasts/IncomingLegacyCallToast.tsx b/src/toasts/IncomingLegacyCallToast.tsx index 59f34af435..26ea393332 100644 --- a/src/toasts/IncomingLegacyCallToast.tsx +++ b/src/toasts/IncomingLegacyCallToast.tsx @@ -39,9 +39,17 @@ interface IState { } export default class IncomingLegacyCallToast extends React.Component { + private readonly roomId: string; + public constructor(props: IProps) { super(props); + const roomId = LegacyCallHandler.instance.roomIdForCall(this.props.call); + if (!roomId) { + throw new Error("Unable to find room for incoming call"); + } + this.roomId = roomId; + this.state = { silenced: LegacyCallHandler.instance.isCallSilenced(this.props.call.callId), }; @@ -67,12 +75,12 @@ export default class IncomingLegacyCallToast extends React.Component { e.stopPropagation(); - LegacyCallHandler.instance.answerCall(LegacyCallHandler.instance.roomIdForCall(this.props.call)); + LegacyCallHandler.instance.answerCall(this.roomId); }; private onRejectClick = (e: React.MouseEvent): void => { e.stopPropagation(); - LegacyCallHandler.instance.hangupOrReject(LegacyCallHandler.instance.roomIdForCall(this.props.call), true); + LegacyCallHandler.instance.hangupOrReject(this.roomId, true); }; private onSilenceClick = (e: React.MouseEvent): void => { @@ -84,9 +92,8 @@ export default class IncomingLegacyCallToast extends React.Component `${cl export const recordClientInformation = async ( matrixClient: MatrixClient, sdkConfig: IConfigOptions, - platform: BasePlatform, + platform?: BasePlatform, ): Promise => { const deviceId = matrixClient.getDeviceId()!; const { brand } = sdkConfig; - const version = await platform.getAppVersion(); + const version = await platform?.getAppVersion(); const type = getClientInformationEventType(deviceId); const url = formatUrl(); diff --git a/src/widgets/Jitsi.ts b/src/widgets/Jitsi.ts index c98f3a4b79..6dca3b3de7 100644 --- a/src/widgets/Jitsi.ts +++ b/src/widgets/Jitsi.ts @@ -68,7 +68,7 @@ export class Jitsi { this.update(cli.getClientWellKnown()); } - private update = async (discoveryResponse: IClientWellKnown): Promise => { + private update = async (discoveryResponse?: IClientWellKnown): Promise => { // Start with a default of the config's domain let domain = SdkConfig.getObject("jitsi")?.get("preferred_domain") || "meet.element.io"; diff --git a/test/components/views/dialogs/SpotlightDialog-test.tsx b/test/components/views/dialogs/SpotlightDialog-test.tsx index 276c42d5d5..f51b0647d9 100644 --- a/test/components/views/dialogs/SpotlightDialog-test.tsx +++ b/test/components/views/dialogs/SpotlightDialog-test.tsx @@ -412,9 +412,9 @@ describe("Spotlight Dialog", () => { jest.advanceTimersByTime(200); await flushPromisesWithFakeTimers(); - expect(screen.getByText(potatoRoom.name)).toBeInTheDocument(); - expect(screen.queryByText(nsfwTopicRoom.name)).not.toBeInTheDocument(); - expect(screen.queryByText(nsfwTopicRoom.name)).not.toBeInTheDocument(); + expect(screen.getByText(potatoRoom.name!)).toBeInTheDocument(); + expect(screen.queryByText(nsfwTopicRoom.name!)).not.toBeInTheDocument(); + expect(screen.queryByText(nsfwTopicRoom.name!)).not.toBeInTheDocument(); }); it("displays rooms with nsfw keywords in results when showNsfwPublicRooms is truthy", async () => { @@ -425,9 +425,9 @@ describe("Spotlight Dialog", () => { jest.advanceTimersByTime(200); await flushPromisesWithFakeTimers(); - expect(screen.getByText(nsfwTopicRoom.name)).toBeInTheDocument(); - expect(screen.getByText(nsfwNameRoom.name)).toBeInTheDocument(); - expect(screen.getByText(potatoRoom.name)).toBeInTheDocument(); + expect(screen.getByText(nsfwTopicRoom.name!)).toBeInTheDocument(); + expect(screen.getByText(nsfwNameRoom.name!)).toBeInTheDocument(); + expect(screen.getByText(potatoRoom.name!)).toBeInTheDocument(); }); }); }); diff --git a/test/components/views/right_panel/UserInfo-test.tsx b/test/components/views/right_panel/UserInfo-test.tsx index f08c34a441..5965b1b9bd 100644 --- a/test/components/views/right_panel/UserInfo-test.tsx +++ b/test/components/views/right_panel/UserInfo-test.tsx @@ -328,7 +328,7 @@ describe("", () => { it("with unverified user and device, displays button without a label", () => { renderComponent(); - expect(screen.getByRole("button", { name: device.getDisplayName() })).toBeInTheDocument; + expect(screen.getByRole("button", { name: device.getDisplayName()! })).toBeInTheDocument; expect(screen.queryByText(/trusted/i)).not.toBeInTheDocument(); }); @@ -343,7 +343,7 @@ describe("", () => { setMockDeviceTrust(true); renderComponent(); - expect(screen.getByText(device.getDisplayName())).toBeInTheDocument(); + expect(screen.getByText(device.getDisplayName()!)).toBeInTheDocument(); expect(screen.queryByText(/trusted/)).not.toBeInTheDocument(); }); @@ -356,7 +356,7 @@ describe("", () => { // expect to see no button in this case expect(screen.queryByRole("button")).not.toBeInTheDocument; - expect(screen.getByText(device.getDisplayName())).toBeInTheDocument(); + expect(screen.getByText(device.getDisplayName()!)).toBeInTheDocument(); }); it("with verified user and device, displays no button and a 'Trusted' label", () => { @@ -365,7 +365,7 @@ describe("", () => { renderComponent(); expect(screen.queryByRole("button")).not.toBeInTheDocument; - expect(screen.getByText(device.getDisplayName())).toBeInTheDocument(); + expect(screen.getByText(device.getDisplayName()!)).toBeInTheDocument(); expect(screen.getByText("Trusted")).toBeInTheDocument(); }); @@ -373,7 +373,7 @@ describe("", () => { mockClient.getUser.mockReturnValueOnce(null); renderComponent(); - const button = screen.getByRole("button", { name: device.getDisplayName() }); + const button = screen.getByRole("button", { name: device.getDisplayName()! }); expect(button).toBeInTheDocument; await userEvent.click(button); @@ -387,7 +387,7 @@ describe("", () => { mockClient.isGuest.mockReturnValueOnce(true); renderComponent(); - const button = screen.getByRole("button", { name: device.getDisplayName() }); + const button = screen.getByRole("button", { name: device.getDisplayName()! }); expect(button).toBeInTheDocument; await userEvent.click(button); diff --git a/test/stores/SetupEncryptionStore-test.ts b/test/stores/SetupEncryptionStore-test.ts index f2e27948f2..8c25861438 100644 --- a/test/stores/SetupEncryptionStore-test.ts +++ b/test/stores/SetupEncryptionStore-test.ts @@ -46,7 +46,7 @@ describe("SetupEncryptionStore", () => { const makeRequest = jest.fn(); client.hasSecretStorageKey.mockResolvedValue(true); client.bootstrapCrossSigning.mockImplementation(async (opts: IBootstrapCrossSigningOpts) => { - await opts?.authUploadDeviceSigningKeys(makeRequest); + await opts?.authUploadDeviceSigningKeys?.(makeRequest); }); mocked(accessSecretStorage).mockImplementation(async (func: () => Promise) => { await func();