Conform more code to strict null checking (#10167)
* Conform more code to strict null checking * Delint * Iterate PR based on feedbackpull/28788/head^2
parent
f7bea2cae5
commit
4574c665ea
|
@ -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<boolean>;
|
||||
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<HTMLVideoElement> {
|
|||
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<ArrayBuffer> {
|
|||
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<void> {
|
||||
if (this.mediaConfig !== null) return;
|
||||
if (this.mediaConfig !== null) return Promise.resolve();
|
||||
|
||||
logger.log("[Media Config] Fetching");
|
||||
return matrixClient
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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<string>();
|
||||
// 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<IKeyBackupInfo> {
|
||||
private async getKeyBackupInfo(): Promise<IKeyBackupInfo | null> {
|
||||
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);
|
||||
|
|
|
@ -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<string> {
|
||||
public async getAccessToken({ check = true } = {}): Promise<string | null> {
|
||||
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();
|
||||
}
|
||||
|
|
|
@ -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 {
|
|||
"<code>turn.matrix.org</code>, 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 },
|
||||
)}
|
||||
</p>
|
||||
|
@ -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,
|
||||
);
|
||||
}
|
||||
|
|
29
src/Login.ts
29
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<LoginFlow>;
|
||||
private defaultDeviceDisplayName: string;
|
||||
private tempClient: MatrixClient;
|
||||
private flows: Array<LoginFlow> = [];
|
||||
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<IMatrixClientCreds> {
|
||||
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<IMatrixClientCreds> => {
|
||||
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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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];
|
||||
|
|
|
@ -82,6 +82,7 @@ const singleMxcUpload = async (): Promise<string | null> => {
|
|||
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,
|
||||
|
|
|
@ -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 () => (
|
||||
<span>
|
||||
|
@ -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 () => (
|
||||
<span>
|
||||
|
|
|
@ -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<IProps, IState> {
|
|||
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<IProps, IState> {
|
|||
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<IProps, IState> {
|
|||
// 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<IProps, IState> {
|
|||
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<IProps, IState> {
|
|||
}
|
||||
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<IProps, IState> {
|
|||
|
||||
const wrapperStyle: CSSProperties = {};
|
||||
if (!isNaN(Number(zIndex))) {
|
||||
menuStyle["zIndex"] = zIndex + 1;
|
||||
menuStyle["zIndex"] = zIndex! + 1;
|
||||
wrapperStyle["zIndex"] = zIndex;
|
||||
}
|
||||
|
||||
|
|
|
@ -97,7 +97,7 @@ class DMUserTile extends React.PureComponent<IDMUserTileProps> {
|
|||
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<IDMUserTileProps> {
|
|||
|
||||
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<IDMRoomTileProps> {
|
|||
}
|
||||
|
||||
public render(): React.ReactNode {
|
||||
let timestamp = null;
|
||||
let timestamp: JSX.Element | undefined;
|
||||
if (this.props.lastActiveTs) {
|
||||
const humanTs = humanizeTime(this.props.lastActiveTs);
|
||||
timestamp = <span className="mx_InviteDialog_tile--room_time">{humanTs}</span>;
|
||||
|
@ -201,7 +201,7 @@ class DMRoomTile extends React.PureComponent<IDMRoomTileProps> {
|
|||
<BaseAvatar
|
||||
url={
|
||||
this.props.member.getMxcAvatarUrl()
|
||||
? mediaFromMxc(this.props.member.getMxcAvatarUrl()).getSquareThumbnailHttp(avatarSize)
|
||||
? mediaFromMxc(this.props.member.getMxcAvatarUrl()!).getSquareThumbnailHttp(avatarSize)
|
||||
: null
|
||||
}
|
||||
name={this.props.member.name}
|
||||
|
@ -211,7 +211,7 @@ class DMRoomTile extends React.PureComponent<IDMRoomTileProps> {
|
|||
/>
|
||||
);
|
||||
|
||||
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 = <div className="mx_InviteDialog_tile--room_selected" />;
|
||||
|
@ -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<Props, IInviteDialogState> {
|
||||
|
@ -324,7 +324,7 @@ export default class InviteDialog extends React.PureComponent<Props, IInviteDial
|
|||
throw new Error("When using KIND_CALL_TRANSFER a call is required for an InviteDialog");
|
||||
}
|
||||
|
||||
const alreadyInvited = new Set([MatrixClientPeg.get().getUserId(), SdkConfig.get("welcome_user_id")]);
|
||||
const alreadyInvited = new Set([MatrixClientPeg.get().getUserId()!, SdkConfig.get("welcome_user_id")]);
|
||||
if (isRoomInvite(props)) {
|
||||
const room = MatrixClientPeg.get().getRoom(props.roomId);
|
||||
if (!room) throw new Error("Room ID given to InviteDialog does not look like a room");
|
||||
|
@ -351,7 +351,6 @@ export default class InviteDialog extends React.PureComponent<Props, IInviteDial
|
|||
|
||||
// These two flags are used for the 'Go' button to communicate what is going on.
|
||||
busy: false,
|
||||
errorText: null,
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -386,7 +385,11 @@ export default class InviteDialog extends React.PureComponent<Props, IInviteDial
|
|||
}
|
||||
}
|
||||
|
||||
const recents = [];
|
||||
const recents: {
|
||||
userId: string;
|
||||
user: RoomMember;
|
||||
lastActive: number;
|
||||
}[] = [];
|
||||
for (const userId in rooms) {
|
||||
// Filter out user IDs that are already in the room / should be excluded
|
||||
if (excludedTargetIds.has(userId)) {
|
||||
|
@ -455,14 +458,16 @@ export default class InviteDialog extends React.PureComponent<Props, IInviteDial
|
|||
// Check to see if there's anything to convert first
|
||||
if (!this.state.filterText || !this.state.filterText.includes("@")) return this.state.targets || [];
|
||||
|
||||
let newMember: Member;
|
||||
let newMember: Member | undefined;
|
||||
if (this.state.filterText.startsWith("@")) {
|
||||
// Assume mxid
|
||||
newMember = new DirectoryMember({ user_id: this.state.filterText, display_name: null, avatar_url: null });
|
||||
newMember = new DirectoryMember({ user_id: this.state.filterText });
|
||||
} else if (SettingsStore.getValue(UIFeature.IdentityServer)) {
|
||||
// Assume email
|
||||
newMember = new ThreepidMember(this.state.filterText);
|
||||
}
|
||||
if (!newMember) return this.state.targets;
|
||||
|
||||
const newTargets = [...(this.state.targets || []), newMember];
|
||||
this.setState({ targets: newTargets, filterText: "" });
|
||||
return newTargets;
|
||||
|
@ -778,8 +783,8 @@ export default class InviteDialog extends React.PureComponent<Props, IInviteDial
|
|||
...this.state.serverResultsMixin,
|
||||
...this.state.threepidResultsMixin,
|
||||
];
|
||||
const toAdd = [];
|
||||
const failed = [];
|
||||
const toAdd: Member[] = [];
|
||||
const failed: string[] = [];
|
||||
const potentialAddresses = text
|
||||
.split(/[\s,]+/)
|
||||
.map((p) => p.trim())
|
||||
|
@ -803,13 +808,11 @@ export default class InviteDialog extends React.PureComponent<Props, IInviteDial
|
|||
|
||||
try {
|
||||
const profile = await MatrixClientPeg.get().getProfileInfo(address);
|
||||
const displayName = profile ? profile.displayname : null;
|
||||
const avatarUrl = profile ? profile.avatar_url : null;
|
||||
toAdd.push(
|
||||
new DirectoryMember({
|
||||
user_id: address,
|
||||
display_name: displayName,
|
||||
avatar_url: avatarUrl,
|
||||
display_name: profile?.displayname,
|
||||
avatar_url: profile?.avatar_url,
|
||||
}),
|
||||
);
|
||||
} catch (e) {
|
||||
|
@ -859,11 +862,11 @@ export default class InviteDialog extends React.PureComponent<Props, IInviteDial
|
|||
this.props.onFinished(false);
|
||||
};
|
||||
|
||||
private renderSection(kind: "recents" | "suggestions"): JSX.Element {
|
||||
private renderSection(kind: "recents" | "suggestions"): ReactNode {
|
||||
let sourceMembers = kind === "recents" ? this.state.recents : this.state.suggestions;
|
||||
let showNum = kind === "recents" ? this.state.numRecentsShown : this.state.numSuggestionsShown;
|
||||
const showMoreFn = kind === "recents" ? this.showMoreRecents.bind(this) : this.showMoreSuggestions.bind(this);
|
||||
const lastActive = (m: Result): number | null => (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<Props, IInviteDial
|
|||
const toRender = sourceMembers.slice(0, showNum);
|
||||
const hasMore = toRender.length < sourceMembers.length;
|
||||
|
||||
let showMore = null;
|
||||
let showMore: JSX.Element | undefined;
|
||||
if (hasMore) {
|
||||
showMore = (
|
||||
<div className="mx_InviteDialog_section_showMore">
|
||||
|
@ -960,7 +963,7 @@ export default class InviteDialog extends React.PureComponent<Props, IInviteDial
|
|||
this.state.targets.length === 0 &&
|
||||
this.state.filterText.length === 0;
|
||||
const targets = this.state.targets.map((t) => (
|
||||
<DMUserTile member={t} onRemove={!this.state.busy && this.removeMember} key={t.userId} />
|
||||
<DMUserTile member={t} onRemove={this.state.busy ? undefined : this.removeMember} key={t.userId} />
|
||||
));
|
||||
const input = (
|
||||
<input
|
||||
|
@ -973,7 +976,7 @@ export default class InviteDialog extends React.PureComponent<Props, IInviteDial
|
|||
autoFocus={true}
|
||||
disabled={this.state.busy || (this.props.kind == KIND_CALL_TRANSFER && this.state.targets.length > 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<Props, IInviteDial
|
|||
);
|
||||
}
|
||||
|
||||
private renderIdentityServerWarning(): JSX.Element {
|
||||
private renderIdentityServerWarning(): ReactNode {
|
||||
if (
|
||||
!this.state.tryingIdentityServer ||
|
||||
this.state.canUseIdentityServer ||
|
||||
|
@ -1080,7 +1083,7 @@ export default class InviteDialog extends React.PureComponent<Props, IInviteDial
|
|||
selectText(e.currentTarget);
|
||||
}
|
||||
|
||||
private get screenName(): ScreenName {
|
||||
private get screenName(): ScreenName | undefined {
|
||||
switch (this.props.kind) {
|
||||
case KIND_DM:
|
||||
return "StartChat";
|
||||
|
@ -1088,7 +1091,7 @@ export default class InviteDialog extends React.PureComponent<Props, IInviteDial
|
|||
}
|
||||
|
||||
public render(): React.ReactNode {
|
||||
let spinner = null;
|
||||
let spinner: JSX.Element | undefined;
|
||||
if (this.state.busy) {
|
||||
spinner = <Spinner w={20} h={20} />;
|
||||
}
|
||||
|
@ -1108,7 +1111,7 @@ export default class InviteDialog extends React.PureComponent<Props, IInviteDial
|
|||
this.state.targets.length > 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<Props, IInviteDial
|
|||
<p>{_t("If you can't see who you're looking for, send them your invite link below.")}</p>
|
||||
</div>
|
||||
);
|
||||
const link = makeUserPermalink(MatrixClientPeg.get().getUserId());
|
||||
const link = makeUserPermalink(MatrixClientPeg.get().getUserId()!);
|
||||
footer = (
|
||||
<div className="mx_InviteDialog_footer">
|
||||
<h3>{_t("Or send invite link")}</h3>
|
||||
<CopyableText getTextToCopy={() => makeUserPermalink(MatrixClientPeg.get().getUserId())}>
|
||||
<CopyableText getTextToCopy={() => makeUserPermalink(MatrixClientPeg.get().getUserId()!)}>
|
||||
<a href={link} onClick={this.onLinkClick}>
|
||||
{link}
|
||||
</a>
|
||||
|
@ -1296,7 +1299,7 @@ export default class InviteDialog extends React.PureComponent<Props, IInviteDial
|
|||
|
||||
let dialogContent;
|
||||
if (this.props.kind === KIND_CALL_TRANSFER) {
|
||||
const tabs = [];
|
||||
const tabs: Tab[] = [];
|
||||
tabs.push(
|
||||
new Tab(TabId.UserDirectory, _td("User Directory"), "mx_InviteDialog_userDirectoryIcon", usersSection),
|
||||
);
|
||||
|
|
|
@ -179,7 +179,7 @@ const toPublicRoomResult = (publicRoom: IPublicRoomsChunkRoom): IPublicRoomResul
|
|||
publicRoom.name?.toLowerCase(),
|
||||
sanitizeHtml(publicRoom.topic?.toLowerCase() ?? "", { allowedTags: [] }),
|
||||
...(publicRoom.aliases?.map((it) => it.toLowerCase()) || []),
|
||||
].filter(Boolean),
|
||||
].filter(Boolean) as string[],
|
||||
});
|
||||
|
||||
const toRoomResult = (room: Room): IRoomResult => {
|
||||
|
@ -310,7 +310,7 @@ const SpotlightDialog: React.FC<IProps> = ({ 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<boolean>(false);
|
||||
const trimmedQuery = useMemo(() => query.trim(), [query]);
|
||||
|
||||
|
@ -465,7 +465,7 @@ const SpotlightDialog: React.FC<IProps> = ({ 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<HTMLInputElement>): void => {
|
||||
const newQuery = e.currentTarget.value;
|
||||
|
@ -473,7 +473,7 @@ const SpotlightDialog: React.FC<IProps> = ({ 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<IProps> = ({ initialText = "", initialFilter = n
|
|||
onFinished();
|
||||
};
|
||||
|
||||
let otherSearchesSection: JSX.Element;
|
||||
let otherSearchesSection: JSX.Element | undefined;
|
||||
if (trimmedQuery || filter !== Filter.PublicRooms) {
|
||||
otherSearchesSection = (
|
||||
<div
|
||||
|
@ -693,7 +693,7 @@ const SpotlightDialog: React.FC<IProps> = ({ initialText = "", initialFilter = n
|
|||
);
|
||||
};
|
||||
|
||||
let peopleSection: JSX.Element;
|
||||
let peopleSection: JSX.Element | undefined;
|
||||
if (results[Section.People].length) {
|
||||
peopleSection = (
|
||||
<div
|
||||
|
@ -707,7 +707,7 @@ const SpotlightDialog: React.FC<IProps> = ({ initialText = "", initialFilter = n
|
|||
);
|
||||
}
|
||||
|
||||
let suggestionsSection: JSX.Element;
|
||||
let suggestionsSection: JSX.Element | undefined;
|
||||
if (results[Section.Suggestions].length && filter === Filter.People) {
|
||||
suggestionsSection = (
|
||||
<div
|
||||
|
@ -721,7 +721,7 @@ const SpotlightDialog: React.FC<IProps> = ({ initialText = "", initialFilter = n
|
|||
);
|
||||
}
|
||||
|
||||
let roomsSection: JSX.Element;
|
||||
let roomsSection: JSX.Element | undefined;
|
||||
if (results[Section.Rooms].length) {
|
||||
roomsSection = (
|
||||
<div
|
||||
|
@ -735,7 +735,7 @@ const SpotlightDialog: React.FC<IProps> = ({ initialText = "", initialFilter = n
|
|||
);
|
||||
}
|
||||
|
||||
let spacesSection: JSX.Element;
|
||||
let spacesSection: JSX.Element | undefined;
|
||||
if (results[Section.Spaces].length) {
|
||||
spacesSection = (
|
||||
<div
|
||||
|
@ -749,7 +749,7 @@ const SpotlightDialog: React.FC<IProps> = ({ initialText = "", initialFilter = n
|
|||
);
|
||||
}
|
||||
|
||||
let publicRoomsSection: JSX.Element;
|
||||
let publicRoomsSection: JSX.Element | undefined;
|
||||
if (filter === Filter.PublicRooms) {
|
||||
publicRoomsSection = (
|
||||
<div
|
||||
|
@ -791,7 +791,7 @@ const SpotlightDialog: React.FC<IProps> = ({ initialText = "", initialFilter = n
|
|||
);
|
||||
}
|
||||
|
||||
let spaceRoomsSection: JSX.Element;
|
||||
let spaceRoomsSection: JSX.Element | undefined;
|
||||
if (spaceResults.length && activeSpace && filter === null) {
|
||||
spaceRoomsSection = (
|
||||
<div
|
||||
|
@ -836,7 +836,7 @@ const SpotlightDialog: React.FC<IProps> = ({ 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<IProps> = ({ initialText = "", initialFilter = n
|
|||
);
|
||||
}
|
||||
|
||||
let hiddenResultsSection: JSX.Element;
|
||||
let hiddenResultsSection: JSX.Element | undefined;
|
||||
if (filter === Filter.People) {
|
||||
hiddenResultsSection = (
|
||||
<div className="mx_SpotlightDialog_section mx_SpotlightDialog_hiddenResults" role="group">
|
||||
|
@ -921,7 +921,7 @@ const SpotlightDialog: React.FC<IProps> = ({ initialText = "", initialFilter = n
|
|||
);
|
||||
}
|
||||
|
||||
let groupChatSection: JSX.Element;
|
||||
let groupChatSection: JSX.Element | undefined;
|
||||
if (filter === Filter.People) {
|
||||
groupChatSection = (
|
||||
<div
|
||||
|
@ -941,7 +941,7 @@ const SpotlightDialog: React.FC<IProps> = ({ initialText = "", initialFilter = n
|
|||
);
|
||||
}
|
||||
|
||||
let messageSearchSection: JSX.Element;
|
||||
let messageSearchSection: JSX.Element | undefined;
|
||||
if (filter === null) {
|
||||
messageSearchSection = (
|
||||
<div
|
||||
|
@ -977,7 +977,7 @@ const SpotlightDialog: React.FC<IProps> = ({ initialText = "", initialFilter = n
|
|||
</>
|
||||
);
|
||||
} else {
|
||||
let recentSearchesSection: JSX.Element;
|
||||
let recentSearchesSection: JSX.Element | undefined;
|
||||
if (recentSearches.length) {
|
||||
recentSearchesSection = (
|
||||
<div
|
||||
|
@ -1075,7 +1075,7 @@ const SpotlightDialog: React.FC<IProps> = ({ initialText = "", initialFilter = n
|
|||
break;
|
||||
}
|
||||
|
||||
let ref: RefObject<HTMLElement>;
|
||||
let ref: RefObject<HTMLElement> | undefined;
|
||||
const accessibilityAction = getKeyBindingsManager().getAccessibilityAction(ev);
|
||||
switch (accessibilityAction) {
|
||||
case KeyBindingAction.Escape:
|
||||
|
|
|
@ -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<Pick<IProps, "avatarUrl" | "avatarDisabled" |
|
|||
setAvatar(file);
|
||||
const reader = new FileReader();
|
||||
reader.onload = (ev) => {
|
||||
setAvatarDataUrl(ev.target.result as string);
|
||||
setAvatarDataUrl(ev.target?.result as string);
|
||||
};
|
||||
reader.readAsDataURL(file);
|
||||
}}
|
||||
|
|
|
@ -53,7 +53,7 @@ const SpecificChildrenPicker: React.FC<ISpecificChildrenPickerProps> = ({
|
|||
|
||||
const matcher = new QueryMatcher<Room>(rooms, {
|
||||
keys: ["name"],
|
||||
funcs: [(r) => [r.getCanonicalAlias(), ...r.getAltAliases()].filter(Boolean)],
|
||||
funcs: [(r) => [r.getCanonicalAlias(), ...r.getAltAliases()].filter(Boolean) as string[]],
|
||||
shouldMatchWordsOnly: false,
|
||||
});
|
||||
|
||||
|
|
|
@ -245,14 +245,14 @@ export const SpaceCreateForm: React.FC<ISpaceCreateFormProps> = ({
|
|||
const SpaceCreateMenu: React.FC<{
|
||||
onFinished(): void;
|
||||
}> = ({ onFinished }) => {
|
||||
const [visibility, setVisibility] = useState<Visibility>(null);
|
||||
const [visibility, setVisibility] = useState<Visibility | null>(null);
|
||||
const [busy, setBusy] = useState<boolean>(false);
|
||||
|
||||
const [name, setName] = useState("");
|
||||
const spaceNameField = useRef<Field>();
|
||||
const [alias, setAlias] = useState("");
|
||||
const spaceAliasField = useRef<RoomAliasField>();
|
||||
const [avatar, setAvatar] = useState<File>(null);
|
||||
const [avatar, setAvatar] = useState<File | undefined>(undefined);
|
||||
const [topic, setTopic] = useState<string>("");
|
||||
|
||||
const onSpaceCreateClick = async (e: ButtonEvent): Promise<void> => {
|
||||
|
|
|
@ -215,7 +215,7 @@ const CreateSpaceButton: React.FC<Pick<IInnerSpacePanelProps, "isPanelCollapsed"
|
|||
}
|
||||
}, [isPanelCollapsed]); // eslint-disable-line react-hooks/exhaustive-deps
|
||||
|
||||
let contextMenu = null;
|
||||
let contextMenu: JSX.Element | undefined;
|
||||
if (menuDisplayed) {
|
||||
contextMenu = <SpaceCreateMenu onFinished={closeMenu} />;
|
||||
}
|
||||
|
|
|
@ -121,11 +121,11 @@ export const SpaceButton = forwardRef<HTMLElement, IButtonProps>(
|
|||
);
|
||||
}
|
||||
|
||||
let contextMenu: JSX.Element;
|
||||
if (menuDisplayed && ContextMenuComponent) {
|
||||
let contextMenu: JSX.Element | undefined;
|
||||
if (menuDisplayed && handle.current && ContextMenuComponent) {
|
||||
contextMenu = (
|
||||
<ContextMenuComponent
|
||||
{...toRightOf(handle.current?.getBoundingClientRect(), 0)}
|
||||
{...toRightOf(handle.current.getBoundingClientRect(), 0)}
|
||||
space={space}
|
||||
onFinished={closeMenu}
|
||||
/>
|
||||
|
@ -242,7 +242,7 @@ export class SpaceItem extends React.PureComponent<IItemProps, IItemState> {
|
|||
}
|
||||
|
||||
private get isCollapsed(): boolean {
|
||||
return this.state.collapsed || this.props.isPanelCollapsed;
|
||||
return this.state.collapsed || !!this.props.isPanelCollapsed;
|
||||
}
|
||||
|
||||
private toggleCollapse = (evt: ButtonEvent): void => {
|
||||
|
|
|
@ -30,7 +30,7 @@ const onClickSendDm = (ev: ButtonEvent): void => {
|
|||
};
|
||||
|
||||
interface Props {
|
||||
useCase: UseCase;
|
||||
useCase: UseCase | null;
|
||||
}
|
||||
|
||||
export function UserOnboardingHeader({ useCase }: Props): JSX.Element {
|
||||
|
|
|
@ -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<boolean>(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);
|
||||
|
|
|
@ -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<IButtonProps> {
|
||||
|
@ -60,16 +61,24 @@ class DialPadButton extends React.PureComponent<IButtonProps> {
|
|||
}
|
||||
}
|
||||
|
||||
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<IProps> {
|
||||
interface IProps extends IBaseProps {
|
||||
hasDial: false;
|
||||
}
|
||||
|
||||
interface IDialProps extends IBaseProps {
|
||||
hasDial: true;
|
||||
onDialPress: () => void;
|
||||
}
|
||||
|
||||
export default class Dialpad extends React.PureComponent<XOR<IProps, IDialProps>> {
|
||||
public render(): React.ReactNode {
|
||||
const buttonNodes = [];
|
||||
const buttonNodes: JSX.Element[] = [];
|
||||
|
||||
for (let i = 0; i < BUTTONS.length; i++) {
|
||||
const button = BUTTONS[i];
|
||||
|
|
|
@ -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<IProps, IState> {
|
|||
}
|
||||
};
|
||||
|
||||
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<IProps, IState> {
|
|||
};
|
||||
}
|
||||
|
||||
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<IProps, IState> {
|
|||
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<IProps, IState> {
|
|||
);
|
||||
}
|
||||
|
||||
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<IProps, IState> {
|
|||
|
||||
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<IProps, IState> {
|
|||
);
|
||||
} 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<IProps, IState> {
|
|||
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,
|
||||
|
|
|
@ -104,9 +104,9 @@ const LegacyCallViewDropdownButton: React.FC<IDropdownButtonProps> = ({ state, d
|
|||
onHover={(hovering) => setHoveringDropdown(hovering)}
|
||||
state={state}
|
||||
/>
|
||||
{menuDisplayed && (
|
||||
{menuDisplayed && buttonRef.current && (
|
||||
<DeviceContextMenu
|
||||
{...alwaysAboveRightOf(buttonRef.current?.getBoundingClientRect())}
|
||||
{...alwaysAboveRightOf(buttonRef.current.getBoundingClientRect())}
|
||||
onFinished={closeMenu}
|
||||
deviceKinds={deviceKinds}
|
||||
/>
|
||||
|
@ -117,7 +117,7 @@ const LegacyCallViewDropdownButton: React.FC<IDropdownButtonProps> = ({ 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<IProps, IState> {
|
||||
private dialpadButton = createRef<HTMLDivElement>();
|
||||
private contextMenuButton = createRef<HTMLDivElement>();
|
||||
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<IProps, IStat
|
|||
});
|
||||
|
||||
let dialPad;
|
||||
if (this.state.showDialpad) {
|
||||
if (this.state.showDialpad && this.dialpadButton.current) {
|
||||
dialPad = (
|
||||
<DialpadContextMenu
|
||||
{...alwaysMenuProps(
|
||||
|
@ -231,7 +231,7 @@ export default class LegacyCallViewButtons extends React.Component<IProps, IStat
|
|||
ChevronFace.None,
|
||||
CONTEXT_MENU_VPADDING,
|
||||
)}
|
||||
// We mount the context menus as a as a child typically in order to include the
|
||||
// We mount the context menus as a child typically in order to include the
|
||||
// context menus when fullscreening the call content.
|
||||
// However, this does not work as well when the call is embedded in a
|
||||
// picture-in-picture frame. Thus, only mount as child when we are *not* in PiP.
|
||||
|
@ -243,7 +243,7 @@ export default class LegacyCallViewButtons extends React.Component<IProps, IStat
|
|||
}
|
||||
|
||||
let contextMenu;
|
||||
if (this.state.showMoreMenu) {
|
||||
if (this.state.showMoreMenu && this.contextMenuButton.current) {
|
||||
contextMenu = (
|
||||
<LegacyCallContextMenu
|
||||
{...alwaysMenuProps(
|
||||
|
|
|
@ -71,9 +71,9 @@ const SecondaryCallInfo: React.FC<ISecondaryCallInfoProps> = ({ callRoom }) => {
|
|||
};
|
||||
|
||||
interface LegacyCallViewHeaderProps {
|
||||
pipMode: boolean;
|
||||
callRooms?: Room[];
|
||||
onPipMouseDown: (event: React.MouseEvent<Element, MouseEvent>) => void;
|
||||
pipMode?: boolean;
|
||||
callRooms: [Room, Room | null];
|
||||
onPipMouseDown?: (event: React.MouseEvent<Element, MouseEvent>) => void;
|
||||
onExpand?: () => void;
|
||||
onPin?: () => void;
|
||||
onMaximize?: () => void;
|
||||
|
@ -81,7 +81,7 @@ interface LegacyCallViewHeaderProps {
|
|||
|
||||
const LegacyCallViewHeader: React.FC<LegacyCallViewHeaderProps> = ({
|
||||
pipMode = false,
|
||||
callRooms = [],
|
||||
callRooms,
|
||||
onPipMouseDown,
|
||||
onExpand,
|
||||
onPin,
|
||||
|
|
|
@ -95,7 +95,7 @@ export default class VideoFeed extends React.PureComponent<IProps, IState> {
|
|||
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) {
|
||||
|
|
|
@ -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!);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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<void> {
|
||||
const pos = callback();
|
||||
let acPromise: Promise<void> = null;
|
||||
let acPromise: Promise<void> | null = null;
|
||||
if (!(pos instanceof Range)) {
|
||||
acPromise = this.setActivePart(pos, true);
|
||||
} else {
|
||||
acPromise = Promise.resolve();
|
||||
}
|
||||
this.updateCallback(pos);
|
||||
this.updateCallback?.(pos);
|
||||
return acPromise;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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, "<br/>");
|
||||
}
|
||||
|
@ -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;
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 = <T>(settingName: string, roomId: string = null, excludeDefault = false): T => {
|
||||
export const useSettingValue = <T>(settingName: string, roomId: string | null = null, excludeDefault = false): T => {
|
||||
const [value, setValue] = useState(SettingsStore.getValue<T>(settingName, roomId, excludeDefault));
|
||||
|
||||
useEffect(() => {
|
||||
|
@ -36,7 +36,7 @@ export const useSettingValue = <T>(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<boolean>(featureName, roomId));
|
||||
|
||||
useEffect(() => {
|
||||
|
|
|
@ -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,
|
||||
)!;
|
||||
|
|
|
@ -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]);
|
||||
|
|
|
@ -22,7 +22,7 @@ import { useLatestResult } from "./useLatestResult";
|
|||
|
||||
export interface IUserDirectoryOpts {
|
||||
limit: number;
|
||||
query?: string;
|
||||
query: string;
|
||||
}
|
||||
|
||||
export const useUserDirectory = (): {
|
||||
|
|
|
@ -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<void> {
|
||||
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<void> {
|
||||
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<void> => {
|
||||
const indexManager = PlatformPeg.get().getEventIndexingManager();
|
||||
private onSync = async (state: SyncState, prevState: SyncState | null, data?: ISyncStateData): Promise<void> => {
|
||||
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<void> => {
|
||||
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<void> {
|
||||
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<void> {
|
||||
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<void> {
|
||||
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<IResultRoomEvents> {
|
||||
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<MatrixEvent[]> {
|
||||
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<boolean> {
|
||||
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<boolean> => {
|
||||
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<IIndexStats>} A promise that will resolve to the index
|
||||
* statistics.
|
||||
*/
|
||||
public async getStats(): Promise<IIndexStats> {
|
||||
const indexManager = PlatformPeg.get().getEventIndexingManager();
|
||||
return indexManager.getStats();
|
||||
public async getStats(): Promise<IIndexStats | undefined> {
|
||||
const indexManager = PlatformPeg.get()?.getEventIndexingManager();
|
||||
return indexManager?.getStats();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -914,9 +915,9 @@ export default class EventIndex extends EventEmitter {
|
|||
* @return {Promise<boolean>} Returns true if the index contains events for
|
||||
* the given room, false otherwise.
|
||||
*/
|
||||
public async isRoomIndexed(roomId: string): Promise<boolean> {
|
||||
const indexManager = PlatformPeg.get().getEventIndexingManager();
|
||||
return indexManager.isRoomIndexed(roomId);
|
||||
public async isRoomIndexed(roomId: string): Promise<boolean | undefined> {
|
||||
const indexManager = PlatformPeg.get()?.getEventIndexingManager();
|
||||
return indexManager?.isRoomIndexed(roomId);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -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<boolean> {
|
||||
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<boolean> {
|
||||
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<void> {
|
||||
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();
|
||||
|
|
|
@ -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<void> {
|
||||
public async open(room: Room, screen?: string, integrationId?: string): Promise<void> {
|
||||
if (!SettingsStore.getValue("integrationProvisioning")) {
|
||||
return IntegrationManagers.sharedInstance().showDisabledDialog();
|
||||
}
|
||||
|
|
|
@ -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<IntegrationManagerInstance>} Resolves to an integration manager instance,
|
||||
* or null if none was found.
|
||||
*/
|
||||
public async tryDiscoverManager(domainName: string): Promise<IntegrationManagerInstance> {
|
||||
public async tryDiscoverManager(domainName: string): Promise<IntegrationManagerInstance | null> {
|
||||
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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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<ViewUserPayload>({
|
||||
action: Action.ViewUser,
|
||||
member: member,
|
||||
member: new User(userId),
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -101,10 +101,10 @@ export class ProxiedModuleApi implements ModuleApi {
|
|||
password: string,
|
||||
displayName?: string,
|
||||
): Promise<AccountAuthInfo> {
|
||||
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);
|
||||
|
|
|
@ -53,6 +53,7 @@ export class PushRuleVectorState {
|
|||
} else if (pushRuleVectorState === VectorState.Loud) {
|
||||
return StandardActions.ACTION_HIGHLIGHT_DEFAULT_SOUND;
|
||||
}
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -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<T>(
|
||||
store: IDBIndex | IDBObjectStore,
|
||||
keyRange: IDBKeyRange,
|
||||
keyRange: IDBKeyRange | undefined,
|
||||
resultMapper: (cursor: IDBCursorWithValue) => T,
|
||||
): Promise<T[]> {
|
||||
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 = () => {
|
||||
|
|
|
@ -437,7 +437,7 @@ export default class SettingsStore {
|
|||
level: SettingLevel,
|
||||
roomId: string | null,
|
||||
calculatedValue: any,
|
||||
calculatedAtLevel: SettingLevel,
|
||||
calculatedAtLevel: SettingLevel | null,
|
||||
): any {
|
||||
let resultingValue = calculatedValue;
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -38,7 +38,7 @@ export class RoomScrollStateStore {
|
|||
// from the focussedEvent.
|
||||
private scrollStateMap = new Map<string, ScrollState>();
|
||||
|
||||
public getScrollState(roomId: string): ScrollState {
|
||||
public getScrollState(roomId: string): ScrollState | undefined {
|
||||
return this.scrollStateMap.get(roomId);
|
||||
}
|
||||
|
||||
|
|
|
@ -712,7 +712,7 @@ export class RoomViewStore extends EventEmitter {
|
|||
}
|
||||
|
||||
// The mxEvent if one is currently being replied to/quoted
|
||||
public getQuotingEvent(): Optional<MatrixEvent> {
|
||||
public getQuotingEvent(): MatrixEvent | null {
|
||||
return this.state.replyingToEvent;
|
||||
}
|
||||
|
||||
|
|
|
@ -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<IState> impl
|
|||
private tagIdToSortAlgo: Record<TagID, SortAlgorithm> = {};
|
||||
private tagMap: ITagMap = {};
|
||||
private counts: Record<TagID, number> = {};
|
||||
private stickyRoomId: string | null;
|
||||
private stickyRoomId: Optional<string>;
|
||||
|
||||
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<void> {
|
||||
|
|
|
@ -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 });
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -137,7 +137,7 @@ export class SpaceStoreClass extends AsyncStoreWithClient<IState> {
|
|||
userIdsBySpace: new Map<Room["roomId"], Set<string>>(),
|
||||
};
|
||||
// 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<Room>();
|
||||
private spaceOrderLocalEchoMap = new Map<string, string>();
|
||||
|
@ -812,13 +812,13 @@ export class SpaceStoreClass extends AsyncStoreWithClient<IState> {
|
|||
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<IState> {
|
|||
// 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<IState> {
|
|||
|
||||
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);
|
||||
|
|
|
@ -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>): string => {
|
||||
const getSpaceCollapsedKey = (roomId: string, parents?: Set<string>): string => {
|
||||
const separator = "/";
|
||||
let path = "";
|
||||
if (parents) {
|
||||
|
@ -35,12 +35,12 @@ export default class SpaceTreeLevelLayoutStore {
|
|||
return SpaceTreeLevelLayoutStore.internalInstance;
|
||||
}
|
||||
|
||||
public setSpaceCollapsedState(roomId: string, parents: Set<string>, collapsed: boolean): void {
|
||||
public setSpaceCollapsedState(roomId: string, parents: Set<string> | undefined, collapsed: boolean): void {
|
||||
// XXX: localStorage doesn't allow booleans
|
||||
localStorage.setItem(getSpaceCollapsedKey(roomId, parents), collapsed.toString());
|
||||
}
|
||||
|
||||
public getSpaceCollapsedState(roomId: string, parents: Set<string>, fallback: boolean): boolean {
|
||||
public getSpaceCollapsedState(roomId: string, parents: Set<string> | undefined, fallback: boolean): boolean {
|
||||
const collapsedLocalStorage = localStorage.getItem(getSpaceCollapsedKey(roomId, parents));
|
||||
// XXX: localStorage doesn't allow booleans
|
||||
return collapsedLocalStorage ? collapsedLocalStorage === "true" : fallback;
|
||||
|
|
|
@ -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(),
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -85,11 +85,11 @@ export const showToast = (kind: Kind): void => {
|
|||
|
||||
const onAccept = async (): Promise<void> => {
|
||||
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,
|
||||
|
|
|
@ -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<boolean> {
|
||||
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<bool
|
|||
}
|
||||
}
|
||||
|
||||
return terms && terms["policies"] && Object.keys(terms["policies"]).length > 0;
|
||||
return !!terms?.["policies"] && Object.keys(terms["policies"]).length > 0;
|
||||
}
|
||||
|
||||
export function doesAccountDataHaveIdentityServer(): boolean {
|
||||
|
|
|
@ -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<string>;
|
||||
public readonly thumbnailUrl: LazyValue<string>;
|
||||
public readonly sourceUrl: LazyValue<string | null>;
|
||||
public readonly thumbnailUrl: LazyValue<string | null>;
|
||||
|
||||
// Either the raw or decrypted (when encrypted) contents of the file.
|
||||
public readonly sourceBlob: LazyValue<Blob>;
|
||||
public readonly thumbnailBlob: LazyValue<Blob>;
|
||||
public readonly thumbnailBlob: LazyValue<Blob | null>;
|
||||
|
||||
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<string> => {
|
||||
private prepareSourceUrl = async (): Promise<string | null> => {
|
||||
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<string> => {
|
||||
private prepareThumbnailUrl = async (): Promise<string | null> => {
|
||||
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<Blob> => {
|
||||
if (this.media.isEncrypted) {
|
||||
const content = this.event.getContent<IMediaEventContent>();
|
||||
return decryptFile(content.file, content.info);
|
||||
return decryptFile(content.file!, content.info);
|
||||
}
|
||||
return this.media.downloadSource().then((r) => r.blob());
|
||||
};
|
||||
|
||||
private fetchThumbnail = (): Promise<Blob> => {
|
||||
private fetchThumbnail = (): Promise<Blob | null> => {
|
||||
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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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?
|
||||
|
||||
|
|
|
@ -173,7 +173,7 @@ export class ThreepidMember extends Member {
|
|||
|
||||
export interface IDMUserTileProps {
|
||||
member: Member;
|
||||
onRemove(member: Member): void;
|
||||
onRemove?(member: Member): void;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -37,7 +37,7 @@ import { bulkSpaceBehaviour } from "./space";
|
|||
import { SdkContextClass } from "../contexts/SDKContext";
|
||||
|
||||
export async function leaveRoomBehaviour(roomId: string, retry = true, spinner = true): Promise<void> {
|
||||
let spinnerModal: IHandle<any>;
|
||||
let spinnerModal: IHandle<any> | 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) =>
|
||||
|
|
|
@ -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]);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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<Element>, 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<Element>, 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.
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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<ISendEventResponse>({ event_id: "$event_id" });
|
||||
});
|
||||
|
||||
describe("sendStickerContentToRoom", () => {
|
||||
|
@ -98,7 +98,7 @@ describe("ContentMessages", () => {
|
|||
mocked(doMaybeLocalRoomAction).mockImplementation(
|
||||
<T>(roomId: string, fn: (actualRoomId: string) => Promise<T>) => 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") {
|
||||
(<HTMLVideoElement>element).load = jest.fn();
|
||||
(<HTMLVideoElement>element).play = () => element.onloadeddata(new Event("loadeddata"));
|
||||
(<HTMLVideoElement>element).play = () => element.onloadeddata!(new Event("loadeddata"));
|
||||
(<HTMLVideoElement>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);
|
||||
});
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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() + "");
|
||||
|
|
|
@ -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(() => {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -109,7 +109,7 @@ describe("<ExportDialog />", () => {
|
|||
plainTextExporterInstance.export.mockClear();
|
||||
|
||||
// default setting value
|
||||
ChatExportMock.getForceChatExportParameters.mockClear().mockReturnValue({});
|
||||
mocked(ChatExportMock.getForceChatExportParameters!).mockClear().mockReturnValue({});
|
||||
});
|
||||
|
||||
it("renders export dialog", () => {
|
||||
|
@ -145,7 +145,7 @@ describe("<ExportDialog />", () => {
|
|||
});
|
||||
|
||||
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("<ExportDialog />", () => {
|
|||
});
|
||||
|
||||
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("<ExportDialog />", () => {
|
|||
});
|
||||
|
||||
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("<ExportDialog />", () => {
|
|||
});
|
||||
|
||||
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("<ExportDialog />", () => {
|
|||
* 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("<ExportDialog />", () => {
|
|||
});
|
||||
|
||||
it("does not render input when set in ForceRoomExportParameters", () => {
|
||||
ChatExportMock.getForceChatExportParameters.mockReturnValue({
|
||||
mocked(ChatExportMock.getForceChatExportParameters!).mockReturnValue({
|
||||
includeAttachments: false,
|
||||
});
|
||||
const component = getComponent();
|
||||
|
|
|
@ -74,7 +74,7 @@ describe("<DevicesPanel />", () => {
|
|||
|
||||
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("<DevicesPanel />", () => {
|
|||
|
||||
// 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);
|
||||
});
|
||||
|
||||
|
|
|
@ -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(() => {
|
||||
|
|
|
@ -923,7 +923,7 @@ describe("ElementCall", () => {
|
|||
jest.spyOn(Modal, "createDialog").mockReturnValue({
|
||||
finished: new Promise((r) => r([sourceId])),
|
||||
} as IHandle<any[]>);
|
||||
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<any[]>);
|
||||
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();
|
||||
|
||||
|
|
|
@ -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", () => {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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: {},
|
||||
});
|
||||
|
|
|
@ -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", () => {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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) =>
|
||||
|
|
|
@ -37,7 +37,7 @@ export function untilDispatch(
|
|||
dispatcher = defaultDispatcher,
|
||||
timeout = 1000,
|
||||
): Promise<ActionPayload> {
|
||||
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<void> {
|
||||
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;
|
||||
|
|
|
@ -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");
|
||||
});
|
||||
|
|
|
@ -42,7 +42,7 @@ describe("useTopic", () => {
|
|||
|
||||
function RoomTopic() {
|
||||
const topic = useTopic(room);
|
||||
return <p>{topic.text}</p>;
|
||||
return <p>{topic!.text}</p>;
|
||||
}
|
||||
|
||||
render(<RoomTopic />);
|
||||
|
|
|
@ -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(`-----`);
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue