diff --git a/src/Avatar.ts b/src/Avatar.ts index 8a3f10a22c..986ae6306a 100644 --- a/src/Avatar.ts +++ b/src/Avatar.ts @@ -31,7 +31,7 @@ export function avatarUrlForMember( height: number, resizeMethod: ResizeMethod, ): string { - let url: string; + let url: string | null | undefined; if (member?.getMxcAvatarUrl()) { url = mediaFromMxc(member.getMxcAvatarUrl()).getThumbnailOfSourceHttp(width, height, resizeMethod); } @@ -118,7 +118,7 @@ export function defaultAvatarUrlForString(s: string): string { * @param {string} name * @return {string} the first letter */ -export function getInitialLetter(name: string): string { +export function getInitialLetter(name: string): string | undefined { if (!name) { // XXX: We should find out what causes the name to sometimes be falsy. console.trace("`name` argument to `getInitialLetter` not supplied"); @@ -146,7 +146,7 @@ export function avatarUrlForRoom( if (!room) return null; // null-guard if (room.getMxcAvatarUrl()) { - return mediaFromMxc(room.getMxcAvatarUrl()).getThumbnailOfSourceHttp(width, height, resizeMethod); + return mediaFromMxc(room.getMxcAvatarUrl() || undefined).getThumbnailOfSourceHttp(width, height, resizeMethod); } // space rooms cannot be DMs so skip the rest diff --git a/src/BasePlatform.ts b/src/BasePlatform.ts index ab8bca4b5f..46f964995a 100644 --- a/src/BasePlatform.ts +++ b/src/BasePlatform.ts @@ -130,7 +130,7 @@ export default abstract class BasePlatform { if (MatrixClientPeg.userRegisteredWithinLastHours(24)) return false; try { - const [version, deferUntil] = JSON.parse(localStorage.getItem(UPDATE_DEFER_KEY)); + const [version, deferUntil] = JSON.parse(localStorage.getItem(UPDATE_DEFER_KEY)!); return newVersion !== version || Date.now() > deferUntil; } catch (e) { return true; @@ -211,7 +211,7 @@ export default abstract class BasePlatform { metricsTrigger: "Notification", }; - if (ev.getThread()) { + if (ev?.getThread()) { payload.event_id = ev.getId(); } @@ -255,7 +255,7 @@ export default abstract class BasePlatform { return false; } - public getSettingValue(settingName: string): Promise { + public async getSettingValue(settingName: string): Promise { return undefined; } @@ -278,7 +278,7 @@ export default abstract class BasePlatform { public setSpellCheckEnabled(enabled: boolean): void {} public async getSpellCheckEnabled(): Promise { - return null; + return false; } public setSpellCheckLanguages(preferredLangs: string[]): void {} @@ -333,7 +333,7 @@ export default abstract class BasePlatform { // persist hs url and is url for when the user is returned to the app with the login token localStorage.setItem(SSO_HOMESERVER_URL_KEY, mxClient.getHomeserverUrl()); if (mxClient.getIdentityServerUrl()) { - localStorage.setItem(SSO_ID_SERVER_URL_KEY, mxClient.getIdentityServerUrl()); + localStorage.setItem(SSO_ID_SERVER_URL_KEY, mxClient.getIdentityServerUrl()!); } if (idpId) { localStorage.setItem(SSO_IDP_ID_KEY, idpId); diff --git a/src/ContentMessages.ts b/src/ContentMessages.ts index 1381e9431e..85ca90067d 100644 --- a/src/ContentMessages.ts +++ b/src/ContentMessages.ts @@ -372,7 +372,7 @@ export default class ContentMessages { const replyToEvent = SdkContextClass.instance.roomViewStore.getQuotingEvent(); if (!this.mediaConfig) { // hot-path optimization to not flash a spinner if we don't need to - const modal = Modal.createDialog(Spinner, null, "mx_Dialog_spinner"); + const modal = Modal.createDialog(Spinner, undefined, "mx_Dialog_spinner"); await this.ensureMediaConfigFetched(matrixClient); modal.close(); } diff --git a/src/DecryptionFailureTracker.ts b/src/DecryptionFailureTracker.ts index c275a176eb..7329c665bc 100644 --- a/src/DecryptionFailureTracker.ts +++ b/src/DecryptionFailureTracker.ts @@ -83,8 +83,8 @@ export class DecryptionFailureTracker { public trackedEvents: Set = new Set(); // Set to an interval ID when `start` is called - public checkInterval: number = null; - public trackInterval: number = null; + public checkInterval: number | null = null; + public trackInterval: number | null = null; // Spread the load on `Analytics` by tracking at a low frequency, `TRACK_INTERVAL_MS`. public static TRACK_INTERVAL_MS = 60000; diff --git a/src/DeviceListener.ts b/src/DeviceListener.ts index be48717415..eccfc1c0e3 100644 --- a/src/DeviceListener.ts +++ b/src/DeviceListener.ts @@ -58,12 +58,12 @@ export default class DeviceListener { private dismissedThisDeviceToast = false; // cache of the key backup info private keyBackupInfo: IKeyBackupInfo | null = null; - private keyBackupFetchedAt: number = null; + private keyBackupFetchedAt: number | null = null; private keyBackupStatusChecked = false; // We keep a list of our own device IDs so we can batch ones that were already // there the last time the app launched into a single toast, but display new // ones in their own toasts. - private ourDeviceIdsAtStart: Set = null; + private ourDeviceIdsAtStart: Set | null = null; // The set of device IDs we're currently displaying toasts for private displayingToastsForDeviceIds = new Set(); private running = false; @@ -203,7 +203,7 @@ export default class DeviceListener { } }; - private onSync = (state: SyncState, prevState?: SyncState): void => { + private onSync = (state: SyncState, prevState: SyncState | null): void => { if (state === "PREPARED" && prevState === null) { this.recheck(); } diff --git a/src/ImageUtils.ts b/src/ImageUtils.ts index 42db71ebab..e8564fb017 100644 --- a/src/ImageUtils.ts +++ b/src/ImageUtils.ts @@ -28,7 +28,12 @@ limitations under the License. * consume in the timeline, when performing scroll offset calculations * (e.g. scroll locking) */ -export function thumbHeight(fullWidth: number, fullHeight: number, thumbWidth: number, thumbHeight: number): number { +export function thumbHeight( + fullWidth: number, + fullHeight: number, + thumbWidth: number, + thumbHeight: number, +): number | null { if (!fullWidth || !fullHeight) { // Cannot calculate thumbnail height for image: missing w/h in metadata. We can't even // log this because it's spammy diff --git a/src/accessibility/KeyboardShortcutUtils.ts b/src/accessibility/KeyboardShortcutUtils.ts index 394daf7ef9..bb42f7c1ce 100644 --- a/src/accessibility/KeyboardShortcutUtils.ts +++ b/src/accessibility/KeyboardShortcutUtils.ts @@ -72,7 +72,7 @@ const getUIOnlyShortcuts = (): IKeyboardShortcuts => { }, }; - if (PlatformPeg.get().overrideBrowserShortcuts()) { + if (PlatformPeg.get()?.overrideBrowserShortcuts()) { // XXX: This keyboard shortcut isn't manually added to // KeyBindingDefaults as it can't be easily handled by the // KeyBindingManager @@ -92,7 +92,7 @@ const getUIOnlyShortcuts = (): IKeyboardShortcuts => { * This function gets keyboard shortcuts that can be consumed by the KeyBindingDefaults. */ export const getKeyboardShortcuts = (): IKeyboardShortcuts => { - const overrideBrowserShortcuts = PlatformPeg.get().overrideBrowserShortcuts(); + const overrideBrowserShortcuts = PlatformPeg.get()?.overrideBrowserShortcuts(); return Object.keys(KEYBOARD_SHORTCUTS) .filter((k: KeyBindingAction) => { @@ -120,11 +120,11 @@ export const getKeyboardShortcutsForUI = (): IKeyboardShortcuts => { }, {} as IKeyboardShortcuts); }; -export const getKeyboardShortcutValue = (name: string): KeyCombo => { +export const getKeyboardShortcutValue = (name: string): KeyCombo | undefined => { return getKeyboardShortcutsForUI()[name]?.default; }; -export const getKeyboardShortcutDisplayName = (name: string): string | null => { +export const getKeyboardShortcutDisplayName = (name: string): string | undefined => { const keyboardShortcutDisplayName = getKeyboardShortcutsForUI()[name]?.displayName; return keyboardShortcutDisplayName && _t(keyboardShortcutDisplayName); }; diff --git a/src/accessibility/RovingTabIndex.tsx b/src/accessibility/RovingTabIndex.tsx index e90aed87a9..605ffb1f5b 100644 --- a/src/accessibility/RovingTabIndex.tsx +++ b/src/accessibility/RovingTabIndex.tsx @@ -56,7 +56,7 @@ export function checkInputableElement(el: HTMLElement): boolean { } export interface IState { - activeRef: Ref; + activeRef?: Ref; refs: Ref[]; } @@ -67,7 +67,6 @@ interface IContext { export const RovingTabIndexContext = createContext({ state: { - activeRef: null, refs: [], // list of refs in DOM order }, dispatch: () => {}, @@ -102,7 +101,7 @@ export const reducer: Reducer = (state: IState, action: IAction return 0; } - const position = a.current.compareDocumentPosition(b.current); + const position = a.current!.compareDocumentPosition(b.current!); if (position & Node.DOCUMENT_POSITION_FOLLOWING || position & Node.DOCUMENT_POSITION_CONTAINED_BY) { return -1; @@ -167,7 +166,7 @@ export const findSiblingElement = ( refs: RefObject[], startIndex: number, backwards = false, -): RefObject => { +): RefObject | undefined => { if (backwards) { for (let i = startIndex; i < refs.length && i >= 0; i--) { if (refs[i].current?.offsetParent !== null) { @@ -191,7 +190,6 @@ export const RovingTabIndexProvider: React.FC = ({ onKeyDown, }) => { const [state, dispatch] = useReducer>(reducer, { - activeRef: null, refs: [], }); @@ -208,7 +206,7 @@ export const RovingTabIndexProvider: React.FC = ({ let handled = false; const action = getKeyBindingsManager().getAccessibilityAction(ev); - let focusRef: RefObject; + let focusRef: RefObject | undefined; // Don't interfere with input default keydown behaviour // but allow people to move focus from it with Tab. if (checkInputableElement(ev.target as HTMLElement)) { @@ -216,7 +214,7 @@ export const RovingTabIndexProvider: React.FC = ({ case KeyBindingAction.Tab: handled = true; if (context.state.refs.length > 0) { - const idx = context.state.refs.indexOf(context.state.activeRef); + const idx = context.state.refs.indexOf(context.state.activeRef!); focusRef = findSiblingElement( context.state.refs, idx + (ev.shiftKey ? -1 : 1), @@ -252,7 +250,7 @@ export const RovingTabIndexProvider: React.FC = ({ ) { handled = true; if (context.state.refs.length > 0) { - const idx = context.state.refs.indexOf(context.state.activeRef); + const idx = context.state.refs.indexOf(context.state.activeRef!); focusRef = findSiblingElement(context.state.refs, idx + 1); } } @@ -266,7 +264,7 @@ export const RovingTabIndexProvider: React.FC = ({ ) { handled = true; if (context.state.refs.length > 0) { - const idx = context.state.refs.indexOf(context.state.activeRef); + const idx = context.state.refs.indexOf(context.state.activeRef!); focusRef = findSiblingElement(context.state.refs, idx - 1, true); } } diff --git a/src/actions/MatrixActionCreators.ts b/src/actions/MatrixActionCreators.ts index 88de8b3d17..bb40a463ba 100644 --- a/src/actions/MatrixActionCreators.ts +++ b/src/actions/MatrixActionCreators.ts @@ -221,7 +221,7 @@ function createRoomTimelineAction( action: "MatrixActions.Room.timeline", event: timelineEvent, isLiveEvent: data.liveEvent, - isLiveUnfilteredRoomTimelineEvent: room && data.timeline.getTimelineSet() === room.getUnfilteredTimelineSet(), + isLiveUnfilteredRoomTimelineEvent: data.timeline.getTimelineSet() === room?.getUnfilteredTimelineSet(), room, }; } diff --git a/src/audio/PlaybackQueue.ts b/src/audio/PlaybackQueue.ts index 7c521b9ca6..0828a3df1d 100644 --- a/src/audio/PlaybackQueue.ts +++ b/src/audio/PlaybackQueue.ts @@ -45,7 +45,7 @@ export class PlaybackQueue { private playbacks = new Map(); // keyed by event ID private clockStates = new Map(); // keyed by event ID private playbackIdOrder: string[] = []; // event IDs, last == current - private currentPlaybackId: string; // event ID, broken out from above for ease of use + private currentPlaybackId: string | null = null; // event ID, broken out from above for ease of use private recentFullPlays = new Set(); // event IDs public constructor(private room: Room) { @@ -68,7 +68,7 @@ export class PlaybackQueue { const room = cli.getRoom(roomId); if (!room) throw new Error("Unknown room"); if (PlaybackQueue.queues.has(room.roomId)) { - return PlaybackQueue.queues.get(room.roomId); + return PlaybackQueue.queues.get(room.roomId)!; } const queue = new PlaybackQueue(room); PlaybackQueue.queues.set(room.roomId, queue); @@ -101,7 +101,7 @@ export class PlaybackQueue { const wasLastPlaying = this.currentPlaybackId === mxEvent.getId(); if (newState === PlaybackState.Stopped && this.clockStates.has(mxEvent.getId()) && !wasLastPlaying) { // noinspection JSIgnoredPromiseFromCall - playback.skipTo(this.clockStates.get(mxEvent.getId())); + playback.skipTo(this.clockStates.get(mxEvent.getId())!); } else if (newState === PlaybackState.Stopped) { // Remove the now-useless clock for some space savings this.clockStates.delete(mxEvent.getId()); diff --git a/src/autocomplete/AutocompleteProvider.tsx b/src/autocomplete/AutocompleteProvider.tsx index 546e052f58..32c2d80c6f 100644 --- a/src/autocomplete/AutocompleteProvider.tsx +++ b/src/autocomplete/AutocompleteProvider.tsx @@ -70,7 +70,7 @@ export default abstract class AutocompleteProvider { * @param {boolean} force True if the user is forcing completion * @return {object} { command, range } where both objects fields are null if no match */ - public getCurrentCommand(query: string, selection: ISelectionRange, force = false): ICommand { + public getCurrentCommand(query: string, selection: ISelectionRange, force = false): ICommand | null { let commandRegex = this.commandRegex; if (force && this.shouldForceComplete()) { @@ -83,7 +83,7 @@ export default abstract class AutocompleteProvider { commandRegex.lastIndex = 0; - let match: RegExpExecArray; + let match: RegExpExecArray | null; while ((match = commandRegex.exec(query)) !== null) { const start = match.index; const end = start + match[0].length; diff --git a/src/autocomplete/Autocompleter.ts b/src/autocomplete/Autocompleter.ts index b609f265f1..c769faf155 100644 --- a/src/autocomplete/Autocompleter.ts +++ b/src/autocomplete/Autocompleter.ts @@ -87,7 +87,7 @@ export default class Autocompleter { to predict whether an action will actually do what is intended */ // list of results from each provider, each being a list of completions or null if it times out - const completionsList: ICompletion[][] = await Promise.all( + const completionsList: Array = await Promise.all( this.providers.map(async (provider): Promise => { return timeout( provider.getCompletions(query, selection, force, limit), @@ -113,6 +113,6 @@ export default class Autocompleter { command: this.providers[i].getCurrentCommand(query, selection, force), }; }) - .filter(Boolean); + .filter(Boolean) as IProviderCompletions[]; } } diff --git a/src/autocomplete/CommandProvider.tsx b/src/autocomplete/CommandProvider.tsx index caafe98f08..113c928790 100644 --- a/src/autocomplete/CommandProvider.tsx +++ b/src/autocomplete/CommandProvider.tsx @@ -56,10 +56,10 @@ export default class CommandProvider extends AutocompleteProvider { if (command[0] !== command[1]) { // The input looks like a command with arguments, perform exact match const name = command[1].slice(1); // strip leading `/` - if (CommandMap.has(name) && CommandMap.get(name).isEnabled()) { + if (CommandMap.has(name) && CommandMap.get(name)!.isEnabled()) { // some commands, namely `me` don't suit having the usage shown whilst typing their arguments - if (CommandMap.get(name).hideCompletionAfterSpace) return []; - matches = [CommandMap.get(name)]; + if (CommandMap.get(name)!.hideCompletionAfterSpace) return []; + matches = [CommandMap.get(name)!]; } } else { if (query === "/") { diff --git a/src/autocomplete/RoomProvider.tsx b/src/autocomplete/RoomProvider.tsx index bf102d55bc..9cde501365 100644 --- a/src/autocomplete/RoomProvider.tsx +++ b/src/autocomplete/RoomProvider.tsx @@ -39,12 +39,12 @@ function canonicalScore(displayedAlias: string, room: Room): number { function matcherObject( room: Room, - displayedAlias: string, + displayedAlias: string | null, matchName = "", ): { room: Room; matchName: string; - displayedAlias: string; + displayedAlias: string | null; } { return { room, @@ -58,7 +58,7 @@ export default class RoomProvider extends AutocompleteProvider { public constructor(room: Room, renderingType?: TimelineRenderingType) { super({ commandRegex: ROOM_REGEX, renderingType }); - this.matcher = new QueryMatcher([], { + this.matcher = new QueryMatcher>([], { keys: ["displayedAlias", "matchName"], }); } @@ -79,7 +79,7 @@ export default class RoomProvider extends AutocompleteProvider { const { command, range } = this.getCurrentCommand(query, selection, force); if (command) { // the only reason we need to do this is because Fuse only matches on properties - let matcherObjects = this.getRooms().reduce((aliases, room) => { + let matcherObjects = this.getRooms().reduce[]>((aliases, room) => { if (room.getCanonicalAlias()) { aliases = aliases.concat(matcherObject(room, room.getCanonicalAlias(), room.name)); } diff --git a/src/components/structures/MatrixChat.tsx b/src/components/structures/MatrixChat.tsx index 44424619a7..cc07f28e4a 100644 --- a/src/components/structures/MatrixChat.tsx +++ b/src/components/structures/MatrixChat.tsx @@ -648,7 +648,7 @@ export default class MatrixChat extends React.PureComponent { onFinished: (confirm) => { if (confirm) { // FIXME: controller shouldn't be loading a view :( - const modal = Modal.createDialog(Spinner, null, "mx_Dialog_spinner"); + const modal = Modal.createDialog(Spinner, undefined, "mx_Dialog_spinner"); MatrixClientPeg.get() .leave(payload.room_id) diff --git a/src/components/views/messages/MKeyVerificationConclusion.tsx b/src/components/views/messages/MKeyVerificationConclusion.tsx index 656745997c..24b925544e 100644 --- a/src/components/views/messages/MKeyVerificationConclusion.tsx +++ b/src/components/views/messages/MKeyVerificationConclusion.tsx @@ -72,7 +72,7 @@ export default class MKeyVerificationConclusion extends React.Component this.forceUpdate(); }; - public static shouldRender(mxEvent: MatrixEvent, request: VerificationRequest): boolean { + public static shouldRender(mxEvent: MatrixEvent, request?: VerificationRequest): boolean { // normally should not happen if (!request) { return false; @@ -99,9 +99,9 @@ export default class MKeyVerificationConclusion extends React.Component return true; } - public render(): JSX.Element { + public render(): JSX.Element | null { const { mxEvent } = this.props; - const request = mxEvent.verificationRequest; + const request = mxEvent.verificationRequest!; if (!MKeyVerificationConclusion.shouldRender(mxEvent, request)) { return null; @@ -110,7 +110,7 @@ export default class MKeyVerificationConclusion extends React.Component const client = MatrixClientPeg.get(); const myUserId = client.getUserId(); - let title; + let title: string | undefined; if (request.done) { title = _t("You verified %(name)s", { diff --git a/src/createRoom.ts b/src/createRoom.ts index 2bd49a4bb6..f4d9769d90 100644 --- a/src/createRoom.ts +++ b/src/createRoom.ts @@ -268,7 +268,7 @@ export default async function createRoom(opts: IOpts): Promise { } let modal; - if (opts.spinner) modal = Modal.createDialog(Spinner, null, "mx_Dialog_spinner"); + if (opts.spinner) modal = Modal.createDialog(Spinner, undefined, "mx_Dialog_spinner"); let roomId: string; let room: Promise; @@ -417,9 +417,9 @@ export async function ensureVirtualRoomExists( client: MatrixClient, userId: string, nativeRoomId: string, -): Promise { +): Promise { const existingDMRoom = findDMForUser(client, userId); - let roomId; + let roomId: string | null; if (existingDMRoom) { roomId = existingDMRoom.roomId; } else { @@ -440,13 +440,13 @@ export async function ensureVirtualRoomExists( return roomId; } -export async function ensureDMExists(client: MatrixClient, userId: string): Promise { +export async function ensureDMExists(client: MatrixClient, userId: string): Promise { const existingDMRoom = findDMForUser(client, userId); - let roomId; + let roomId: string | null; if (existingDMRoom) { roomId = existingDMRoom.roomId; } else { - let encryption: boolean = undefined; + let encryption: boolean | undefined; if (privateShouldBeEncrypted()) { encryption = await canEncryptToAllUsers(client, [userId]); } diff --git a/src/customisations/Media.ts b/src/customisations/Media.ts index 7c9ca139a2..c3debef86f 100644 --- a/src/customisations/Media.ts +++ b/src/customisations/Media.ts @@ -75,7 +75,7 @@ export class Media { /** * The HTTP URL for the source media. */ - public get srcHttp(): string { + public get srcHttp(): string | null { // eslint-disable-next-line no-restricted-properties return this.client.mxcUrlToHttp(this.srcMxc); } @@ -87,7 +87,7 @@ export class Media { public get thumbnailHttp(): string | undefined | null { if (!this.hasThumbnail) return null; // eslint-disable-next-line no-restricted-properties - return this.client.mxcUrlToHttp(this.thumbnailMxc); + return this.client.mxcUrlToHttp(this.thumbnailMxc!); } /** @@ -98,13 +98,13 @@ export class Media { * @param {"scale"|"crop"} mode The desired thumbnailing mode. Defaults to scale. * @returns {string} The HTTP URL which points to the thumbnail. */ - public getThumbnailHttp(width: number, height: number, mode: ResizeMethod = "scale"): string | null | undefined { + public getThumbnailHttp(width: number, height: number, mode: ResizeMethod = "scale"): string | null { if (!this.hasThumbnail) return null; // scale using the device pixel ratio to keep images clear width = Math.floor(width * window.devicePixelRatio); height = Math.floor(height * window.devicePixelRatio); // eslint-disable-next-line no-restricted-properties - return this.client.mxcUrlToHttp(this.thumbnailMxc, width, height, mode); + return this.client.mxcUrlToHttp(this.thumbnailMxc!, width, height, mode); } /** @@ -114,7 +114,7 @@ export class Media { * @param {"scale"|"crop"} mode The desired thumbnailing mode. Defaults to scale. * @returns {string} The HTTP URL which points to the thumbnail. */ - public getThumbnailOfSourceHttp(width: number, height: number, mode: ResizeMethod = "scale"): string { + public getThumbnailOfSourceHttp(width: number, height: number, mode: ResizeMethod = "scale"): string | null { // scale using the device pixel ratio to keep images clear width = Math.floor(width * window.devicePixelRatio); height = Math.floor(height * window.devicePixelRatio); @@ -128,7 +128,7 @@ export class Media { * @param {number} dim The desired width and height. * @returns {string} An HTTP URL for the thumbnail. */ - public getSquareThumbnailHttp(dim: number): string { + public getSquareThumbnailHttp(dim: number): string | null { dim = Math.floor(dim * window.devicePixelRatio); // scale using the device pixel ratio to keep images clear if (this.hasThumbnail) { return this.getThumbnailHttp(dim, dim, "crop"); @@ -161,6 +161,6 @@ export function mediaFromContent(content: Partial, client?: * @param {MatrixClient} client? Optional client to use. * @returns {Media} The media object. */ -export function mediaFromMxc(mxc: string, client?: MatrixClient): Media { +export function mediaFromMxc(mxc?: string, client?: MatrixClient): Media { return mediaFromContent({ url: mxc }, client); } diff --git a/src/customisations/Security.ts b/src/customisations/Security.ts index bff7fc19f6..bfc2caaddd 100644 --- a/src/customisations/Security.ts +++ b/src/customisations/Security.ts @@ -30,19 +30,19 @@ function persistCredentials(credentials: IMatrixClientCreds): void { } /* eslint-disable-next-line @typescript-eslint/no-unused-vars */ -function createSecretStorageKey(): Uint8Array { +function createSecretStorageKey(): Uint8Array | null { // E.g. generate or retrieve secret storage key somehow return null; } /* eslint-disable-next-line @typescript-eslint/no-unused-vars */ -function getSecretStorageKey(): Uint8Array { +function getSecretStorageKey(): Uint8Array | null { // E.g. retrieve secret storage key from some other place return null; } /* eslint-disable-next-line @typescript-eslint/no-unused-vars */ -function getDehydrationKey(keyInfo: ISecretStorageKeyInfo): Promise { +function getDehydrationKey(keyInfo: ISecretStorageKeyInfo): Promise { return Promise.resolve(null); } diff --git a/src/customisations/models/IMediaEventContent.ts b/src/customisations/models/IMediaEventContent.ts index 299432eaaa..bd17ba2204 100644 --- a/src/customisations/models/IMediaEventContent.ts +++ b/src/customisations/models/IMediaEventContent.ts @@ -72,7 +72,7 @@ export interface IMediaObject { * @throws Throws if the given content cannot be packaged into a prepared media object. */ export function prepEventContentAsMedia(content: Partial): IPreparedMedia { - let thumbnail: IMediaObject = null; + let thumbnail: IMediaObject | undefined; if (content?.info?.thumbnail_url) { thumbnail = { mxc: content.info.thumbnail_url, diff --git a/src/editor/caret.ts b/src/editor/caret.ts index ea6777c057..61c6a85640 100644 --- a/src/editor/caret.ts +++ b/src/editor/caret.ts @@ -32,7 +32,7 @@ export function setSelection(editor: HTMLDivElement, model: EditorModel, selecti } function setDocumentRangeSelection(editor: HTMLDivElement, model: EditorModel, range: Range): void { - const sel = document.getSelection(); + const sel = document.getSelection()!; sel.removeAllRanges(); const selectionRange = document.createRange(); const start = getNodeAndOffsetForPosition(editor, model, range.start); @@ -50,7 +50,7 @@ export function setCaretPosition(editor: HTMLDivElement, model: EditorModel, car range.setStart(node, offset); range.collapse(true); - const sel = document.getSelection(); + const sel = document.getSelection()!; if (sel.rangeCount === 1) { const existingRange = sel.getRangeAt(0); if ( @@ -124,7 +124,7 @@ function findNodeInLineForPart(parts: Part[], partIndex: number): { lineIndex: n let lineIndex = 0; let nodeIndex = -1; - let prevPart = null; + let prevPart: Part | undefined; // go through to parts up till (and including) the index // to find newline parts for (let i = 0; i <= partIndex; ++i) { @@ -132,7 +132,7 @@ function findNodeInLineForPart(parts: Part[], partIndex: number): { lineIndex: n if (part.type === Type.Newline) { lineIndex += 1; nodeIndex = -1; - prevPart = null; + prevPart = undefined; } else { nodeIndex += 1; if (needsCaretNodeBefore(part, prevPart)) { diff --git a/src/editor/deserialize.ts b/src/editor/deserialize.ts index f8ecd19c16..d4d0de6ed8 100644 --- a/src/editor/deserialize.ts +++ b/src/editor/deserialize.ts @@ -51,7 +51,7 @@ export function longestBacktickSequence(text: string): number { } function isListChild(n: Node): boolean { - return LIST_TYPES.includes(n.parentNode?.nodeName); + return LIST_TYPES.includes(n.parentNode?.nodeName || ""); } function parseAtRoomMentions(text: string, pc: PartCreator, opts: IParseOptions): Part[] { diff --git a/src/editor/history.ts b/src/editor/history.ts index dd9bd19b26..2f0260025d 100644 --- a/src/editor/history.ts +++ b/src/editor/history.ts @@ -31,7 +31,7 @@ export default class HistoryManager { private newlyTypedCharCount = 0; private currentIndex = -1; private changedSinceLastPush = false; - private lastCaret: Caret = null; + private lastCaret: Caret | null = null; private nonWordBoundarySinceLastPush = false; private addedSinceLastPush = false; private removedSinceLastPush = false; @@ -65,7 +65,7 @@ export default class HistoryManager { // as long as you've only been adding or removing since the last push if (this.addedSinceLastPush !== this.removedSinceLastPush) { // add steps by word boundary, up to MAX_STEP_LENGTH characters - const str = diff.added ? diff.added : diff.removed; + const str = diff.added ? diff.added : diff.removed!; const isWordBoundary = str === " " || str === "\t" || str === "\n"; if (this.nonWordBoundarySinceLastPush && isWordBoundary) { return true; diff --git a/src/editor/model.ts b/src/editor/model.ts index 4ec76bf008..f4c59e3665 100644 --- a/src/editor/model.ts +++ b/src/editor/model.ts @@ -51,13 +51,13 @@ type ManualTransformCallback = () => Caret; export default class EditorModel { private _parts: Part[]; private readonly _partCreator: PartCreator; - private activePartIdx: number = null; - private _autoComplete: AutocompleteWrapperModel = null; - private autoCompletePartIdx: number = null; + private activePartIdx: number | null = null; + private _autoComplete: AutocompleteWrapperModel | null = null; + private autoCompletePartIdx: number | null = null; private autoCompletePartCount = 0; - private transformCallback: TransformCallback = null; + private transformCallback: TransformCallback | null = null; - public constructor(parts: Part[], partCreator: PartCreator, private updateCallback: UpdateCallback = null) { + public constructor(parts: Part[], partCreator: PartCreator, private updateCallback: UpdateCallback | null = null) { this._parts = parts; this._partCreator = partCreator; this.transformCallback = null; diff --git a/src/editor/parts.ts b/src/editor/parts.ts index 2c8596b492..8202f765d3 100644 --- a/src/editor/parts.ts +++ b/src/editor/parts.ts @@ -455,7 +455,7 @@ class AtRoomPillPart extends RoomPillPart { } class UserPillPart extends PillPart { - public constructor(userId, displayName, private member: RoomMember) { + public constructor(userId, displayName, private member?: RoomMember) { super(userId, displayName); } @@ -625,7 +625,7 @@ export class PartCreator { public userPill(displayName: string, userId: string): UserPillPart { const member = this.room.getMember(userId); - return new UserPillPart(userId, displayName, member); + return new UserPillPart(userId, displayName, member || undefined); } private static isRegionalIndicator(c: string): boolean { diff --git a/src/editor/position.ts b/src/editor/position.ts index e2a08752b2..3f23016a8a 100644 --- a/src/editor/position.ts +++ b/src/editor/position.ts @@ -79,6 +79,8 @@ export default class DocumentPosition implements IPosition { offset = 0; } } + + return this; // impossible but Typescript doesn't believe us } public backwardsWhile(model: EditorModel, predicate: Predicate): DocumentPosition { @@ -104,6 +106,8 @@ export default class DocumentPosition implements IPosition { offset = parts[index].text.length; } } + + return this; // impossible but Typescript doesn't believe us } public asOffset(model: EditorModel): DocumentOffset { diff --git a/src/editor/render.ts b/src/editor/render.ts index 317218cd8a..bb7b088780 100644 --- a/src/editor/render.ts +++ b/src/editor/render.ts @@ -18,7 +18,7 @@ limitations under the License. import { Part, Type } from "./parts"; import EditorModel from "./model"; -export function needsCaretNodeBefore(part: Part, prevPart: Part): boolean { +export function needsCaretNodeBefore(part: Part, prevPart?: Part): boolean { const isFirst = !prevPart || prevPart.type === Type.Newline; return !part.acceptsCaret && (isFirst || !prevPart.acceptsCaret); } @@ -30,9 +30,9 @@ export function needsCaretNodeAfter(part: Part, isLastOfLine: boolean): boolean function insertAfter(node: HTMLElement, nodeToInsert: HTMLElement): void { const next = node.nextSibling; if (next) { - node.parentElement.insertBefore(nodeToInsert, next); + node.parentElement!.insertBefore(nodeToInsert, next); } else { - node.parentElement.appendChild(nodeToInsert); + node.parentElement!.appendChild(nodeToInsert); } } @@ -58,11 +58,11 @@ function updateCaretNode(node: HTMLElement): void { } } -export function isCaretNode(node: HTMLElement): boolean { - return node && node.tagName === "SPAN" && node.className === "caretNode"; +export function isCaretNode(node?: Node | null): node is HTMLElement { + return !!node && node instanceof HTMLElement && node.tagName === "SPAN" && node.className === "caretNode"; } -function removeNextSiblings(node: ChildNode): void { +function removeNextSiblings(node: ChildNode | null): void { if (!node) { return; } @@ -83,13 +83,13 @@ function removeChildren(parent: HTMLElement): void { } function reconcileLine(lineContainer: ChildNode, parts: Part[]): void { - let currentNode; - let prevPart; + let currentNode: ChildNode | null = null; + let prevPart: Part | undefined; const lastPart = parts[parts.length - 1]; for (const part of parts) { const isFirst = !prevPart; - currentNode = isFirst ? lineContainer.firstChild : currentNode.nextSibling; + currentNode = isFirst ? lineContainer.firstChild : currentNode!.nextSibling; if (needsCaretNodeBefore(part, prevPart)) { if (isCaretNode(currentNode)) { @@ -109,18 +109,18 @@ function reconcileLine(lineContainer: ChildNode, parts: Part[]): void { if (currentNode && part) { part.updateDOMNode(currentNode); } else if (part) { - currentNode = part.toDOMNode(); + currentNode = part.toDOMNode() as ChildNode; // hooks up nextSibling for next iteration lineContainer.appendChild(currentNode); } if (needsCaretNodeAfter(part, part === lastPart)) { - if (isCaretNode(currentNode.nextSibling)) { - currentNode = currentNode.nextSibling; - updateCaretNode(currentNode); + if (isCaretNode(currentNode?.nextSibling)) { + currentNode = currentNode!.nextSibling; + updateCaretNode(currentNode as HTMLElement); } else { const caretNode = createCaretNode(); - insertAfter(currentNode, caretNode); + insertAfter(currentNode as HTMLElement, caretNode); currentNode = caretNode; } } @@ -150,7 +150,7 @@ function reconcileEmptyLine(lineContainer: HTMLElement): void { } export function renderModel(editor: HTMLDivElement, model: EditorModel): void { - const lines = model.parts.reduce( + const lines = model.parts.reduce( (linesArr, part) => { if (part.type === Type.Newline) { linesArr.push([]); diff --git a/src/stores/OwnProfileStore.ts b/src/stores/OwnProfileStore.ts index 1aad5fac9a..1ad90b9ee4 100644 --- a/src/stores/OwnProfileStore.ts +++ b/src/stores/OwnProfileStore.ts @@ -50,8 +50,8 @@ export class OwnProfileStore extends AsyncStoreWithClient { // round-trip after the client is ready, and we often load widgets in that time, and we'd // and up passing them an incorrect display name super(defaultDispatcher, { - displayName: window.localStorage.getItem(KEY_DISPLAY_NAME), - avatarUrl: window.localStorage.getItem(KEY_AVATAR_URL), + displayName: window.localStorage.getItem(KEY_DISPLAY_NAME) || undefined, + avatarUrl: window.localStorage.getItem(KEY_AVATAR_URL) || undefined, }); } diff --git a/src/stores/ReadyWatchingStore.ts b/src/stores/ReadyWatchingStore.ts index a233a83ea3..24117bab37 100644 --- a/src/stores/ReadyWatchingStore.ts +++ b/src/stores/ReadyWatchingStore.ts @@ -25,7 +25,7 @@ import { IDestroyable } from "../utils/IDestroyable"; import { Action } from "../dispatcher/actions"; export abstract class ReadyWatchingStore extends EventEmitter implements IDestroyable { - protected matrixClient: MatrixClient; + protected matrixClient: MatrixClient | null = null; private dispatcherRef: string | null = null; public constructor(protected readonly dispatcher: Dispatcher) { @@ -42,7 +42,7 @@ export abstract class ReadyWatchingStore extends EventEmitter implements IDestro } } - public get mxClient(): MatrixClient { + public get mxClient(): MatrixClient | null { return this.matrixClient; // for external readonly access } diff --git a/src/stores/UIStore.ts b/src/stores/UIStore.ts index f7a6e76d0b..b1cdbae951 100644 --- a/src/stores/UIStore.ts +++ b/src/stores/UIStore.ts @@ -21,7 +21,7 @@ export enum UI_EVENTS { } export default class UIStore extends EventEmitter { - private static _instance: UIStore = null; + private static _instance: UIStore | null = null; private resizeObserver: ResizeObserver; @@ -58,7 +58,7 @@ export default class UIStore extends EventEmitter { } } - public getElementDimensions(name: string): DOMRectReadOnly { + public getElementDimensions(name: string): DOMRectReadOnly | undefined { return this.uiElementDimensions.get(name); } @@ -68,7 +68,7 @@ export default class UIStore extends EventEmitter { } public stopTrackingElementDimensions(name: string): void { - let trackedElement: Element; + let trackedElement: Element | undefined; this.trackedUiElements.forEach((trackedElementName, element) => { if (trackedElementName === name) { trackedElement = element; diff --git a/src/stores/spaces/flattenSpaceHierarchy.ts b/src/stores/spaces/flattenSpaceHierarchy.ts index 0fc6e4fd31..758681a60d 100644 --- a/src/stores/spaces/flattenSpaceHierarchy.ts +++ b/src/stores/spaces/flattenSpaceHierarchy.ts @@ -66,7 +66,7 @@ export const flattenSpaceHierarchyWithCache = useCache = true, ): Set => { if (useCache && cache.has(spaceId)) { - return cache.get(spaceId); + return cache.get(spaceId)!; } const result = flattenSpaceHierarchy(spaceEntityMap, spaceDescendantMap, spaceId); cache.set(spaceId, result); diff --git a/src/toasts/UpdateToast.tsx b/src/toasts/UpdateToast.tsx index c14c57ea79..2783e16943 100644 --- a/src/toasts/UpdateToast.tsx +++ b/src/toasts/UpdateToast.tsx @@ -37,12 +37,12 @@ function checkVersion(ver: string): boolean { } function installUpdate(): void { - PlatformPeg.get().installUpdate(); + PlatformPeg.get()?.installUpdate(); } export const showToast = (version: string, newVersion: string, releaseNotes?: string): void => { function onReject(): void { - PlatformPeg.get().deferUpdate(newVersion); + PlatformPeg.get()?.deferUpdate(newVersion); } let onAccept; @@ -55,7 +55,7 @@ export const showToast = (version: string, newVersion: string, releaseNotes?: st button: _t("Update"), onFinished: (update) => { if (update && PlatformPeg.get()) { - PlatformPeg.get().installUpdate(); + PlatformPeg.get()!.installUpdate(); } }, }); @@ -67,7 +67,7 @@ export const showToast = (version: string, newVersion: string, releaseNotes?: st newVersion, onFinished: (update) => { if (update && PlatformPeg.get()) { - PlatformPeg.get().installUpdate(); + PlatformPeg.get()!.installUpdate(); } }, }); diff --git a/src/utils/DialogOpener.ts b/src/utils/DialogOpener.ts index ac38eb5018..0121fb7c18 100644 --- a/src/utils/DialogOpener.ts +++ b/src/utils/DialogOpener.ts @@ -62,7 +62,7 @@ export class DialogOpener { roomId: payload.room_id || SdkContextClass.instance.roomViewStore.getRoomId(), initialTabId: payload.initial_tab_id, }, - /*className=*/ null, + /*className=*/ undefined, /*isPriority=*/ false, /*isStatic=*/ true, ); @@ -90,7 +90,7 @@ export class DialogOpener { initialTabId: payload.initalTabId, space: payload.space, }, - null, + undefined, false, true, ); @@ -102,7 +102,7 @@ export class DialogOpener { matrixClient: payload.space.client, space: payload.space, }, - /*className=*/ null, + /*className=*/ undefined, /*isPriority=*/ false, /*isStatic=*/ true, ); diff --git a/src/utils/RoomUpgrade.ts b/src/utils/RoomUpgrade.ts index d534c12e2f..669a8e8bfe 100644 --- a/src/utils/RoomUpgrade.ts +++ b/src/utils/RoomUpgrade.ts @@ -62,9 +62,9 @@ export async function upgradeRoom( progressCallback?: (progress: IProgress) => void, ): Promise { const cli = room.client; - let spinnerModal: IHandle; + let spinnerModal: IHandle | undefined; if (!progressCallback) { - spinnerModal = Modal.createDialog(Spinner, null, "mx_Dialog_spinner"); + spinnerModal = Modal.createDialog(Spinner, undefined, "mx_Dialog_spinner"); } let toInvite: string[] = []; @@ -78,7 +78,9 @@ export async function upgradeRoom( if (updateSpaces) { parentsToRelink = Array.from(SpaceStore.instance.getKnownParents(room.roomId)) .map((roomId) => cli.getRoom(roomId)) - .filter((parent) => parent?.currentState.maySendStateEvent(EventType.SpaceChild, cli.getUserId())); + .filter((parent) => + parent?.currentState.maySendStateEvent(EventType.SpaceChild, cli.getUserId()!), + ) as Room[]; } const progress: IProgress = { @@ -117,7 +119,7 @@ export async function upgradeRoom( if (toInvite.length > 0) { // Errors are handled internally to this function await inviteUsersToRoom(newRoomId, toInvite, false, () => { - progress.inviteUsersProgress++; + progress.inviteUsersProgress!++; progressCallback?.(progress); }); } @@ -137,7 +139,7 @@ export async function upgradeRoom( ); await cli.sendStateEvent(parent.roomId, EventType.SpaceChild, {}, room.roomId); - progress.updateSpacesProgress++; + progress.updateSpacesProgress!++; progressCallback?.(progress); } } catch (e) { diff --git a/src/utils/ShieldUtils.ts b/src/utils/ShieldUtils.ts index 6bf57801be..089cf3feeb 100644 --- a/src/utils/ShieldUtils.ts +++ b/src/utils/ShieldUtils.ts @@ -51,7 +51,7 @@ export async function shieldStatusForRoom(client: MatrixClient, room: Room): Pro !inDMMap && // Don't alarm for self in DMs with other users members.length !== 2) || // Don't alarm for self in 1:1 chats with other users members.length === 1; // Do alarm for self if we're alone in a room - const targets = includeUser ? [...verified, client.getUserId()] : verified; + const targets = includeUser ? [...verified, client.getUserId()!] : verified; for (const userId of targets) { const devices = client.getStoredDevicesForUser(userId); const anyDeviceNotVerified = devices.some(({ deviceId }) => { diff --git a/src/utils/StorageManager.ts b/src/utils/StorageManager.ts index 5bb8861311..6cab834855 100644 --- a/src/utils/StorageManager.ts +++ b/src/utils/StorageManager.ts @@ -181,7 +181,7 @@ export function setCryptoInitialised(cryptoInited: boolean): void { /* Simple wrapper functions around IndexedDB. */ -let idb: IDBDatabase = null; +let idb: IDBDatabase | null = null; async function idbInit(): Promise { if (!indexedDB) { @@ -206,7 +206,7 @@ export async function idbLoad(table: string, key: string | string[]): Promise { - const txn = idb.transaction([table], "readonly"); + const txn = idb!.transaction([table], "readonly"); txn.onerror = reject; const objectStore = txn.objectStore(table); @@ -223,7 +223,7 @@ export async function idbSave(table: string, key: string | string[], data: any): await idbInit(); } return new Promise((resolve, reject) => { - const txn = idb.transaction([table], "readwrite"); + const txn = idb!.transaction([table], "readwrite"); txn.onerror = reject; const objectStore = txn.objectStore(table); @@ -240,7 +240,7 @@ export async function idbDelete(table: string, key: string | string[]): Promise< await idbInit(); } return new Promise((resolve, reject) => { - const txn = idb.transaction([table], "readwrite"); + const txn = idb!.transaction([table], "readwrite"); txn.onerror = reject; const objectStore = txn.objectStore(table); diff --git a/src/utils/Timer.ts b/src/utils/Timer.ts index 1038aed4f9..975a498e96 100644 --- a/src/utils/Timer.ts +++ b/src/utils/Timer.ts @@ -26,8 +26,8 @@ Once a timer is finished or aborted, it can't be started again a new one through `clone()` or `cloneIfRun()`. */ export default class Timer { - private timerHandle: number; - private startTs: number; + private timerHandle?: number; + private startTs?: number; private promise: Promise; private resolve: () => void; private reject: (Error) => void; @@ -37,19 +37,19 @@ export default class Timer { } private setNotStarted(): void { - this.timerHandle = null; - this.startTs = null; + this.timerHandle = undefined; + this.startTs = undefined; this.promise = new Promise((resolve, reject) => { this.resolve = resolve; this.reject = reject; }).finally(() => { - this.timerHandle = null; + this.timerHandle = undefined; }); } private onTimeout = (): void => { const now = Date.now(); - const elapsed = now - this.startTs; + const elapsed = now - this.startTs!; if (elapsed >= this.timeout) { this.resolve(); this.setNotStarted(); @@ -124,6 +124,6 @@ export default class Timer { } public isRunning(): boolean { - return this.timerHandle !== null; + return this.timerHandle !== undefined; } } diff --git a/src/utils/UrlUtils.ts b/src/utils/UrlUtils.ts index bc185b2056..9c4b81ad3e 100644 --- a/src/utils/UrlUtils.ts +++ b/src/utils/UrlUtils.ts @@ -31,7 +31,7 @@ export function abbreviateUrl(u: string): string { if (parsedUrl.path === "/") { // we ignore query / hash parts: these aren't relevant for IS server URLs - return parsedUrl.host; + return parsedUrl.host || ""; } return u; diff --git a/src/utils/arrays.ts b/src/utils/arrays.ts index 09cc1de3b9..d5436c601c 100644 --- a/src/utils/arrays.ts +++ b/src/utils/arrays.ts @@ -266,7 +266,7 @@ export class ArrayUtil { const obj = this.a.reduce((rv: Map, val: T) => { const k = fn(val); if (!rv.has(k)) rv.set(k, []); - rv.get(k).push(val); + rv.get(k)!.push(val); return rv; }, new Map()); return new GroupedArray(obj); @@ -299,7 +299,7 @@ export class GroupedArray { const a: T[] = []; for (const k of keyOrder) { if (!this.val.has(k)) continue; - a.push(...this.val.get(k)); + a.push(...this.val.get(k)!); } return new ArrayUtil(a); } diff --git a/src/utils/leave-behaviour.ts b/src/utils/leave-behaviour.ts index deaa9f92ae..11047c5fb7 100644 --- a/src/utils/leave-behaviour.ts +++ b/src/utils/leave-behaviour.ts @@ -39,7 +39,7 @@ import { SdkContextClass } from "../contexts/SDKContext"; export async function leaveRoomBehaviour(roomId: string, retry = true, spinner = true): Promise { let spinnerModal: IHandle; if (spinner) { - spinnerModal = Modal.createDialog(Spinner, null, "mx_Dialog_spinner"); + spinnerModal = Modal.createDialog(Spinner, undefined, "mx_Dialog_spinner"); } const cli = MatrixClientPeg.get(); diff --git a/src/utils/permalinks/Permalinks.ts b/src/utils/permalinks/Permalinks.ts index e5ed070c03..eb72a87362 100644 --- a/src/utils/permalinks/Permalinks.ts +++ b/src/utils/permalinks/Permalinks.ts @@ -79,14 +79,13 @@ const ANY_REGEX = /.*/; // the list and magically have the link work. export class RoomPermalinkCreator { - private room: Room; private roomId: string; - private highestPlUserId: string; - private populationMap: { [serverName: string]: number }; - private bannedHostsRegexps: RegExp[]; - private allowedHostsRegexps: RegExp[]; - private _serverCandidates: string[]; - private started: boolean; + private highestPlUserId: string | null = null; + private populationMap: { [serverName: string]: number } | null = null; + private bannedHostsRegexps: RegExp[] | null = null; + private allowedHostsRegexps: RegExp[] | null = null; + private _serverCandidates: string[] | null = null; + private started = false; // We support being given a roomId as a fallback in the event the `room` object // doesn't exist or is not healthy for us to rely on. For example, loading a @@ -94,15 +93,8 @@ export class RoomPermalinkCreator { // Some of the tests done by this class are relatively expensive, so normally // throttled to not happen on every update. Pass false as the shouldThrottle // param to disable this behaviour, eg. for tests. - public constructor(room: Room, roomId: string | null = null, shouldThrottle = true) { - this.room = room; - this.roomId = room ? room.roomId : roomId; - this.highestPlUserId = null; - this.populationMap = null; - this.bannedHostsRegexps = null; - this.allowedHostsRegexps = null; - this._serverCandidates = null; - this.started = false; + public constructor(private room: Room, roomId: string | null = null, shouldThrottle = true) { + this.roomId = room ? room.roomId : roomId!; if (!this.roomId) { throw new Error("Failed to resolve a roomId for the permalink creator to use"); @@ -316,7 +308,7 @@ export function isPermalinkHost(host: string): boolean { * @param {string} entity The entity to transform. * @returns {string|null} The transformed permalink or null if unable. */ -export function tryTransformEntityToPermalink(entity: string): string { +export function tryTransformEntityToPermalink(entity: string): string | null { if (!entity) return null; // Check to see if it is a bare entity for starters @@ -391,7 +383,7 @@ export function tryTransformPermalinkToLocalHref(permalink: string): string { return permalink; } -export function getPrimaryPermalinkEntity(permalink: string): string { +export function getPrimaryPermalinkEntity(permalink: string): string | null { try { let permalinkParts = parsePermalink(permalink); @@ -425,7 +417,7 @@ function getPermalinkConstructor(): PermalinkConstructor { return new MatrixToPermalinkConstructor(); } -export function parsePermalink(fullUrl: string): PermalinkParts { +export function parsePermalink(fullUrl: string): PermalinkParts | null { try { const elementPrefix = SdkConfig.get("permalink_prefix"); if (decodeURIComponent(fullUrl).startsWith(matrixtoBaseUrl)) { diff --git a/src/utils/space.tsx b/src/utils/space.tsx index fe062a080c..fc183a3a7d 100644 --- a/src/utils/space.tsx +++ b/src/utils/space.tsx @@ -41,7 +41,7 @@ import { OpenAddExistingToSpaceDialogPayload } from "../dispatcher/payloads/Open import { SdkContextClass } from "../contexts/SDKContext"; export const shouldShowSpaceSettings = (space: Room): boolean => { - const userId = space.client.getUserId(); + const userId = space.client.getUserId()!; return ( space.getMyMembership() === "join" && (space.currentState.maySendStateEvent(EventType.RoomAvatar, userId) || @@ -88,7 +88,7 @@ export const showCreateNewRoom = async (space: Room, type?: RoomType): Promise - ((space?.getMyMembership() === "join" && space.canInvite(space.client.getUserId())) || + ((space?.getMyMembership() === "join" && space.canInvite(space.client.getUserId()!)) || space.getJoinRule() === JoinRule.Public) && shouldShowComponent(UIComponent.InviteUsers); @@ -149,7 +149,7 @@ export const bulkSpaceBehaviour = async ( children: Room[], fn: (room: Room) => Promise, ): Promise => { - const modal = Modal.createDialog(Spinner, null, "mx_Dialog_spinner"); + const modal = Modal.createDialog(Spinner, undefined, "mx_Dialog_spinner"); try { for (const room of children) { await fn(room); diff --git a/src/utils/tooltipify.tsx b/src/utils/tooltipify.tsx index 3cf6ec50a0..e3280f7fe2 100644 --- a/src/utils/tooltipify.tsx +++ b/src/utils/tooltipify.tsx @@ -47,9 +47,9 @@ export function tooltipifyLinks(rootNodes: ArrayLike, ignoredNodes: Ele if ( node.tagName === "A" && node.getAttribute("href") && - node.getAttribute("href") !== node.textContent.trim() + node.getAttribute("href") !== node.textContent?.trim() ) { - let href = node.getAttribute("href"); + let href = node.getAttribute("href")!; try { href = new URL(href, window.location.href).toString(); } catch (e) {