diff --git a/src/ContentMessages.ts b/src/ContentMessages.ts index 85ca90067d..e05858bb16 100644 --- a/src/ContentMessages.ts +++ b/src/ContentMessages.ts @@ -89,7 +89,7 @@ async function loadImageElement(imageFile: File): Promise<{ // check for hi-dpi PNGs and fudge display resolution as needed. // this is mainly needed for macOS screencaps - let parsePromise: Promise; + let parsePromise = Promise.resolve(false); if (imageFile.type === "image/png") { // in practice macOS happens to order the chunks so they fall in // the first 0x1000 bytes (thanks to a massive ICC header). @@ -101,7 +101,7 @@ async function loadImageElement(imageFile: File): Promise<{ const chunks = extractPngChunks(buffer); for (const chunk of chunks) { if (chunk.name === "pHYs") { - if (chunk.data.byteLength !== PHYS_HIDPI.length) return; + if (chunk.data.byteLength !== PHYS_HIDPI.length) return false; return chunk.data.every((val, i) => val === PHYS_HIDPI[i]); } } @@ -199,10 +199,10 @@ function loadVideoElement(videoFile: File): Promise { reject(e); }; - let dataUrl = ev.target.result as string; + let dataUrl = ev.target?.result as string; // Chrome chokes on quicktime but likes mp4, and `file.type` is // read only, so do this horrible hack to unbreak quicktime - if (dataUrl.startsWith("data:video/quicktime;")) { + if (dataUrl?.startsWith("data:video/quicktime;")) { dataUrl = dataUrl.replace("data:video/quicktime;", "data:video/mp4;"); } @@ -258,7 +258,7 @@ function readFileAsArrayBuffer(file: File | Blob): Promise { return new Promise((resolve, reject) => { const reader = new FileReader(); reader.onload = function (e): void { - resolve(e.target.result as ArrayBuffer); + resolve(e.target?.result as ArrayBuffer); }; reader.onerror = function (e): void { reject(e); @@ -329,7 +329,7 @@ export async function uploadFile( export default class ContentMessages { private inprogress: RoomUpload[] = []; - private mediaConfig: IMediaConfig = null; + private mediaConfig: IMediaConfig | null = null; public sendStickerContentToRoom( url: string, @@ -377,8 +377,8 @@ export default class ContentMessages { modal.close(); } - const tooBigFiles = []; - const okFiles = []; + const tooBigFiles: File[] = []; + const okFiles: File[] = []; for (const file of files) { if (this.isFileSizeAcceptable(file)) { @@ -420,7 +420,14 @@ export default class ContentMessages { } promBefore = doMaybeLocalRoomAction(roomId, (actualRoomId) => - this.sendContentToRoom(file, actualRoomId, relation, matrixClient, replyToEvent, loopPromiseBefore), + this.sendContentToRoom( + file, + actualRoomId, + relation, + matrixClient, + replyToEvent ?? undefined, + loopPromiseBefore, + ), ); } @@ -584,7 +591,7 @@ export default class ContentMessages { } private ensureMediaConfigFetched(matrixClient: MatrixClient): Promise { - if (this.mediaConfig !== null) return; + if (this.mediaConfig !== null) return Promise.resolve(); logger.log("[Media Config] Fetching"); return matrixClient diff --git a/src/DecryptionFailureTracker.ts b/src/DecryptionFailureTracker.ts index 7329c665bc..256fe245ee 100644 --- a/src/DecryptionFailureTracker.ts +++ b/src/DecryptionFailureTracker.ts @@ -138,7 +138,7 @@ export class DecryptionFailureTracker { return; } if (err) { - this.addDecryptionFailure(new DecryptionFailure(e.getId(), err.code)); + this.addDecryptionFailure(new DecryptionFailure(e.getId()!, err.code)); } else { // Could be an event in the failures, remove it this.removeDecryptionFailuresForEvent(e); @@ -146,7 +146,7 @@ export class DecryptionFailureTracker { } public addVisibleEvent(e: MatrixEvent): void { - const eventId = e.getId(); + const eventId = e.getId()!; if (this.trackedEvents.has(eventId)) { return; @@ -154,7 +154,7 @@ export class DecryptionFailureTracker { this.visibleEvents.add(eventId); if (this.failures.has(eventId) && !this.visibleFailures.has(eventId)) { - this.visibleFailures.set(eventId, this.failures.get(eventId)); + this.visibleFailures.set(eventId, this.failures.get(eventId)!); } } @@ -172,7 +172,7 @@ export class DecryptionFailureTracker { } public removeDecryptionFailuresForEvent(e: MatrixEvent): void { - const eventId = e.getId(); + const eventId = e.getId()!; this.failures.delete(eventId); this.visibleFailures.delete(eventId); } @@ -193,8 +193,8 @@ export class DecryptionFailureTracker { * Clear state and stop checking for and tracking failures. */ public stop(): void { - clearInterval(this.checkInterval); - clearInterval(this.trackInterval); + if (this.checkInterval) clearInterval(this.checkInterval); + if (this.trackInterval) clearInterval(this.trackInterval); this.failures = new Map(); this.visibleEvents = new Set(); diff --git a/src/DeviceListener.ts b/src/DeviceListener.ts index eccfc1c0e3..039adc27cd 100644 --- a/src/DeviceListener.ts +++ b/src/DeviceListener.ts @@ -51,7 +51,7 @@ import { isBulkUnverifiedDeviceReminderSnoozed } from "./utils/device/snoozeBulk const KEY_BACKUP_POLL_INTERVAL = 5 * 60 * 1000; export default class DeviceListener { - private dispatcherRef: string; + private dispatcherRef: string | null; // device IDs for which the user has dismissed the verify toast ('Later') private dismissed = new Set(); // has the user dismissed any of the various nag toasts to setup encryption on this device? @@ -152,7 +152,7 @@ export default class DeviceListener { private ensureDeviceIdsAtStartPopulated(): void { if (this.ourDeviceIdsAtStart === null) { const cli = MatrixClientPeg.get(); - this.ourDeviceIdsAtStart = new Set(cli.getStoredDevicesForUser(cli.getUserId()).map((d) => d.deviceId)); + this.ourDeviceIdsAtStart = new Set(cli.getStoredDevicesForUser(cli.getUserId()!).map((d) => d.deviceId)); } } @@ -162,7 +162,7 @@ export default class DeviceListener { // devicesAtStart list to the devices that we see after the fetch. if (initialFetch) return; - const myUserId = MatrixClientPeg.get().getUserId(); + const myUserId = MatrixClientPeg.get().getUserId()!; if (users.includes(myUserId)) this.ensureDeviceIdsAtStartPopulated(); // No need to do a recheck here: we just need to get a snapshot of our devices @@ -170,7 +170,7 @@ export default class DeviceListener { }; private onDevicesUpdated = (users: string[]): void => { - if (!users.includes(MatrixClientPeg.get().getUserId())) return; + if (!users.includes(MatrixClientPeg.get().getUserId()!)) return; this.recheck(); }; @@ -225,7 +225,7 @@ export default class DeviceListener { // The server doesn't tell us when key backup is set up, so we poll // & cache the result - private async getKeyBackupInfo(): Promise { + private async getKeyBackupInfo(): Promise { const now = new Date().getTime(); if (!this.keyBackupInfo || this.keyBackupFetchedAt < now - KEY_BACKUP_POLL_INTERVAL) { this.keyBackupInfo = await MatrixClientPeg.get().getKeyBackupVersion(); @@ -265,10 +265,10 @@ export default class DeviceListener { this.checkKeyBackupStatus(); } else if (this.shouldShowSetupEncryptionToast()) { // make sure our keys are finished downloading - await cli.downloadKeys([cli.getUserId()]); + await cli.downloadKeys([cli.getUserId()!]); // cross signing isn't enabled - nag to enable it // There are 3 different toasts for: - if (!cli.getCrossSigningId() && cli.getStoredCrossSigningForUser(cli.getUserId())) { + if (!cli.getCrossSigningId() && cli.getStoredCrossSigningForUser(cli.getUserId()!)) { // Cross-signing on account but this device doesn't trust the master key (verify this session) showSetupEncryptionToast(SetupKind.VERIFY_THIS_SESSION); this.checkKeyBackupStatus(); @@ -310,13 +310,13 @@ export default class DeviceListener { // as long as cross-signing isn't ready, // you can't see or dismiss any device toasts if (crossSigningReady) { - const devices = cli.getStoredDevicesForUser(cli.getUserId()); + const devices = cli.getStoredDevicesForUser(cli.getUserId()!); for (const device of devices) { if (device.deviceId === cli.deviceId) continue; const deviceTrust = await cli.checkDeviceTrust(cli.getUserId()!, device.deviceId!); if (!deviceTrust.isCrossSigningVerified() && !this.dismissed.has(device.deviceId)) { - if (this.ourDeviceIdsAtStart.has(device.deviceId)) { + if (this.ourDeviceIdsAtStart?.has(device.deviceId)) { oldUnverifiedDeviceIds.add(device.deviceId); } else { newUnverifiedDeviceIds.add(device.deviceId); diff --git a/src/IdentityAuthClient.tsx b/src/IdentityAuthClient.tsx index 293a3c19a6..12f42a3add 100644 --- a/src/IdentityAuthClient.tsx +++ b/src/IdentityAuthClient.tsx @@ -67,7 +67,7 @@ export default class IdentityAuthClient { window.localStorage.setItem("mx_is_access_token", this.accessToken); } - private readToken(): string { + private readToken(): string | null { if (this.tempClient) return null; // temporary client: ignore return window.localStorage.getItem("mx_is_access_token"); } @@ -77,13 +77,13 @@ export default class IdentityAuthClient { } // Returns a promise that resolves to the access_token string from the IS - public async getAccessToken({ check = true } = {}): Promise { + public async getAccessToken({ check = true } = {}): Promise { if (!this.authEnabled) { // The current IS doesn't support authentication return null; } - let token = this.accessToken; + let token: string | null = this.accessToken; if (!token) { token = this.readToken(); } diff --git a/src/LegacyCallHandler.tsx b/src/LegacyCallHandler.tsx index c3ca256646..34fffffc50 100644 --- a/src/LegacyCallHandler.tsx +++ b/src/LegacyCallHandler.tsx @@ -181,7 +181,7 @@ export default class LegacyCallHandler extends EventEmitter { * Gets the user-facing room associated with a call (call.roomId may be the call "virtual room" * if a voip_mxid_translate_pattern is set in the config) */ - public roomIdForCall(call: MatrixCall): string { + public roomIdForCall(call?: MatrixCall): string | null { if (!call) return null; // check asserted identity: if we're not obeying asserted identity, @@ -194,7 +194,7 @@ export default class LegacyCallHandler extends EventEmitter { } } - return VoipUserMapper.sharedInstance().nativeRoomForVirtualRoom(call.roomId) || call.roomId; + return VoipUserMapper.sharedInstance().nativeRoomForVirtualRoom(call.roomId) ?? call.roomId ?? null; } public start(): void { @@ -282,7 +282,7 @@ export default class LegacyCallHandler extends EventEmitter { } public unSilenceCall(callId: string): void { - if (this.isForcedSilent) return; + if (this.isForcedSilent()) return; this.silencedCalls.delete(callId); this.emit(LegacyCallHandlerEvent.SilencedCallsChanged, this.silencedCalls); this.play(AudioID.Ring); @@ -341,14 +341,14 @@ export default class LegacyCallHandler extends EventEmitter { } private shouldObeyAssertedfIdentity(): boolean { - return SdkConfig.getObject("voip")?.get("obey_asserted_identity"); + return !!SdkConfig.getObject("voip")?.get("obey_asserted_identity"); } - public getSupportsPstnProtocol(): boolean { + public getSupportsPstnProtocol(): boolean | null { return this.supportsPstnProtocol; } - public getSupportsVirtualRooms(): boolean { + public getSupportsVirtualRooms(): boolean | null { return this.supportsSipNativeVirtual; } @@ -414,7 +414,7 @@ export default class LegacyCallHandler extends EventEmitter { cli.prepareToEncrypt(cli.getRoom(call.roomId)); }; - public getCallById(callId: string): MatrixCall { + public getCallById(callId: string): MatrixCall | null { for (const call of this.calls.values()) { if (call.callId === callId) return call; } @@ -435,7 +435,7 @@ export default class LegacyCallHandler extends EventEmitter { } public getAllActiveCalls(): MatrixCall[] { - const activeCalls = []; + const activeCalls: MatrixCall[] = []; for (const call of this.calls.values()) { if (call.state !== CallState.Ended && call.state !== CallState.Ringing) { @@ -446,7 +446,7 @@ export default class LegacyCallHandler extends EventEmitter { } public getAllActiveCallsNotInRoom(notInThisRoomId: string): MatrixCall[] { - const callsNotInThatRoom = []; + const callsNotInThatRoom: MatrixCall[] = []; for (const [roomId, call] of this.calls.entries()) { if (roomId !== notInThisRoomId && call.state !== CallState.Ended) { @@ -547,7 +547,7 @@ export default class LegacyCallHandler extends EventEmitter { const mappedRoomId = this.roomIdForCall(call); const callForThisRoom = this.getCallForRoom(mappedRoomId); - return callForThisRoom && call.callId === callForThisRoom.callId; + return !!callForThisRoom && call.callId === callForThisRoom.callId; } private setCallListeners(call: MatrixCall): void { @@ -610,7 +610,7 @@ export default class LegacyCallHandler extends EventEmitter { return; } - const newAssertedIdentity = call.getRemoteAssertedIdentity().id; + const newAssertedIdentity = call.getRemoteAssertedIdentity()?.id; let newNativeAssertedIdentity = newAssertedIdentity; if (newAssertedIdentity) { const response = await this.sipNativeLookup(newAssertedIdentity); @@ -642,7 +642,7 @@ export default class LegacyCallHandler extends EventEmitter { }); } - private onCallStateChanged = (newState: CallState, oldState: CallState, call: MatrixCall): void => { + private onCallStateChanged = (newState: CallState, oldState: CallState | null, call: MatrixCall): void => { if (!this.matchesCallForThisRoom(call)) return; const mappedRoomId = this.roomIdForCall(call); @@ -830,7 +830,7 @@ export default class LegacyCallHandler extends EventEmitter { "turn.matrix.org, but this will not be as reliable, and " + "it will share your IP address with that server. You can also manage " + "this in Settings.", - null, + undefined, { code }, )}

@@ -843,7 +843,7 @@ export default class LegacyCallHandler extends EventEmitter { cli.setFallbackICEServerAllowed(allow); }, }, - null, + undefined, true, ); } @@ -882,7 +882,7 @@ export default class LegacyCallHandler extends EventEmitter { title, description, }, - null, + undefined, true, ); } diff --git a/src/Login.ts b/src/Login.ts index dbcdfe954e..73e6366956 100644 --- a/src/Login.ts +++ b/src/Login.ts @@ -29,20 +29,17 @@ interface ILoginOptions { } export default class Login { - private hsUrl: string; - private isUrl: string; - private fallbackHsUrl: string; - private flows: Array; - private defaultDeviceDisplayName: string; - private tempClient: MatrixClient; + private flows: Array = []; + private readonly defaultDeviceDisplayName?: string; + private tempClient: MatrixClient | null = null; // memoize - public constructor(hsUrl: string, isUrl: string, fallbackHsUrl?: string, opts?: ILoginOptions) { - this.hsUrl = hsUrl; - this.isUrl = isUrl; - this.fallbackHsUrl = fallbackHsUrl; - this.flows = []; + public constructor( + private hsUrl: string, + private isUrl: string, + private fallbackHsUrl: string | null, + opts: ILoginOptions, + ) { this.defaultDeviceDisplayName = opts.defaultDeviceDisplayName; - this.tempClient = null; // memoize } public getHomeserverUrl(): string { @@ -96,7 +93,7 @@ export default class Login { phoneNumber: string | undefined, password: string, ): Promise { - const isEmail = username?.indexOf("@") > 0; + const isEmail = !!username && username.indexOf("@") > 0; let identifier; if (phoneCountry && phoneNumber) { @@ -127,7 +124,7 @@ export default class Login { }; const tryFallbackHs = (originalError: Error): Promise => { - return sendLoginRequest(this.fallbackHsUrl, this.isUrl, "m.login.password", loginParams).catch( + return sendLoginRequest(this.fallbackHsUrl!, this.isUrl, "m.login.password", loginParams).catch( (fallbackError) => { logger.log("fallback HS login failed", fallbackError); // throw the original error @@ -136,13 +133,13 @@ export default class Login { ); }; - let originalLoginError = null; + let originalLoginError: Error | null = null; return sendLoginRequest(this.hsUrl, this.isUrl, "m.login.password", loginParams) .catch((error) => { originalLoginError = error; if (error.httpStatus === 403) { if (this.fallbackHsUrl) { - return tryFallbackHs(originalLoginError); + return tryFallbackHs(originalLoginError!); } } throw originalLoginError; diff --git a/src/ScalarAuthClient.ts b/src/ScalarAuthClient.ts index 1859399d41..558e6362b9 100644 --- a/src/ScalarAuthClient.ts +++ b/src/ScalarAuthClient.ts @@ -256,7 +256,7 @@ export default class ScalarAuthClient { } } - public getScalarInterfaceUrlForRoom(room: Room, screen: string, id: string): string { + public getScalarInterfaceUrlForRoom(room: Room, screen?: string, id?: string): string { const roomId = room.roomId; const roomName = room.name; let url = this.uiUrl; diff --git a/src/SecurityManager.ts b/src/SecurityManager.ts index 73f4be06ea..2a56de8751 100644 --- a/src/SecurityManager.ts +++ b/src/SecurityManager.ts @@ -102,7 +102,7 @@ async function getSecretStorageKey({ }): Promise<[string, Uint8Array]> { const cli = MatrixClientPeg.get(); let keyId = await cli.getDefaultSecretStorageKeyId(); - let keyInfo: ISecretStorageKeyInfo; + let keyInfo!: ISecretStorageKeyInfo; if (keyId) { // use the default SSSS key if set keyInfo = keyInfos[keyId]; diff --git a/src/SlashCommands.tsx b/src/SlashCommands.tsx index e1f028bedd..66f673445d 100644 --- a/src/SlashCommands.tsx +++ b/src/SlashCommands.tsx @@ -82,6 +82,7 @@ const singleMxcUpload = async (): Promise => { fileSelector.setAttribute("type", "file"); fileSelector.onchange = (ev: HTMLInputEvent) => { const file = ev.target.files?.[0]; + if (!file) return; Modal.createDialog(UploadConfirmDialog, { file, @@ -304,7 +305,7 @@ export const Commands = [ if (args) { const cli = MatrixClientPeg.get(); const room = cli.getRoom(roomId); - if (!room.currentState.mayClientSendStateEvent("m.room.tombstone", cli)) { + if (!room?.currentState.mayClientSendStateEvent("m.room.tombstone", cli)) { return reject( newTranslatableError("You do not have the required permissions to use this command."), ); @@ -313,7 +314,7 @@ export const Commands = [ const { finished } = Modal.createDialog( RoomUpgradeWarningDialog, { roomId: roomId, targetVersion: args }, - /*className=*/ null, + /*className=*/ undefined, /*isPriority=*/ false, /*isStatic=*/ true, ); @@ -1199,7 +1200,7 @@ export const Commands = [ description: _td("Switches to this room's virtual room, if it has one"), category: CommandCategories.advanced, isEnabled(): boolean { - return LegacyCallHandler.instance.getSupportsVirtualRooms() && !isCurrentLocalRoom(); + return !!LegacyCallHandler.instance.getSupportsVirtualRooms() && !isCurrentLocalRoom(); }, runFn: (roomId) => { return success( @@ -1389,7 +1390,7 @@ export function parseCommandString(input: string): { cmd?: string; args?: string const bits = input.match(/^(\S+?)(?:[ \n]+((.|\n)*))?$/); let cmd: string; - let args: string; + let args: string | undefined; if (bits) { cmd = bits[1].substring(1).toLowerCase(); args = bits[2]; @@ -1414,7 +1415,7 @@ interface ICmd { export function getCommand(input: string): ICmd { const { cmd, args } = parseCommandString(input); - if (CommandMap.has(cmd) && CommandMap.get(cmd)!.isEnabled()) { + if (cmd && CommandMap.has(cmd) && CommandMap.get(cmd)!.isEnabled()) { return { cmd: CommandMap.get(cmd), args, diff --git a/src/TextForEvent.tsx b/src/TextForEvent.tsx index 1798a5016f..790ce2571b 100644 --- a/src/TextForEvent.tsx +++ b/src/TextForEvent.tsx @@ -120,20 +120,20 @@ function textForMemberEvent(ev: MatrixEvent, allowJSX: boolean, showHiddenEvents // We're taking the display namke directly from the event content here so we need // to strip direction override chars which the js-sdk would normally do when // calculating the display name - oldDisplayName: removeDirectionOverrideChars(prevContent.displayname), - displayName: removeDirectionOverrideChars(content.displayname), + oldDisplayName: removeDirectionOverrideChars(prevContent.displayname!), + displayName: removeDirectionOverrideChars(content.displayname!), }); } else if (!prevContent.displayname && content.displayname) { return () => _t("%(senderName)s set their display name to %(displayName)s", { senderName: ev.getSender(), - displayName: removeDirectionOverrideChars(content.displayname), + displayName: removeDirectionOverrideChars(content.displayname!), }); } else if (prevContent.displayname && !content.displayname) { return () => _t("%(senderName)s removed their display name (%(oldDisplayName)s)", { senderName, - oldDisplayName: removeDirectionOverrideChars(prevContent.displayname), + oldDisplayName: removeDirectionOverrideChars(prevContent.displayname!), }); } else if (prevContent.avatar_url && !content.avatar_url) { return () => _t("%(senderName)s removed their profile picture", { senderName }); @@ -545,7 +545,7 @@ function textForPinnedEvent(event: MatrixEvent, allowJSX: boolean): (() => Rende if (newlyPinned.length === 1 && newlyUnpinned.length === 0) { // A single message was pinned, include a link to that message. if (allowJSX) { - const messageId = newlyPinned.pop(); + const messageId = newlyPinned.pop()!; return () => ( @@ -578,7 +578,7 @@ function textForPinnedEvent(event: MatrixEvent, allowJSX: boolean): (() => Rende if (newlyUnpinned.length === 1 && newlyPinned.length === 0) { // A single message was unpinned, include a link to that message. if (allowJSX) { - const messageId = newlyUnpinned.pop(); + const messageId = newlyUnpinned.pop()!; return () => ( diff --git a/src/components/structures/ContextMenu.tsx b/src/components/structures/ContextMenu.tsx index 118e1d8d95..5e66c883e7 100644 --- a/src/components/structures/ContextMenu.tsx +++ b/src/components/structures/ContextMenu.tsx @@ -105,7 +105,7 @@ export interface IProps extends MenuProps { } interface IState { - contextMenuElem: HTMLDivElement; + contextMenuElem?: HTMLDivElement; } // Generic ContextMenu Portal wrapper @@ -122,9 +122,7 @@ export default class ContextMenu extends React.PureComponent { public constructor(props: IProps) { super(props); - this.state = { - contextMenuElem: null, - }; + this.state = {}; // persist what had focus when we got initialized so we can return it after this.initialFocus = document.activeElement as HTMLElement; @@ -181,7 +179,7 @@ export default class ContextMenu extends React.PureComponent { button: 0, // Left relatedTarget: null, }); - document.elementFromPoint(x, y).dispatchEvent(clickEvent); + document.elementFromPoint(x, y)?.dispatchEvent(clickEvent); }); } }; @@ -239,7 +237,7 @@ export default class ContextMenu extends React.PureComponent { // MessageActionBar), we should close any ContextMenu that is open. KeyBindingAction.ArrowLeft, KeyBindingAction.ArrowRight, - ].includes(action) + ].includes(action!) ) { this.props.onFinished(); } @@ -312,12 +310,12 @@ export default class ContextMenu extends React.PureComponent { position.top = Math.min(position.top, maxTop); // Adjust the chevron if necessary if (chevronOffset.top !== undefined) { - chevronOffset.top = propsChevronOffset + top - position.top; + chevronOffset.top = propsChevronOffset! + top! - position.top; } } else if (position.bottom !== undefined) { position.bottom = Math.min(position.bottom, windowHeight - contextMenuRect.height - WINDOW_PADDING); if (chevronOffset.top !== undefined) { - chevronOffset.top = propsChevronOffset + position.bottom - bottom; + chevronOffset.top = propsChevronOffset! + position.bottom - bottom!; } } if (position.left !== undefined) { @@ -327,12 +325,12 @@ export default class ContextMenu extends React.PureComponent { } position.left = Math.min(position.left, maxLeft); if (chevronOffset.left !== undefined) { - chevronOffset.left = propsChevronOffset + left - position.left; + chevronOffset.left = propsChevronOffset! + left! - position.left; } } else if (position.right !== undefined) { position.right = Math.min(position.right, windowWidth - contextMenuRect.width - WINDOW_PADDING); if (chevronOffset.left !== undefined) { - chevronOffset.left = propsChevronOffset + position.right - right; + chevronOffset.left = propsChevronOffset! + position.right - right!; } } } @@ -389,7 +387,7 @@ export default class ContextMenu extends React.PureComponent { const wrapperStyle: CSSProperties = {}; if (!isNaN(Number(zIndex))) { - menuStyle["zIndex"] = zIndex + 1; + menuStyle["zIndex"] = zIndex! + 1; wrapperStyle["zIndex"] = zIndex; } diff --git a/src/components/views/dialogs/InviteDialog.tsx b/src/components/views/dialogs/InviteDialog.tsx index f1c8913da8..3084440619 100644 --- a/src/components/views/dialogs/InviteDialog.tsx +++ b/src/components/views/dialogs/InviteDialog.tsx @@ -97,7 +97,7 @@ class DMUserTile extends React.PureComponent { e.preventDefault(); e.stopPropagation(); - this.props.onRemove(this.props.member); + this.props.onRemove!(this.props.member); }; public render(): React.ReactNode { @@ -132,7 +132,7 @@ class DMUserTile extends React.PureComponent { interface IDMRoomTileProps { member: Member; - lastActiveTs: number; + lastActiveTs?: number; onToggle(member: Member): void; highlightWord: string; isSelected: boolean; @@ -188,7 +188,7 @@ class DMRoomTile extends React.PureComponent { } public render(): React.ReactNode { - let timestamp = null; + let timestamp: JSX.Element | undefined; if (this.props.lastActiveTs) { const humanTs = humanizeTime(this.props.lastActiveTs); timestamp = {humanTs}; @@ -201,7 +201,7 @@ class DMRoomTile extends React.PureComponent { { /> ); - let checkmark = null; + let checkmark: JSX.Element | undefined; if (this.props.isSelected) { // To reduce flickering we put the 'selected' room tile above the real avatar checkmark =
; @@ -301,7 +301,7 @@ interface IInviteDialogState { // These two flags are used for the 'Go' button to communicate what is going on. busy: boolean; - errorText: string; + errorText?: string; } export default class InviteDialog extends React.PureComponent { @@ -324,7 +324,7 @@ export default class InviteDialog extends React.PureComponent p.trim()) @@ -803,13 +808,11 @@ export default class InviteDialog extends React.PureComponent (kind === "recents" ? m.lastActive : null); + const lastActive = (m: Result): number | undefined => (kind === "recents" ? m.lastActive : undefined); let sectionName = kind === "recents" ? _t("Recent Conversations") : _t("Suggestions"); if (this.props.kind === KIND_INVITE) { @@ -924,7 +927,7 @@ export default class InviteDialog extends React.PureComponent @@ -960,7 +963,7 @@ export default class InviteDialog extends React.PureComponent ( - + )); const input = ( 0)} autoComplete="off" - placeholder={hasPlaceholder ? _t("Search") : null} + placeholder={hasPlaceholder ? _t("Search") : undefined} data-testid="invite-dialog-input" /> ); @@ -985,7 +988,7 @@ export default class InviteDialog extends React.PureComponent; } @@ -1108,7 +1111,7 @@ export default class InviteDialog extends React.PureComponent 0 || (this.state.filterText && this.state.filterText.includes("@")); const cli = MatrixClientPeg.get(); - const userId = cli.getUserId(); + const userId = cli.getUserId()!; if (this.props.kind === KIND_DM) { title = _t("Direct Messages"); @@ -1150,11 +1153,11 @@ export default class InviteDialog extends React.PureComponent{_t("If you can't see who you're looking for, send them your invite link below.")}

); - const link = makeUserPermalink(MatrixClientPeg.get().getUserId()); + const link = makeUserPermalink(MatrixClientPeg.get().getUserId()!); footer = (

{_t("Or send invite link")}

- makeUserPermalink(MatrixClientPeg.get().getUserId())}> + makeUserPermalink(MatrixClientPeg.get().getUserId()!)}> {link} @@ -1296,7 +1299,7 @@ export default class InviteDialog extends React.PureComponent it.toLowerCase()) || []), - ].filter(Boolean), + ].filter(Boolean) as string[], }); const toRoomResult = (room: Room): IRoomResult => { @@ -310,7 +310,7 @@ const SpotlightDialog: React.FC = ({ initialText = "", initialFilter = n }, [cli]); const msc3946ProcessDynamicPredecessor = useFeatureEnabled("feature_dynamic_room_predecessors"); - const ownInviteLink = makeUserPermalink(cli.getUserId()); + const ownInviteLink = makeUserPermalink(cli.getUserId()!); const [inviteLinkCopied, setInviteLinkCopied] = useState(false); const trimmedQuery = useMemo(() => query.trim(), [query]); @@ -465,7 +465,7 @@ const SpotlightDialog: React.FC = ({ initialText = "", initialFilter = n useWebSearchMetrics(numResults, query.length, true); const activeSpace = SpaceStore.instance.activeSpaceRoom; - const [spaceResults, spaceResultsLoading] = useSpaceResults(activeSpace, query); + const [spaceResults, spaceResultsLoading] = useSpaceResults(activeSpace ?? undefined, query); const setQuery = (e: ChangeEvent): void => { const newQuery = e.currentTarget.value; @@ -473,7 +473,7 @@ const SpotlightDialog: React.FC = ({ initialText = "", initialFilter = n }; useEffect(() => { setImmediate(() => { - let ref: Ref; + let ref: Ref | undefined; if (rovingContext.state.refs) { ref = rovingContext.state.refs[0]; } @@ -521,7 +521,7 @@ const SpotlightDialog: React.FC = ({ initialText = "", initialFilter = n onFinished(); }; - let otherSearchesSection: JSX.Element; + let otherSearchesSection: JSX.Element | undefined; if (trimmedQuery || filter !== Filter.PublicRooms) { otherSearchesSection = (
= ({ initialText = "", initialFilter = n ); }; - let peopleSection: JSX.Element; + let peopleSection: JSX.Element | undefined; if (results[Section.People].length) { peopleSection = (
= ({ initialText = "", initialFilter = n ); } - let suggestionsSection: JSX.Element; + let suggestionsSection: JSX.Element | undefined; if (results[Section.Suggestions].length && filter === Filter.People) { suggestionsSection = (
= ({ initialText = "", initialFilter = n ); } - let roomsSection: JSX.Element; + let roomsSection: JSX.Element | undefined; if (results[Section.Rooms].length) { roomsSection = (
= ({ initialText = "", initialFilter = n ); } - let spacesSection: JSX.Element; + let spacesSection: JSX.Element | undefined; if (results[Section.Spaces].length) { spacesSection = (
= ({ initialText = "", initialFilter = n ); } - let publicRoomsSection: JSX.Element; + let publicRoomsSection: JSX.Element | undefined; if (filter === Filter.PublicRooms) { publicRoomsSection = (
= ({ initialText = "", initialFilter = n ); } - let spaceRoomsSection: JSX.Element; + let spaceRoomsSection: JSX.Element | undefined; if (spaceResults.length && activeSpace && filter === null) { spaceRoomsSection = (
= ({ initialText = "", initialFilter = n ); } - let joinRoomSection: JSX.Element; + let joinRoomSection: JSX.Element | undefined; if ( trimmedQuery.startsWith("#") && trimmedQuery.includes(":") && @@ -868,7 +868,7 @@ const SpotlightDialog: React.FC = ({ initialText = "", initialFilter = n ); } - let hiddenResultsSection: JSX.Element; + let hiddenResultsSection: JSX.Element | undefined; if (filter === Filter.People) { hiddenResultsSection = (
@@ -921,7 +921,7 @@ const SpotlightDialog: React.FC = ({ initialText = "", initialFilter = n ); } - let groupChatSection: JSX.Element; + let groupChatSection: JSX.Element | undefined; if (filter === Filter.People) { groupChatSection = (
= ({ initialText = "", initialFilter = n ); } - let messageSearchSection: JSX.Element; + let messageSearchSection: JSX.Element | undefined; if (filter === null) { messageSearchSection = (
= ({ initialText = "", initialFilter = n ); } else { - let recentSearchesSection: JSX.Element; + let recentSearchesSection: JSX.Element | undefined; if (recentSearches.length) { recentSearchesSection = (
= ({ initialText = "", initialFilter = n break; } - let ref: RefObject; + let ref: RefObject | undefined; const accessibilityAction = getKeyBindingsManager().getAccessibilityAction(ev); switch (accessibilityAction) { case KeyBindingAction.Escape: diff --git a/src/components/views/spaces/SpaceBasicSettings.tsx b/src/components/views/spaces/SpaceBasicSettings.tsx index d5db0370a1..84f762d15e 100644 --- a/src/components/views/spaces/SpaceBasicSettings.tsx +++ b/src/components/views/spaces/SpaceBasicSettings.tsx @@ -24,11 +24,11 @@ import { chromeFileInputFix } from "../../../utils/BrowserWorkarounds"; interface IProps { avatarUrl?: string; avatarDisabled?: boolean; - name?: string; + name: string; nameDisabled?: boolean; topic?: string; topicDisabled?: boolean; - setAvatar(avatar: File): void; + setAvatar(avatar?: File): void; setName(name: string): void; setTopic(topic: string): void; } @@ -102,7 +102,7 @@ export const SpaceAvatar: React.FC { - setAvatarDataUrl(ev.target.result as string); + setAvatarDataUrl(ev.target?.result as string); }; reader.readAsDataURL(file); }} diff --git a/src/components/views/spaces/SpaceChildrenPicker.tsx b/src/components/views/spaces/SpaceChildrenPicker.tsx index 8bbf0e3868..3cf63fd0ad 100644 --- a/src/components/views/spaces/SpaceChildrenPicker.tsx +++ b/src/components/views/spaces/SpaceChildrenPicker.tsx @@ -53,7 +53,7 @@ const SpecificChildrenPicker: React.FC = ({ const matcher = new QueryMatcher(rooms, { keys: ["name"], - funcs: [(r) => [r.getCanonicalAlias(), ...r.getAltAliases()].filter(Boolean)], + funcs: [(r) => [r.getCanonicalAlias(), ...r.getAltAliases()].filter(Boolean) as string[]], shouldMatchWordsOnly: false, }); diff --git a/src/components/views/spaces/SpaceCreateMenu.tsx b/src/components/views/spaces/SpaceCreateMenu.tsx index 01fa0faf84..3a23b4074b 100644 --- a/src/components/views/spaces/SpaceCreateMenu.tsx +++ b/src/components/views/spaces/SpaceCreateMenu.tsx @@ -245,14 +245,14 @@ export const SpaceCreateForm: React.FC = ({ const SpaceCreateMenu: React.FC<{ onFinished(): void; }> = ({ onFinished }) => { - const [visibility, setVisibility] = useState(null); + const [visibility, setVisibility] = useState(null); const [busy, setBusy] = useState(false); const [name, setName] = useState(""); const spaceNameField = useRef(); const [alias, setAlias] = useState(""); const spaceAliasField = useRef(); - const [avatar, setAvatar] = useState(null); + const [avatar, setAvatar] = useState(undefined); const [topic, setTopic] = useState(""); const onSpaceCreateClick = async (e: ButtonEvent): Promise => { diff --git a/src/components/views/spaces/SpacePanel.tsx b/src/components/views/spaces/SpacePanel.tsx index 7d3bd5bd22..92d919c5d2 100644 --- a/src/components/views/spaces/SpacePanel.tsx +++ b/src/components/views/spaces/SpacePanel.tsx @@ -215,7 +215,7 @@ const CreateSpaceButton: React.FC; } diff --git a/src/components/views/spaces/SpaceTreeLevel.tsx b/src/components/views/spaces/SpaceTreeLevel.tsx index 6e39c818d8..8338101794 100644 --- a/src/components/views/spaces/SpaceTreeLevel.tsx +++ b/src/components/views/spaces/SpaceTreeLevel.tsx @@ -121,11 +121,11 @@ export const SpaceButton = forwardRef( ); } - let contextMenu: JSX.Element; - if (menuDisplayed && ContextMenuComponent) { + let contextMenu: JSX.Element | undefined; + if (menuDisplayed && handle.current && ContextMenuComponent) { contextMenu = ( @@ -242,7 +242,7 @@ export class SpaceItem extends React.PureComponent { } private get isCollapsed(): boolean { - return this.state.collapsed || this.props.isPanelCollapsed; + return this.state.collapsed || !!this.props.isPanelCollapsed; } private toggleCollapse = (evt: ButtonEvent): void => { diff --git a/src/components/views/user-onboarding/UserOnboardingHeader.tsx b/src/components/views/user-onboarding/UserOnboardingHeader.tsx index 77902a9aca..42ed7f0695 100644 --- a/src/components/views/user-onboarding/UserOnboardingHeader.tsx +++ b/src/components/views/user-onboarding/UserOnboardingHeader.tsx @@ -30,7 +30,7 @@ const onClickSendDm = (ev: ButtonEvent): void => { }; interface Props { - useCase: UseCase; + useCase: UseCase | null; } export function UserOnboardingHeader({ useCase }: Props): JSX.Element { diff --git a/src/components/views/user-onboarding/UserOnboardingPage.tsx b/src/components/views/user-onboarding/UserOnboardingPage.tsx index 8f8be5da95..242023deb5 100644 --- a/src/components/views/user-onboarding/UserOnboardingPage.tsx +++ b/src/components/views/user-onboarding/UserOnboardingPage.tsx @@ -38,7 +38,7 @@ interface Props { // We decided to only show the new user onboarding page to new users // For now, that means we set the cutoff at 2022-07-01 00:00 UTC const USER_ONBOARDING_CUTOFF_DATE = new Date(1_656_633_600); -export function showUserOnboardingPage(useCase: UseCase): boolean { +export function showUserOnboardingPage(useCase: UseCase | null): boolean { return useCase !== null || MatrixClientPeg.userRegisteredAfter(USER_ONBOARDING_CUTOFF_DATE); } @@ -55,13 +55,11 @@ export function UserOnboardingPage({ justRegistered = false }: Props): JSX.Eleme const [showList, setShowList] = useState(false); useEffect(() => { if (initialSyncComplete) { - let handler: number | null = window.setTimeout(() => { - handler = null; + const handler = window.setTimeout(() => { setShowList(true); }, ANIMATION_DURATION); return () => { clearTimeout(handler); - handler = null; }; } else { setShowList(false); diff --git a/src/components/views/voip/DialPad.tsx b/src/components/views/voip/DialPad.tsx index 407e0d72a6..634b88d144 100644 --- a/src/components/views/voip/DialPad.tsx +++ b/src/components/views/voip/DialPad.tsx @@ -18,6 +18,7 @@ import * as React from "react"; import AccessibleButton, { ButtonEvent } from "../elements/AccessibleButton"; import { _t } from "../../../languageHandler"; +import { XOR } from "../../../@types/common"; const BUTTONS = ["1", "2", "3", "4", "5", "6", "7", "8", "9", "*", "0", "#"]; const BUTTON_LETTERS = ["", "ABC", "DEF", "GHI", "JKL", "MNO", "PQRS", "TUV", "WXYZ", "", "+", ""]; @@ -31,7 +32,7 @@ interface IButtonProps { kind: DialPadButtonKind; digit?: string; digitSubtext?: string; - onButtonPress: (digit: string, ev: ButtonEvent) => void; + onButtonPress: (digit: string | undefined, ev: ButtonEvent) => void; } class DialPadButton extends React.PureComponent { @@ -60,16 +61,24 @@ class DialPadButton extends React.PureComponent { } } -interface IProps { +interface IBaseProps { onDigitPress: (digit: string, ev: ButtonEvent) => void; - hasDial: boolean; onDeletePress?: (ev: ButtonEvent) => void; - onDialPress?: () => void; + hasDial: boolean; } -export default class Dialpad extends React.PureComponent { +interface IProps extends IBaseProps { + hasDial: false; +} + +interface IDialProps extends IBaseProps { + hasDial: true; + onDialPress: () => void; +} + +export default class Dialpad extends React.PureComponent> { public render(): React.ReactNode { - const buttonNodes = []; + const buttonNodes: JSX.Element[] = []; for (let i = 0; i < BUTTONS.length; i++) { const button = BUTTONS[i]; diff --git a/src/components/views/voip/LegacyCallView.tsx b/src/components/views/voip/LegacyCallView.tsx index 41ad1897a2..1e8b4621f9 100644 --- a/src/components/views/voip/LegacyCallView.tsx +++ b/src/components/views/voip/LegacyCallView.tsx @@ -76,7 +76,7 @@ interface IState { sidebarShown: boolean; } -function getFullScreenElement(): Element | undefined { +function getFullScreenElement(): Element | null { return ( document.fullscreenElement || // moz omitted because firefox supports this unprefixed now (webkit here for safari) @@ -180,7 +180,7 @@ export default class LegacyCallView extends React.Component { } }; - private updateCallListeners(oldCall: MatrixCall, newCall: MatrixCall | null): void { + private updateCallListeners(oldCall: MatrixCall | null, newCall: MatrixCall | null): void { if (oldCall === newCall) return; if (oldCall) { @@ -245,7 +245,7 @@ export default class LegacyCallView extends React.Component { }; } - let primary: CallFeed; + let primary: CallFeed | undefined; // Try to use a screensharing as primary, a remote one if possible const screensharingFeeds = feeds.filter((feed) => feed.purpose === SDPStreamMetadataPurpose.Screenshare); @@ -289,7 +289,7 @@ export default class LegacyCallView extends React.Component { if (this.state.screensharing) { isScreensharing = await this.props.call.setScreensharingEnabled(false); } else { - if (PlatformPeg.get().supportsDesktopCapturer()) { + if (PlatformPeg.get()?.supportsDesktopCapturer()) { const { finished } = Modal.createDialog<[string]>(DesktopCapturerSourcePicker); const [source] = await finished; if (!source) return; @@ -403,7 +403,7 @@ export default class LegacyCallView extends React.Component { ); } - private renderToast(): JSX.Element { + private renderToast(): JSX.Element | null { const { call } = this.props; const someoneIsScreensharing = call.getFeeds().some((feed) => { return feed.purpose === SDPStreamMetadataPurpose.Screenshare; @@ -413,8 +413,8 @@ export default class LegacyCallView extends React.Component { const isScreensharing = call.isScreensharing(); const { primaryFeed, sidebarShown } = this.state; - const sharerName = primaryFeed?.getMember().name; - if (!sharerName) return; + const sharerName = primaryFeed?.getMember()?.name; + if (!sharerName) return null; let text = isScreensharing ? _t("You are presenting") : _t("%(sharerName)s is presenting", { sharerName }); if (!sidebarShown) { @@ -495,7 +495,7 @@ export default class LegacyCallView extends React.Component { ); } else if (isLocalOnHold) { onHoldText = _t("%(peerName)s held the call", { - peerName: call.getOpponentMember().name, + peerName: call.getOpponentMember()?.name, }); } @@ -556,8 +556,8 @@ export default class LegacyCallView extends React.Component { const client = MatrixClientPeg.get(); const callRoomId = LegacyCallHandler.instance.roomIdForCall(call); const secondaryCallRoomId = LegacyCallHandler.instance.roomIdForCall(secondaryCall); - const callRoom = client.getRoom(callRoomId); - const secCallRoom = secondaryCall ? client.getRoom(secondaryCallRoomId) : null; + const callRoom = callRoomId ? client.getRoom(callRoomId) : null; + const secCallRoom = secondaryCallRoomId ? client.getRoom(secondaryCallRoomId) : null; const callViewClasses = classNames({ mx_LegacyCallView: true, diff --git a/src/components/views/voip/LegacyCallView/LegacyCallViewButtons.tsx b/src/components/views/voip/LegacyCallView/LegacyCallViewButtons.tsx index b7125eb6a0..0ff66fc4e8 100644 --- a/src/components/views/voip/LegacyCallView/LegacyCallViewButtons.tsx +++ b/src/components/views/voip/LegacyCallView/LegacyCallViewButtons.tsx @@ -104,9 +104,9 @@ const LegacyCallViewDropdownButton: React.FC = ({ state, d onHover={(hovering) => setHoveringDropdown(hovering)} state={state} /> - {menuDisplayed && ( + {menuDisplayed && buttonRef.current && ( @@ -117,7 +117,7 @@ const LegacyCallViewDropdownButton: React.FC = ({ state, d interface IProps { call: MatrixCall; - pipMode: boolean; + pipMode?: boolean; handlers: { onHangupClick: () => void; onScreenshareClick: () => void; @@ -150,7 +150,7 @@ interface IState { export default class LegacyCallViewButtons extends React.Component { private dialpadButton = createRef(); private contextMenuButton = createRef(); - private controlsHideTimer: number = null; + private controlsHideTimer: number | null = null; public constructor(props: IProps) { super(props); @@ -223,7 +223,7 @@ export default class LegacyCallViewButtons extends React.Component = ({ callRoom }) => { }; interface LegacyCallViewHeaderProps { - pipMode: boolean; - callRooms?: Room[]; - onPipMouseDown: (event: React.MouseEvent) => void; + pipMode?: boolean; + callRooms: [Room, Room | null]; + onPipMouseDown?: (event: React.MouseEvent) => void; onExpand?: () => void; onPin?: () => void; onMaximize?: () => void; @@ -81,7 +81,7 @@ interface LegacyCallViewHeaderProps { const LegacyCallViewHeader: React.FC = ({ pipMode = false, - callRooms = [], + callRooms, onPipMouseDown, onExpand, onPin, diff --git a/src/components/views/voip/VideoFeed.tsx b/src/components/views/voip/VideoFeed.tsx index 9cd0086c31..35fb851f4a 100644 --- a/src/components/views/voip/VideoFeed.tsx +++ b/src/components/views/voip/VideoFeed.tsx @@ -95,7 +95,7 @@ export default class VideoFeed extends React.PureComponent { element.addEventListener("resize", this.onResize); }; - private updateFeed(oldFeed: CallFeed, newFeed: CallFeed): void { + private updateFeed(oldFeed: CallFeed | null, newFeed: CallFeed | null): void { if (oldFeed === newFeed) return; if (oldFeed) { diff --git a/src/customisations/Media.ts b/src/customisations/Media.ts index c3debef86f..9c07288b18 100644 --- a/src/customisations/Media.ts +++ b/src/customisations/Media.ts @@ -84,7 +84,7 @@ export class Media { * The HTTP URL for the thumbnail media (without any specified width, height, etc). Null/undefined * if no thumbnail media recorded. */ - public get thumbnailHttp(): string | undefined | null { + public get thumbnailHttp(): string | null { if (!this.hasThumbnail) return null; // eslint-disable-next-line no-restricted-properties return this.client.mxcUrlToHttp(this.thumbnailMxc!); diff --git a/src/editor/commands.tsx b/src/editor/commands.tsx index 55dae83503..f65507054b 100644 --- a/src/editor/commands.tsx +++ b/src/editor/commands.tsx @@ -45,7 +45,7 @@ export function isSlashCommand(model: EditorModel): boolean { return false; } -export function getSlashCommand(model: EditorModel): [Command, string, string] { +export function getSlashCommand(model: EditorModel): [Command | undefined, string | undefined, string] { const commandText = model.parts.reduce((text, part) => { // use mxid to textify user pills in a command and room alias/id for room pills if (part.type === Type.UserPill || part.type === Type.RoomPill) { @@ -69,7 +69,7 @@ export async function runSlashCommand( if (result.promise) { try { if (cmd.category === CommandCategories.messages || cmd.category === CommandCategories.effects) { - messageContent = await result.promise; + messageContent = (await result.promise) ?? null; } else { await result.promise; } diff --git a/src/editor/dom.ts b/src/editor/dom.ts index 19bccdbef7..05d5789066 100644 --- a/src/editor/dom.ts +++ b/src/editor/dom.ts @@ -32,7 +32,7 @@ export function walkDOMDepthFirst(rootNode: Node, enterNodeCallback: Predicate, } else if (node.nextSibling) { node = node.nextSibling; } else { - while (!node.nextSibling && node !== rootNode) { + while (node && !node.nextSibling && node !== rootNode) { node = node.parentElement; if (node !== rootNode) { leaveNodeCallback(node); diff --git a/src/editor/model.ts b/src/editor/model.ts index 975e63aa4c..83f0a232f6 100644 --- a/src/editor/model.ts +++ b/src/editor/model.ts @@ -127,7 +127,7 @@ export default class EditorModel { return this._parts; } - public get autoComplete(): AutocompleteWrapperModel { + public get autoComplete(): AutocompleteWrapperModel | null { if (this.activePartIdx === this.autoCompletePartIdx) { return this._autoComplete; } @@ -212,12 +212,12 @@ export default class EditorModel { const transformAddedLen = this.getTransformAddedLen(newPosition, inputType, diff); newPosition = this.positionForOffset(caretOffset + transformAddedLen, true); } - this.updateCallback(newPosition, inputType, diff); + this.updateCallback?.(newPosition, inputType, diff); return acPromise; } private getTransformAddedLen(newPosition: DocumentPosition, inputType: string, diff: IDiff): number { - const result = this.transformCallback(newPosition, inputType, diff); + const result = this.transformCallback?.(newPosition, inputType, diff); return Number.isFinite(result) ? (result as number) : 0; } @@ -268,13 +268,13 @@ export default class EditorModel { // rerender even if editor contents didn't change // to make sure the MessageEditor checks // model.autoComplete being empty and closes it - this.updateCallback(pos); + this.updateCallback?.(pos); }; private mergeAdjacentParts(): void { let prevPart: Part | undefined; for (let i = 0; i < this._parts.length; ++i) { - let part = this._parts[i]; + let part: Part | undefined = this._parts[i]; const isEmpty = !part.text.length; const isMerged = !isEmpty && prevPart && prevPart.merge?.(part); if (isEmpty || isMerged) { @@ -452,13 +452,13 @@ export default class EditorModel { */ public transform(callback: ManualTransformCallback): Promise { const pos = callback(); - let acPromise: Promise = null; + let acPromise: Promise | null = null; if (!(pos instanceof Range)) { acPromise = this.setActivePart(pos, true); } else { acPromise = Promise.resolve(); } - this.updateCallback(pos); + this.updateCallback?.(pos); return acPromise; } } diff --git a/src/editor/parts.ts b/src/editor/parts.ts index d5a5c39988..8fde5e4f84 100644 --- a/src/editor/parts.ts +++ b/src/editor/parts.ts @@ -422,7 +422,7 @@ class RoomPillPart extends PillPart { protected setAvatar(node: HTMLElement): void { let initialLetter = ""; - let avatarUrl = Avatar.avatarUrlForRoom(this.room, 16, 16, "crop"); + let avatarUrl = Avatar.avatarUrlForRoom(this.room ?? null, 16, 16, "crop"); if (!avatarUrl) { initialLetter = Avatar.getInitialLetter(this.room?.name || this.resourceId) ?? ""; avatarUrl = Avatar.defaultAvatarUrlForString(this.room?.roomId ?? this.resourceId); @@ -541,7 +541,7 @@ export class PartCreator { public constructor( private readonly room: Room, private readonly client: MatrixClient, - autoCompleteCreator: AutoCompleteCreator = null, + autoCompleteCreator: AutoCompleteCreator | null = null, ) { // pre-create the creator as an object even without callback so it can already be passed // to PillCandidatePart (e.g. while deserializing) and set later on @@ -574,7 +574,7 @@ export class PartCreator { return this.plain(text); } - public deserializePart(part: SerializedPart): Part { + public deserializePart(part: SerializedPart): Part | undefined { switch (part.type) { case Type.Plain: return this.plain(part.text); @@ -612,7 +612,7 @@ export class PartCreator { public roomPill(alias: string, roomId?: string): RoomPillPart { let room: Room | undefined; if (roomId || alias[0] !== "#") { - room = this.client.getRoom(roomId || alias); + room = this.client.getRoom(roomId || alias) ?? undefined; } else { room = this.client.getRooms().find((r) => { return r.getCanonicalAlias() === alias || r.getAltAliases().includes(alias); @@ -691,7 +691,7 @@ export class CommandPartCreator extends PartCreator { return new CommandPart(text, this.autoCompleteCreator); } - public deserializePart(part: SerializedPart): Part { + public deserializePart(part: SerializedPart): Part | undefined { if (part.type === Type.Command) { return this.command(part.text); } else { diff --git a/src/editor/render.ts b/src/editor/render.ts index a5c90a9077..213d68413c 100644 --- a/src/editor/render.ts +++ b/src/editor/render.ts @@ -93,8 +93,8 @@ function reconcileLine(lineContainer: ChildNode, parts: Part[]): void { if (needsCaretNodeBefore(part, prevPart)) { if (isCaretNode(currentNode as Element)) { - updateCaretNode(currentNode); - currentNode = currentNode.nextSibling; + updateCaretNode(currentNode!); + currentNode = currentNode!.nextSibling; } else { lineContainer.insertBefore(createCaretNode(), currentNode); } diff --git a/src/editor/serialize.ts b/src/editor/serialize.ts index eb27c122dc..20ed9ab67e 100644 --- a/src/editor/serialize.ts +++ b/src/editor/serialize.ts @@ -63,7 +63,7 @@ interface ISerializeOpts { export function htmlSerializeIfNeeded( model: EditorModel, { forceHTML = false, useMarkdown = true }: ISerializeOpts = {}, -): string { +): string | undefined { if (!useMarkdown) { return escapeHtml(textSerialize(model)).replace(/\n/g, "
"); } @@ -72,7 +72,7 @@ export function htmlSerializeIfNeeded( return htmlSerializeFromMdIfNeeded(md, { forceHTML }); } -export function htmlSerializeFromMdIfNeeded(md: string, { forceHTML = false } = {}): string { +export function htmlSerializeFromMdIfNeeded(md: string, { forceHTML = false } = {}): string | undefined { // copy of raw input to remove unwanted math later const orig = md; diff --git a/src/hooks/usePublicRoomDirectory.ts b/src/hooks/usePublicRoomDirectory.ts index eeda3480f1..e1bf806c0b 100644 --- a/src/hooks/usePublicRoomDirectory.ts +++ b/src/hooks/usePublicRoomDirectory.ts @@ -139,7 +139,7 @@ export const usePublicRoomDirectory = (): { SdkConfig.getObject("room_directory")?.get("servers")?.includes(lsRoomServer) || SettingsStore.getValue("room_directory_servers")?.includes(lsRoomServer) ) { - roomServer = lsRoomServer; + roomServer = lsRoomServer!; } let instanceId: string | undefined = undefined; diff --git a/src/hooks/useSettings.ts b/src/hooks/useSettings.ts index 1e5ef56952..cabd4cbee9 100644 --- a/src/hooks/useSettings.ts +++ b/src/hooks/useSettings.ts @@ -19,7 +19,7 @@ import { useEffect, useState } from "react"; import SettingsStore from "../settings/SettingsStore"; // Hook to fetch the value of a setting and dynamically update when it changes -export const useSettingValue = (settingName: string, roomId: string = null, excludeDefault = false): T => { +export const useSettingValue = (settingName: string, roomId: string | null = null, excludeDefault = false): T => { const [value, setValue] = useState(SettingsStore.getValue(settingName, roomId, excludeDefault)); useEffect(() => { @@ -36,7 +36,7 @@ export const useSettingValue = (settingName: string, roomId: string = null, e }; // Hook to fetch whether a feature is enabled and dynamically update when that changes -export const useFeatureEnabled = (featureName: string, roomId: string = null): boolean => { +export const useFeatureEnabled = (featureName: string, roomId: string | null = null): boolean => { const [enabled, setEnabled] = useState(SettingsStore.getValue(featureName, roomId)); useEffect(() => { diff --git a/src/hooks/useSlidingSyncRoomSearch.ts b/src/hooks/useSlidingSyncRoomSearch.ts index 7d5d1d92aa..c4dfbbf409 100644 --- a/src/hooks/useSlidingSyncRoomSearch.ts +++ b/src/hooks/useSlidingSyncRoomSearch.ts @@ -23,7 +23,7 @@ import { SlidingSyncManager } from "../SlidingSyncManager"; export interface SlidingSyncRoomSearchOpts { limit: number; - query?: string; + query: string; } export const useSlidingSyncRoomSearch = (): { @@ -55,7 +55,7 @@ export const useSlidingSyncRoomSearch = (): { room_name_like: term, }, }); - const rooms = []; + const rooms: Room[] = []; const { roomIndexToRoomId } = SlidingSyncManager.instance.slidingSync.getListData( SlidingSyncManager.ListSearch, )!; diff --git a/src/hooks/useTimeout.ts b/src/hooks/useTimeout.ts index 411dcf8501..df00e8bbb5 100644 --- a/src/hooks/useTimeout.ts +++ b/src/hooks/useTimeout.ts @@ -31,7 +31,7 @@ export const useTimeout = (handler: Handler, timeoutMs: number): void => { // Set up timer useEffect(() => { const timeoutID = window.setTimeout(() => { - savedHandler.current(); + savedHandler.current?.(); }, timeoutMs); return () => clearTimeout(timeoutID); }, [timeoutMs]); @@ -50,7 +50,7 @@ export const useInterval = (handler: Handler, intervalMs: number): void => { // Set up timer useEffect(() => { const intervalID = window.setInterval(() => { - savedHandler.current(); + savedHandler.current?.(); }, intervalMs); return () => clearInterval(intervalID); }, [intervalMs]); diff --git a/src/hooks/useUserDirectory.ts b/src/hooks/useUserDirectory.ts index c256112af7..d775994e3a 100644 --- a/src/hooks/useUserDirectory.ts +++ b/src/hooks/useUserDirectory.ts @@ -22,7 +22,7 @@ import { useLatestResult } from "./useLatestResult"; export interface IUserDirectoryOpts { limit: number; - query?: string; + query: string; } export const useUserDirectory = (): { diff --git a/src/indexing/EventIndex.ts b/src/indexing/EventIndex.ts index e3407e73b0..fbc528dec4 100644 --- a/src/indexing/EventIndex.ts +++ b/src/indexing/EventIndex.ts @@ -27,6 +27,7 @@ import { IEventWithRoomId, IMatrixProfile, IResultRoomEvents } from "matrix-js-s import { logger } from "matrix-js-sdk/src/logger"; import { EventType } from "matrix-js-sdk/src/@types/event"; import { ClientEvent, MatrixClient } from "matrix-js-sdk/src/client"; +import { ISyncStateData, SyncState } from "matrix-js-sdk/src/sync"; import PlatformPeg from "../PlatformPeg"; import { MatrixClientPeg } from "../MatrixClientPeg"; @@ -50,11 +51,11 @@ interface ICrawler { */ export default class EventIndex extends EventEmitter { private crawlerCheckpoints: ICrawlerCheckpoint[] = []; - private crawler: ICrawler = null; - private currentCheckpoint: ICrawlerCheckpoint = null; + private crawler: ICrawler | null = null; + private currentCheckpoint: ICrawlerCheckpoint | null = null; public async init(): Promise { - const indexManager = PlatformPeg.get().getEventIndexingManager(); + const indexManager = PlatformPeg.get()?.getEventIndexingManager(); this.crawlerCheckpoints = await indexManager.loadCheckpoints(); logger.log("EventIndex: Loaded checkpoints", this.crawlerCheckpoints); @@ -91,7 +92,7 @@ export default class EventIndex extends EventEmitter { * Get crawler checkpoints for the encrypted rooms and store them in the index. */ public async addInitialCheckpoints(): Promise { - const indexManager = PlatformPeg.get().getEventIndexingManager(); + const indexManager = PlatformPeg.get()?.getEventIndexingManager(); const client = MatrixClientPeg.get(); const rooms = client.getRooms(); @@ -157,8 +158,8 @@ export default class EventIndex extends EventEmitter { * - Every other sync, tell the event index to commit all the queued up * live events */ - private onSync = async (state: string, prevState: string, data: object): Promise => { - const indexManager = PlatformPeg.get().getEventIndexingManager(); + private onSync = async (state: SyncState, prevState: SyncState | null, data?: ISyncStateData): Promise => { + const indexManager = PlatformPeg.get()?.getEventIndexingManager(); if (prevState === "PREPARED" && state === "SYNCING") { // If our indexer is empty we're most likely running Element the @@ -188,7 +189,7 @@ export default class EventIndex extends EventEmitter { */ private onRoomTimeline = async ( ev: MatrixEvent, - room: Room | null, + room: Room | undefined, toStartOfTimeline: boolean, removed: boolean, data: IRoomTimelineData, @@ -198,7 +199,7 @@ export default class EventIndex extends EventEmitter { const client = MatrixClientPeg.get(); // We only index encrypted rooms locally. - if (!client.isRoomEncrypted(ev.getRoomId())) return; + if (!client.isRoomEncrypted(ev.getRoomId()!)) return; if (ev.isRedaction()) { return this.redactEvent(ev); @@ -228,7 +229,7 @@ export default class EventIndex extends EventEmitter { * We cannot rely on Room.redaction as this only fires if the redaction applied to an event the js-sdk has loaded. */ private redactEvent = async (ev: MatrixEvent): Promise => { - const indexManager = PlatformPeg.get().getEventIndexingManager(); + const indexManager = PlatformPeg.get()?.getEventIndexingManager(); try { await indexManager.deleteEvent(ev.getAssociatedId()); @@ -321,15 +322,15 @@ export default class EventIndex extends EventEmitter { * @param {MatrixEvent} ev The event that should be added to the index. */ private async addLiveEventToIndex(ev: MatrixEvent): Promise { - const indexManager = PlatformPeg.get().getEventIndexingManager(); + const indexManager = PlatformPeg.get()?.getEventIndexingManager(); - if (!this.isValidEvent(ev)) return; + if (!indexManager || !this.isValidEvent(ev)) return; const e = this.eventToJson(ev); const profile = { - displayname: ev.sender.rawDisplayName, - avatar_url: ev.sender.getMxcAvatarUrl(), + displayname: ev.sender?.rawDisplayName, + avatar_url: ev.sender?.getMxcAvatarUrl(), }; await indexManager.addEventToIndex(e, profile); @@ -353,7 +354,7 @@ export default class EventIndex extends EventEmitter { } private async addRoomCheckpoint(roomId: string, fullCrawl = false): Promise { - const indexManager = PlatformPeg.get().getEventIndexingManager(); + const indexManager = PlatformPeg.get()?.getEventIndexingManager(); const client = MatrixClientPeg.get(); const room = client.getRoom(roomId); @@ -401,7 +402,7 @@ export default class EventIndex extends EventEmitter { let cancelled = false; const client = MatrixClientPeg.get(); - const indexManager = PlatformPeg.get().getEventIndexingManager(); + const indexManager = PlatformPeg.get()?.getEventIndexingManager(); this.crawler = { cancel: () => { @@ -649,7 +650,7 @@ export default class EventIndex extends EventEmitter { * task, and closes the index. */ public async close(): Promise { - const indexManager = PlatformPeg.get().getEventIndexingManager(); + const indexManager = PlatformPeg.get()?.getEventIndexingManager(); this.removeListeners(); this.stopCrawler(); await indexManager.closeEventIndex(); @@ -665,7 +666,7 @@ export default class EventIndex extends EventEmitter { * of search results once the search is done. */ public async search(searchArgs: ISearchArgs): Promise { - const indexManager = PlatformPeg.get().getEventIndexingManager(); + const indexManager = PlatformPeg.get()?.getEventIndexingManager(); return indexManager.searchEventIndex(searchArgs); } @@ -693,11 +694,11 @@ export default class EventIndex extends EventEmitter { public async loadFileEvents( room: Room, limit = 10, - fromEvent: string = null, + fromEvent?: string, direction: string = EventTimeline.BACKWARDS, ): Promise { const client = MatrixClientPeg.get(); - const indexManager = PlatformPeg.get().getEventIndexingManager(); + const indexManager = PlatformPeg.get()?.getEventIndexingManager(); const loadArgs: ILoadArgs = { roomId: room.roomId, @@ -790,7 +791,7 @@ export default class EventIndex extends EventEmitter { timeline: EventTimeline, room: Room, limit = 10, - fromEvent: string = null, + fromEvent?: string, direction: string = EventTimeline.BACKWARDS, ): Promise { const matrixEvents = await this.loadFileEvents(room, limit, fromEvent, direction); @@ -807,7 +808,7 @@ export default class EventIndex extends EventEmitter { // Add the events to the timeline of the file panel. matrixEvents.forEach((e) => { - if (!timelineSet.eventIdToTimeline(e.getId())) { + if (!timelineSet.eventIdToTimeline(e.getId()!)) { timelineSet.addEventToTimeline(e, timeline, direction == EventTimeline.BACKWARDS); } }); @@ -817,7 +818,7 @@ export default class EventIndex extends EventEmitter { // Set the pagination token to the oldest event that we retrieved. if (matrixEvents.length > 0) { - paginationToken = matrixEvents[matrixEvents.length - 1].getId(); + paginationToken = matrixEvents[matrixEvents.length - 1].getId()!; ret = true; } @@ -878,11 +879,11 @@ export default class EventIndex extends EventEmitter { ): Promise => { const timeline = timelineIndex.timeline; const timelineSet = timeline.getTimelineSet(); - const token = timeline.getPaginationToken(direction); + const token = timeline.getPaginationToken(direction) ?? undefined; const ret = await this.populateFileTimeline(timelineSet, timeline, room, limit, token, direction); - timelineIndex.pendingPaginate = null; + timelineIndex.pendingPaginate = undefined; timelineWindow.extend(direction, limit); return ret; @@ -900,9 +901,9 @@ export default class EventIndex extends EventEmitter { * @return {Promise} A promise that will resolve to the index * statistics. */ - public async getStats(): Promise { - const indexManager = PlatformPeg.get().getEventIndexingManager(); - return indexManager.getStats(); + public async getStats(): Promise { + const indexManager = PlatformPeg.get()?.getEventIndexingManager(); + return indexManager?.getStats(); } /** @@ -914,9 +915,9 @@ export default class EventIndex extends EventEmitter { * @return {Promise} Returns true if the index contains events for * the given room, false otherwise. */ - public async isRoomIndexed(roomId: string): Promise { - const indexManager = PlatformPeg.get().getEventIndexingManager(); - return indexManager.isRoomIndexed(roomId); + public async isRoomIndexed(roomId: string): Promise { + const indexManager = PlatformPeg.get()?.getEventIndexingManager(); + return indexManager?.isRoomIndexed(roomId); } /** diff --git a/src/indexing/EventIndexPeg.ts b/src/indexing/EventIndexPeg.ts index 77b93bf6f5..11c4696fc1 100644 --- a/src/indexing/EventIndexPeg.ts +++ b/src/indexing/EventIndexPeg.ts @@ -36,8 +36,8 @@ const INDEX_VERSION = 1; * you'll find a `EventIndex` hanging on the `EventIndexPeg`. */ export class EventIndexPeg { - public index: EventIndex = null; - public error: Error = null; + public index: EventIndex | null = null; + public error: Error | null = null; private _supportIsInstalled = false; @@ -49,7 +49,7 @@ export class EventIndexPeg { * EventIndex was successfully initialized, false otherwise. */ public async init(): Promise { - const indexManager = PlatformPeg.get().getEventIndexingManager(); + const indexManager = PlatformPeg.get()?.getEventIndexingManager(); if (!indexManager) { logger.log("EventIndex: Platform doesn't support event indexing, not initializing."); return false; @@ -78,11 +78,14 @@ export class EventIndexPeg { */ public async initEventIndex(): Promise { const index = new EventIndex(); - const indexManager = PlatformPeg.get().getEventIndexingManager(); + const indexManager = PlatformPeg.get()?.getEventIndexingManager(); const client = MatrixClientPeg.get(); + if (!indexManager || !client) { + throw new Error("Unable to init event index"); + } - const userId = client.getUserId(); - const deviceId = client.getDeviceId(); + const userId = client.getUserId()!; + const deviceId = client.getDeviceId()!; try { await indexManager.initEventIndex(userId, deviceId); @@ -120,7 +123,7 @@ export class EventIndexPeg { * does not mean that support is installed. */ public platformHasSupport(): boolean { - return PlatformPeg.get().getEventIndexingManager() !== null; + return PlatformPeg.get()?.getEventIndexingManager() != null; } /** @@ -141,7 +144,7 @@ export class EventIndexPeg { * * @return {EventIndex} The current event index. */ - public get(): EventIndex { + public get(): EventIndex | null { return this.index; } @@ -178,9 +181,9 @@ export class EventIndexPeg { * deleted. */ public async deleteEventIndex(): Promise { - const indexManager = PlatformPeg.get().getEventIndexingManager(); + const indexManager = PlatformPeg.get()?.getEventIndexingManager(); - if (indexManager !== null) { + if (indexManager) { await this.unset(); logger.log("EventIndex: Deleting event index."); await indexManager.deleteEventIndex(); diff --git a/src/integrations/IntegrationManagerInstance.ts b/src/integrations/IntegrationManagerInstance.ts index 68ca528e77..8a61705dae 100644 --- a/src/integrations/IntegrationManagerInstance.ts +++ b/src/integrations/IntegrationManagerInstance.ts @@ -33,18 +33,13 @@ export enum Kind { } export class IntegrationManagerInstance { - public readonly apiUrl: string; - public readonly uiUrl: string; - public readonly kind: string; - public readonly id: string; // only applicable in some cases - // Per the spec: UI URL is optional. - public constructor(kind: string, apiUrl: string, uiUrl: string = apiUrl, id?: string) { - this.kind = kind; - this.apiUrl = apiUrl; - this.uiUrl = uiUrl; - this.id = id; - } + public constructor( + public readonly kind: string, + public readonly apiUrl: string, + public readonly uiUrl: string = apiUrl, + public readonly id?: string, // only applicable in some cases + ) {} public get name(): string { const parsed = url.parse(this.uiUrl); @@ -62,7 +57,7 @@ export class IntegrationManagerInstance { return new ScalarAuthClient(this.apiUrl, this.uiUrl); } - public async open(room: Room = null, screen: string = null, integrationId: string = null): Promise { + public async open(room: Room, screen?: string, integrationId?: string): Promise { if (!SettingsStore.getValue("integrationProvisioning")) { return IntegrationManagers.sharedInstance().showDisabledDialog(); } diff --git a/src/integrations/IntegrationManagers.ts b/src/integrations/IntegrationManagers.ts index 7aeaf4ab47..c1f8eb5a3c 100644 --- a/src/integrations/IntegrationManagers.ts +++ b/src/integrations/IntegrationManagers.ts @@ -40,7 +40,7 @@ export class IntegrationManagers { private managers: IntegrationManagerInstance[] = []; private client: MatrixClient; - private primaryManager: IntegrationManagerInstance; + private primaryManager: IntegrationManagerInstance | null; public static sharedInstance(): IntegrationManagers { if (!IntegrationManagers.instance) { @@ -146,7 +146,7 @@ export class IntegrationManagers { } public getOrderedManagers(): IntegrationManagerInstance[] { - const ordered = []; + const ordered: IntegrationManagerInstance[] = []; for (const kind of KIND_PREFERENCE) { const managers = this.managers.filter((m) => m.kind === kind); if (!managers || !managers.length) continue; @@ -161,7 +161,7 @@ export class IntegrationManagers { return ordered; } - public getPrimaryManager(): IntegrationManagerInstance { + public getPrimaryManager(): IntegrationManagerInstance | null { if (this.hasManager()) { if (this.primaryManager) return this.primaryManager; @@ -195,7 +195,7 @@ export class IntegrationManagers { * @returns {Promise} Resolves to an integration manager instance, * or null if none was found. */ - public async tryDiscoverManager(domainName: string): Promise { + public async tryDiscoverManager(domainName: string): Promise { logger.log("Looking up integration manager via .well-known"); if (domainName.startsWith("http:") || domainName.startsWith("https:")) { // trim off the scheme and just use the domain diff --git a/src/languageHandler.tsx b/src/languageHandler.tsx index 623737175d..ec37d32965 100644 --- a/src/languageHandler.tsx +++ b/src/languageHandler.tsx @@ -194,7 +194,7 @@ const annotateStrings = (result: TranslatedString, translationKey: string): Tran */ // eslint-next-line @typescript-eslint/naming-convention export function _t(text: string, variables?: IVariables): string; -export function _t(text: string, variables: IVariables, tags: Tags): React.ReactNode; +export function _t(text: string, variables: IVariables | undefined, tags: Tags): React.ReactNode; export function _t(text: string, variables?: IVariables, tags?: Tags): TranslatedString { // The translation returns text so there's no XSS vector here (no unsafe HTML, no code execution) const { translated } = safeCounterpartTranslate(text, variables); diff --git a/src/linkify-matrix.ts b/src/linkify-matrix.ts index 64a66345ed..29258558ce 100644 --- a/src/linkify-matrix.ts +++ b/src/linkify-matrix.ts @@ -19,7 +19,7 @@ import * as linkifyjs from "linkifyjs"; import { Opts, registerCustomProtocol, registerPlugin } from "linkifyjs"; import linkifyElement from "linkify-element"; import linkifyString from "linkify-string"; -import { RoomMember } from "matrix-js-sdk/src/models/room-member"; +import { User } from "matrix-js-sdk/src/matrix"; import { parsePermalink, @@ -105,13 +105,9 @@ function matrixOpaqueIdLinkifyParser({ function onUserClick(event: MouseEvent, userId: string): void { event.preventDefault(); - const member = new RoomMember(null, userId); - if (!member) { - return; - } dis.dispatch({ action: Action.ViewUser, - member: member, + member: new User(userId), }); } diff --git a/src/modules/ProxiedModuleApi.ts b/src/modules/ProxiedModuleApi.ts index 03374a7df0..56fc3ff121 100644 --- a/src/modules/ProxiedModuleApi.ts +++ b/src/modules/ProxiedModuleApi.ts @@ -101,10 +101,10 @@ export class ProxiedModuleApi implements ModuleApi { password: string, displayName?: string, ): Promise { - const hsUrl = SdkConfig.get("validated_server_config").hsUrl; + const hsUrl = SdkConfig.get("validated_server_config")?.hsUrl; const client = Matrix.createClient({ baseUrl: hsUrl }); const deviceName = - SdkConfig.get("default_device_display_name") || PlatformPeg.get().getDefaultDeviceDisplayName(); + SdkConfig.get("default_device_display_name") || PlatformPeg.get()?.getDefaultDeviceDisplayName(); const req: IRegisterRequestParams = { username, password, @@ -134,9 +134,9 @@ export class ProxiedModuleApi implements ModuleApi { return { homeserverUrl: hsUrl, - userId: creds.user_id, - deviceId: creds.device_id, - accessToken: creds.access_token, + userId: creds.user_id!, + deviceId: creds.device_id!, + accessToken: creds.access_token!, }; } @@ -163,8 +163,8 @@ export class ProxiedModuleApi implements ModuleApi { navigateToPermalink(uri); const parts = parsePermalink(uri); - if (parts.roomIdOrAlias && andJoin) { - let roomId = parts.roomIdOrAlias; + if (parts?.roomIdOrAlias && andJoin) { + let roomId: string | undefined = parts.roomIdOrAlias; let servers = parts.viaServers; if (roomId.startsWith("#")) { roomId = getCachedRoomIDForAlias(parts.roomIdOrAlias); diff --git a/src/notifications/PushRuleVectorState.ts b/src/notifications/PushRuleVectorState.ts index 47b4e93774..2ff504815f 100644 --- a/src/notifications/PushRuleVectorState.ts +++ b/src/notifications/PushRuleVectorState.ts @@ -53,6 +53,7 @@ export class PushRuleVectorState { } else if (pushRuleVectorState === VectorState.Loud) { return StandardActions.ACTION_HIGHLIGHT_DEFAULT_SOUND; } + return []; } /** diff --git a/src/rageshake/rageshake.ts b/src/rageshake/rageshake.ts index c0b538f6fb..9238e230dd 100644 --- a/src/rageshake/rageshake.ts +++ b/src/rageshake/rageshake.ts @@ -76,7 +76,7 @@ export class ConsoleLogger { } public bypassRageshake(fnName: LogFunctionName, ...args: (Error | DOMException | object | string)[]): void { - this.originalFunctions[fnName](...args); + this.originalFunctions[fnName]?.(...args); } public log(level: string, ...args: (Error | DOMException | object | string)[]): void { @@ -152,7 +152,7 @@ export class IndexedDBLogStore { }; req.onerror = () => { - const err = "Failed to open log database: " + req.error.name; + const err = "Failed to open log database: " + req.error?.name; logger.error(err); reject(new Error(err)); }; @@ -234,7 +234,7 @@ export class IndexedDBLogStore { }; txn.onerror = () => { logger.error("Failed to flush logs : ", txn.error); - reject(new Error("Failed to write logs: " + txn.error.message)); + reject(new Error("Failed to write logs: " + txn.error?.message)); }; objStore.add(this.generateLogEntry(lines)); const lastModStore = txn.objectStore("logslastmod"); @@ -267,7 +267,7 @@ export class IndexedDBLogStore { const query = objectStore.index("id").openCursor(IDBKeyRange.only(id), "prev"); let lines = ""; query.onerror = () => { - reject(new Error("Query failed: " + query.error.message)); + reject(new Error("Query failed: " + query.error?.message)); }; query.onsuccess = () => { const cursor = query.result; @@ -322,7 +322,7 @@ export class IndexedDBLogStore { resolve(); }; txn.onerror = () => { - reject(new Error("Failed to delete logs for " + `'${id}' : ${query.error.message}`)); + reject(new Error("Failed to delete logs for " + `'${id}' : ${query.error?.message}`)); }; // delete last modified entries const lastModStore = txn.objectStore("logslastmod"); @@ -401,14 +401,14 @@ export class IndexedDBLogStore { */ function selectQuery( store: IDBIndex | IDBObjectStore, - keyRange: IDBKeyRange, + keyRange: IDBKeyRange | undefined, resultMapper: (cursor: IDBCursorWithValue) => T, ): Promise { const query = store.openCursor(keyRange); return new Promise((resolve, reject) => { const results: T[] = []; query.onerror = () => { - reject(new Error("Query failed: " + query.error.message)); + reject(new Error("Query failed: " + query.error?.message)); }; // collect results query.onsuccess = () => { diff --git a/src/settings/SettingsStore.ts b/src/settings/SettingsStore.ts index ef03e96683..bcc7b14509 100644 --- a/src/settings/SettingsStore.ts +++ b/src/settings/SettingsStore.ts @@ -437,7 +437,7 @@ export default class SettingsStore { level: SettingLevel, roomId: string | null, calculatedValue: any, - calculatedAtLevel: SettingLevel, + calculatedAtLevel: SettingLevel | null, ): any { let resultingValue = calculatedValue; diff --git a/src/settings/controllers/IncompatibleController.ts b/src/settings/controllers/IncompatibleController.ts index 3621a736a4..e69848733d 100644 --- a/src/settings/controllers/IncompatibleController.ts +++ b/src/settings/controllers/IncompatibleController.ts @@ -36,7 +36,7 @@ export default class IncompatibleController extends SettingController { level: SettingLevel, roomId: string, calculatedValue: any, - calculatedAtLevel: SettingLevel, + calculatedAtLevel: SettingLevel | null, ): any { if (this.incompatibleSetting) { return this.forcedValue; diff --git a/src/settings/controllers/NotificationControllers.ts b/src/settings/controllers/NotificationControllers.ts index 6c8c351b0f..aae9b16ba2 100644 --- a/src/settings/controllers/NotificationControllers.ts +++ b/src/settings/controllers/NotificationControllers.ts @@ -53,7 +53,7 @@ export class NotificationsEnabledController extends SettingController { level: SettingLevel, roomId: string, calculatedValue: any, - calculatedAtLevel: SettingLevel, + calculatedAtLevel: SettingLevel | null, ): any { if (!getNotifier().isPossible()) return false; diff --git a/src/settings/controllers/OrderedMultiController.ts b/src/settings/controllers/OrderedMultiController.ts index 70ffa99b68..fa8535ffd6 100644 --- a/src/settings/controllers/OrderedMultiController.ts +++ b/src/settings/controllers/OrderedMultiController.ts @@ -35,7 +35,7 @@ export class OrderedMultiController extends SettingController { level: SettingLevel, roomId: string, calculatedValue: any, - calculatedAtLevel: SettingLevel, + calculatedAtLevel: SettingLevel | null, ): any { for (const controller of this.controllers) { const override = controller.getValueOverride(level, roomId, calculatedValue, calculatedAtLevel); diff --git a/src/settings/controllers/ReducedMotionController.ts b/src/settings/controllers/ReducedMotionController.ts index d98ca16513..c9f293fbd7 100644 --- a/src/settings/controllers/ReducedMotionController.ts +++ b/src/settings/controllers/ReducedMotionController.ts @@ -27,7 +27,7 @@ export default class ReducedMotionController extends SettingController { level: SettingLevel, roomId: string, calculatedValue: any, - calculatedAtLevel: SettingLevel, + calculatedAtLevel: SettingLevel | null, ): any { if (this.prefersReducedMotion()) { return false; diff --git a/src/settings/controllers/SettingController.ts b/src/settings/controllers/SettingController.ts index 725dccc623..d3a52dbb8c 100644 --- a/src/settings/controllers/SettingController.ts +++ b/src/settings/controllers/SettingController.ts @@ -41,7 +41,7 @@ export default abstract class SettingController { level: SettingLevel, roomId: string | null, calculatedValue: any, - calculatedAtLevel: SettingLevel, + calculatedAtLevel: SettingLevel | null, ): any { return null; // no override } diff --git a/src/settings/controllers/ThemeController.ts b/src/settings/controllers/ThemeController.ts index ab5410c459..9c7ce6cc7a 100644 --- a/src/settings/controllers/ThemeController.ts +++ b/src/settings/controllers/ThemeController.ts @@ -26,7 +26,7 @@ export default class ThemeController extends SettingController { level: SettingLevel, roomId: string, calculatedValue: any, - calculatedAtLevel: SettingLevel, + calculatedAtLevel: SettingLevel | null, ): any { if (!calculatedValue) return null; // Don't override null themes diff --git a/src/settings/controllers/UIFeatureController.ts b/src/settings/controllers/UIFeatureController.ts index 2748eec16a..31a2be0efc 100644 --- a/src/settings/controllers/UIFeatureController.ts +++ b/src/settings/controllers/UIFeatureController.ts @@ -34,7 +34,7 @@ export default class UIFeatureController extends SettingController { level: SettingLevel, roomId: string, calculatedValue: any, - calculatedAtLevel: SettingLevel, + calculatedAtLevel: SettingLevel | null, ): any { if (this.settingDisabled) { // per the docs: we force a disabled state when the feature isn't active diff --git a/src/settings/enums/ImageSize.ts b/src/settings/enums/ImageSize.ts index cc5596a344..28f72cb8de 100644 --- a/src/settings/enums/ImageSize.ts +++ b/src/settings/enums/ImageSize.ts @@ -22,7 +22,7 @@ const SIZE_LARGE = { w: 800, h: 600 }; const SIZE_NORMAL_LANDSCAPE = { w: 324, h: 324 }; // for w > h const SIZE_NORMAL_PORTRAIT = { w: Math.ceil(324 * (9 / 16)), h: 324 }; // for h > w -type Dimensions = { w: number; h: number }; +type Dimensions = { w?: number; h?: number }; export enum ImageSize { Normal = "normal", @@ -36,7 +36,7 @@ export enum ImageSize { * @returns {Dimensions} The suggested maximum dimensions for the image */ export function suggestedSize(size: ImageSize, contentSize: Dimensions, maxHeight?: number): Dimensions { - const aspectRatio = contentSize.w / contentSize.h; + const aspectRatio = contentSize.w! / contentSize.h!; const portrait = aspectRatio < 1; const maxSize = size === ImageSize.Large ? SIZE_LARGE : portrait ? SIZE_NORMAL_PORTRAIT : SIZE_NORMAL_LANDSCAPE; diff --git a/src/stores/RoomScrollStateStore.ts b/src/stores/RoomScrollStateStore.ts index 87b48dad57..61e89ed8de 100644 --- a/src/stores/RoomScrollStateStore.ts +++ b/src/stores/RoomScrollStateStore.ts @@ -38,7 +38,7 @@ export class RoomScrollStateStore { // from the focussedEvent. private scrollStateMap = new Map(); - public getScrollState(roomId: string): ScrollState { + public getScrollState(roomId: string): ScrollState | undefined { return this.scrollStateMap.get(roomId); } diff --git a/src/stores/RoomViewStore.tsx b/src/stores/RoomViewStore.tsx index 6dca6dce24..86f0f6bdb2 100644 --- a/src/stores/RoomViewStore.tsx +++ b/src/stores/RoomViewStore.tsx @@ -712,7 +712,7 @@ export class RoomViewStore extends EventEmitter { } // The mxEvent if one is currently being replied to/quoted - public getQuotingEvent(): Optional { + public getQuotingEvent(): MatrixEvent | null { return this.state.replyingToEvent; } diff --git a/src/stores/room-list/SlidingRoomListStore.ts b/src/stores/room-list/SlidingRoomListStore.ts index ff2113379d..cc4de473b8 100644 --- a/src/stores/room-list/SlidingRoomListStore.ts +++ b/src/stores/room-list/SlidingRoomListStore.ts @@ -17,6 +17,7 @@ limitations under the License. import { Room } from "matrix-js-sdk/src/models/room"; import { logger } from "matrix-js-sdk/src/logger"; import { MSC3575Filter, SlidingSyncEvent } from "matrix-js-sdk/src/sliding-sync"; +import { Optional } from "matrix-events-sdk"; import { RoomUpdateCause, TagID, OrderedDefaultTagIDs, DefaultTagID } from "./models"; import { ITagMap, ListAlgorithm, SortAlgorithm } from "./algorithms/models"; @@ -79,12 +80,11 @@ export class SlidingRoomListStoreClass extends AsyncStoreWithClient impl private tagIdToSortAlgo: Record = {}; private tagMap: ITagMap = {}; private counts: Record = {}; - private stickyRoomId: string | null; + private stickyRoomId: Optional; public constructor(dis: MatrixDispatcher, private readonly context: SdkContextClass) { super(dis); this.setMaxListeners(20); // RoomList + LeftPanel + 8xRoomSubList + spares - this.stickyRoomId = null; } public async setTagSorting(tagId: TagID, sort: SortAlgorithm): Promise { diff --git a/src/stores/room-list/previews/ReactionEventPreview.ts b/src/stores/room-list/previews/ReactionEventPreview.ts index 6a9ae21afc..6af7ebab70 100644 --- a/src/stores/room-list/previews/ReactionEventPreview.ts +++ b/src/stores/room-list/previews/ReactionEventPreview.ts @@ -24,14 +24,17 @@ import SettingsStore from "../../../settings/SettingsStore"; import DMRoomMap from "../../../utils/DMRoomMap"; export class ReactionEventPreview implements IPreview { - public getTextFor(event: MatrixEvent, tagId?: TagID, isThread?: boolean): string { + public getTextFor(event: MatrixEvent, tagId?: TagID, isThread?: boolean): string | null { const showDms = SettingsStore.getValue("feature_roomlist_preview_reactions_dms"); const showAll = SettingsStore.getValue("feature_roomlist_preview_reactions_all"); + const roomId = event.getRoomId(); + if (!roomId) return null; // not a room event + // If we're not showing all reactions, see if we're showing DMs instead if (!showAll) { // If we're not showing reactions on DMs, or we are and the room isn't a DM, skip - if (!(showDms && DMRoomMap.shared().getUserIdForRoomId(event.getRoomId()))) { + if (!(showDms && DMRoomMap.shared().getUserIdForRoomId(roomId))) { return null; } } @@ -42,7 +45,7 @@ export class ReactionEventPreview implements IPreview { const reaction = relation.key; if (!reaction) return null; // invalid reaction (unknown format) - if (isThread || isSelf(event) || !shouldPrefixMessagesIn(event.getRoomId(), tagId)) { + if (isThread || isSelf(event) || !shouldPrefixMessagesIn(roomId, tagId)) { return reaction; } else { return _t("%(senderName)s: %(reaction)s", { senderName: getSenderName(event), reaction }); diff --git a/src/stores/room-list/previews/utils.ts b/src/stores/room-list/previews/utils.ts index b12128943b..116e97b515 100644 --- a/src/stores/room-list/previews/utils.ts +++ b/src/stores/room-list/previews/utils.ts @@ -27,7 +27,7 @@ export function isSelf(event: MatrixEvent): boolean { return event.getSender() === selfUserId; } -export function shouldPrefixMessagesIn(roomId: string, tagId: TagID): boolean { +export function shouldPrefixMessagesIn(roomId: string, tagId?: TagID): boolean { if (tagId !== DefaultTagID.DM) return true; // We don't prefix anything in 1:1s diff --git a/src/stores/spaces/SpaceStore.ts b/src/stores/spaces/SpaceStore.ts index 5188af7354..3405155382 100644 --- a/src/stores/spaces/SpaceStore.ts +++ b/src/stores/spaces/SpaceStore.ts @@ -137,7 +137,7 @@ export class SpaceStoreClass extends AsyncStoreWithClient { userIdsBySpace: new Map>(), }; // The space currently selected in the Space Panel - private _activeSpace?: SpaceKey = MetaSpace.Home; // set properly by onReady + private _activeSpace: SpaceKey = MetaSpace.Home; // set properly by onReady private _suggestedRooms: ISuggestedRoom[] = []; private _invitedSpaces = new Set(); private spaceOrderLocalEchoMap = new Map(); @@ -812,13 +812,13 @@ export class SpaceStoreClass extends AsyncStoreWithClient { const spaceDiff = mapDiff(prevChildSpacesBySpace, this.childSpacesBySpace); // filter out keys which changed by reference only by checking whether the sets differ const roomsChanged = roomDiff.changed.filter((k) => { - return setHasDiff(prevRoomsBySpace.get(k), this.roomIdsBySpace.get(k)); + return setHasDiff(prevRoomsBySpace.get(k)!, this.roomIdsBySpace.get(k)!); }); const usersChanged = userDiff.changed.filter((k) => { - return setHasDiff(prevUsersBySpace.get(k), this.userIdsBySpace.get(k)); + return setHasDiff(prevUsersBySpace.get(k)!, this.userIdsBySpace.get(k)!); }); const spacesChanged = spaceDiff.changed.filter((k) => { - return setHasDiff(prevChildSpacesBySpace.get(k), this.childSpacesBySpace.get(k)); + return setHasDiff(prevChildSpacesBySpace.get(k)!, this.childSpacesBySpace.get(k)!); }); const changeSet = new Set([ @@ -1142,9 +1142,8 @@ export class SpaceStoreClass extends AsyncStoreWithClient { // restore selected state from last session if any and still valid const lastSpaceId = window.localStorage.getItem(ACTIVE_SPACE_LS_KEY); const valid = - lastSpaceId && !isMetaSpace(lastSpaceId) - ? this.matrixClient.getRoom(lastSpaceId) - : enabledMetaSpaces[lastSpaceId]; + lastSpaceId && + (!isMetaSpace(lastSpaceId) ? this.matrixClient.getRoom(lastSpaceId) : enabledMetaSpaces[lastSpaceId]); if (valid) { // don't context switch here as it may break permalinks this.setActiveSpace(lastSpaceId, false); @@ -1285,7 +1284,7 @@ export class SpaceStoreClass extends AsyncStoreWithClient { public getNotificationState(key: SpaceKey): SpaceNotificationState { if (this.notificationStateMap.has(key)) { - return this.notificationStateMap.get(key); + return this.notificationStateMap.get(key)!; } const state = new SpaceNotificationState(getRoomFn); diff --git a/src/stores/spaces/SpaceTreeLevelLayoutStore.ts b/src/stores/spaces/SpaceTreeLevelLayoutStore.ts index 3e5d5faa98..ced6ea5a71 100644 --- a/src/stores/spaces/SpaceTreeLevelLayoutStore.ts +++ b/src/stores/spaces/SpaceTreeLevelLayoutStore.ts @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -const getSpaceCollapsedKey = (roomId: string, parents: Set): string => { +const getSpaceCollapsedKey = (roomId: string, parents?: Set): string => { const separator = "/"; let path = ""; if (parents) { @@ -35,12 +35,12 @@ export default class SpaceTreeLevelLayoutStore { return SpaceTreeLevelLayoutStore.internalInstance; } - public setSpaceCollapsedState(roomId: string, parents: Set, collapsed: boolean): void { + public setSpaceCollapsedState(roomId: string, parents: Set | undefined, collapsed: boolean): void { // XXX: localStorage doesn't allow booleans localStorage.setItem(getSpaceCollapsedKey(roomId, parents), collapsed.toString()); } - public getSpaceCollapsedState(roomId: string, parents: Set, fallback: boolean): boolean { + public getSpaceCollapsedState(roomId: string, parents: Set | undefined, fallback: boolean): boolean { const collapsedLocalStorage = localStorage.getItem(getSpaceCollapsedKey(roomId, parents)); // XXX: localStorage doesn't allow booleans return collapsedLocalStorage ? collapsedLocalStorage === "true" : fallback; diff --git a/src/stores/widgets/StopGapWidget.ts b/src/stores/widgets/StopGapWidget.ts index f87da3bc18..3b0a49f818 100644 --- a/src/stores/widgets/StopGapWidget.ts +++ b/src/stores/widgets/StopGapWidget.ts @@ -216,8 +216,8 @@ export class StopGapWidget extends EventEmitter { const defaults: ITemplateParams = { widgetRoomId: this.roomId, currentUserId: this.client.getUserId()!, - userDisplayName: OwnProfileStore.instance.displayName, - userHttpAvatarUrl: OwnProfileStore.instance.getHttpAvatarUrl(), + userDisplayName: OwnProfileStore.instance.displayName ?? undefined, + userHttpAvatarUrl: OwnProfileStore.instance.getHttpAvatarUrl() ?? undefined, clientId: ELEMENT_CLIENT_ID, clientTheme: SettingsStore.getValue("theme"), clientLanguage: getUserLanguage(), diff --git a/src/stores/widgets/WidgetLayoutStore.ts b/src/stores/widgets/WidgetLayoutStore.ts index 1d3b2aa1d9..a204b7d6ed 100644 --- a/src/stores/widgets/WidgetLayoutStore.ts +++ b/src/stores/widgets/WidgetLayoutStore.ts @@ -477,7 +477,7 @@ export class WidgetLayoutStore extends ReadyWatchingStore { this.updateUserLayout(room, newLayout); } - public hasMaximisedWidget(room: Room): boolean { + public hasMaximisedWidget(room?: Room): boolean { return this.getContainerWidgets(room, Container.Center).length > 0; } diff --git a/src/toasts/SetupEncryptionToast.ts b/src/toasts/SetupEncryptionToast.ts index b711cce972..3b60705ec7 100644 --- a/src/toasts/SetupEncryptionToast.ts +++ b/src/toasts/SetupEncryptionToast.ts @@ -85,11 +85,11 @@ export const showToast = (kind: Kind): void => { const onAccept = async (): Promise => { if (kind === Kind.VERIFY_THIS_SESSION) { - Modal.createDialog(SetupEncryptionDialog, {}, null, /* priority = */ false, /* static = */ true); + Modal.createDialog(SetupEncryptionDialog, {}, undefined, /* priority = */ false, /* static = */ true); } else { const modal = Modal.createDialog( Spinner, - null, + undefined, "mx_Dialog_spinner", /* priority */ false, /* static */ true, diff --git a/src/utils/IdentityServerUtils.ts b/src/utils/IdentityServerUtils.ts index 7b820dc5c8..a9a5c754a0 100644 --- a/src/utils/IdentityServerUtils.ts +++ b/src/utils/IdentityServerUtils.ts @@ -19,6 +19,7 @@ import { logger } from "matrix-js-sdk/src/logger"; import SdkConfig from "../SdkConfig"; import { MatrixClientPeg } from "../MatrixClientPeg"; +import { Policies } from "../Terms"; export function getDefaultIdentityServerUrl(): string { return SdkConfig.get("validated_server_config").isUrl; @@ -33,7 +34,7 @@ export function setToDefaultIdentityServer(): void { } export async function doesIdentityServerHaveTerms(fullUrl: string): Promise { - let terms; + let terms: { policies?: Policies } | null; try { terms = await MatrixClientPeg.get().getTerms(SERVICE_TYPES.IS, fullUrl); } catch (e) { @@ -45,7 +46,7 @@ export async function doesIdentityServerHaveTerms(fullUrl: string): Promise 0; + return !!terms?.["policies"] && Object.keys(terms["policies"]).length > 0; } export function doesAccountDataHaveIdentityServer(): boolean { diff --git a/src/utils/MediaEventHelper.ts b/src/utils/MediaEventHelper.ts index 18b119b63f..2e576f9a11 100644 --- a/src/utils/MediaEventHelper.ts +++ b/src/utils/MediaEventHelper.ts @@ -28,12 +28,12 @@ import { IDestroyable } from "./IDestroyable"; export class MediaEventHelper implements IDestroyable { // Either an HTTP or Object URL (when encrypted) to the media. - public readonly sourceUrl: LazyValue; - public readonly thumbnailUrl: LazyValue; + public readonly sourceUrl: LazyValue; + public readonly thumbnailUrl: LazyValue; // Either the raw or decrypted (when encrypted) contents of the file. public readonly sourceBlob: LazyValue; - public readonly thumbnailBlob: LazyValue; + public readonly thumbnailBlob: LazyValue; public readonly media: Media; @@ -56,12 +56,12 @@ export class MediaEventHelper implements IDestroyable { public destroy(): void { if (this.media.isEncrypted) { - if (this.sourceUrl.present) URL.revokeObjectURL(this.sourceUrl.cachedValue); - if (this.thumbnailUrl.present) URL.revokeObjectURL(this.thumbnailUrl.cachedValue); + if (this.sourceUrl.cachedValue) URL.revokeObjectURL(this.sourceUrl.cachedValue); + if (this.thumbnailUrl.cachedValue) URL.revokeObjectURL(this.thumbnailUrl.cachedValue); } } - private prepareSourceUrl = async (): Promise => { + private prepareSourceUrl = async (): Promise => { if (this.media.isEncrypted) { const blob = await this.sourceBlob.value; return URL.createObjectURL(blob); @@ -70,7 +70,7 @@ export class MediaEventHelper implements IDestroyable { } }; - private prepareThumbnailUrl = async (): Promise => { + private prepareThumbnailUrl = async (): Promise => { if (this.media.isEncrypted) { const blob = await this.thumbnailBlob.value; if (blob === null) return null; @@ -83,12 +83,12 @@ export class MediaEventHelper implements IDestroyable { private fetchSource = (): Promise => { if (this.media.isEncrypted) { const content = this.event.getContent(); - return decryptFile(content.file, content.info); + return decryptFile(content.file!, content.info); } return this.media.downloadSource().then((r) => r.blob()); }; - private fetchThumbnail = (): Promise => { + private fetchThumbnail = (): Promise => { if (!this.media.hasThumbnail) return Promise.resolve(null); if (this.media.isEncrypted) { @@ -113,7 +113,7 @@ export class MediaEventHelper implements IDestroyable { const content = event.getContent(); const mediaMsgTypes: string[] = [MsgType.Video, MsgType.Audio, MsgType.Image, MsgType.File]; - if (mediaMsgTypes.includes(content.msgtype)) return true; + if (mediaMsgTypes.includes(content.msgtype!)) return true; if (typeof content.url === "string") return true; // Finally, it's probably not media diff --git a/src/utils/WidgetUtils.ts b/src/utils/WidgetUtils.ts index e8d1dd1e32..3d4dca2fe2 100644 --- a/src/utils/WidgetUtils.ts +++ b/src/utils/WidgetUtils.ts @@ -96,7 +96,7 @@ export default class WidgetUtils { * @param {[type]} testUrlString URL to check * @return {Boolean} True if specified URL is a scalar URL */ - public static isScalarUrl(testUrlString: string): boolean { + public static isScalarUrl(testUrlString?: string): boolean { if (!testUrlString) { logger.error("Scalar URL check failed. No URL specified"); return false; @@ -554,7 +554,7 @@ export default class WidgetUtils { // noinspection JSIgnoredPromiseFromCall IntegrationManagers.sharedInstance() .getPrimaryManager() - .open(room, "type_" + app.type, app.id); + ?.open(room, "type_" + app.type, app.id); } public static isManagedByManager(app: IApp): boolean { @@ -563,7 +563,7 @@ export default class WidgetUtils { if (managers.hasManager()) { // TODO: Pick the right manager for the widget const defaultManager = managers.getPrimaryManager(); - return WidgetUtils.isScalarUrl(defaultManager.apiUrl); + return WidgetUtils.isScalarUrl(defaultManager?.apiUrl); } } return false; diff --git a/src/utils/beacon/useBeacon.ts b/src/utils/beacon/useBeacon.ts index 2726262ec4..f857fc8b66 100644 --- a/src/utils/beacon/useBeacon.ts +++ b/src/utils/beacon/useBeacon.ts @@ -29,7 +29,7 @@ export const useBeacon = (beaconInfoEvent: MatrixEvent): Beacon | undefined => { const beaconIdentifier = getBeaconInfoIdentifier(beaconInfoEvent); const room = matrixClient.getRoom(roomId); - const beaconInstance = room.currentState.beacons.get(beaconIdentifier); + const beaconInstance = room?.currentState.beacons.get(beaconIdentifier); // TODO could this be less stupid? diff --git a/src/utils/direct-messages.ts b/src/utils/direct-messages.ts index 5e5a5d9c19..aede86f93b 100644 --- a/src/utils/direct-messages.ts +++ b/src/utils/direct-messages.ts @@ -173,7 +173,7 @@ export class ThreepidMember extends Member { export interface IDMUserTileProps { member: Member; - onRemove(member: Member): void; + onRemove?(member: Member): void; } /** diff --git a/src/utils/dm/createDmLocalRoom.ts b/src/utils/dm/createDmLocalRoom.ts index ed7a849a33..ac14834303 100644 --- a/src/utils/dm/createDmLocalRoom.ts +++ b/src/utils/dm/createDmLocalRoom.ts @@ -92,7 +92,7 @@ export async function createDmLocalRoom(client: MatrixClient, targets: Member[]) type: EventType.RoomMember, content: { displayname: target.name, - avatar_url: target.getMxcAvatarUrl(), + avatar_url: target.getMxcAvatarUrl() ?? undefined, membership: "invite", isDirect: true, }, @@ -107,7 +107,7 @@ export async function createDmLocalRoom(client: MatrixClient, targets: Member[]) type: EventType.RoomMember, content: { displayname: target.name, - avatar_url: target.getMxcAvatarUrl(), + avatar_url: target.getMxcAvatarUrl() ?? undefined, membership: "join", }, state_key: target.userId, diff --git a/src/utils/leave-behaviour.ts b/src/utils/leave-behaviour.ts index 11047c5fb7..1031997724 100644 --- a/src/utils/leave-behaviour.ts +++ b/src/utils/leave-behaviour.ts @@ -37,7 +37,7 @@ import { bulkSpaceBehaviour } from "./space"; import { SdkContextClass } from "../contexts/SDKContext"; export async function leaveRoomBehaviour(roomId: string, retry = true, spinner = true): Promise { - let spinnerModal: IHandle; + let spinnerModal: IHandle | undefined; if (spinner) { spinnerModal = Modal.createDialog(Spinner, undefined, "mx_Dialog_spinner"); } @@ -60,7 +60,7 @@ export async function leaveRoomBehaviour(roomId: string, retry = true, spinner = room .getPendingEvents() .filter((ev) => { - return [EventStatus.QUEUED, EventStatus.ENCRYPTING, EventStatus.SENDING].includes(ev.status); + return [EventStatus.QUEUED, EventStatus.ENCRYPTING, EventStatus.SENDING].includes(ev.status!); }) .map( (ev) => diff --git a/src/utils/location/parseGeoUri.ts b/src/utils/location/parseGeoUri.ts index 080ff5359b..0ad4adbad9 100644 --- a/src/utils/location/parseGeoUri.ts +++ b/src/utils/location/parseGeoUri.ts @@ -14,8 +14,8 @@ See the License for the specific language governing permissions and limitations under the License. */ -export const parseGeoUri = (uri: string): GeolocationCoordinates => { - function parse(s: string): number { +export const parseGeoUri = (uri: string): GeolocationCoordinates | undefined => { + function parse(s: string): number | undefined { const ret = parseFloat(s); if (Number.isNaN(ret)) { return undefined; @@ -28,7 +28,7 @@ export const parseGeoUri = (uri: string): GeolocationCoordinates => { if (!m) return; const parts = m[1].split(";"); const coords = parts[0].split(","); - let uncertainty: number; + let uncertainty: number | undefined; for (const param of parts.slice(1)) { const m = param.match(/u=(.*)/); if (m) uncertainty = parse(m[1]); diff --git a/src/utils/login.ts b/src/utils/login.ts index 8e66909952..2688e0e151 100644 --- a/src/utils/login.ts +++ b/src/utils/login.ts @@ -23,5 +23,5 @@ export function isLoggedIn(): boolean { // store to hold this state. // See also https://github.com/vector-im/element-web/issues/15034. const app = window.matrixChat; - return app && (app as MatrixChat).state.view === Views.LOGGED_IN; + return (app as MatrixChat)?.state.view === Views.LOGGED_IN; } diff --git a/src/utils/pillify.tsx b/src/utils/pillify.tsx index ff21567b9c..8100b6c453 100644 --- a/src/utils/pillify.tsx +++ b/src/utils/pillify.tsx @@ -38,7 +38,7 @@ import { parsePermalink } from "./permalinks/Permalinks"; * The initial caller should pass in an empty array to seed the accumulator. */ export function pillifyLinks(nodes: ArrayLike, mxEvent: MatrixEvent, pills: Element[]): void { - const room = MatrixClientPeg.get().getRoom(mxEvent.getRoomId()); + const room = MatrixClientPeg.get().getRoom(mxEvent.getRoomId()) ?? undefined; const shouldShowPillAvatar = SettingsStore.getValue("Pill.shouldShowPillAvatar"); let node = nodes[0]; while (node) { @@ -49,7 +49,7 @@ export function pillifyLinks(nodes: ArrayLike, mxEvent: MatrixEvent, pi node = node.nextSibling as Element; continue; } else if (node.tagName === "A" && node.getAttribute("href")) { - const href = node.getAttribute("href"); + const href = node.getAttribute("href")!; const parts = parsePermalink(href); // If the link is a (localised) matrix.to link, replace it with a pill // We don't want to pill event permalinks, so those are ignored. diff --git a/src/voice-broadcast/stores/VoiceBroadcastPlaybacksStore.ts b/src/voice-broadcast/stores/VoiceBroadcastPlaybacksStore.ts index e0afea2bc1..d76241b7d1 100644 --- a/src/voice-broadcast/stores/VoiceBroadcastPlaybacksStore.ts +++ b/src/voice-broadcast/stores/VoiceBroadcastPlaybacksStore.ts @@ -71,17 +71,17 @@ export class VoiceBroadcastPlaybacksStore } public getByInfoEvent(infoEvent: MatrixEvent, client: MatrixClient): VoiceBroadcastPlayback { - const infoEventId = infoEvent.getId(); + const infoEventId = infoEvent.getId()!; if (!this.playbacks.has(infoEventId)) { this.addPlayback(new VoiceBroadcastPlayback(infoEvent, client, this.recordings)); } - return this.playbacks.get(infoEventId); + return this.playbacks.get(infoEventId)!; } private addPlayback(playback: VoiceBroadcastPlayback): void { - const infoEventId = playback.infoEvent.getId(); + const infoEventId = playback.infoEvent.getId()!; if (this.playbacks.has(infoEventId)) return; diff --git a/test/ContentMessages-test.ts b/test/ContentMessages-test.ts index 5bfc9c315f..323cb1834b 100644 --- a/test/ContentMessages-test.ts +++ b/test/ContentMessages-test.ts @@ -57,7 +57,7 @@ describe("ContentMessages", () => { uploadContent: jest.fn().mockResolvedValue({ content_uri: "mxc://server/file" }), } as unknown as MatrixClient; contentMessages = new ContentMessages(); - prom = Promise.resolve(null); + prom = Promise.resolve({ event_id: "$event_id" }); }); describe("sendStickerContentToRoom", () => { @@ -98,7 +98,7 @@ describe("ContentMessages", () => { mocked(doMaybeLocalRoomAction).mockImplementation( (roomId: string, fn: (actualRoomId: string) => Promise) => fn(roomId), ); - mocked(BlurhashEncoder.instance.getBlurhash).mockResolvedValue(undefined); + mocked(BlurhashEncoder.instance.getBlurhash).mockResolvedValue("blurhashstring"); }); it("should use m.image for image files", async () => { @@ -134,7 +134,7 @@ describe("ContentMessages", () => { const element = createElement(tagName); if (tagName === "video") { (element).load = jest.fn(); - (element).play = () => element.onloadeddata(new Event("loadeddata")); + (element).play = () => element.onloadeddata!(new Event("loadeddata")); (element).pause = jest.fn(); Object.defineProperty(element, "videoHeight", { get() { @@ -200,8 +200,8 @@ describe("ContentMessages", () => { expect(upload.loaded).toBe(0); expect(upload.total).toBe(file.size); - const { progressHandler } = mocked(client.uploadContent).mock.calls[0][1]; - progressHandler({ loaded: 123, total: 1234 }); + const { progressHandler } = mocked(client.uploadContent).mock.calls[0][1]!; + progressHandler!({ loaded: 123, total: 1234 }); expect(upload.loaded).toBe(123); expect(upload.total).toBe(1234); await prom; @@ -256,11 +256,11 @@ describe("ContentMessages", () => { mocked(client.uploadContent).mockReturnValue(deferred.promise); const file1 = new File([], "file1"); const prom = contentMessages.sendContentToRoom(file1, roomId, undefined, client, undefined); - const { abortController } = mocked(client.uploadContent).mock.calls[0][1]; - expect(abortController.signal.aborted).toBeFalsy(); + const { abortController } = mocked(client.uploadContent).mock.calls[0][1]!; + expect(abortController!.signal.aborted).toBeFalsy(); const [upload] = contentMessages.getCurrentUploads(); contentMessages.cancelUpload(upload); - expect(abortController.signal.aborted).toBeTruthy(); + expect(abortController!.signal.aborted).toBeTruthy(); deferred.resolve({} as UploadResponse); await prom; }); @@ -325,7 +325,7 @@ describe("uploadFile", () => { const file = new Blob([]); const prom = uploadFile(client, "!roomId:server", file); - mocked(client.uploadContent).mock.calls[0][1].abortController.abort(); + mocked(client.uploadContent).mock.calls[0][1]!.abortController!.abort(); deferred.resolve({ content_uri: "mxc://foo/bar" }); await expect(prom).rejects.toThrowError(UploadCanceledError); }); diff --git a/test/Reply-test.ts b/test/Reply-test.ts index 4ae7f6fa82..358e34e540 100644 --- a/test/Reply-test.ts +++ b/test/Reply-test.ts @@ -134,12 +134,14 @@ But this is not expect(getNestedReplyText(event, mockPermalinkGenerator)).toMatchSnapshot(); }); - [ - ["m.room.message", MsgType.Location, LocationAssetType.Pin], - ["m.room.message", MsgType.Location, LocationAssetType.Self], - [M_BEACON_INFO.name, undefined, LocationAssetType.Pin], - [M_BEACON_INFO.name, undefined, LocationAssetType.Self], - ].forEach(([type, msgType, assetType]) => { + ( + [ + ["m.room.message", MsgType.Location, LocationAssetType.Pin], + ["m.room.message", MsgType.Location, LocationAssetType.Self], + [M_BEACON_INFO.name, undefined, LocationAssetType.Pin], + [M_BEACON_INFO.name, undefined, LocationAssetType.Self], + ] as const + ).forEach(([type, msgType, assetType]) => { it(`should create the expected fallback text for ${assetType} ${type}/${msgType}`, () => { const event = makeTestEvent(type, { body: "body", diff --git a/test/Terms-test.tsx b/test/Terms-test.tsx index e96d4f253d..7d02c3fe66 100644 --- a/test/Terms-test.tsx +++ b/test/Terms-test.tsx @@ -49,7 +49,7 @@ describe("Terms", function () { beforeEach(function () { jest.clearAllMocks(); - mockClient.getAccountData.mockReturnValue(null); + mockClient.getAccountData.mockReturnValue(undefined); mockClient.getTerms.mockResolvedValue(null); mockClient.setAccountData.mockResolvedValue({}); }); @@ -59,7 +59,7 @@ describe("Terms", function () { }); it("should prompt for all terms & services if no account data", async function () { - mockClient.getAccountData.mockReturnValue(null); + mockClient.getAccountData.mockReturnValue(undefined); mockClient.getTerms.mockResolvedValue({ policies: { policy_the_first: POLICY_ONE, diff --git a/test/components/structures/RoomStatusBar-test.tsx b/test/components/structures/RoomStatusBar-test.tsx index aad8251c2a..a11bd899d3 100644 --- a/test/components/structures/RoomStatusBar-test.tsx +++ b/test/components/structures/RoomStatusBar-test.tsx @@ -34,7 +34,7 @@ describe("RoomStatusBar", () => { stubClient(); client = MatrixClientPeg.get(); - room = new Room(ROOM_ID, client, client.getUserId(), { + room = new Room(ROOM_ID, client, client.getUserId()!, { pendingEventOrdering: PendingEventOrdering.Detached, }); event = mkEvent({ @@ -72,7 +72,7 @@ describe("RoomStatusBar", () => { length: 2, }); rootEvent.status = EventStatus.NOT_SENT; - room.addPendingEvent(rootEvent, rootEvent.getId()); + room.addPendingEvent(rootEvent, rootEvent.getId()!); for (const event of events) { event.status = EventStatus.NOT_SENT; room.addPendingEvent(event, Date.now() + Math.random() + ""); diff --git a/test/components/structures/ThreadView-test.tsx b/test/components/structures/ThreadView-test.tsx index 31ec611557..2993a3b6d6 100644 --- a/test/components/structures/ThreadView-test.tsx +++ b/test/components/structures/ThreadView-test.tsx @@ -99,10 +99,10 @@ describe("ThreadView", () => { "is_falling_back": true, "m.in_reply_to": { event_id: rootEvent - .getThread() + .getThread()! .lastReply((ev: MatrixEvent) => { return ev.isRelation(THREAD_RELATION_TYPE.name); - }) + })! .getId(), }, "rel_type": RelationType.Thread, @@ -126,8 +126,8 @@ describe("ThreadView", () => { const res = mkThread({ room, client: mockClient, - authorId: mockClient.getUserId(), - participantUserIds: [mockClient.getUserId()], + authorId: mockClient.getUserId()!, + participantUserIds: [mockClient.getUserId()!], }); rootEvent = res.rootEvent; @@ -154,8 +154,8 @@ describe("ThreadView", () => { const { rootEvent: rootEvent2 } = mkThread({ room, client: mockClient, - authorId: mockClient.getUserId(), - participantUserIds: [mockClient.getUserId()], + authorId: mockClient.getUserId()!, + participantUserIds: [mockClient.getUserId()!], }); act(() => { diff --git a/test/components/views/context_menus/ThreadListContextMenu-test.tsx b/test/components/views/context_menus/ThreadListContextMenu-test.tsx index 74c74addc5..2f1c5df7f5 100644 --- a/test/components/views/context_menus/ThreadListContextMenu-test.tsx +++ b/test/components/views/context_menus/ThreadListContextMenu-test.tsx @@ -54,8 +54,8 @@ describe("ThreadListContextMenu", () => { const res = mkThread({ room, client: mockClient, - authorId: mockClient.getUserId(), - participantUserIds: [mockClient.getUserId()], + authorId: mockClient.getUserId()!, + participantUserIds: [mockClient.getUserId()!], }); event = res.rootEvent; diff --git a/test/components/views/dialogs/ExportDialog-test.tsx b/test/components/views/dialogs/ExportDialog-test.tsx index 9e4d0148dc..afc490546f 100644 --- a/test/components/views/dialogs/ExportDialog-test.tsx +++ b/test/components/views/dialogs/ExportDialog-test.tsx @@ -109,7 +109,7 @@ describe("", () => { plainTextExporterInstance.export.mockClear(); // default setting value - ChatExportMock.getForceChatExportParameters.mockClear().mockReturnValue({}); + mocked(ChatExportMock.getForceChatExportParameters!).mockClear().mockReturnValue({}); }); it("renders export dialog", () => { @@ -145,7 +145,7 @@ describe("", () => { }); it("exports room using values set from ForceRoomExportParameters", async () => { - ChatExportMock.getForceChatExportParameters.mockReturnValue({ + mocked(ChatExportMock.getForceChatExportParameters!).mockReturnValue({ format: ExportFormat.PlainText, range: ExportType.Beginning, sizeMb: 7000, @@ -198,7 +198,7 @@ describe("", () => { }); it("does not render export format when set in ForceRoomExportParameters", () => { - ChatExportMock.getForceChatExportParameters.mockReturnValue({ + mocked(ChatExportMock.getForceChatExportParameters!).mockReturnValue({ format: ExportFormat.PlainText, }); const component = getComponent(); @@ -219,7 +219,7 @@ describe("", () => { }); it("does not render export type when set in ForceRoomExportParameters", () => { - ChatExportMock.getForceChatExportParameters.mockReturnValue({ + mocked(ChatExportMock.getForceChatExportParameters!).mockReturnValue({ range: ExportType.Beginning, }); const component = getComponent(); @@ -310,7 +310,7 @@ describe("", () => { }); it("does not render size limit input when set in ForceRoomExportParameters", () => { - ChatExportMock.getForceChatExportParameters.mockReturnValue({ + mocked(ChatExportMock.getForceChatExportParameters!).mockReturnValue({ sizeMb: 10000, }); const component = getComponent(); @@ -321,7 +321,7 @@ describe("", () => { * 2000mb size limit does not apply when higher limit is configured in config */ it("exports when size limit set in ForceRoomExportParameters is larger than 2000", async () => { - ChatExportMock.getForceChatExportParameters.mockReturnValue({ + mocked(ChatExportMock.getForceChatExportParameters!).mockReturnValue({ sizeMb: 10000, }); const component = getComponent(); @@ -344,7 +344,7 @@ describe("", () => { }); it("does not render input when set in ForceRoomExportParameters", () => { - ChatExportMock.getForceChatExportParameters.mockReturnValue({ + mocked(ChatExportMock.getForceChatExportParameters!).mockReturnValue({ includeAttachments: false, }); const component = getComponent(); diff --git a/test/components/views/settings/DevicesPanel-test.tsx b/test/components/views/settings/DevicesPanel-test.tsx index 3565e59c43..530bf55a79 100644 --- a/test/components/views/settings/DevicesPanel-test.tsx +++ b/test/components/views/settings/DevicesPanel-test.tsx @@ -74,7 +74,7 @@ describe("", () => { const toggleDeviceSelection = (container: HTMLElement, deviceId: string) => act(() => { - const checkbox = container.querySelector(`#device-tile-checkbox-${deviceId}`); + const checkbox = container.querySelector(`#device-tile-checkbox-${deviceId}`)!; fireEvent.click(checkbox); }); @@ -204,7 +204,7 @@ describe("", () => { // close the modal without submission act(() => { - const modalCloseButton = document.querySelector('[aria-label="Close dialog"]'); + const modalCloseButton = document.querySelector('[aria-label="Close dialog"]')!; fireEvent.click(modalCloseButton); }); diff --git a/test/components/views/settings/tabs/user/PreferencesUserSettingsTab-test.tsx b/test/components/views/settings/tabs/user/PreferencesUserSettingsTab-test.tsx index 0e69f1878c..4f7441f59a 100644 --- a/test/components/views/settings/tabs/user/PreferencesUserSettingsTab-test.tsx +++ b/test/components/views/settings/tabs/user/PreferencesUserSettingsTab-test.tsx @@ -49,6 +49,7 @@ describe("PreferencesUserSettingsTab", () => { const client = MatrixClientPeg.get(); jest.spyOn(client, "isVersionSupported").mockImplementation(async (version: string) => { if (version === "v1.4") return val; + return false; }); }; @@ -61,8 +62,12 @@ describe("PreferencesUserSettingsTab", () => { }; }; - const expectSetValueToHaveBeenCalled = (name: string, roomId: string, level: SettingLevel, value: boolean) => - expect(SettingsStore.setValue).toHaveBeenCalledWith(name, roomId, level, value); + const expectSetValueToHaveBeenCalled = ( + name: string, + roomId: string | undefined, + level: SettingLevel, + value: boolean, + ) => expect(SettingsStore.setValue).toHaveBeenCalledWith(name, roomId, level, value); describe("with server support", () => { beforeEach(() => { diff --git a/test/models/Call-test.ts b/test/models/Call-test.ts index 2f01f06636..028cf12018 100644 --- a/test/models/Call-test.ts +++ b/test/models/Call-test.ts @@ -923,7 +923,7 @@ describe("ElementCall", () => { jest.spyOn(Modal, "createDialog").mockReturnValue({ finished: new Promise((r) => r([sourceId])), } as IHandle); - jest.spyOn(PlatformPeg.get(), "supportsDesktopCapturer").mockReturnValue(true); + jest.spyOn(PlatformPeg.get()!, "supportsDesktopCapturer").mockReturnValue(true); await call.connect(); @@ -951,7 +951,7 @@ describe("ElementCall", () => { jest.spyOn(Modal, "createDialog").mockReturnValue({ finished: new Promise((r) => r([null])), } as IHandle); - jest.spyOn(PlatformPeg.get(), "supportsDesktopCapturer").mockReturnValue(true); + jest.spyOn(PlatformPeg.get()!, "supportsDesktopCapturer").mockReturnValue(true); await call.connect(); @@ -976,7 +976,7 @@ describe("ElementCall", () => { }); it("replies with pending: false if we don't support desktop capturer", async () => { - jest.spyOn(PlatformPeg.get(), "supportsDesktopCapturer").mockReturnValue(false); + jest.spyOn(PlatformPeg.get()!, "supportsDesktopCapturer").mockReturnValue(false); await call.connect(); diff --git a/test/settings/enums/ImageSize-test.ts b/test/settings/enums/ImageSize-test.ts index 0eb89a45bb..9103bb7307 100644 --- a/test/settings/enums/ImageSize-test.ts +++ b/test/settings/enums/ImageSize-test.ts @@ -31,7 +31,7 @@ describe("ImageSize", () => { expect(size).toStrictEqual({ w: 800, h: 400 }); }); it("returns max values if content size is not specified", () => { - const size = suggestedSize(ImageSize.Normal, { w: null, h: null }); + const size = suggestedSize(ImageSize.Normal, {}); expect(size).toStrictEqual({ w: 324, h: 324 }); }); it("returns integer values", () => { diff --git a/test/stores/OwnBeaconStore-test.ts b/test/stores/OwnBeaconStore-test.ts index afe48edd32..79ff78530e 100644 --- a/test/stores/OwnBeaconStore-test.ts +++ b/test/stores/OwnBeaconStore-test.ts @@ -102,9 +102,9 @@ describe("OwnBeaconStore", () => { }; const expireBeaconAndEmit = (store: OwnBeaconStore, beaconInfoEvent: MatrixEvent): void => { - const beacon = store.getBeaconById(getBeaconInfoIdentifier(beaconInfoEvent)); + const beacon = store.getBeaconById(getBeaconInfoIdentifier(beaconInfoEvent))!; // time travel until beacon is expired - advanceDateAndTime(beacon.beaconInfo.timeout + 100); + advanceDateAndTime(beacon.beaconInfo!.timeout + 100); // force an update on the beacon // @ts-ignore @@ -118,13 +118,13 @@ describe("OwnBeaconStore", () => { beaconInfoEvent: MatrixEvent, isLive: boolean, ): void => { - const beacon = store.getBeaconById(getBeaconInfoIdentifier(beaconInfoEvent)); + const beacon = store.getBeaconById(getBeaconInfoIdentifier(beaconInfoEvent))!; // matches original state of event content // except for live property const updateEvent = makeBeaconInfoEvent( - beaconInfoEvent.getSender(), - beaconInfoEvent.getRoomId(), - { isLive, timeout: beacon.beaconInfo.timeout }, + beaconInfoEvent.getSender()!, + beaconInfoEvent.getRoomId()!, + { isLive, timeout: beacon.beaconInfo!.timeout }, "update-event-id", ); beacon.update(updateEvent); @@ -236,12 +236,12 @@ describe("OwnBeaconStore", () => { expect(mockClient.sendEvent).toHaveBeenCalledWith( room1Id, M_BEACON.name, - makeBeaconContent(defaultLocationUri, now, alicesRoom1BeaconInfo.getId()), + makeBeaconContent(defaultLocationUri, now, alicesRoom1BeaconInfo.getId()!), ); expect(mockClient.sendEvent).toHaveBeenCalledWith( room2Id, M_BEACON.name, - makeBeaconContent(defaultLocationUri, now, alicesRoom2BeaconInfo.getId()), + makeBeaconContent(defaultLocationUri, now, alicesRoom2BeaconInfo.getId()!), ); }); }); @@ -263,7 +263,7 @@ describe("OwnBeaconStore", () => { it("destroys beacons", async () => { const [room1] = makeRoomsWithStateEvents([alicesRoom1BeaconInfo]); const store = await makeOwnBeaconStore(); - const beacon = room1.currentState.beacons.get(getBeaconInfoIdentifier(alicesRoom1BeaconInfo)); + const beacon = room1.currentState.beacons.get(getBeaconInfoIdentifier(alicesRoom1BeaconInfo))!; const destroySpy = jest.spyOn(beacon, "destroy"); // @ts-ignore store.onNotReady(); @@ -559,7 +559,7 @@ describe("OwnBeaconStore", () => { const [room1] = makeRoomsWithStateEvents([alicesRoom1BeaconInfo, alicesRoom2BeaconInfo]); const store = await makeOwnBeaconStore(); - const room1BeaconInstance = store.beacons.get(getBeaconInfoIdentifier(alicesRoom1BeaconInfo)); + const room1BeaconInstance = store.beacons.get(getBeaconInfoIdentifier(alicesRoom1BeaconInfo))!; const beaconDestroySpy = jest.spyOn(room1BeaconInstance, "destroy"); const emitSpy = jest.spyOn(store, "emit"); @@ -610,7 +610,7 @@ describe("OwnBeaconStore", () => { expect(store.hasLiveBeacons()).toBe(true); const emitSpy = jest.spyOn(store, "emit"); - const beacon = store.getBeaconById(getBeaconInfoIdentifier(alicesRoom1BeaconInfo)); + const beacon = store.getBeaconById(getBeaconInfoIdentifier(alicesRoom1BeaconInfo))!; beacon.destroy(); mockClient.emit(BeaconEvent.Destroy, beacon.identifier); diff --git a/test/stores/SpaceStore-test.ts b/test/stores/SpaceStore-test.ts index 1b9ae8da60..907d000a48 100644 --- a/test/stores/SpaceStore-test.ts +++ b/test/stores/SpaceStore-test.ts @@ -103,10 +103,11 @@ describe("SpaceStore", () => { const viewRoom = (roomId: string) => defaultDispatcher.dispatch({ action: Action.ViewRoom, room_id: roomId }, true); const run = async () => { - mocked(client).getRoom.mockImplementation((roomId) => rooms.find((room) => room.roomId === roomId)); - mocked(client).getRoomUpgradeHistory.mockImplementation((roomId) => [ - rooms.find((room) => room.roomId === roomId), - ]); + mocked(client).getRoom.mockImplementation((roomId) => rooms.find((room) => room.roomId === roomId) || null); + mocked(client).getRoomUpgradeHistory.mockImplementation((roomId) => { + const room = rooms.find((room) => room.roomId === roomId); + return room ? [room] : []; + }); await testUtils.setupAsyncStoreWithClient(store, client); jest.runOnlyPendingTimers(); }; @@ -312,10 +313,12 @@ describe("SpaceStore", () => { mkSpace(space3, [invite2]); mkSpace(space4, [room4, fav2, space2, space3]); - mocked(client).getRoom.mockImplementation((roomId) => rooms.find((room) => room.roomId === roomId)); + mocked(client).getRoom.mockImplementation( + (roomId) => rooms.find((room) => room.roomId === roomId) || null, + ); [fav1, fav2, fav3].forEach((roomId) => { - client.getRoom(roomId).tags = { + client.getRoom(roomId)!.tags = { "m.favourite": { order: 0.5, }, @@ -323,20 +326,20 @@ describe("SpaceStore", () => { }); [invite1, invite2].forEach((roomId) => { - mocked(client.getRoom(roomId)).getMyMembership.mockReturnValue("invite"); + mocked(client.getRoom(roomId)!).getMyMembership.mockReturnValue("invite"); }); // have dmPartner1 be in space1 with you const mySpace1Member = new RoomMember(space1, testUserId); mySpace1Member.membership = "join"; - (rooms.find((r) => r.roomId === space1).getMembers as jest.Mock).mockReturnValue([ + (rooms.find((r) => r.roomId === space1)!.getMembers as jest.Mock).mockReturnValue([ mySpace1Member, dm1Partner, ]); // have dmPartner2 be in space2 with you const mySpace2Member = new RoomMember(space2, testUserId); mySpace2Member.membership = "join"; - (rooms.find((r) => r.roomId === space2).getMembers as jest.Mock).mockReturnValue([ + (rooms.find((r) => r.roomId === space2)!.getMembers as jest.Mock).mockReturnValue([ mySpace2Member, dm2Partner, ]); @@ -349,15 +352,15 @@ describe("SpaceStore", () => { event: true, type: EventType.SpaceParent, room: room2, - user: client.getUserId(), + user: client.getUserId()!, skey: space2, content: { via: [], canonical: true }, ts: Date.now(), }) as MatrixEvent, ]); - mocked(cliRoom2.currentState).getStateEvents.mockImplementation(room2MockStateEvents); + mocked(cliRoom2!.currentState).getStateEvents.mockImplementation(room2MockStateEvents); const cliSpace2 = client.getRoom(space2); - mocked(cliSpace2.currentState).maySendStateEvent.mockImplementation( + mocked(cliSpace2!.currentState).maySendStateEvent.mockImplementation( (evType: string, userId: string) => { if (evType === EventType.SpaceChild) { return userId === client.getUserId(); @@ -368,13 +371,13 @@ describe("SpaceStore", () => { // room 3 claims to be a child of space3 but is not due to invalid m.space.parent (permissions) const cliRoom3 = client.getRoom(room3); - mocked(cliRoom3.currentState).getStateEvents.mockImplementation( + mocked(cliRoom3!.currentState).getStateEvents.mockImplementation( testUtils.mockStateEventImplementation([ mkEvent({ event: true, type: EventType.SpaceParent, room: room3, - user: client.getUserId(), + user: client.getUserId()!, skey: space3, content: { via: [], canonical: true }, ts: Date.now(), @@ -382,7 +385,7 @@ describe("SpaceStore", () => { ]), ); const cliSpace3 = client.getRoom(space3); - mocked(cliSpace3.currentState).maySendStateEvent.mockImplementation( + mocked(cliSpace3!.currentState).maySendStateEvent.mockImplementation( (evType: string, userId: string) => { if (evType === EventType.SpaceChild) { return false; @@ -813,7 +816,7 @@ describe("SpaceStore", () => { content: { membership: "join" }, ts: Date.now(), }); - const spaceRoom = client.getRoom(spaceId); + const spaceRoom = client.getRoom(spaceId)!; mocked(spaceRoom.currentState).getStateEvents.mockImplementation( testUtils.mockStateEventImplementation([memberEvent]), ); @@ -929,7 +932,7 @@ describe("SpaceStore", () => { it("switch to unknown space is a nop", async () => { expect(store.activeSpace).toBe(MetaSpace.Home); - const space = client.getRoom(room1); // not a space + const space = client.getRoom(room1)!; // not a space store.setActiveSpace(space.roomId); expect(fn).not.toHaveBeenCalledWith(UPDATE_SELECTED_SPACE, space.roomId); expect(store.activeSpace).toBe(MetaSpace.Home); @@ -962,6 +965,7 @@ describe("SpaceStore", () => { member.membership = "join"; return member; } + return null; }); client.emit(RoomStateEvent.Members, event, space.currentState, dm1Partner); @@ -1088,7 +1092,7 @@ describe("SpaceStore", () => { mkSpace(space1, [room1, room2, room3]); mkSpace(space2, [room1, room2]); - const cliRoom2 = client.getRoom(room2); + const cliRoom2 = client.getRoom(room2)!; mocked(cliRoom2.currentState).getStateEvents.mockImplementation( testUtils.mockStateEventImplementation([ mkEvent({ @@ -1267,8 +1271,9 @@ describe("SpaceStore", () => { case dm1Partner.userId: return rootSpaceFriend; } + return null; }); - expect(SpaceStore.instance.getSpaceFilteredUserIds(space1).has(dm1Partner.userId)).toBeFalsy(); + expect(SpaceStore.instance.getSpaceFilteredUserIds(space1)!.has(dm1Partner.userId)).toBeFalsy(); const memberEvent = mkEvent({ event: true, type: EventType.RoomMember, @@ -1281,7 +1286,7 @@ describe("SpaceStore", () => { }); client.emit(RoomStateEvent.Members, memberEvent, rootSpace.currentState, dm1Partner); jest.runOnlyPendingTimers(); - expect(SpaceStore.instance.getSpaceFilteredUserIds(space1).has(dm1Partner.userId)).toBeTruthy(); + expect(SpaceStore.instance.getSpaceFilteredUserIds(space1)!.has(dm1Partner.userId)).toBeTruthy(); const dm1Room = mkRoom(dm1); dm1Room.getMyMembership.mockReturnValue("join"); client.emit(ClientEvent.Room, dm1Room); diff --git a/test/stores/room-list/SlidingRoomListStore-test.ts b/test/stores/room-list/SlidingRoomListStore-test.ts index 3014423349..6d23e16698 100644 --- a/test/stores/room-list/SlidingRoomListStore-test.ts +++ b/test/stores/room-list/SlidingRoomListStore-test.ts @@ -198,10 +198,10 @@ describe("SlidingRoomListStore", () => { return keyToListData[key] || null; }); - expect(store.getTagsForRoom(new Room(roomA, context.client!, context.client!.getUserId()))).toEqual([ + expect(store.getTagsForRoom(new Room(roomA, context.client!, context.client!.getUserId()!))).toEqual([ DefaultTagID.Untagged, ]); - expect(store.getTagsForRoom(new Room(roomB, context.client!, context.client!.getUserId()))).toEqual([ + expect(store.getTagsForRoom(new Room(roomB, context.client!, context.client!.getUserId()!))).toEqual([ DefaultTagID.Favourite, DefaultTagID.Untagged, ]); @@ -221,9 +221,9 @@ describe("SlidingRoomListStore", () => { 0: roomA, }; const rooms = [ - new Room(roomA, context.client!, context.client!.getUserId()), - new Room(roomB, context.client!, context.client!.getUserId()), - new Room(roomC, context.client!, context.client!.getUserId()), + new Room(roomA, context.client!, context.client!.getUserId()!), + new Room(roomB, context.client!, context.client!.getUserId()!), + new Room(roomC, context.client!, context.client!.getUserId()!), ]; mocked(context.client!.getRoom).mockImplementation((roomId: string) => { switch (roomId) { @@ -257,9 +257,9 @@ describe("SlidingRoomListStore", () => { 2: roomIdC, 0: roomIdA, }; - const roomA = new Room(roomIdA, context.client!, context.client!.getUserId()); - const roomB = new Room(roomIdB, context.client!, context.client!.getUserId()); - const roomC = new Room(roomIdC, context.client!, context.client!.getUserId()); + const roomA = new Room(roomIdA, context.client!, context.client!.getUserId()!); + const roomB = new Room(roomIdB, context.client!, context.client!.getUserId()!); + const roomC = new Room(roomIdC, context.client!, context.client!.getUserId()!); mocked(context.client!.getRoom).mockImplementation((roomId: string) => { switch (roomId) { case roomIdA: @@ -321,8 +321,8 @@ describe("SlidingRoomListStore", () => { const tagId = DefaultTagID.Favourite; const joinCount = 10; // seed the store with 2 rooms - const roomA = new Room(roomIdA, context.client!, context.client!.getUserId()); - const roomC = new Room(roomIdC, context.client!, context.client!.getUserId()); + const roomA = new Room(roomIdA, context.client!, context.client!.getUserId()!); + const roomC = new Room(roomIdC, context.client!, context.client!.getUserId()!); mocked(context.client!.getRoom).mockImplementation((roomId: string) => { switch (roomId) { case roomIdA: diff --git a/test/stores/room-list/SpaceWatcher-test.ts b/test/stores/room-list/SpaceWatcher-test.ts index e8d43b66bd..c8a7c4f660 100644 --- a/test/stores/room-list/SpaceWatcher-test.ts +++ b/test/stores/room-list/SpaceWatcher-test.ts @@ -29,11 +29,13 @@ import { MatrixClientPeg } from "../../../src/MatrixClientPeg"; import { SpaceFilterCondition } from "../../../src/stores/room-list/filters/SpaceFilterCondition"; import DMRoomMap from "../../../src/utils/DMRoomMap"; -let filter: SpaceFilterCondition = null; +let filter: SpaceFilterCondition | null = null; const mockRoomListStore = { addFilter: (f: SpaceFilterCondition) => (filter = f), - removeFilter: (): void => (filter = null), + removeFilter: (): void => { + filter = null; + }, } as unknown as RoomListStoreClass; const getUserIdForRoomId = jest.fn(); @@ -74,7 +76,7 @@ describe("SpaceWatcher", () => { [MetaSpace.Orphans]: true, }); - client.getRoom.mockImplementation((roomId) => rooms.find((room) => room.roomId === roomId)); + client.getRoom.mockImplementation((roomId) => rooms.find((room) => room.roomId === roomId) || null); await setupAsyncStoreWithClient(store, client); }); @@ -99,7 +101,7 @@ describe("SpaceWatcher", () => { await setShowAllRooms(false); expect(filter).toBeInstanceOf(SpaceFilterCondition); - expect(filter["space"]).toBe(MetaSpace.Home); + expect(filter!["space"]).toBe(MetaSpace.Home); }); it("sets filter correctly for all -> space transition", async () => { @@ -109,7 +111,7 @@ describe("SpaceWatcher", () => { SpaceStore.instance.setActiveSpace(space1); expect(filter).toBeInstanceOf(SpaceFilterCondition); - expect(filter["space"]).toBe(space1); + expect(filter!["space"]).toBe(space1); }); it("removes filter for home -> all transition", async () => { @@ -128,7 +130,7 @@ describe("SpaceWatcher", () => { SpaceStore.instance.setActiveSpace(space1); expect(filter).toBeInstanceOf(SpaceFilterCondition); - expect(filter["space"]).toBe(space1); + expect(filter!["space"]).toBe(space1); }); it("removes filter for space -> all transition", async () => { @@ -137,7 +139,7 @@ describe("SpaceWatcher", () => { SpaceStore.instance.setActiveSpace(space1); expect(filter).toBeInstanceOf(SpaceFilterCondition); - expect(filter["space"]).toBe(space1); + expect(filter!["space"]).toBe(space1); SpaceStore.instance.setActiveSpace(MetaSpace.Home); expect(filter).toBeNull(); @@ -149,7 +151,7 @@ describe("SpaceWatcher", () => { SpaceStore.instance.setActiveSpace(MetaSpace.Favourites); expect(filter).toBeInstanceOf(SpaceFilterCondition); - expect(filter["space"]).toBe(MetaSpace.Favourites); + expect(filter!["space"]).toBe(MetaSpace.Favourites); SpaceStore.instance.setActiveSpace(MetaSpace.Home); expect(filter).toBeNull(); @@ -161,7 +163,7 @@ describe("SpaceWatcher", () => { SpaceStore.instance.setActiveSpace(MetaSpace.People); expect(filter).toBeInstanceOf(SpaceFilterCondition); - expect(filter["space"]).toBe(MetaSpace.People); + expect(filter!["space"]).toBe(MetaSpace.People); SpaceStore.instance.setActiveSpace(MetaSpace.Home); expect(filter).toBeNull(); @@ -173,7 +175,7 @@ describe("SpaceWatcher", () => { SpaceStore.instance.setActiveSpace(MetaSpace.Orphans); expect(filter).toBeInstanceOf(SpaceFilterCondition); - expect(filter["space"]).toBe(MetaSpace.Orphans); + expect(filter!["space"]).toBe(MetaSpace.Orphans); SpaceStore.instance.setActiveSpace(MetaSpace.Home); expect(filter).toBeNull(); @@ -185,11 +187,11 @@ describe("SpaceWatcher", () => { new SpaceWatcher(mockRoomListStore); expect(filter).toBeInstanceOf(SpaceFilterCondition); - expect(filter["space"]).toBe(space1); + expect(filter!["space"]).toBe(space1); SpaceStore.instance.setActiveSpace(MetaSpace.Home); expect(filter).toBeInstanceOf(SpaceFilterCondition); - expect(filter["space"]).toBe(MetaSpace.Home); + expect(filter!["space"]).toBe(MetaSpace.Home); }); it("updates filter correctly for space -> orphans transition", async () => { @@ -198,11 +200,11 @@ describe("SpaceWatcher", () => { new SpaceWatcher(mockRoomListStore); expect(filter).toBeInstanceOf(SpaceFilterCondition); - expect(filter["space"]).toBe(space1); + expect(filter!["space"]).toBe(space1); SpaceStore.instance.setActiveSpace(MetaSpace.Orphans); expect(filter).toBeInstanceOf(SpaceFilterCondition); - expect(filter["space"]).toBe(MetaSpace.Orphans); + expect(filter!["space"]).toBe(MetaSpace.Orphans); }); it("updates filter correctly for orphans -> people transition", async () => { @@ -211,11 +213,11 @@ describe("SpaceWatcher", () => { new SpaceWatcher(mockRoomListStore); expect(filter).toBeInstanceOf(SpaceFilterCondition); - expect(filter["space"]).toBe(MetaSpace.Orphans); + expect(filter!["space"]).toBe(MetaSpace.Orphans); SpaceStore.instance.setActiveSpace(MetaSpace.People); expect(filter).toBeInstanceOf(SpaceFilterCondition); - expect(filter["space"]).toBe(MetaSpace.People); + expect(filter!["space"]).toBe(MetaSpace.People); }); it("updates filter correctly for space -> space transition", async () => { @@ -224,11 +226,11 @@ describe("SpaceWatcher", () => { new SpaceWatcher(mockRoomListStore); expect(filter).toBeInstanceOf(SpaceFilterCondition); - expect(filter["space"]).toBe(space1); + expect(filter!["space"]).toBe(space1); SpaceStore.instance.setActiveSpace(space2); expect(filter).toBeInstanceOf(SpaceFilterCondition); - expect(filter["space"]).toBe(space2); + expect(filter!["space"]).toBe(space2); }); it("doesn't change filter when changing showAllRooms mode to true", async () => { @@ -237,11 +239,11 @@ describe("SpaceWatcher", () => { new SpaceWatcher(mockRoomListStore); expect(filter).toBeInstanceOf(SpaceFilterCondition); - expect(filter["space"]).toBe(space1); + expect(filter!["space"]).toBe(space1); await setShowAllRooms(true); expect(filter).toBeInstanceOf(SpaceFilterCondition); - expect(filter["space"]).toBe(space1); + expect(filter!["space"]).toBe(space1); }); it("doesn't change filter when changing showAllRooms mode to false", async () => { @@ -250,10 +252,10 @@ describe("SpaceWatcher", () => { new SpaceWatcher(mockRoomListStore); expect(filter).toBeInstanceOf(SpaceFilterCondition); - expect(filter["space"]).toBe(space1); + expect(filter!["space"]).toBe(space1); await setShowAllRooms(false); expect(filter).toBeInstanceOf(SpaceFilterCondition); - expect(filter["space"]).toBe(space1); + expect(filter!["space"]).toBe(space1); }); }); diff --git a/test/stores/room-list/filters/VisibilityProvider-test.ts b/test/stores/room-list/filters/VisibilityProvider-test.ts index e8f2781fec..80296c2bda 100644 --- a/test/stores/room-list/filters/VisibilityProvider-test.ts +++ b/test/stores/room-list/filters/VisibilityProvider-test.ts @@ -109,14 +109,14 @@ describe("VisibilityProvider", () => { }); it("should return false if visibility customisation returns false", () => { - mocked(RoomListCustomisations.isRoomVisible).mockReturnValue(false); + mocked(RoomListCustomisations.isRoomVisible!).mockReturnValue(false); const room = createRoom(); expect(VisibilityProvider.instance.isRoomVisible(room)).toBe(false); expect(RoomListCustomisations.isRoomVisible).toHaveBeenCalledWith(room); }); it("should return true if visibility customisation returns true", () => { - mocked(RoomListCustomisations.isRoomVisible).mockReturnValue(true); + mocked(RoomListCustomisations.isRoomVisible!).mockReturnValue(true); const room = createRoom(); expect(VisibilityProvider.instance.isRoomVisible(room)).toBe(true); expect(RoomListCustomisations.isRoomVisible).toHaveBeenCalledWith(room); diff --git a/test/stores/widgets/StopGapWidget-test.ts b/test/stores/widgets/StopGapWidget-test.ts index 66b7a25e35..38e9983d49 100644 --- a/test/stores/widgets/StopGapWidget-test.ts +++ b/test/stores/widgets/StopGapWidget-test.ts @@ -80,8 +80,8 @@ describe("StopGapWidget", () => { beforeEach(() => { voiceBroadcastInfoEvent = mkEvent({ event: true, - room: client.getRoom("x").roomId, - user: client.getUserId(), + room: client.getRoom("x")?.roomId, + user: client.getUserId()!, type: VoiceBroadcastInfoEventType, content: {}, }); diff --git a/test/stores/widgets/WidgetPermissionStore-test.ts b/test/stores/widgets/WidgetPermissionStore-test.ts index aa3af71f79..d9f1cbb804 100644 --- a/test/stores/widgets/WidgetPermissionStore-test.ts +++ b/test/stores/widgets/WidgetPermissionStore-test.ts @@ -59,22 +59,22 @@ describe("WidgetPermissionStore", () => { }); it("should persist OIDCState.Allowed for a widget", () => { - widgetPermissionStore.setOIDCState(w, WidgetKind.Account, null, OIDCState.Allowed); + widgetPermissionStore.setOIDCState(w, WidgetKind.Account, roomId, OIDCState.Allowed); // check it remembered the value - expect(widgetPermissionStore.getOIDCState(w, WidgetKind.Account, null)).toEqual(OIDCState.Allowed); + expect(widgetPermissionStore.getOIDCState(w, WidgetKind.Account, roomId)).toEqual(OIDCState.Allowed); }); it("should persist OIDCState.Denied for a widget", () => { - widgetPermissionStore.setOIDCState(w, WidgetKind.Account, null, OIDCState.Denied); + widgetPermissionStore.setOIDCState(w, WidgetKind.Account, roomId, OIDCState.Denied); // check it remembered the value - expect(widgetPermissionStore.getOIDCState(w, WidgetKind.Account, null)).toEqual(OIDCState.Denied); + expect(widgetPermissionStore.getOIDCState(w, WidgetKind.Account, roomId)).toEqual(OIDCState.Denied); }); it("should update OIDCState for a widget", () => { - widgetPermissionStore.setOIDCState(w, WidgetKind.Account, null, OIDCState.Allowed); - widgetPermissionStore.setOIDCState(w, WidgetKind.Account, null, OIDCState.Denied); + widgetPermissionStore.setOIDCState(w, WidgetKind.Account, roomId, OIDCState.Allowed); + widgetPermissionStore.setOIDCState(w, WidgetKind.Account, roomId, OIDCState.Denied); // check it remembered the latest value - expect(widgetPermissionStore.getOIDCState(w, WidgetKind.Account, null)).toEqual(OIDCState.Denied); + expect(widgetPermissionStore.getOIDCState(w, WidgetKind.Account, roomId)).toEqual(OIDCState.Denied); }); it("should scope the location for a widget when setting OIDC state", () => { diff --git a/test/test-utils/beacon.ts b/test/test-utils/beacon.ts index 0a388336a7..18ef73bdae 100644 --- a/test/test-utils/beacon.ts +++ b/test/test-utils/beacon.ts @@ -197,7 +197,7 @@ export const makeRoomWithBeacons = ( locationEvents?: MatrixEvent[], ): Beacon[] => { const room = makeRoomWithStateEvents(beaconInfoEvents, { roomId, mockClient }); - const beacons = beaconInfoEvents.map((event) => room.currentState.beacons.get(getBeaconInfoIdentifier(event))); + const beacons = beaconInfoEvents.map((event) => room.currentState.beacons.get(getBeaconInfoIdentifier(event))!); if (locationEvents) { beacons.forEach((beacon) => { // this filtering happens in roomState, which is bypassed here diff --git a/test/test-utils/composer.ts b/test/test-utils/composer.ts index 80cc9f0470..85227acbc1 100644 --- a/test/test-utils/composer.ts +++ b/test/test-utils/composer.ts @@ -31,7 +31,7 @@ export const addTextToComposer = (container: HTMLElement, text: string) => getData: (type: string) => (type === "text/plain" ? text : undefined), } as unknown as DataTransfer, }; - fireEvent.paste(container.querySelector('[role="textbox"]'), pasteEvent); + fireEvent.paste(container.querySelector('[role="textbox"]')!, pasteEvent); }); export const addTextToComposerEnzyme = (wrapper: ReactWrapper, text: string) => diff --git a/test/test-utils/utilities.ts b/test/test-utils/utilities.ts index e2acc904e3..a03e10b83e 100644 --- a/test/test-utils/utilities.ts +++ b/test/test-utils/utilities.ts @@ -37,7 +37,7 @@ export function untilDispatch( dispatcher = defaultDispatcher, timeout = 1000, ): Promise { - const callerLine = new Error().stack.toString().split("\n")[2]; + const callerLine = new Error().stack!.toString().split("\n")[2]; if (typeof waitForAction === "string") { const action = waitForAction; waitForAction = (payload) => { @@ -89,10 +89,10 @@ export function untilDispatch( export function untilEmission( emitter: EventEmitter, eventName: string, - check: (...args: any[]) => boolean = undefined, + check?: (...args: any[]) => boolean, timeout = 1000, ): Promise { - const callerLine = new Error().stack.toString().split("\n")[2]; + const callerLine = new Error().stack!.toString().split("\n")[2]; return new Promise((resolve, reject) => { let fulfilled = false; let timeoutId: number; diff --git a/test/theme-test.ts b/test/theme-test.ts index 0298cd48c8..d1dc5f68cc 100644 --- a/test/theme-test.ts +++ b/test/theme-test.ts @@ -58,7 +58,7 @@ describe("theme", () => { // When await new Promise((resolve) => { setTheme("light").then(resolve); - lightTheme.onload(void 0); + lightTheme.onload!({} as Event); }); // Then @@ -72,7 +72,7 @@ describe("theme", () => { return expect( new Promise((resolve) => { setTheme("light").catch((e) => resolve(e)); - lightTheme.onerror("call onerror"); + lightTheme.onerror!("call onerror"); }), ).resolves.toBe("call onerror"); }); diff --git a/test/useTopic-test.tsx b/test/useTopic-test.tsx index 5cab29e9b6..2f7e0ac757 100644 --- a/test/useTopic-test.tsx +++ b/test/useTopic-test.tsx @@ -42,7 +42,7 @@ describe("useTopic", () => { function RoomTopic() { const topic = useTopic(room); - return

{topic.text}

; + return

{topic!.text}

; } render(); diff --git a/test/utils/MegolmExportEncryption-test.ts b/test/utils/MegolmExportEncryption-test.ts index 4e22c7aec4..69d803073f 100644 --- a/test/utils/MegolmExportEncryption-test.ts +++ b/test/utils/MegolmExportEncryption-test.ts @@ -85,10 +85,6 @@ describe("MegolmExportEncryption", function () { MegolmExportEncryption = require("../../src/utils/MegolmExportEncryption"); }); - afterAll(() => { - window.crypto = undefined; - }); - describe("decrypt", function () { it("should handle missing header", function () { const input = stringToArray(`-----`); diff --git a/test/utils/beacon/geolocation-test.ts b/test/utils/beacon/geolocation-test.ts index 0f9aba483f..c3407a01f5 100644 --- a/test/utils/beacon/geolocation-test.ts +++ b/test/utils/beacon/geolocation-test.ts @@ -62,8 +62,6 @@ describe("geolocation utilities", () => { const pos: GenericPosition = { latitude: 43.2, longitude: 12.4, - altitude: null, - accuracy: null, timestamp: 12334, }; diff --git a/test/utils/export-test.tsx b/test/utils/export-test.tsx index 95d4799d31..a75af856db 100644 --- a/test/utils/export-test.tsx +++ b/test/utils/export-test.tsx @@ -45,6 +45,8 @@ interface ITestContent extends IContent { } describe("export", function () { + const setProgressText = jest.fn(); + let mockExportOptions: IExportOptions; let mockRoom: Room; let ts0: number; @@ -63,7 +65,7 @@ describe("export", function () { }; function createRoom() { - const room = new Room(generateRoomId(), null, client.getUserId()); + const room = new Room(generateRoomId(), client, client.getUserId()!); return room; } mockRoom = createRoom(); @@ -146,7 +148,7 @@ describe("export", function () { } function mkEvents() { - const matrixEvents = []; + const matrixEvents: MatrixEvent[] = []; let i: number; // plain text for (i = 0; i < 10; i++) { @@ -237,7 +239,7 @@ describe("export", function () { }); it("checks if the icons' html corresponds to export regex", function () { - const exporter = new HTMLExporter(mockRoom, ExportType.Beginning, mockExportOptions, null); + const exporter = new HTMLExporter(mockRoom, ExportType.Beginning, mockExportOptions, setProgressText); const fileRegex = /.*?<\/span>/; expect(fileRegex.test(renderToString(exporter.getEventTile(mkFileEvent(), true)))).toBeTruthy(); }); @@ -251,7 +253,7 @@ describe("export", function () { maxSize: 100 * 1024 * 1024, attachmentsIncluded: true, }, - null, + setProgressText, ); const imageRegex = //; expect(imageRegex.test(renderToString(exporter.getEventTile(mkImageEvent(), true)))).toBeTruthy(); @@ -284,13 +286,13 @@ describe("export", function () { ], ]; it.each(invalidExportOptions)("%s", (_d, options) => { - expect(() => new PlainTextExporter(mockRoom, ExportType.Beginning, options, null)).toThrowError( + expect(() => new PlainTextExporter(mockRoom, ExportType.Beginning, options, setProgressText)).toThrowError( "Invalid export options", ); }); it("tests the file extension splitter", function () { - const exporter = new PlainTextExporter(mockRoom, ExportType.Beginning, mockExportOptions, null); + const exporter = new PlainTextExporter(mockRoom, ExportType.Beginning, mockExportOptions, setProgressText); const fileNameWithExtensions: Record = { "": ["", ""], "name": ["name", ""], @@ -327,14 +329,14 @@ describe("export", function () { expectedText: '<@me:here "This"> Reply', }, ]; - const exporter = new PlainTextExporter(mockRoom, ExportType.Beginning, mockExportOptions, null); + const exporter = new PlainTextExporter(mockRoom, ExportType.Beginning, mockExportOptions, setProgressText); for (const content of eventContents) { expect(exporter.textForReplyEvent(content)).toBe(content.expectedText); } }); it("checks if the render to string doesn't throw any error for different types of events", function () { - const exporter = new HTMLExporter(mockRoom, ExportType.Beginning, mockExportOptions, null); + const exporter = new HTMLExporter(mockRoom, ExportType.Beginning, mockExportOptions, setProgressText); for (const event of events) { expect(renderToString(exporter.getEventTile(event, false))).toBeTruthy(); } diff --git a/test/voice-broadcast/audio/VoiceBroadcastRecorder-test.ts b/test/voice-broadcast/audio/VoiceBroadcastRecorder-test.ts index b2a0e7da50..7d47918b1f 100644 --- a/test/voice-broadcast/audio/VoiceBroadcastRecorder-test.ts +++ b/test/voice-broadcast/audio/VoiceBroadcastRecorder-test.ts @@ -83,15 +83,15 @@ describe("VoiceBroadcastRecorder", () => { const simulateFirstChunk = (): void => { // send headers in wrong order and multiple times to test robustness for that - voiceRecording.onDataAvailable(headers2); - voiceRecording.onDataAvailable(headers1); - voiceRecording.onDataAvailable(headers1); - voiceRecording.onDataAvailable(headers2); + voiceRecording.onDataAvailable!(headers2); + voiceRecording.onDataAvailable!(headers1); + voiceRecording.onDataAvailable!(headers1); + voiceRecording.onDataAvailable!(headers2); // set recorder seconds to something greater than the test chunk length of 30 // @ts-ignore voiceRecording.recorderSeconds = 42; - voiceRecording.onDataAvailable(chunk1); - voiceRecording.onDataAvailable(headers1); + voiceRecording.onDataAvailable!(chunk1); + voiceRecording.onDataAvailable!(headers1); }; const expectOnFirstChunkRecorded = (): void => { @@ -164,7 +164,7 @@ describe("VoiceBroadcastRecorder", () => { describe("when the first header from recorder has been received", () => { beforeEach(() => { - voiceRecording.onDataAvailable(headers1); + voiceRecording.onDataAvailable!(headers1); }); itShouldNotEmitAChunkRecordedEvent(); @@ -172,8 +172,8 @@ describe("VoiceBroadcastRecorder", () => { describe("when the second header from recorder has been received", () => { beforeEach(() => { - voiceRecording.onDataAvailable(headers1); - voiceRecording.onDataAvailable(headers2); + voiceRecording.onDataAvailable!(headers1); + voiceRecording.onDataAvailable!(headers2); }); itShouldNotEmitAChunkRecordedEvent(); @@ -181,9 +181,9 @@ describe("VoiceBroadcastRecorder", () => { describe("when a third page from recorder has been received", () => { beforeEach(() => { - voiceRecording.onDataAvailable(headers1); - voiceRecording.onDataAvailable(headers2); - voiceRecording.onDataAvailable(chunk1); + voiceRecording.onDataAvailable!(headers1); + voiceRecording.onDataAvailable!(headers2); + voiceRecording.onDataAvailable!(chunk1); }); itShouldNotEmitAChunkRecordedEvent(); @@ -235,15 +235,15 @@ describe("VoiceBroadcastRecorder", () => { simulateFirstChunk(); // simulate a second chunk - voiceRecording.onDataAvailable(chunk2a); + voiceRecording.onDataAvailable!(chunk2a); // send headers again to test robustness for that - voiceRecording.onDataAvailable(headers2); + voiceRecording.onDataAvailable!(headers2); // add another 30 seconds for the next chunk // @ts-ignore voiceRecording.recorderSeconds = 72; - voiceRecording.onDataAvailable(chunk2b); + voiceRecording.onDataAvailable!(chunk2b); }); it("should emit ChunkRecorded events", () => {