Conform more of the code base to strict null checking (#10147)

* Conform more of the code base to strict null checking

* More strict fixes

* More strict work

* Fix missing optional type

* Iterate
This commit is contained in:
Michael Telatynski 2023-02-13 17:01:43 +00:00 committed by GitHub
parent fa036a5080
commit da7aa4055e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
380 changed files with 682 additions and 694 deletions

View file

@ -35,8 +35,8 @@ export default class DMRoomMap {
private static sharedInstance: DMRoomMap;
// TODO: convert these to maps
private roomToUser: { [key: string]: string } = null;
private userToRooms: { [key: string]: string[] } = null;
private roomToUser: { [key: string]: string } | null = null;
private userToRooms: { [key: string]: string[] } | null = null;
private hasSentOutPatchDirectAccountDataPatch: boolean;
private mDirectEvent: { [key: string]: string[] };
@ -98,7 +98,7 @@ export default class DMRoomMap {
* modifying userToRooms
*/
private patchUpSelfDMs(userToRooms: Record<string, string[]>): boolean {
const myUserId = this.matrixClient.getUserId();
const myUserId = this.matrixClient.getUserId()!;
const selfRoomIds = userToRooms[myUserId];
if (selfRoomIds) {
// any self-chats that should not be self-chats?
@ -112,7 +112,7 @@ export default class DMRoomMap {
}
}
})
.filter((ids) => !!ids); //filter out
.filter((ids) => !!ids) as { userId: string; roomId: string }[]; //filter out
// these are actually all legit self-chats
// bail out
if (!guessedUserIdsThatChanged.length) {
@ -132,6 +132,7 @@ export default class DMRoomMap {
});
return true;
}
return false;
}
public getDMRoomsForUserId(userId: string): string[] {
@ -145,7 +146,7 @@ export default class DMRoomMap {
* @param {string[]} ids The identifiers (user IDs and email addresses) to look for.
* @returns {Room} The DM room which all IDs given share, or falsy if no common room.
*/
public getDMRoomForIdentifiers(ids: string[]): Room {
public getDMRoomForIdentifiers(ids: string[]): Room | null {
// TODO: [Canonical DMs] Handle lookups for email addresses.
// For now we'll pretend we only get user IDs and end up returning nothing for email addresses
@ -174,14 +175,14 @@ export default class DMRoomMap {
}
// Here, we return undefined if the room is not in the map:
// the room ID you gave is not a DM room for any user.
if (this.roomToUser[roomId] === undefined) {
if (this.roomToUser![roomId] === undefined) {
// no entry? if the room is an invite, look for the is_direct hint.
const room = this.matrixClient.getRoom(roomId);
if (room) {
return room.getDMInviter();
}
}
return this.roomToUser[roomId];
return this.roomToUser![roomId];
}
public getUniqueRoomsWithIndividuals(): { [userId: string]: Room } {
@ -205,7 +206,7 @@ export default class DMRoomMap {
private getUserToRooms(): { [key: string]: string[] } {
if (!this.userToRooms) {
const userToRooms = this.mDirectEvent;
const myUserId = this.matrixClient.getUserId();
const myUserId = this.matrixClient.getUserId()!;
const selfDMs = userToRooms[myUserId];
if (selfDMs?.length) {
const neededPatching = this.patchUpSelfDMs(userToRooms);
@ -228,7 +229,7 @@ export default class DMRoomMap {
private populateRoomToUser(): void {
this.roomToUser = {};
for (const user of Object.keys(this.getUserToRooms())) {
for (const roomId of this.userToRooms[user]) {
for (const roomId of this.userToRooms![user]) {
this.roomToUser[roomId] = user;
}
}

View file

@ -25,8 +25,8 @@ import DocumentOffset from "../editor/offset";
* upon receiving the remote echo for an unsent event.
*/
export default class EditorStateTransfer {
private serializedParts: SerializedPart[] = null;
private caret: DocumentOffset = null;
private serializedParts: SerializedPart[] | null = null;
private caret: DocumentOffset | null = null;
public constructor(private readonly event: MatrixEvent) {}
@ -39,11 +39,11 @@ export default class EditorStateTransfer {
return !!this.serializedParts;
}
public getSerializedParts(): SerializedPart[] {
public getSerializedParts(): SerializedPart[] | null {
return this.serializedParts;
}
public getCaret(): DocumentOffset {
public getCaret(): DocumentOffset | null {
return this.caret;
}

View file

@ -161,7 +161,7 @@ const getMsc3531Enabled = (): boolean => {
if (msc3531Enabled === null) {
msc3531Enabled = SettingsStore.getValue("feature_msc3531_hide_messages_pending_moderation");
}
return msc3531Enabled;
return msc3531Enabled!;
};
/**
@ -193,14 +193,14 @@ export function getMessageModerationState(mxEvent: MatrixEvent, client?: MatrixC
const room = client.getRoom(mxEvent.getRoomId());
if (
EVENT_VISIBILITY_CHANGE_TYPE.name &&
room.currentState.maySendStateEvent(EVENT_VISIBILITY_CHANGE_TYPE.name, client.getUserId())
room?.currentState.maySendStateEvent(EVENT_VISIBILITY_CHANGE_TYPE.name, client.getUserId()!)
) {
// We're a moderator (as indicated by prefixed event name), show the message.
return MessageModerationState.SEE_THROUGH_FOR_CURRENT_USER;
}
if (
EVENT_VISIBILITY_CHANGE_TYPE.altName &&
room.currentState.maySendStateEvent(EVENT_VISIBILITY_CHANGE_TYPE.altName, client.getUserId())
room?.currentState.maySendStateEvent(EVENT_VISIBILITY_CHANGE_TYPE.altName, client.getUserId()!)
) {
// We're a moderator (as indicated by unprefixed event name), show the message.
return MessageModerationState.SEE_THROUGH_FOR_CURRENT_USER;
@ -220,7 +220,7 @@ export async function fetchInitialEvent(
roomId: string,
eventId: string,
): Promise<MatrixEvent | null> {
let initialEvent: MatrixEvent;
let initialEvent: MatrixEvent | null;
try {
const eventData = await client.fetchRoomEvent(roomId, eventId);
@ -231,12 +231,12 @@ export async function fetchInitialEvent(
}
if (client.supportsThreads() && initialEvent?.isRelation(THREAD_RELATION_TYPE.name) && !initialEvent.getThread()) {
const threadId = initialEvent.threadRootId;
const threadId = initialEvent.threadRootId!;
const room = client.getRoom(roomId);
const mapper = client.getEventMapper();
const rootEvent = room.findEventById(threadId) ?? mapper(await client.fetchRoomEvent(roomId, threadId));
const rootEvent = room?.findEventById(threadId) ?? mapper(await client.fetchRoomEvent(roomId, threadId));
try {
room.createThread(threadId, rootEvent, [initialEvent], true);
room?.createThread(threadId, rootEvent, [initialEvent], true);
} catch (e) {
logger.warn("Could not find root event: " + threadId);
}
@ -271,12 +271,12 @@ export const isLocationEvent = (event: MatrixEvent): boolean => {
const eventType = event.getType();
return (
M_LOCATION.matches(eventType) ||
(eventType === EventType.RoomMessage && M_LOCATION.matches(event.getContent().msgtype))
(eventType === EventType.RoomMessage && M_LOCATION.matches(event.getContent().msgtype!))
);
};
export function hasThreadSummary(event: MatrixEvent): boolean {
return event.isThreadRoot && event.getThread()?.length && !!event.getThread().replyToEvent;
return event.isThreadRoot && !!event.getThread()?.length && !!event.getThread()!.replyToEvent;
}
export function canPinEvent(event: MatrixEvent): boolean {

View file

@ -19,5 +19,5 @@ import SettingsStore from "../settings/SettingsStore";
import { UIFeature } from "../settings/UIFeature";
export function shouldShowFeedback(): boolean {
return SdkConfig.get().bug_report_endpoint_url && SettingsStore.getValue(UIFeature.Feedback);
return !!SdkConfig.get().bug_report_endpoint_url && SettingsStore.getValue(UIFeature.Feedback);
}

View file

@ -68,14 +68,14 @@ function getManagedIframe(): { iframe: HTMLIFrameElement; onLoadPromise: Promise
* additional control over the styling/position of the iframe itself.
*/
export class FileDownloader {
private onLoadPromise: Promise<void>;
private onLoadPromise?: Promise<void>;
/**
* Creates a new file downloader
* @param iframeFn Function to get a pre-configured iframe. Set to null to have the downloader
* use a generic, hidden, iframe.
*/
public constructor(private iframeFn: getIframeFn = null) {}
public constructor(private iframeFn?: getIframeFn) {}
private get iframe(): HTMLIFrameElement {
const iframe = this.iframeFn?.();
@ -84,14 +84,14 @@ export class FileDownloader {
this.onLoadPromise = managed.onLoadPromise;
return managed.iframe;
}
this.onLoadPromise = null;
this.onLoadPromise = undefined;
return iframe;
}
public async download({ blob, name, autoDownload = true, opts = DEFAULT_STYLES }: DownloadOptions): Promise<void> {
const iframe = this.iframe; // get the iframe first just in case we need to await onload
if (this.onLoadPromise) await this.onLoadPromise;
iframe.contentWindow.postMessage(
iframe.contentWindow?.postMessage(
{
...opts,
blob: blob,

View file

@ -66,7 +66,7 @@ export function formatBytes(bytes: number, decimals = 2): string {
* @return {string}
*/
export function formatCryptoKey(key: string): string {
return key.match(/.{1,4}/g).join(" ");
return key.match(/.{1,4}/g)!.join(" ");
}
/**
* calculates a numeric hash for a given string

View file

@ -17,7 +17,7 @@ limitations under the License.
import SdkConfig from "../SdkConfig";
import { MatrixClientPeg } from "../MatrixClientPeg";
export function getHostingLink(campaign: string): string {
export function getHostingLink(campaign: string): string | null {
const hostingLink = SdkConfig.get().hosting_signup_link;
if (!hostingLink) return null;
if (!campaign) return hostingLink;

View file

@ -18,7 +18,7 @@ import { arrayHasDiff } from "./arrays";
export function mayBeAnimated(mimeType?: string): boolean {
// AVIF animation support at the time of writing is only available in Chrome hence not having `blobIsAnimated` check
return ["image/gif", "image/webp", "image/png", "image/apng", "image/avif"].includes(mimeType);
return ["image/gif", "image/webp", "image/png", "image/apng", "image/avif"].includes(mimeType!);
}
function arrayBufferRead(arr: ArrayBuffer, start: number, len: number): Uint8Array {

View file

@ -20,29 +20,21 @@ limitations under the License.
* @param {WheelEvent} event to normalize
* @returns {WheelEvent} normalized event event
*/
export function normalizeWheelEvent(event: WheelEvent): WheelEvent {
export function normalizeWheelEvent({ deltaMode, deltaX, deltaY, deltaZ, ...event }: WheelEvent): WheelEvent {
const LINE_HEIGHT = 18;
let deltaX;
let deltaY;
let deltaZ;
if (event.deltaMode === 1) {
if (deltaMode === 1) {
// Units are lines
deltaX = event.deltaX * LINE_HEIGHT;
deltaY = event.deltaY * LINE_HEIGHT;
deltaZ = event.deltaZ * LINE_HEIGHT;
} else {
deltaX = event.deltaX;
deltaY = event.deltaY;
deltaZ = event.deltaZ;
deltaX *= LINE_HEIGHT;
deltaY *= LINE_HEIGHT;
deltaZ *= LINE_HEIGHT;
}
return new WheelEvent("syntheticWheel", {
deltaMode: 0,
deltaY: deltaY,
deltaX: deltaX,
deltaZ: deltaZ,
deltaY,
deltaX,
deltaZ,
...event,
});
}

View file

@ -57,8 +57,8 @@ export default class MultiInviter {
private _fatal = false;
private completionStates: CompletionStates = {}; // State of each address (invited or error)
private errors: Record<string, IError> = {}; // { address: {errorText, errcode} }
private deferred: IDeferred<CompletionStates> = null;
private reason: string = null;
private deferred: IDeferred<CompletionStates> | null = null;
private reason: string | undefined;
/**
* @param {string} roomId The ID of the room to invite to
@ -134,7 +134,7 @@ export default class MultiInviter {
if (!this.busy) return;
this.canceled = true;
this.deferred.reject(new Error("canceled"));
this.deferred?.reject(new Error("canceled"));
}
public getCompletionState(addr: string): InviteState {
@ -216,7 +216,7 @@ export default class MultiInviter {
const isSpace = this.roomId && this.matrixClient.getRoom(this.roomId)?.isSpaceRoom();
let errorText: string;
let errorText: string | undefined;
let fatal = false;
switch (err.errcode) {
case "M_FORBIDDEN":
@ -310,7 +310,7 @@ export default class MultiInviter {
if (unknownProfileUsers.length > 0) {
const inviteUnknowns = (): void => {
const promises = unknownProfileUsers.map((u) => this.doInvite(u, true));
Promise.all(promises).then(() => this.deferred.resolve(this.completionStates));
Promise.all(promises).then(() => this.deferred?.resolve(this.completionStates));
};
if (!SettingsStore.getValue("promptBeforeInviteUnknownUsers", this.roomId)) {
@ -330,13 +330,13 @@ export default class MultiInviter {
for (const addr of unknownProfileUsers) {
this.completionStates[addr] = InviteState.Invited;
}
this.deferred.resolve(this.completionStates);
this.deferred?.resolve(this.completionStates);
},
});
return;
}
}
this.deferred.resolve(this.completionStates);
this.deferred?.resolve(this.completionStates);
return;
}
@ -361,6 +361,6 @@ export default class MultiInviter {
.then(() => {
this.inviteMore(nextIndex + 1, ignoreProfile);
})
.catch(() => this.deferred.resolve(this.completionStates));
.catch(() => this.deferred?.resolve(this.completionStates));
}
}

View file

@ -60,12 +60,12 @@ _td("Short keyboard patterns are easy to guess");
* @param {string} password Password to score
* @returns {object} Score result with `score` and `feedback` properties
*/
export function scorePassword(password: string): zxcvbn.ZXCVBNResult {
export function scorePassword(password: string): zxcvbn.ZXCVBNResult | null {
if (password.length === 0) return null;
const userInputs = ZXCVBN_USER_INPUTS.slice();
if (MatrixClientPeg.get()) {
userInputs.push(MatrixClientPeg.get().getUserIdLocalpart());
userInputs.push(MatrixClientPeg.get().getUserIdLocalpart()!);
}
let zxcvbnResult = zxcvbn(password, userInputs);

View file

@ -96,8 +96,8 @@ export function getNestedReplyText(
// dev note: do not rely on `body` being safe for HTML usage below.
const evLink = permalinkCreator.forEvent(ev.getId());
const userLink = makeUserPermalink(ev.getSender());
const evLink = permalinkCreator.forEvent(ev.getId()!);
const userLink = makeUserPermalink(ev.getSender()!);
const mxid = ev.getSender();
if (M_BEACON_INFO.matches(ev.getType())) {
@ -191,8 +191,8 @@ export function makeReplyMixIn(ev?: MatrixEvent): IEventRelation {
// with those that do. They should set the m.in_reply_to part as usual, and then add on
// "rel_type": "m.thread" and "event_id": "$thread_root", copying $thread_root from the replied-to event.
const relation = ev.getRelation();
mixin.rel_type = relation.rel_type;
mixin.event_id = relation.event_id;
mixin.rel_type = relation?.rel_type;
mixin.event_id = relation?.event_id;
}
}

View file

@ -55,7 +55,7 @@ export class Singleflight {
* @param {string} key A string key relevant to that instance to namespace under.
* @returns {SingleflightContext} Returns the context to execute the function.
*/
public static for(instance: Object, key: string): SingleflightContext {
public static for(instance?: Object | null, key?: string | null): SingleflightContext {
if (!instance || !key) throw new Error("An instance and key must be supplied");
return new SingleflightContext(instance, key);
}

View file

@ -50,12 +50,12 @@ export function getCallBehaviourWellKnown(): ICallBehaviourWellKnown {
return clientWellKnown?.[CALL_BEHAVIOUR_WK_KEY];
}
export function getE2EEWellKnown(): IE2EEWellKnown {
export function getE2EEWellKnown(): IE2EEWellKnown | null {
const clientWellKnown = MatrixClientPeg.get().getClientWellKnown();
if (clientWellKnown && clientWellKnown[E2EE_WK_KEY]) {
if (clientWellKnown?.[E2EE_WK_KEY]) {
return clientWellKnown[E2EE_WK_KEY];
}
if (clientWellKnown && clientWellKnown[E2EE_WK_KEY_DEPRECATED]) {
if (clientWellKnown?.[E2EE_WK_KEY_DEPRECATED]) {
return clientWellKnown[E2EE_WK_KEY_DEPRECATED];
}
return null;
@ -78,8 +78,7 @@ export function embeddedPagesFromWellKnown(clientWellKnown?: IClientWellKnown):
}
export function isSecureBackupRequired(): boolean {
const wellKnown = getE2EEWellKnown();
return wellKnown && wellKnown["secure_backup_required"] === true;
return getE2EEWellKnown()?.["secure_backup_required"] === true;
}
export enum SecureBackupSetupMethod {

View file

@ -119,7 +119,7 @@ export default class WidgetUtils {
if (
testUrl.protocol === scalarUrl.protocol &&
testUrl.host === scalarUrl.host &&
testUrl.pathname.startsWith(scalarUrl.pathname)
testUrl.pathname?.startsWith(scalarUrl.pathname)
) {
return true;
}
@ -143,8 +143,8 @@ export default class WidgetUtils {
return new Promise((resolve, reject) => {
// Tests an account data event, returning true if it's in the state
// we're waiting for it to be in
function eventInIntendedState(ev: MatrixEvent): boolean {
if (!ev || !ev.getContent()) return false;
function eventInIntendedState(ev?: MatrixEvent): boolean {
if (!ev) return false;
if (add) {
return ev.getContent()[widgetId] !== undefined;
} else {
@ -190,12 +190,12 @@ export default class WidgetUtils {
return new Promise((resolve, reject) => {
// Tests a list of state events, returning true if it's in the state
// we're waiting for it to be in
function eventsInIntendedState(evList: MatrixEvent[]): boolean {
const widgetPresent = evList.some((ev) => {
function eventsInIntendedState(evList?: MatrixEvent[]): boolean {
const widgetPresent = evList?.some((ev) => {
return ev.getContent() && ev.getContent()["id"] === widgetId;
});
if (add) {
return widgetPresent;
return !!widgetPresent;
} else {
return !widgetPresent;
}
@ -203,7 +203,7 @@ export default class WidgetUtils {
const room = MatrixClientPeg.get().getRoom(roomId);
// TODO: Enable support for m.widget event type (https://github.com/vector-im/element-web/issues/13111)
const startingWidgetEvents = room.currentState.getStateEvents("im.vector.modular.widgets");
const startingWidgetEvents = room?.currentState.getStateEvents("im.vector.modular.widgets");
if (eventsInIntendedState(startingWidgetEvents)) {
resolve();
return;
@ -213,7 +213,7 @@ export default class WidgetUtils {
if (ev.getRoomId() !== roomId || ev.getType() !== "im.vector.modular.widgets") return;
// TODO: Enable support for m.widget event type (https://github.com/vector-im/element-web/issues/13111)
const currentWidgetEvents = room.currentState.getStateEvents("im.vector.modular.widgets");
const currentWidgetEvents = room?.currentState.getStateEvents("im.vector.modular.widgets");
if (eventsInIntendedState(currentWidgetEvents)) {
MatrixClientPeg.get().removeListener(RoomStateEvent.Events, onRoomStateEvents);
@ -261,7 +261,7 @@ export default class WidgetUtils {
if (addingWidget) {
userWidgets[widgetId] = {
content: content,
sender: client.getUserId(),
sender: client.getUserId()!,
state_key: widgetId,
type: "m.widget",
id: widgetId,
@ -299,7 +299,7 @@ export default class WidgetUtils {
content = {
// TODO: Enable support for m.widget event type (https://github.com/vector-im/element-web/issues/13111)
// For now we'll send the legacy event type for compatibility with older apps/elements
type: widgetType.legacy,
type: widgetType?.legacy,
url: widgetUrl,
name: widgetName,
data: widgetData,
@ -513,7 +513,7 @@ export default class WidgetUtils {
"roomId=$matrix_room_id",
"theme=$theme",
"roomName=$roomName",
`supportsScreensharing=${PlatformPeg.get().supportsJitsiScreensharing()}`,
`supportsScreensharing=${PlatformPeg.get()?.supportsJitsiScreensharing()}`,
"language=$org.matrix.msc2873.client_language",
];
if (opts.auth) {

View file

@ -31,10 +31,10 @@ import { determineCreateRoomEncryptionOption, Member } from "../../../src/utils/
* @returns {Promise<LocalRoom>} Resolves to the new local room
*/
export async function createDmLocalRoom(client: MatrixClient, targets: Member[]): Promise<LocalRoom> {
const userId = client.getUserId();
const userId = client.getUserId()!;
const localRoom = new LocalRoom(LOCAL_ROOM_ID_PREFIX + client.makeTxnId(), client, userId);
const events = [];
const events: MatrixEvent[] = [];
events.push(
new MatrixEvent({
@ -121,7 +121,7 @@ export async function createDmLocalRoom(client: MatrixClient, targets: Member[])
localRoom.updateMyMembership("join");
localRoom.addLiveEvents(events);
localRoom.currentState.setStateEvents(events);
localRoom.name = localRoom.getDefaultRoomName(client.getUserId());
localRoom.name = localRoom.getDefaultRoomName(client.getUserId()!);
client.store.storeRoom(localRoom);
return localRoom;

View file

@ -35,7 +35,7 @@ export async function startDm(client: MatrixClient, targets: Member[], showSpinn
const targetIds = targets.map((t) => t.userId);
// Check if there is already a DM with these people and reuse it if possible.
let existingRoom: Room;
let existingRoom: Room | undefined;
if (targetIds.length === 1) {
existingRoom = findDMForUser(client, targetIds[0]);
} else {
@ -66,12 +66,15 @@ export async function startDm(client: MatrixClient, targets: Member[], showSpinn
}
if (targetIds.length > 1) {
createRoomOptions.createOpts = targetIds.reduce(
createRoomOptions.createOpts = targetIds.reduce<{
invite_3pid: IInvite3PID[];
invite: string[];
}>(
(roomOptions, address) => {
const type = getAddressType(address);
if (type === "email") {
const invite: IInvite3PID = {
id_server: client.getIdentityServerUrl(true),
id_server: client.getIdentityServerUrl(true)!,
medium: "email",
address,
};

View file

@ -21,7 +21,7 @@ import { _t } from "../languageHandler";
import DMRoomMap from "./DMRoomMap";
export interface RoomContextDetails {
details: string;
details: string | null;
ariaLabel?: string;
}

View file

@ -37,7 +37,7 @@ export const useMap = ({ interactive, bodyId, onError }: UseMapProps): MapLibreM
useEffect(
() => {
try {
setMap(createMap(interactive, bodyId, onError));
setMap(createMap(!!interactive, bodyId, onError));
} catch (error) {
onError(error);
}

View file

@ -43,13 +43,13 @@ export class EnhancedMap<K, V> extends Map<K, V> {
public getOrCreate(key: K, def: V): V {
if (this.has(key)) {
return this.get(key);
return this.get(key)!;
}
this.set(key, def);
return def;
}
public remove(key: K): V {
public remove(key: K): V | undefined {
const v = this.get(key);
this.delete(key);
return v;

View file

@ -36,7 +36,7 @@ export async function createLocalNotificationSettingsIfNeeded(cli: MatrixClient)
if (cli.isGuest()) {
return;
}
const eventType = getLocalNotificationAccountDataEventType(cli.deviceId);
const eventType = getLocalNotificationAccountDataEventType(cli.deviceId!);
const event = cli.getAccountData(eventType);
// New sessions will create an account data event to signify they support
// remote toggling of push notifications on this device. Default `is_silenced=true`
@ -54,7 +54,7 @@ export async function createLocalNotificationSettingsIfNeeded(cli: MatrixClient)
}
export function localNotificationsAreSilenced(cli: MatrixClient): boolean {
const eventType = getLocalNotificationAccountDataEventType(cli.deviceId);
const eventType = getLocalNotificationAccountDataEventType(cli.deviceId!);
const event = cli.getAccountData(eventType);
return event?.getContent<LocalNotificationSettings>()?.is_silenced ?? false;
}

View file

@ -20,7 +20,7 @@ import { IConfigOptions } from "../IConfigOptions";
import { getEmbeddedPagesWellKnown } from "../utils/WellKnownUtils";
import { SnakedObject } from "./SnakedObject";
export function getHomePageUrl(appConfig: IConfigOptions): string | null {
export function getHomePageUrl(appConfig: IConfigOptions): string | undefined {
const config = new SnakedObject(appConfig);
const pagesConfig = config.get("embedded_pages");

View file

@ -33,7 +33,7 @@ export async function retry<T, E extends Error>(
num: number,
predicate?: (e: E) => boolean,
): Promise<T> {
let lastErr: E;
let lastErr!: E;
for (let i = 0; i < num; i++) {
try {
const v = await fn();

View file

@ -25,7 +25,7 @@ import { isSupportedReceiptType } from "matrix-js-sdk/src/utils";
* @returns True if the read receipt update includes the client, false otherwise.
*/
export function readReceiptChangeIsFor(event: MatrixEvent, client: MatrixClient): boolean {
const myUserId = client.getUserId();
const myUserId = client.getUserId()!;
for (const eventId of Object.keys(event.getContent())) {
for (const [receiptType, receipt] of Object.entries(event.getContent()[eventId])) {
if (!isSupportedReceiptType(receiptType)) continue;
@ -33,4 +33,5 @@ export function readReceiptChangeIsFor(event: MatrixEvent, client: MatrixClient)
if (Object.keys(receipt || {}).includes(myUserId)) return true;
}
}
return false;
}