You've already forked matrix-react-sdk
							
							
				mirror of
				https://github.com/matrix-org/matrix-react-sdk.git
				synced 2025-11-03 00:33:22 +03:00 
			
		
		
		
	Merge branch 'develop' into andybalaam/bump-analytics-events-to-0.4.0
This commit is contained in:
		@@ -100,8 +100,12 @@ module.exports = {
 | 
			
		||||
            files: ["src/**/*.{ts,tsx}", "test/**/*.{ts,tsx}", "cypress/**/*.ts"],
 | 
			
		||||
            extends: ["plugin:matrix-org/typescript", "plugin:matrix-org/react"],
 | 
			
		||||
            rules: {
 | 
			
		||||
                // temporary disabled
 | 
			
		||||
                "@typescript-eslint/explicit-function-return-type": "off",
 | 
			
		||||
                "@typescript-eslint/explicit-function-return-type": [
 | 
			
		||||
                    "error",
 | 
			
		||||
                    {
 | 
			
		||||
                        allowExpressions: true,
 | 
			
		||||
                    },
 | 
			
		||||
                ],
 | 
			
		||||
 | 
			
		||||
                // Things we do that break the ideal style
 | 
			
		||||
                "prefer-promise-reject-errors": "off",
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										8
									
								
								src/@types/diff-dom.d.ts
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										8
									
								
								src/@types/diff-dom.d.ts
									
									
									
									
										vendored
									
									
								
							@@ -20,10 +20,10 @@ declare module "diff-dom" {
 | 
			
		||||
        name: string;
 | 
			
		||||
        text?: string;
 | 
			
		||||
        route: number[];
 | 
			
		||||
        value: string;
 | 
			
		||||
        element: unknown;
 | 
			
		||||
        oldValue: string;
 | 
			
		||||
        newValue: string;
 | 
			
		||||
        value: HTMLElement | string;
 | 
			
		||||
        element: HTMLElement | string;
 | 
			
		||||
        oldValue: HTMLElement | string;
 | 
			
		||||
        newValue: HTMLElement | string;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    interface IOpts {}
 | 
			
		||||
 
 | 
			
		||||
@@ -15,7 +15,7 @@ limitations under the License.
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
// This is intended to fix re-resizer because of its unguarded `instanceof TouchEvent` checks.
 | 
			
		||||
export function polyfillTouchEvent() {
 | 
			
		||||
export function polyfillTouchEvent(): void {
 | 
			
		||||
    // Firefox doesn't have touch events without touch devices being present, so create a fake
 | 
			
		||||
    // one we can rely on lying about.
 | 
			
		||||
    if (!window.TouchEvent) {
 | 
			
		||||
 
 | 
			
		||||
@@ -47,7 +47,7 @@ export default class AsyncWrapper extends React.Component<IProps, IState> {
 | 
			
		||||
        error: null,
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    public componentDidMount() {
 | 
			
		||||
    public componentDidMount(): void {
 | 
			
		||||
        // XXX: temporary logging to try to diagnose
 | 
			
		||||
        // https://github.com/vector-im/element-web/issues/3148
 | 
			
		||||
        logger.log("Starting load of AsyncWrapper for modal");
 | 
			
		||||
@@ -69,15 +69,15 @@ export default class AsyncWrapper extends React.Component<IProps, IState> {
 | 
			
		||||
            });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public componentWillUnmount() {
 | 
			
		||||
    public componentWillUnmount(): void {
 | 
			
		||||
        this.unmounted = true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private onWrapperCancelClick = () => {
 | 
			
		||||
    private onWrapperCancelClick = (): void => {
 | 
			
		||||
        this.props.onFinished(false);
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    public render() {
 | 
			
		||||
    public render(): JSX.Element {
 | 
			
		||||
        if (this.state.component) {
 | 
			
		||||
            const Component = this.state.component;
 | 
			
		||||
            return <Component {...this.props} />;
 | 
			
		||||
 
 | 
			
		||||
@@ -137,7 +137,12 @@ export function getInitialLetter(name: string): string {
 | 
			
		||||
    return split(name, "", 1)[0].toUpperCase();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export function avatarUrlForRoom(room: Room, width: number, height: number, resizeMethod?: ResizeMethod) {
 | 
			
		||||
export function avatarUrlForRoom(
 | 
			
		||||
    room: Room,
 | 
			
		||||
    width: number,
 | 
			
		||||
    height: number,
 | 
			
		||||
    resizeMethod?: ResizeMethod,
 | 
			
		||||
): string | null {
 | 
			
		||||
    if (!room) return null; // null-guard
 | 
			
		||||
 | 
			
		||||
    if (room.getMxcAvatarUrl()) {
 | 
			
		||||
 
 | 
			
		||||
@@ -272,7 +272,7 @@ export default abstract class BasePlatform {
 | 
			
		||||
        return null;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public setLanguage(preferredLangs: string[]) {}
 | 
			
		||||
    public setLanguage(preferredLangs: string[]): void {}
 | 
			
		||||
 | 
			
		||||
    public setSpellCheckEnabled(enabled: boolean): void {}
 | 
			
		||||
 | 
			
		||||
@@ -280,7 +280,7 @@ export default abstract class BasePlatform {
 | 
			
		||||
        return null;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public setSpellCheckLanguages(preferredLangs: string[]) {}
 | 
			
		||||
    public setSpellCheckLanguages(preferredLangs: string[]): void {}
 | 
			
		||||
 | 
			
		||||
    public getSpellCheckLanguages(): Promise<string[]> | null {
 | 
			
		||||
        return null;
 | 
			
		||||
 
 | 
			
		||||
@@ -40,7 +40,7 @@ export class BlurhashEncoder {
 | 
			
		||||
        this.worker.onmessage = this.onMessage;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private onMessage = (ev: MessageEvent<IBlurhashWorkerResponse>) => {
 | 
			
		||||
    private onMessage = (ev: MessageEvent<IBlurhashWorkerResponse>): void => {
 | 
			
		||||
        const { seq, blurhash } = ev.data;
 | 
			
		||||
        const deferred = this.pendingDeferredMap.get(seq);
 | 
			
		||||
        if (deferred) {
 | 
			
		||||
 
 | 
			
		||||
@@ -68,16 +68,20 @@ interface IMediaConfig {
 | 
			
		||||
 * @param {File} imageFile The file to load in an image element.
 | 
			
		||||
 * @return {Promise} A promise that resolves with the html image element.
 | 
			
		||||
 */
 | 
			
		||||
async function loadImageElement(imageFile: File) {
 | 
			
		||||
async function loadImageElement(imageFile: File): Promise<{
 | 
			
		||||
    width: number;
 | 
			
		||||
    height: number;
 | 
			
		||||
    img: HTMLImageElement;
 | 
			
		||||
}> {
 | 
			
		||||
    // Load the file into an html element
 | 
			
		||||
    const img = new Image();
 | 
			
		||||
    const objectUrl = URL.createObjectURL(imageFile);
 | 
			
		||||
    const imgPromise = new Promise((resolve, reject) => {
 | 
			
		||||
        img.onload = function () {
 | 
			
		||||
        img.onload = function (): void {
 | 
			
		||||
            URL.revokeObjectURL(objectUrl);
 | 
			
		||||
            resolve(img);
 | 
			
		||||
        };
 | 
			
		||||
        img.onerror = function (e) {
 | 
			
		||||
        img.onerror = function (e): void {
 | 
			
		||||
            reject(e);
 | 
			
		||||
        };
 | 
			
		||||
    });
 | 
			
		||||
@@ -185,13 +189,13 @@ function loadVideoElement(videoFile: File): Promise<HTMLVideoElement> {
 | 
			
		||||
 | 
			
		||||
        const reader = new FileReader();
 | 
			
		||||
 | 
			
		||||
        reader.onload = function (ev) {
 | 
			
		||||
        reader.onload = function (ev): void {
 | 
			
		||||
            // Wait until we have enough data to thumbnail the first frame.
 | 
			
		||||
            video.onloadeddata = async function () {
 | 
			
		||||
            video.onloadeddata = async function (): Promise<void> {
 | 
			
		||||
                resolve(video);
 | 
			
		||||
                video.pause();
 | 
			
		||||
            };
 | 
			
		||||
            video.onerror = function (e) {
 | 
			
		||||
            video.onerror = function (e): void {
 | 
			
		||||
                reject(e);
 | 
			
		||||
            };
 | 
			
		||||
 | 
			
		||||
@@ -206,7 +210,7 @@ function loadVideoElement(videoFile: File): Promise<HTMLVideoElement> {
 | 
			
		||||
            video.load();
 | 
			
		||||
            video.play();
 | 
			
		||||
        };
 | 
			
		||||
        reader.onerror = function (e) {
 | 
			
		||||
        reader.onerror = function (e): void {
 | 
			
		||||
            reject(e);
 | 
			
		||||
        };
 | 
			
		||||
        reader.readAsDataURL(videoFile);
 | 
			
		||||
@@ -253,10 +257,10 @@ function infoForVideoFile(
 | 
			
		||||
function readFileAsArrayBuffer(file: File | Blob): Promise<ArrayBuffer> {
 | 
			
		||||
    return new Promise((resolve, reject) => {
 | 
			
		||||
        const reader = new FileReader();
 | 
			
		||||
        reader.onload = function (e) {
 | 
			
		||||
        reader.onload = function (e): void {
 | 
			
		||||
            resolve(e.target.result as ArrayBuffer);
 | 
			
		||||
        };
 | 
			
		||||
        reader.onerror = function (e) {
 | 
			
		||||
        reader.onerror = function (e): void {
 | 
			
		||||
            reject(e);
 | 
			
		||||
        };
 | 
			
		||||
        reader.readAsArrayBuffer(file);
 | 
			
		||||
@@ -461,7 +465,7 @@ export default class ContentMessages {
 | 
			
		||||
        matrixClient: MatrixClient,
 | 
			
		||||
        replyToEvent: MatrixEvent | undefined,
 | 
			
		||||
        promBefore?: Promise<any>,
 | 
			
		||||
    ) {
 | 
			
		||||
    ): Promise<void> {
 | 
			
		||||
        const fileName = file.name || _t("Attachment");
 | 
			
		||||
        const content: Omit<IMediaEventContent, "info"> & { info: Partial<IMediaEventInfo> } = {
 | 
			
		||||
            body: fileName,
 | 
			
		||||
@@ -491,7 +495,7 @@ export default class ContentMessages {
 | 
			
		||||
        this.inprogress.push(upload);
 | 
			
		||||
        dis.dispatch<UploadStartedPayload>({ action: Action.UploadStarted, upload });
 | 
			
		||||
 | 
			
		||||
        function onProgress(progress: UploadProgress) {
 | 
			
		||||
        function onProgress(progress: UploadProgress): void {
 | 
			
		||||
            upload.onProgress(progress);
 | 
			
		||||
            dis.dispatch<UploadProgressPayload>({ action: Action.UploadProgress, upload });
 | 
			
		||||
        }
 | 
			
		||||
@@ -568,7 +572,7 @@ export default class ContentMessages {
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private isFileSizeAcceptable(file: File) {
 | 
			
		||||
    private isFileSizeAcceptable(file: File): boolean {
 | 
			
		||||
        if (
 | 
			
		||||
            this.mediaConfig !== null &&
 | 
			
		||||
            this.mediaConfig["m.upload.size"] !== undefined &&
 | 
			
		||||
@@ -599,7 +603,7 @@ export default class ContentMessages {
 | 
			
		||||
            });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static sharedInstance() {
 | 
			
		||||
    public static sharedInstance(): ContentMessages {
 | 
			
		||||
        if (window.mxContentMessages === undefined) {
 | 
			
		||||
            window.mxContentMessages = new ContentMessages();
 | 
			
		||||
        }
 | 
			
		||||
 
 | 
			
		||||
@@ -188,7 +188,7 @@ export function wantsDateSeparator(prevEventDate: Date, nextEventDate: Date): bo
 | 
			
		||||
    return prevEventDate.getDay() !== nextEventDate.getDay();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export function formatFullDateNoDay(date: Date) {
 | 
			
		||||
export function formatFullDateNoDay(date: Date): string {
 | 
			
		||||
    return _t("%(date)s at %(time)s", {
 | 
			
		||||
        date: date.toLocaleDateString().replace(/\//g, "-"),
 | 
			
		||||
        time: date.toLocaleTimeString().replace(/:/g, "-"),
 | 
			
		||||
@@ -205,7 +205,7 @@ export function formatFullDateNoDayISO(date: Date): string {
 | 
			
		||||
    return date.toISOString();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export function formatFullDateNoDayNoTime(date: Date) {
 | 
			
		||||
export function formatFullDateNoDayNoTime(date: Date): string {
 | 
			
		||||
    return date.getFullYear() + "/" + pad(date.getMonth() + 1) + "/" + pad(date.getDate());
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -19,6 +19,7 @@ import { logger } from "matrix-js-sdk/src/logger";
 | 
			
		||||
import { CryptoEvent } from "matrix-js-sdk/src/crypto";
 | 
			
		||||
import { ClientEvent, EventType, RoomStateEvent } from "matrix-js-sdk/src/matrix";
 | 
			
		||||
import { SyncState } from "matrix-js-sdk/src/sync";
 | 
			
		||||
import { IKeyBackupInfo } from "matrix-js-sdk/src/crypto/keybackup";
 | 
			
		||||
 | 
			
		||||
import { MatrixClientPeg } from "./MatrixClientPeg";
 | 
			
		||||
import dis from "./dispatcher/dispatcher";
 | 
			
		||||
@@ -56,7 +57,7 @@ export default class DeviceListener {
 | 
			
		||||
    // has the user dismissed any of the various nag toasts to setup encryption on this device?
 | 
			
		||||
    private dismissedThisDeviceToast = false;
 | 
			
		||||
    // cache of the key backup info
 | 
			
		||||
    private keyBackupInfo: object = null;
 | 
			
		||||
    private keyBackupInfo: IKeyBackupInfo | null = null;
 | 
			
		||||
    private keyBackupFetchedAt: number = null;
 | 
			
		||||
    private keyBackupStatusChecked = false;
 | 
			
		||||
    // We keep a list of our own device IDs so we can batch ones that were already
 | 
			
		||||
@@ -70,12 +71,12 @@ export default class DeviceListener {
 | 
			
		||||
    private enableBulkUnverifiedSessionsReminder = true;
 | 
			
		||||
    private deviceClientInformationSettingWatcherRef: string | undefined;
 | 
			
		||||
 | 
			
		||||
    public static sharedInstance() {
 | 
			
		||||
    public static sharedInstance(): DeviceListener {
 | 
			
		||||
        if (!window.mxDeviceListener) window.mxDeviceListener = new DeviceListener();
 | 
			
		||||
        return window.mxDeviceListener;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public start() {
 | 
			
		||||
    public start(): void {
 | 
			
		||||
        this.running = true;
 | 
			
		||||
        MatrixClientPeg.get().on(CryptoEvent.WillUpdateDevices, this.onWillUpdateDevices);
 | 
			
		||||
        MatrixClientPeg.get().on(CryptoEvent.DevicesUpdated, this.onDevicesUpdated);
 | 
			
		||||
@@ -98,7 +99,7 @@ export default class DeviceListener {
 | 
			
		||||
        this.updateClientInformation();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public stop() {
 | 
			
		||||
    public stop(): void {
 | 
			
		||||
        this.running = false;
 | 
			
		||||
        if (MatrixClientPeg.get()) {
 | 
			
		||||
            MatrixClientPeg.get().removeListener(CryptoEvent.WillUpdateDevices, this.onWillUpdateDevices);
 | 
			
		||||
@@ -134,7 +135,7 @@ export default class DeviceListener {
 | 
			
		||||
     *
 | 
			
		||||
     * @param {String[]} deviceIds List of device IDs to dismiss notifications for
 | 
			
		||||
     */
 | 
			
		||||
    public async dismissUnverifiedSessions(deviceIds: Iterable<string>) {
 | 
			
		||||
    public async dismissUnverifiedSessions(deviceIds: Iterable<string>): Promise<void> {
 | 
			
		||||
        logger.log("Dismissing unverified sessions: " + Array.from(deviceIds).join(","));
 | 
			
		||||
        for (const d of deviceIds) {
 | 
			
		||||
            this.dismissed.add(d);
 | 
			
		||||
@@ -143,19 +144,19 @@ export default class DeviceListener {
 | 
			
		||||
        this.recheck();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public dismissEncryptionSetup() {
 | 
			
		||||
    public dismissEncryptionSetup(): void {
 | 
			
		||||
        this.dismissedThisDeviceToast = true;
 | 
			
		||||
        this.recheck();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private ensureDeviceIdsAtStartPopulated() {
 | 
			
		||||
    private ensureDeviceIdsAtStartPopulated(): void {
 | 
			
		||||
        if (this.ourDeviceIdsAtStart === null) {
 | 
			
		||||
            const cli = MatrixClientPeg.get();
 | 
			
		||||
            this.ourDeviceIdsAtStart = new Set(cli.getStoredDevicesForUser(cli.getUserId()).map((d) => d.deviceId));
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private onWillUpdateDevices = async (users: string[], initialFetch?: boolean) => {
 | 
			
		||||
    private onWillUpdateDevices = async (users: string[], initialFetch?: boolean): Promise<void> => {
 | 
			
		||||
        // If we didn't know about *any* devices before (ie. it's fresh login),
 | 
			
		||||
        // then they are all pre-existing devices, so ignore this and set the
 | 
			
		||||
        // devicesAtStart list to the devices that we see after the fetch.
 | 
			
		||||
@@ -168,26 +169,26 @@ export default class DeviceListener {
 | 
			
		||||
        // before we download any new ones.
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    private onDevicesUpdated = (users: string[]) => {
 | 
			
		||||
    private onDevicesUpdated = (users: string[]): void => {
 | 
			
		||||
        if (!users.includes(MatrixClientPeg.get().getUserId())) return;
 | 
			
		||||
        this.recheck();
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    private onDeviceVerificationChanged = (userId: string) => {
 | 
			
		||||
    private onDeviceVerificationChanged = (userId: string): void => {
 | 
			
		||||
        if (userId !== MatrixClientPeg.get().getUserId()) return;
 | 
			
		||||
        this.recheck();
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    private onUserTrustStatusChanged = (userId: string) => {
 | 
			
		||||
    private onUserTrustStatusChanged = (userId: string): void => {
 | 
			
		||||
        if (userId !== MatrixClientPeg.get().getUserId()) return;
 | 
			
		||||
        this.recheck();
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    private onCrossSingingKeysChanged = () => {
 | 
			
		||||
    private onCrossSingingKeysChanged = (): void => {
 | 
			
		||||
        this.recheck();
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    private onAccountData = (ev: MatrixEvent) => {
 | 
			
		||||
    private onAccountData = (ev: MatrixEvent): void => {
 | 
			
		||||
        // User may have:
 | 
			
		||||
        // * migrated SSSS to symmetric
 | 
			
		||||
        // * uploaded keys to secret storage
 | 
			
		||||
@@ -202,13 +203,13 @@ export default class DeviceListener {
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    private onSync = (state: SyncState, prevState?: SyncState) => {
 | 
			
		||||
    private onSync = (state: SyncState, prevState?: SyncState): void => {
 | 
			
		||||
        if (state === "PREPARED" && prevState === null) {
 | 
			
		||||
            this.recheck();
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    private onRoomStateEvents = (ev: MatrixEvent) => {
 | 
			
		||||
    private onRoomStateEvents = (ev: MatrixEvent): void => {
 | 
			
		||||
        if (ev.getType() !== EventType.RoomEncryption) return;
 | 
			
		||||
 | 
			
		||||
        // If a room changes to encrypted, re-check as it may be our first
 | 
			
		||||
@@ -216,7 +217,7 @@ export default class DeviceListener {
 | 
			
		||||
        this.recheck();
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    private onAction = ({ action }: ActionPayload) => {
 | 
			
		||||
    private onAction = ({ action }: ActionPayload): void => {
 | 
			
		||||
        if (action !== Action.OnLoggedIn) return;
 | 
			
		||||
        this.recheck();
 | 
			
		||||
        this.updateClientInformation();
 | 
			
		||||
@@ -224,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() {
 | 
			
		||||
    private async getKeyBackupInfo(): Promise<IKeyBackupInfo> {
 | 
			
		||||
        const now = new Date().getTime();
 | 
			
		||||
        if (!this.keyBackupInfo || this.keyBackupFetchedAt < now - KEY_BACKUP_POLL_INTERVAL) {
 | 
			
		||||
            this.keyBackupInfo = await MatrixClientPeg.get().getKeyBackupVersion();
 | 
			
		||||
@@ -233,7 +234,7 @@ export default class DeviceListener {
 | 
			
		||||
        return this.keyBackupInfo;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private shouldShowSetupEncryptionToast() {
 | 
			
		||||
    private shouldShowSetupEncryptionToast(): boolean {
 | 
			
		||||
        // If we're in the middle of a secret storage operation, we're likely
 | 
			
		||||
        // modifying the state involved here, so don't add new toasts to setup.
 | 
			
		||||
        if (isSecretStorageBeingAccessed()) return false;
 | 
			
		||||
@@ -242,7 +243,7 @@ export default class DeviceListener {
 | 
			
		||||
        return cli && cli.getRooms().some((r) => cli.isRoomEncrypted(r.roomId));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private async recheck() {
 | 
			
		||||
    private async recheck(): Promise<void> {
 | 
			
		||||
        if (!this.running) return; // we have been stopped
 | 
			
		||||
        const cli = MatrixClientPeg.get();
 | 
			
		||||
 | 
			
		||||
@@ -359,7 +360,7 @@ export default class DeviceListener {
 | 
			
		||||
        this.displayingToastsForDeviceIds = newUnverifiedDeviceIds;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private checkKeyBackupStatus = async () => {
 | 
			
		||||
    private checkKeyBackupStatus = async (): Promise<void> => {
 | 
			
		||||
        if (this.keyBackupStatusChecked) {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
@@ -388,7 +389,7 @@ export default class DeviceListener {
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    private updateClientInformation = async () => {
 | 
			
		||||
    private updateClientInformation = async (): Promise<void> => {
 | 
			
		||||
        try {
 | 
			
		||||
            if (this.shouldRecordClientInformation) {
 | 
			
		||||
                await recordClientInformation(MatrixClientPeg.get(), SdkConfig.get(), PlatformPeg.get());
 | 
			
		||||
 
 | 
			
		||||
@@ -16,5 +16,6 @@ limitations under the License.
 | 
			
		||||
 | 
			
		||||
import { TimelineRenderingType } from "./contexts/RoomContext";
 | 
			
		||||
 | 
			
		||||
export const editorRoomKey = (roomId: string, context: TimelineRenderingType) => `mx_edit_room_${roomId}_${context}`;
 | 
			
		||||
export const editorStateKey = (eventId: string) => `mx_edit_state_${eventId}`;
 | 
			
		||||
export const editorRoomKey = (roomId: string, context: TimelineRenderingType): string =>
 | 
			
		||||
    `mx_edit_room_${roomId}_${context}`;
 | 
			
		||||
export const editorStateKey = (eventId: string): string => `mx_edit_state_${eventId}`;
 | 
			
		||||
 
 | 
			
		||||
@@ -449,9 +449,9 @@ export interface IOptsReturnString extends IOpts {
 | 
			
		||||
    returnString: true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const emojiToHtmlSpan = (emoji: string) =>
 | 
			
		||||
const emojiToHtmlSpan = (emoji: string): string =>
 | 
			
		||||
    `<span class='mx_Emoji' title='${unicodeToShortcode(emoji)}'>${emoji}</span>`;
 | 
			
		||||
const emojiToJsxSpan = (emoji: string, key: number) => (
 | 
			
		||||
const emojiToJsxSpan = (emoji: string, key: number): JSX.Element => (
 | 
			
		||||
    <span key={key} className="mx_Emoji" title={unicodeToShortcode(emoji)}>
 | 
			
		||||
        {emoji}
 | 
			
		||||
    </span>
 | 
			
		||||
@@ -505,7 +505,7 @@ function formatEmojis(message: string, isHtmlMessage: boolean): (JSX.Element | s
 | 
			
		||||
 */
 | 
			
		||||
export function bodyToHtml(content: IContent, highlights: Optional<string[]>, opts: IOptsReturnString): string;
 | 
			
		||||
export function bodyToHtml(content: IContent, highlights: Optional<string[]>, opts: IOptsReturnNode): ReactNode;
 | 
			
		||||
export function bodyToHtml(content: IContent, highlights: Optional<string[]>, opts: IOpts = {}) {
 | 
			
		||||
export function bodyToHtml(content: IContent, highlights: Optional<string[]>, opts: IOpts = {}): ReactNode | string {
 | 
			
		||||
    const isFormattedBody = content.format === "org.matrix.custom.html" && !!content.formatted_body;
 | 
			
		||||
    let bodyHasEmoji = false;
 | 
			
		||||
    let isHtmlMessage = false;
 | 
			
		||||
 
 | 
			
		||||
@@ -28,7 +28,7 @@ limitations under the License.
 | 
			
		||||
 * consume in the timeline, when performing scroll offset calculations
 | 
			
		||||
 * (e.g. scroll locking)
 | 
			
		||||
 */
 | 
			
		||||
export function thumbHeight(fullWidth: number, fullHeight: number, thumbWidth: number, thumbHeight: number) {
 | 
			
		||||
export function thumbHeight(fullWidth: number, fullHeight: number, thumbWidth: number, thumbHeight: number): number {
 | 
			
		||||
    if (!fullWidth || !fullHeight) {
 | 
			
		||||
        // Cannot calculate thumbnail height for image: missing w/h in metadata. We can't even
 | 
			
		||||
        // log this because it's spammy
 | 
			
		||||
 
 | 
			
		||||
@@ -76,7 +76,7 @@ export const Key = {
 | 
			
		||||
 | 
			
		||||
export const IS_MAC = navigator.platform.toUpperCase().includes("MAC");
 | 
			
		||||
 | 
			
		||||
export function isOnlyCtrlOrCmdKeyEvent(ev) {
 | 
			
		||||
export function isOnlyCtrlOrCmdKeyEvent(ev: KeyboardEvent): boolean {
 | 
			
		||||
    if (IS_MAC) {
 | 
			
		||||
        return ev.metaKey && !ev.altKey && !ev.ctrlKey && !ev.shiftKey;
 | 
			
		||||
    } else {
 | 
			
		||||
 
 | 
			
		||||
@@ -169,7 +169,7 @@ export default class LegacyCallHandler extends EventEmitter {
 | 
			
		||||
 | 
			
		||||
    private silencedCalls = new Set<string>(); // callIds
 | 
			
		||||
 | 
			
		||||
    public static get instance() {
 | 
			
		||||
    public static get instance(): LegacyCallHandler {
 | 
			
		||||
        if (!window.mxLegacyCallHandler) {
 | 
			
		||||
            window.mxLegacyCallHandler = new LegacyCallHandler();
 | 
			
		||||
        }
 | 
			
		||||
@@ -456,7 +456,7 @@ export default class LegacyCallHandler extends EventEmitter {
 | 
			
		||||
        return callsNotInThatRoom;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public getAllActiveCallsForPip(roomId: string) {
 | 
			
		||||
    public getAllActiveCallsForPip(roomId: string): MatrixCall[] {
 | 
			
		||||
        const room = MatrixClientPeg.get().getRoom(roomId);
 | 
			
		||||
        if (WidgetLayoutStore.instance.hasMaximisedWidget(room)) {
 | 
			
		||||
            // This checks if there is space for the call view in the aux panel
 | 
			
		||||
@@ -478,7 +478,7 @@ export default class LegacyCallHandler extends EventEmitter {
 | 
			
		||||
        const audio = document.getElementById(audioId) as HTMLMediaElement;
 | 
			
		||||
        if (audio) {
 | 
			
		||||
            this.addEventListenersForAudioElement(audio);
 | 
			
		||||
            const playAudio = async () => {
 | 
			
		||||
            const playAudio = async (): Promise<void> => {
 | 
			
		||||
                try {
 | 
			
		||||
                    if (audio.muted) {
 | 
			
		||||
                        logger.error(
 | 
			
		||||
@@ -524,7 +524,7 @@ export default class LegacyCallHandler extends EventEmitter {
 | 
			
		||||
        // TODO: Attach an invisible element for this instead
 | 
			
		||||
        // which listens?
 | 
			
		||||
        const audio = document.getElementById(audioId) as HTMLMediaElement;
 | 
			
		||||
        const pauseAudio = () => {
 | 
			
		||||
        const pauseAudio = (): void => {
 | 
			
		||||
            logger.debug(`${logPrefix} pausing audio`);
 | 
			
		||||
            // pause doesn't return a promise, so just do it
 | 
			
		||||
            audio.pause();
 | 
			
		||||
@@ -600,7 +600,7 @@ export default class LegacyCallHandler extends EventEmitter {
 | 
			
		||||
            this.setCallListeners(newCall);
 | 
			
		||||
            this.setCallState(newCall, newCall.state);
 | 
			
		||||
        });
 | 
			
		||||
        call.on(CallEvent.AssertedIdentityChanged, async () => {
 | 
			
		||||
        call.on(CallEvent.AssertedIdentityChanged, async (): Promise<void> => {
 | 
			
		||||
            if (!this.matchesCallForThisRoom(call)) return;
 | 
			
		||||
 | 
			
		||||
            logger.log(`Call ID ${call.callId} got new asserted identity:`, call.getRemoteAssertedIdentity());
 | 
			
		||||
@@ -808,7 +808,7 @@ export default class LegacyCallHandler extends EventEmitter {
 | 
			
		||||
 | 
			
		||||
    private showICEFallbackPrompt(): void {
 | 
			
		||||
        const cli = MatrixClientPeg.get();
 | 
			
		||||
        const code = (sub) => <code>{sub}</code>;
 | 
			
		||||
        const code = (sub: string): JSX.Element => <code>{sub}</code>;
 | 
			
		||||
        Modal.createDialog(
 | 
			
		||||
            QuestionDialog,
 | 
			
		||||
            {
 | 
			
		||||
 
 | 
			
		||||
@@ -219,7 +219,7 @@ export function attemptTokenLogin(
 | 
			
		||||
    })
 | 
			
		||||
        .then(function (creds) {
 | 
			
		||||
            logger.log("Logged in with token");
 | 
			
		||||
            return clearStorage().then(async () => {
 | 
			
		||||
            return clearStorage().then(async (): Promise<boolean> => {
 | 
			
		||||
                await persistCredentials(creds);
 | 
			
		||||
                // remember that we just logged in
 | 
			
		||||
                sessionStorage.setItem("mx_fresh_login", String(true));
 | 
			
		||||
@@ -406,7 +406,7 @@ async function pickleKeyToAesKey(pickleKey: string): Promise<Uint8Array> {
 | 
			
		||||
    );
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
async function abortLogin() {
 | 
			
		||||
async function abortLogin(): Promise<void> {
 | 
			
		||||
    const signOut = await showStorageEvictedDialog();
 | 
			
		||||
    if (signOut) {
 | 
			
		||||
        await clearStorage();
 | 
			
		||||
 
 | 
			
		||||
@@ -20,14 +20,14 @@ import { MatrixClientPeg } from "./MatrixClientPeg";
 | 
			
		||||
import SdkConfig from "./SdkConfig";
 | 
			
		||||
import { ElementWidgetActions } from "./stores/widgets/ElementWidgetActions";
 | 
			
		||||
 | 
			
		||||
export function getConfigLivestreamUrl() {
 | 
			
		||||
export function getConfigLivestreamUrl(): string | undefined {
 | 
			
		||||
    return SdkConfig.get("audio_stream_url");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Dummy rtmp URL used to signal that we want a special audio-only stream
 | 
			
		||||
const AUDIOSTREAM_DUMMY_URL = "rtmp://audiostream.dummy/";
 | 
			
		||||
 | 
			
		||||
async function createLiveStream(roomId: string) {
 | 
			
		||||
async function createLiveStream(roomId: string): Promise<void> {
 | 
			
		||||
    const openIdToken = await MatrixClientPeg.get().getOpenIdToken();
 | 
			
		||||
 | 
			
		||||
    const url = getConfigLivestreamUrl() + "/createStream";
 | 
			
		||||
@@ -47,7 +47,7 @@ async function createLiveStream(roomId: string) {
 | 
			
		||||
    return respBody["stream_id"];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export async function startJitsiAudioLivestream(widgetMessaging: ClientWidgetApi, roomId: string) {
 | 
			
		||||
export async function startJitsiAudioLivestream(widgetMessaging: ClientWidgetApi, roomId: string): Promise<void> {
 | 
			
		||||
    const streamId = await createLiveStream(roomId);
 | 
			
		||||
 | 
			
		||||
    await widgetMessaging.transport.send(ElementWidgetActions.StartLiveStream, {
 | 
			
		||||
 
 | 
			
		||||
@@ -122,7 +122,7 @@ export default class Login {
 | 
			
		||||
            initial_device_display_name: this.defaultDeviceDisplayName,
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        const tryFallbackHs = (originalError) => {
 | 
			
		||||
        const tryFallbackHs = (originalError: Error): Promise<IMatrixClientCreds> => {
 | 
			
		||||
            return sendLoginRequest(this.fallbackHsUrl, this.isUrl, "m.login.password", loginParams).catch(
 | 
			
		||||
                (fallbackError) => {
 | 
			
		||||
                    logger.log("fallback HS login failed", fallbackError);
 | 
			
		||||
 
 | 
			
		||||
@@ -56,7 +56,7 @@ function isMultiLine(node: commonmark.Node): boolean {
 | 
			
		||||
    return par.firstChild != par.lastChild;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function getTextUntilEndOrLinebreak(node: commonmark.Node) {
 | 
			
		||||
function getTextUntilEndOrLinebreak(node: commonmark.Node): string {
 | 
			
		||||
    let currentNode = node;
 | 
			
		||||
    let text = "";
 | 
			
		||||
    while (currentNode !== null && currentNode.type !== "softbreak" && currentNode.type !== "linebreak") {
 | 
			
		||||
@@ -137,7 +137,7 @@ export default class Markdown {
 | 
			
		||||
     * See: https://github.com/vector-im/element-web/issues/4674
 | 
			
		||||
     * @param parsed
 | 
			
		||||
     */
 | 
			
		||||
    private repairLinks(parsed: commonmark.Node) {
 | 
			
		||||
    private repairLinks(parsed: commonmark.Node): commonmark.Node {
 | 
			
		||||
        const walker = parsed.walker();
 | 
			
		||||
        let event: commonmark.NodeWalkingStep = null;
 | 
			
		||||
        let text = "";
 | 
			
		||||
 
 | 
			
		||||
@@ -77,7 +77,7 @@ export class ModalManager extends TypedEventEmitter<ModalManagerEvent, HandlerMa
 | 
			
		||||
    // Neither the static nor priority modal will be in this list.
 | 
			
		||||
    private modals: IModal<any>[] = [];
 | 
			
		||||
 | 
			
		||||
    private static getOrCreateContainer() {
 | 
			
		||||
    private static getOrCreateContainer(): HTMLElement {
 | 
			
		||||
        let container = document.getElementById(DIALOG_CONTAINER_ID);
 | 
			
		||||
 | 
			
		||||
        if (!container) {
 | 
			
		||||
@@ -89,7 +89,7 @@ export class ModalManager extends TypedEventEmitter<ModalManagerEvent, HandlerMa
 | 
			
		||||
        return container;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private static getOrCreateStaticContainer() {
 | 
			
		||||
    private static getOrCreateStaticContainer(): HTMLElement {
 | 
			
		||||
        let container = document.getElementById(STATIC_DIALOG_CONTAINER_ID);
 | 
			
		||||
 | 
			
		||||
        if (!container) {
 | 
			
		||||
@@ -101,31 +101,31 @@ export class ModalManager extends TypedEventEmitter<ModalManagerEvent, HandlerMa
 | 
			
		||||
        return container;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public toggleCurrentDialogVisibility() {
 | 
			
		||||
    public toggleCurrentDialogVisibility(): void {
 | 
			
		||||
        const modal = this.getCurrentModal();
 | 
			
		||||
        if (!modal) return;
 | 
			
		||||
        modal.hidden = !modal.hidden;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public hasDialogs() {
 | 
			
		||||
        return this.priorityModal || this.staticModal || this.modals.length > 0;
 | 
			
		||||
    public hasDialogs(): boolean {
 | 
			
		||||
        return !!this.priorityModal || !!this.staticModal || this.modals.length > 0;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public createDialog<T extends any[]>(
 | 
			
		||||
        Element: React.ComponentType<any>,
 | 
			
		||||
        ...rest: ParametersWithoutFirst<ModalManager["createDialogAsync"]>
 | 
			
		||||
    ) {
 | 
			
		||||
    ): IHandle<T> {
 | 
			
		||||
        return this.createDialogAsync<T>(Promise.resolve(Element), ...rest);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public appendDialog<T extends any[]>(
 | 
			
		||||
        Element: React.ComponentType,
 | 
			
		||||
        ...rest: ParametersWithoutFirst<ModalManager["appendDialogAsync"]>
 | 
			
		||||
    ) {
 | 
			
		||||
    ): IHandle<T> {
 | 
			
		||||
        return this.appendDialogAsync<T>(Promise.resolve(Element), ...rest);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public closeCurrentModal(reason: string) {
 | 
			
		||||
    public closeCurrentModal(reason: string): void {
 | 
			
		||||
        const modal = this.getCurrentModal();
 | 
			
		||||
        if (!modal) {
 | 
			
		||||
            return;
 | 
			
		||||
@@ -139,7 +139,11 @@ export class ModalManager extends TypedEventEmitter<ModalManagerEvent, HandlerMa
 | 
			
		||||
        props?: IProps<T>,
 | 
			
		||||
        className?: string,
 | 
			
		||||
        options?: IOptions<T>,
 | 
			
		||||
    ) {
 | 
			
		||||
    ): {
 | 
			
		||||
        modal: IModal<T>;
 | 
			
		||||
        closeDialog: IHandle<T>["close"];
 | 
			
		||||
        onFinishedProm: IHandle<T>["finished"];
 | 
			
		||||
    } {
 | 
			
		||||
        const modal: IModal<T> = {
 | 
			
		||||
            onFinished: props ? props.onFinished : null,
 | 
			
		||||
            onBeforeClose: options.onBeforeClose,
 | 
			
		||||
@@ -173,7 +177,7 @@ export class ModalManager extends TypedEventEmitter<ModalManagerEvent, HandlerMa
 | 
			
		||||
    ): [IHandle<T>["close"], IHandle<T>["finished"]] {
 | 
			
		||||
        const deferred = defer<T>();
 | 
			
		||||
        return [
 | 
			
		||||
            async (...args: T) => {
 | 
			
		||||
            async (...args: T): Promise<void> => {
 | 
			
		||||
                if (modal.beforeClosePromise) {
 | 
			
		||||
                    await modal.beforeClosePromise;
 | 
			
		||||
                } else if (modal.onBeforeClose) {
 | 
			
		||||
@@ -302,7 +306,7 @@ export class ModalManager extends TypedEventEmitter<ModalManagerEvent, HandlerMa
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private onBackgroundClick = () => {
 | 
			
		||||
    private onBackgroundClick = (): void => {
 | 
			
		||||
        const modal = this.getCurrentModal();
 | 
			
		||||
        if (!modal) {
 | 
			
		||||
            return;
 | 
			
		||||
@@ -320,7 +324,7 @@ export class ModalManager extends TypedEventEmitter<ModalManagerEvent, HandlerMa
 | 
			
		||||
        return this.priorityModal ? this.priorityModal : this.modals[0] || this.staticModal;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private async reRender() {
 | 
			
		||||
    private async reRender(): Promise<void> {
 | 
			
		||||
        // await next tick because sometimes ReactDOM can race with itself and cause the modal to wrongly stick around
 | 
			
		||||
        await sleep(0);
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -32,13 +32,13 @@ import { PlatformSetPayload } from "./dispatcher/payloads/PlatformSetPayload";
 | 
			
		||||
 * object.
 | 
			
		||||
 */
 | 
			
		||||
export class PlatformPeg {
 | 
			
		||||
    private platform: BasePlatform = null;
 | 
			
		||||
    private platform: BasePlatform | null = null;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Returns the current Platform object for the application.
 | 
			
		||||
     * This should be an instance of a class extending BasePlatform.
 | 
			
		||||
     */
 | 
			
		||||
    public get() {
 | 
			
		||||
    public get(): BasePlatform | null {
 | 
			
		||||
        return this.platform;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -46,7 +46,7 @@ export class PlatformPeg {
 | 
			
		||||
     * Sets the current platform handler object to use for the application.
 | 
			
		||||
     * @param {BasePlatform} platform an instance of a class extending BasePlatform.
 | 
			
		||||
     */
 | 
			
		||||
    public set(platform: BasePlatform) {
 | 
			
		||||
    public set(platform: BasePlatform): void {
 | 
			
		||||
        this.platform = platform;
 | 
			
		||||
        defaultDispatcher.dispatch<PlatformSetPayload>({
 | 
			
		||||
            action: Action.PlatformSet,
 | 
			
		||||
 
 | 
			
		||||
@@ -175,7 +175,7 @@ export class PosthogAnalytics {
 | 
			
		||||
        this.onLayoutUpdated();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private onLayoutUpdated = () => {
 | 
			
		||||
    private onLayoutUpdated = (): void => {
 | 
			
		||||
        let layout: UserProperties["WebLayout"];
 | 
			
		||||
 | 
			
		||||
        switch (SettingsStore.getValue("layout")) {
 | 
			
		||||
@@ -195,7 +195,7 @@ export class PosthogAnalytics {
 | 
			
		||||
        this.setProperty("WebLayout", layout);
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    private onAction = (payload: ActionPayload) => {
 | 
			
		||||
    private onAction = (payload: ActionPayload): void => {
 | 
			
		||||
        if (payload.action !== Action.SettingUpdated) return;
 | 
			
		||||
        const settingsPayload = payload as SettingUpdatedPayload;
 | 
			
		||||
        if (["layout", "useCompactLayout"].includes(settingsPayload.settingName)) {
 | 
			
		||||
@@ -232,7 +232,7 @@ export class PosthogAnalytics {
 | 
			
		||||
        return properties;
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    private registerSuperProperties(properties: Properties) {
 | 
			
		||||
    private registerSuperProperties(properties: Properties): void {
 | 
			
		||||
        if (this.enabled) {
 | 
			
		||||
            this.posthog.register(properties);
 | 
			
		||||
        }
 | 
			
		||||
@@ -255,7 +255,7 @@ export class PosthogAnalytics {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // eslint-disable-nextline no-unused-varsx
 | 
			
		||||
    private capture(eventName: string, properties: Properties, options?: IPostHogEventOptions) {
 | 
			
		||||
    private capture(eventName: string, properties: Properties, options?: IPostHogEventOptions): void {
 | 
			
		||||
        if (!this.enabled) {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 
 | 
			
		||||
@@ -107,20 +107,20 @@ export default class PosthogTrackers {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export class PosthogScreenTracker extends PureComponent<{ screenName: ScreenName }> {
 | 
			
		||||
    public componentDidMount() {
 | 
			
		||||
    public componentDidMount(): void {
 | 
			
		||||
        PosthogTrackers.instance.trackOverride(this.props.screenName);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public componentDidUpdate() {
 | 
			
		||||
    public componentDidUpdate(): void {
 | 
			
		||||
        // We do not clear the old override here so that we do not send the non-override screen as a transition
 | 
			
		||||
        PosthogTrackers.instance.trackOverride(this.props.screenName);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public componentWillUnmount() {
 | 
			
		||||
    public componentWillUnmount(): void {
 | 
			
		||||
        PosthogTrackers.instance.clearOverride(this.props.screenName);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public render() {
 | 
			
		||||
    public render(): JSX.Element {
 | 
			
		||||
        return null; // no need to render anything, we just need to hook into the React lifecycle
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -41,7 +41,7 @@ class Presence {
 | 
			
		||||
     * Start listening the user activity to evaluate his presence state.
 | 
			
		||||
     * Any state change will be sent to the homeserver.
 | 
			
		||||
     */
 | 
			
		||||
    public async start() {
 | 
			
		||||
    public async start(): Promise<void> {
 | 
			
		||||
        this.unavailableTimer = new Timer(UNAVAILABLE_TIME_MS);
 | 
			
		||||
        // the user_activity_start action starts the timer
 | 
			
		||||
        this.dispatcherRef = dis.register(this.onAction);
 | 
			
		||||
@@ -58,7 +58,7 @@ class Presence {
 | 
			
		||||
    /**
 | 
			
		||||
     * Stop tracking user activity
 | 
			
		||||
     */
 | 
			
		||||
    public stop() {
 | 
			
		||||
    public stop(): void {
 | 
			
		||||
        if (this.dispatcherRef) {
 | 
			
		||||
            dis.unregister(this.dispatcherRef);
 | 
			
		||||
            this.dispatcherRef = null;
 | 
			
		||||
@@ -73,11 +73,11 @@ class Presence {
 | 
			
		||||
     * Get the current presence state.
 | 
			
		||||
     * @returns {string} the presence state (see PRESENCE enum)
 | 
			
		||||
     */
 | 
			
		||||
    public getState() {
 | 
			
		||||
    public getState(): State {
 | 
			
		||||
        return this.state;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private onAction = (payload: ActionPayload) => {
 | 
			
		||||
    private onAction = (payload: ActionPayload): void => {
 | 
			
		||||
        if (payload.action === "user_activity") {
 | 
			
		||||
            this.setState(State.Online);
 | 
			
		||||
            this.unavailableTimer.restart();
 | 
			
		||||
@@ -89,7 +89,7 @@ class Presence {
 | 
			
		||||
     * If the state has changed, the homeserver will be notified.
 | 
			
		||||
     * @param {string} newState the new presence state (see PRESENCE enum)
 | 
			
		||||
     */
 | 
			
		||||
    private async setState(newState: State) {
 | 
			
		||||
    private async setState(newState: State): Promise<void> {
 | 
			
		||||
        if (newState === this.state) {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 
 | 
			
		||||
@@ -49,7 +49,7 @@ export default class ScalarAuthClient {
 | 
			
		||||
        this.isDefaultManager = apiUrl === configApiUrl && configUiUrl === uiUrl;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private writeTokenToStore() {
 | 
			
		||||
    private writeTokenToStore(): void {
 | 
			
		||||
        window.localStorage.setItem("mx_scalar_token_at_" + this.apiUrl, this.scalarToken);
 | 
			
		||||
        if (this.isDefaultManager) {
 | 
			
		||||
            // We remove the old token from storage to migrate upwards. This is safe
 | 
			
		||||
@@ -72,7 +72,7 @@ export default class ScalarAuthClient {
 | 
			
		||||
        return this.readTokenFromStore();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public setTermsInteractionCallback(callback) {
 | 
			
		||||
    public setTermsInteractionCallback(callback: TermsInteractionCallback): void {
 | 
			
		||||
        this.termsInteractionCallback = callback;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -711,7 +711,7 @@ function returnStateEvent(event: MessageEvent<any>, roomId: string, eventType: s
 | 
			
		||||
    sendResponse(event, stateEvent.getContent());
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
async function getOpenIdToken(event: MessageEvent<any>) {
 | 
			
		||||
async function getOpenIdToken(event: MessageEvent<any>): Promise<void> {
 | 
			
		||||
    try {
 | 
			
		||||
        const tokenObject = await MatrixClientPeg.get().getOpenIdToken();
 | 
			
		||||
        sendResponse(event, tokenObject);
 | 
			
		||||
@@ -728,7 +728,7 @@ async function sendEvent(
 | 
			
		||||
        content?: IContent;
 | 
			
		||||
    }>,
 | 
			
		||||
    roomId: string,
 | 
			
		||||
) {
 | 
			
		||||
): Promise<void> {
 | 
			
		||||
    const eventType = event.data.type;
 | 
			
		||||
    const stateKey = event.data.state_key;
 | 
			
		||||
    const content = event.data.content;
 | 
			
		||||
@@ -786,7 +786,7 @@ async function readEvents(
 | 
			
		||||
        limit?: number;
 | 
			
		||||
    }>,
 | 
			
		||||
    roomId: string,
 | 
			
		||||
) {
 | 
			
		||||
): Promise<void> {
 | 
			
		||||
    const eventType = event.data.type;
 | 
			
		||||
    const stateKey = event.data.state_key;
 | 
			
		||||
    const limit = event.data.limit;
 | 
			
		||||
 
 | 
			
		||||
@@ -56,7 +56,7 @@ export default class SdkConfig {
 | 
			
		||||
    private static instance: IConfigOptions;
 | 
			
		||||
    private static fallback: SnakedObject<IConfigOptions>;
 | 
			
		||||
 | 
			
		||||
    private static setInstance(i: IConfigOptions) {
 | 
			
		||||
    private static setInstance(i: IConfigOptions): void {
 | 
			
		||||
        SdkConfig.instance = i;
 | 
			
		||||
        SdkConfig.fallback = new SnakedObject(i);
 | 
			
		||||
 | 
			
		||||
@@ -90,18 +90,18 @@ export default class SdkConfig {
 | 
			
		||||
        return val === undefined ? undefined : null;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static put(cfg: Partial<IConfigOptions>) {
 | 
			
		||||
    public static put(cfg: Partial<IConfigOptions>): void {
 | 
			
		||||
        SdkConfig.setInstance({ ...DEFAULTS, ...cfg });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Resets the config to be completely empty.
 | 
			
		||||
     */
 | 
			
		||||
    public static unset() {
 | 
			
		||||
    public static unset(): void {
 | 
			
		||||
        SdkConfig.setInstance(<IConfigOptions>{}); // safe to cast - defaults will be applied
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static add(cfg: Partial<IConfigOptions>) {
 | 
			
		||||
    public static add(cfg: Partial<IConfigOptions>): void {
 | 
			
		||||
        SdkConfig.put({ ...SdkConfig.get(), ...cfg });
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -86,7 +86,7 @@ async function confirmToDismiss(): Promise<boolean> {
 | 
			
		||||
type KeyParams = { passphrase: string; recoveryKey: string };
 | 
			
		||||
 | 
			
		||||
function makeInputToKey(keyInfo: ISecretStorageKeyInfo): (keyParams: KeyParams) => Promise<Uint8Array> {
 | 
			
		||||
    return async ({ passphrase, recoveryKey }) => {
 | 
			
		||||
    return async ({ passphrase, recoveryKey }): Promise<Uint8Array> => {
 | 
			
		||||
        if (passphrase) {
 | 
			
		||||
            return deriveKey(passphrase, keyInfo.passphrase.salt, keyInfo.passphrase.iterations);
 | 
			
		||||
        } else {
 | 
			
		||||
@@ -151,7 +151,7 @@ async function getSecretStorageKey({
 | 
			
		||||
        /* props= */
 | 
			
		||||
        {
 | 
			
		||||
            keyInfo,
 | 
			
		||||
            checkPrivateKey: async (input: KeyParams) => {
 | 
			
		||||
            checkPrivateKey: async (input: KeyParams): Promise<boolean> => {
 | 
			
		||||
                const key = await inputToKey(input);
 | 
			
		||||
                return MatrixClientPeg.get().checkSecretStorageKey(key, keyInfo);
 | 
			
		||||
            },
 | 
			
		||||
@@ -160,7 +160,7 @@ async function getSecretStorageKey({
 | 
			
		||||
        /* isPriorityModal= */ false,
 | 
			
		||||
        /* isStaticModal= */ false,
 | 
			
		||||
        /* options= */ {
 | 
			
		||||
            onBeforeClose: async (reason) => {
 | 
			
		||||
            onBeforeClose: async (reason): Promise<boolean> => {
 | 
			
		||||
                if (reason === "backgroundClick") {
 | 
			
		||||
                    return confirmToDismiss();
 | 
			
		||||
                }
 | 
			
		||||
@@ -196,7 +196,7 @@ export async function getDehydrationKey(
 | 
			
		||||
        /* props= */
 | 
			
		||||
        {
 | 
			
		||||
            keyInfo,
 | 
			
		||||
            checkPrivateKey: async (input) => {
 | 
			
		||||
            checkPrivateKey: async (input): Promise<boolean> => {
 | 
			
		||||
                const key = await inputToKey(input);
 | 
			
		||||
                try {
 | 
			
		||||
                    checkFunc(key);
 | 
			
		||||
@@ -210,7 +210,7 @@ export async function getDehydrationKey(
 | 
			
		||||
        /* isPriorityModal= */ false,
 | 
			
		||||
        /* isStaticModal= */ false,
 | 
			
		||||
        /* options= */ {
 | 
			
		||||
            onBeforeClose: async (reason) => {
 | 
			
		||||
            onBeforeClose: async (reason): Promise<boolean> => {
 | 
			
		||||
                if (reason === "backgroundClick") {
 | 
			
		||||
                    return confirmToDismiss();
 | 
			
		||||
                }
 | 
			
		||||
@@ -324,7 +324,7 @@ export async function promptForBackupPassphrase(): Promise<Uint8Array> {
 | 
			
		||||
 * bootstrapped. Optional.
 | 
			
		||||
 * @param {bool} [forceReset] Reset secret storage even if it's already set up
 | 
			
		||||
 */
 | 
			
		||||
export async function accessSecretStorage(func = async () => {}, forceReset = false) {
 | 
			
		||||
export async function accessSecretStorage(func = async (): Promise<void> => {}, forceReset = false): Promise<void> {
 | 
			
		||||
    const cli = MatrixClientPeg.get();
 | 
			
		||||
    secretStorageBeingAccessed = true;
 | 
			
		||||
    try {
 | 
			
		||||
@@ -342,7 +342,7 @@ export async function accessSecretStorage(func = async () => {}, forceReset = fa
 | 
			
		||||
                /* priority = */ false,
 | 
			
		||||
                /* static = */ true,
 | 
			
		||||
                /* options = */ {
 | 
			
		||||
                    onBeforeClose: async (reason) => {
 | 
			
		||||
                    onBeforeClose: async (reason): Promise<boolean> => {
 | 
			
		||||
                        // If Secure Backup is required, you cannot leave the modal.
 | 
			
		||||
                        if (reason === "backgroundClick") {
 | 
			
		||||
                            return !isSecureBackupRequired();
 | 
			
		||||
@@ -357,7 +357,7 @@ export async function accessSecretStorage(func = async () => {}, forceReset = fa
 | 
			
		||||
            }
 | 
			
		||||
        } else {
 | 
			
		||||
            await cli.bootstrapCrossSigning({
 | 
			
		||||
                authUploadDeviceSigningKeys: async (makeRequest) => {
 | 
			
		||||
                authUploadDeviceSigningKeys: async (makeRequest): Promise<void> => {
 | 
			
		||||
                    const { finished } = Modal.createDialog(InteractiveAuthDialog, {
 | 
			
		||||
                        title: _t("Setting up keys"),
 | 
			
		||||
                        matrixClient: cli,
 | 
			
		||||
 
 | 
			
		||||
@@ -60,7 +60,7 @@ export default class SendHistoryManager {
 | 
			
		||||
        };
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public save(editorModel: EditorModel, replyEvent?: MatrixEvent) {
 | 
			
		||||
    public save(editorModel: EditorModel, replyEvent?: MatrixEvent): void {
 | 
			
		||||
        const item = SendHistoryManager.createItem(editorModel, replyEvent);
 | 
			
		||||
        this.history.push(item);
 | 
			
		||||
        this.currentIndex = this.history.length;
 | 
			
		||||
 
 | 
			
		||||
@@ -85,7 +85,7 @@ const singleMxcUpload = async (): Promise<string | null> => {
 | 
			
		||||
 | 
			
		||||
            Modal.createDialog(UploadConfirmDialog, {
 | 
			
		||||
                file,
 | 
			
		||||
                onFinished: async (shouldContinue) => {
 | 
			
		||||
                onFinished: async (shouldContinue): Promise<void> => {
 | 
			
		||||
                    if (shouldContinue) {
 | 
			
		||||
                        const { content_uri: uri } = await MatrixClientPeg.get().uploadContent(file);
 | 
			
		||||
                        resolve(uri);
 | 
			
		||||
@@ -151,11 +151,11 @@ export class Command {
 | 
			
		||||
        this.analyticsName = opts.analyticsName;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public getCommand() {
 | 
			
		||||
    public getCommand(): string {
 | 
			
		||||
        return `/${this.command}`;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public getCommandWithArgs() {
 | 
			
		||||
    public getCommandWithArgs(): string {
 | 
			
		||||
        return this.getCommand() + " " + this.args;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -184,7 +184,7 @@ export class Command {
 | 
			
		||||
        return this.runFn(roomId, args);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public getUsage() {
 | 
			
		||||
    public getUsage(): string {
 | 
			
		||||
        return _t("Usage") + ": " + this.getCommandWithArgs();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -193,15 +193,15 @@ export class Command {
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function reject(error) {
 | 
			
		||||
function reject(error?: any): RunResult {
 | 
			
		||||
    return { error };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function success(promise?: Promise<any>) {
 | 
			
		||||
function success(promise?: Promise<any>): RunResult {
 | 
			
		||||
    return { promise };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function successSync(value: any) {
 | 
			
		||||
function successSync(value: any): RunResult {
 | 
			
		||||
    return success(Promise.resolve(value));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -319,7 +319,7 @@ export const Commands = [
 | 
			
		||||
                );
 | 
			
		||||
 | 
			
		||||
                return success(
 | 
			
		||||
                    finished.then(async ([resp]) => {
 | 
			
		||||
                    finished.then(async ([resp]): Promise<void> => {
 | 
			
		||||
                        if (!resp?.continue) return;
 | 
			
		||||
                        await upgradeRoom(room, args, resp.invite);
 | 
			
		||||
                    }),
 | 
			
		||||
@@ -338,7 +338,7 @@ export const Commands = [
 | 
			
		||||
        runFn: function (roomId, args) {
 | 
			
		||||
            if (args) {
 | 
			
		||||
                return success(
 | 
			
		||||
                    (async () => {
 | 
			
		||||
                    (async (): Promise<void> => {
 | 
			
		||||
                        const unixTimestamp = Date.parse(args);
 | 
			
		||||
                        if (!unixTimestamp) {
 | 
			
		||||
                            throw newTranslatableError(
 | 
			
		||||
@@ -501,7 +501,9 @@ export const Commands = [
 | 
			
		||||
                ? ContentHelpers.parseTopicContent(content)
 | 
			
		||||
                : { text: _t("This room has no topic.") };
 | 
			
		||||
 | 
			
		||||
            const ref = (e) => e && linkifyElement(e);
 | 
			
		||||
            const ref = (e): void => {
 | 
			
		||||
                if (e) linkifyElement(e);
 | 
			
		||||
            };
 | 
			
		||||
            const body = topicToHtml(topic.text, topic.html, ref, true);
 | 
			
		||||
 | 
			
		||||
            Modal.createDialog(InfoDialog, {
 | 
			
		||||
@@ -1028,7 +1030,7 @@ export const Commands = [
 | 
			
		||||
                    const fingerprint = matches[3];
 | 
			
		||||
 | 
			
		||||
                    return success(
 | 
			
		||||
                        (async () => {
 | 
			
		||||
                        (async (): Promise<void> => {
 | 
			
		||||
                            const device = cli.getStoredDevice(userId, deviceId);
 | 
			
		||||
                            if (!device) {
 | 
			
		||||
                                throw newTranslatableError("Unknown (user, session) pair: (%(userId)s, %(deviceId)s)", {
 | 
			
		||||
@@ -1205,7 +1207,7 @@ export const Commands = [
 | 
			
		||||
        },
 | 
			
		||||
        runFn: (roomId) => {
 | 
			
		||||
            return success(
 | 
			
		||||
                (async () => {
 | 
			
		||||
                (async (): Promise<void> => {
 | 
			
		||||
                    const room = await VoipUserMapper.sharedInstance().getVirtualRoomForRoom(roomId);
 | 
			
		||||
                    if (!room) throw newTranslatableError("No virtual room for this room");
 | 
			
		||||
                    dis.dispatch<ViewRoomPayload>({
 | 
			
		||||
@@ -1231,7 +1233,7 @@ export const Commands = [
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return success(
 | 
			
		||||
                (async () => {
 | 
			
		||||
                (async (): Promise<void> => {
 | 
			
		||||
                    if (isPhoneNumber) {
 | 
			
		||||
                        const results = await LegacyCallHandler.instance.pstnLookup(userId);
 | 
			
		||||
                        if (!results || results.length === 0 || !results[0].userid) {
 | 
			
		||||
@@ -1265,7 +1267,7 @@ export const Commands = [
 | 
			
		||||
                    const [userId, msg] = matches.slice(1);
 | 
			
		||||
                    if (userId && userId.startsWith("@") && userId.includes(":")) {
 | 
			
		||||
                        return success(
 | 
			
		||||
                            (async () => {
 | 
			
		||||
                            (async (): Promise<void> => {
 | 
			
		||||
                                const cli = MatrixClientPeg.get();
 | 
			
		||||
                                const roomId = await ensureDMExists(cli, userId);
 | 
			
		||||
                                dis.dispatch<ViewRoomPayload>({
 | 
			
		||||
 
 | 
			
		||||
@@ -302,7 +302,7 @@ export class SlidingSyncManager {
 | 
			
		||||
     * @param batchSize The number of rooms to return in each request.
 | 
			
		||||
     * @param gapBetweenRequestsMs The number of milliseconds to wait between requests.
 | 
			
		||||
     */
 | 
			
		||||
    public async startSpidering(batchSize: number, gapBetweenRequestsMs: number) {
 | 
			
		||||
    public async startSpidering(batchSize: number, gapBetweenRequestsMs: number): Promise<void> {
 | 
			
		||||
        await sleep(gapBetweenRequestsMs); // wait a bit as this is called on first render so let's let things load
 | 
			
		||||
        const listIndex = this.getOrAllocateListIndex(SlidingSyncManager.ListSearch);
 | 
			
		||||
        let startIndex = batchSize;
 | 
			
		||||
 
 | 
			
		||||
@@ -75,7 +75,7 @@ export type TermsInteractionCallback = (
 | 
			
		||||
export async function startTermsFlow(
 | 
			
		||||
    services: Service[],
 | 
			
		||||
    interactionCallback: TermsInteractionCallback = dialogTermsInteractionCallback,
 | 
			
		||||
) {
 | 
			
		||||
): Promise<void> {
 | 
			
		||||
    const termsPromises = services.map((s) => MatrixClientPeg.get().getTerms(s.serviceType, s.baseUrl));
 | 
			
		||||
 | 
			
		||||
    /*
 | 
			
		||||
@@ -176,7 +176,7 @@ export async function startTermsFlow(
 | 
			
		||||
            urlsForService,
 | 
			
		||||
        );
 | 
			
		||||
    });
 | 
			
		||||
    return Promise.all(agreePromises);
 | 
			
		||||
    await Promise.all(agreePromises);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export async function dialogTermsInteractionCallback(
 | 
			
		||||
 
 | 
			
		||||
@@ -228,7 +228,7 @@ function textForTombstoneEvent(ev: MatrixEvent): () => string | null {
 | 
			
		||||
    return () => _t("%(senderDisplayName)s upgraded this room.", { senderDisplayName });
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const onViewJoinRuleSettingsClick = () => {
 | 
			
		||||
const onViewJoinRuleSettingsClick = (): void => {
 | 
			
		||||
    defaultDispatcher.dispatch({
 | 
			
		||||
        action: "open_room_settings",
 | 
			
		||||
        initial_tab_id: ROOM_SECURITY_TAB,
 | 
			
		||||
 
 | 
			
		||||
@@ -50,7 +50,7 @@ export default class UserActivity {
 | 
			
		||||
        this.activeRecentlyTimeout = new Timer(RECENTLY_ACTIVE_THRESHOLD_MS);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static sharedInstance() {
 | 
			
		||||
    public static sharedInstance(): UserActivity {
 | 
			
		||||
        if (window.mxUserActivity === undefined) {
 | 
			
		||||
            window.mxUserActivity = new UserActivity(window, document);
 | 
			
		||||
        }
 | 
			
		||||
@@ -66,7 +66,7 @@ export default class UserActivity {
 | 
			
		||||
     * later on when the user does become active.
 | 
			
		||||
     * @param {Timer} timer the timer to use
 | 
			
		||||
     */
 | 
			
		||||
    public timeWhileActiveNow(timer: Timer) {
 | 
			
		||||
    public timeWhileActiveNow(timer: Timer): void {
 | 
			
		||||
        this.timeWhile(timer, this.attachedActiveNowTimers);
 | 
			
		||||
        if (this.userActiveNow()) {
 | 
			
		||||
            timer.start();
 | 
			
		||||
@@ -82,14 +82,14 @@ export default class UserActivity {
 | 
			
		||||
     * later on when the user does become active.
 | 
			
		||||
     * @param {Timer} timer the timer to use
 | 
			
		||||
     */
 | 
			
		||||
    public timeWhileActiveRecently(timer: Timer) {
 | 
			
		||||
    public timeWhileActiveRecently(timer: Timer): void {
 | 
			
		||||
        this.timeWhile(timer, this.attachedActiveRecentlyTimers);
 | 
			
		||||
        if (this.userActiveRecently()) {
 | 
			
		||||
            timer.start();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private timeWhile(timer: Timer, attachedTimers: Timer[]) {
 | 
			
		||||
    private timeWhile(timer: Timer, attachedTimers: Timer[]): void {
 | 
			
		||||
        // important this happens first
 | 
			
		||||
        const index = attachedTimers.indexOf(timer);
 | 
			
		||||
        if (index === -1) {
 | 
			
		||||
@@ -113,7 +113,7 @@ export default class UserActivity {
 | 
			
		||||
    /**
 | 
			
		||||
     * Start listening to user activity
 | 
			
		||||
     */
 | 
			
		||||
    public start() {
 | 
			
		||||
    public start(): void {
 | 
			
		||||
        this.document.addEventListener("mousedown", this.onUserActivity);
 | 
			
		||||
        this.document.addEventListener("mousemove", this.onUserActivity);
 | 
			
		||||
        this.document.addEventListener("keydown", this.onUserActivity);
 | 
			
		||||
@@ -133,7 +133,7 @@ export default class UserActivity {
 | 
			
		||||
    /**
 | 
			
		||||
     * Stop tracking user activity
 | 
			
		||||
     */
 | 
			
		||||
    public stop() {
 | 
			
		||||
    public stop(): void {
 | 
			
		||||
        this.document.removeEventListener("mousedown", this.onUserActivity);
 | 
			
		||||
        this.document.removeEventListener("mousemove", this.onUserActivity);
 | 
			
		||||
        this.document.removeEventListener("keydown", this.onUserActivity);
 | 
			
		||||
@@ -152,7 +152,7 @@ export default class UserActivity {
 | 
			
		||||
     * user's attention at any given moment.
 | 
			
		||||
     * @returns {boolean} true if user is currently 'active'
 | 
			
		||||
     */
 | 
			
		||||
    public userActiveNow() {
 | 
			
		||||
    public userActiveNow(): boolean {
 | 
			
		||||
        return this.activeNowTimeout.isRunning();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -164,11 +164,11 @@ export default class UserActivity {
 | 
			
		||||
     * (or they may have gone to make tea and left the window focused).
 | 
			
		||||
     * @returns {boolean} true if user has been active recently
 | 
			
		||||
     */
 | 
			
		||||
    public userActiveRecently() {
 | 
			
		||||
    public userActiveRecently(): boolean {
 | 
			
		||||
        return this.activeRecentlyTimeout.isRunning();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private onPageVisibilityChanged = (e) => {
 | 
			
		||||
    private onPageVisibilityChanged = (e): void => {
 | 
			
		||||
        if (this.document.visibilityState === "hidden") {
 | 
			
		||||
            this.activeNowTimeout.abort();
 | 
			
		||||
            this.activeRecentlyTimeout.abort();
 | 
			
		||||
@@ -177,12 +177,12 @@ export default class UserActivity {
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    private onWindowBlurred = () => {
 | 
			
		||||
    private onWindowBlurred = (): void => {
 | 
			
		||||
        this.activeNowTimeout.abort();
 | 
			
		||||
        this.activeRecentlyTimeout.abort();
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    private onUserActivity = (event: MouseEvent) => {
 | 
			
		||||
    private onUserActivity = (event: MouseEvent): void => {
 | 
			
		||||
        // ignore anything if the window isn't focused
 | 
			
		||||
        if (!this.document.hasFocus()) return;
 | 
			
		||||
 | 
			
		||||
@@ -214,7 +214,7 @@ export default class UserActivity {
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    private static async runTimersUntilTimeout(attachedTimers: Timer[], timeout: Timer) {
 | 
			
		||||
    private static async runTimersUntilTimeout(attachedTimers: Timer[], timeout: Timer): Promise<void> {
 | 
			
		||||
        attachedTimers.forEach((t) => t.start());
 | 
			
		||||
        try {
 | 
			
		||||
            await timeout.finished();
 | 
			
		||||
 
 | 
			
		||||
@@ -87,7 +87,7 @@ interface IAction {
 | 
			
		||||
    };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export const reducer = (state: IState, action: IAction) => {
 | 
			
		||||
export const reducer: Reducer<IState, IAction> = (state: IState, action: IAction) => {
 | 
			
		||||
    switch (action.type) {
 | 
			
		||||
        case Type.Register: {
 | 
			
		||||
            if (!state.activeRef) {
 | 
			
		||||
 
 | 
			
		||||
@@ -26,7 +26,7 @@ interface IProps extends Omit<React.HTMLProps<HTMLDivElement>, "onKeyDown"> {}
 | 
			
		||||
// https://www.w3.org/TR/wai-aria-practices-1.1/#toolbar
 | 
			
		||||
// All buttons passed in children must use RovingTabIndex to set `onFocus`, `isActive`, `ref`
 | 
			
		||||
const Toolbar: React.FC<IProps> = ({ children, ...props }) => {
 | 
			
		||||
    const onKeyDown = (ev: React.KeyboardEvent) => {
 | 
			
		||||
    const onKeyDown = (ev: React.KeyboardEvent): void => {
 | 
			
		||||
        const target = ev.target as HTMLElement;
 | 
			
		||||
        // Don't interfere with input default keydown behaviour
 | 
			
		||||
        if (target.tagName === "INPUT") return;
 | 
			
		||||
 
 | 
			
		||||
@@ -33,7 +33,7 @@ interface IProps extends React.ComponentProps<typeof StyledCheckbox> {
 | 
			
		||||
export const StyledMenuItemCheckbox: React.FC<IProps> = ({ children, label, onChange, onClose, ...props }) => {
 | 
			
		||||
    const [onFocus, isActive, ref] = useRovingTabIndex<HTMLInputElement>();
 | 
			
		||||
 | 
			
		||||
    const onKeyDown = (e: React.KeyboardEvent) => {
 | 
			
		||||
    const onKeyDown = (e: React.KeyboardEvent): void => {
 | 
			
		||||
        let handled = true;
 | 
			
		||||
        const action = getKeyBindingsManager().getAccessibilityAction(e);
 | 
			
		||||
 | 
			
		||||
@@ -55,7 +55,7 @@ export const StyledMenuItemCheckbox: React.FC<IProps> = ({ children, label, onCh
 | 
			
		||||
            e.preventDefault();
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
    const onKeyUp = (e: React.KeyboardEvent) => {
 | 
			
		||||
    const onKeyUp = (e: React.KeyboardEvent): void => {
 | 
			
		||||
        const action = getKeyBindingsManager().getAccessibilityAction(e);
 | 
			
		||||
        switch (action) {
 | 
			
		||||
            case KeyBindingAction.Space:
 | 
			
		||||
 
 | 
			
		||||
@@ -33,7 +33,7 @@ interface IProps extends React.ComponentProps<typeof StyledRadioButton> {
 | 
			
		||||
export const StyledMenuItemRadio: React.FC<IProps> = ({ children, label, onChange, onClose, ...props }) => {
 | 
			
		||||
    const [onFocus, isActive, ref] = useRovingTabIndex<HTMLInputElement>();
 | 
			
		||||
 | 
			
		||||
    const onKeyDown = (e: React.KeyboardEvent) => {
 | 
			
		||||
    const onKeyDown = (e: React.KeyboardEvent): void => {
 | 
			
		||||
        let handled = true;
 | 
			
		||||
        const action = getKeyBindingsManager().getAccessibilityAction(e);
 | 
			
		||||
 | 
			
		||||
@@ -55,7 +55,7 @@ export const StyledMenuItemRadio: React.FC<IProps> = ({ children, label, onChang
 | 
			
		||||
            e.preventDefault();
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
    const onKeyUp = (e: React.KeyboardEvent) => {
 | 
			
		||||
    const onKeyUp = (e: React.KeyboardEvent): void => {
 | 
			
		||||
        const action = getKeyBindingsManager().getAccessibilityAction(e);
 | 
			
		||||
        switch (action) {
 | 
			
		||||
            case KeyBindingAction.Enter:
 | 
			
		||||
 
 | 
			
		||||
@@ -15,7 +15,7 @@ See the License for the specific language governing permissions and
 | 
			
		||||
limitations under the License.
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
import { AsyncActionPayload } from "../dispatcher/payloads";
 | 
			
		||||
import { AsyncActionFn, AsyncActionPayload } from "../dispatcher/payloads";
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Create an action thunk that will dispatch actions indicating the current
 | 
			
		||||
@@ -45,7 +45,7 @@ import { AsyncActionPayload } from "../dispatcher/payloads";
 | 
			
		||||
 *                     `fn`.
 | 
			
		||||
 */
 | 
			
		||||
export function asyncAction(id: string, fn: () => Promise<any>, pendingFn: () => any | null): AsyncActionPayload {
 | 
			
		||||
    const helper = (dispatch) => {
 | 
			
		||||
    const helper: AsyncActionFn = (dispatch) => {
 | 
			
		||||
        dispatch({
 | 
			
		||||
            action: id + ".pending",
 | 
			
		||||
            request: typeof pendingFn === "function" ? pendingFn() : undefined,
 | 
			
		||||
 
 | 
			
		||||
@@ -22,7 +22,7 @@ import defaultDispatcher from "../../dispatcher/dispatcher";
 | 
			
		||||
 * Redirect to the correct device manager section
 | 
			
		||||
 * Based on the labs setting
 | 
			
		||||
 */
 | 
			
		||||
export const viewUserDeviceSettings = (isNewDeviceManagerEnabled: boolean) => {
 | 
			
		||||
export const viewUserDeviceSettings = (isNewDeviceManagerEnabled: boolean): void => {
 | 
			
		||||
    defaultDispatcher.dispatch({
 | 
			
		||||
        action: Action.ViewUserSettings,
 | 
			
		||||
        initialTabId: isNewDeviceManagerEnabled ? UserTab.SessionManager : UserTab.Security,
 | 
			
		||||
 
 | 
			
		||||
@@ -56,7 +56,7 @@ export default class ManageEventIndexDialog extends React.Component<IProps, ISta
 | 
			
		||||
        };
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public updateCurrentRoom = async (room) => {
 | 
			
		||||
    public updateCurrentRoom = async (room): Promise<void> => {
 | 
			
		||||
        const eventIndex = EventIndexPeg.get();
 | 
			
		||||
        let stats;
 | 
			
		||||
 | 
			
		||||
@@ -131,17 +131,17 @@ export default class ManageEventIndexDialog extends React.Component<IProps, ISta
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private onDisable = async () => {
 | 
			
		||||
    private onDisable = async (): Promise<void> => {
 | 
			
		||||
        const DisableEventIndexDialog = (await import("./DisableEventIndexDialog")).default;
 | 
			
		||||
        Modal.createDialog(DisableEventIndexDialog, null, null, /* priority = */ false, /* static = */ true);
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    private onCrawlerSleepTimeChange = (e) => {
 | 
			
		||||
    private onCrawlerSleepTimeChange = (e): void => {
 | 
			
		||||
        this.setState({ crawlerSleepTime: e.target.value });
 | 
			
		||||
        SettingsStore.setValue("crawlerSleepTime", null, SettingLevel.DEVICE, e.target.value);
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    public render() {
 | 
			
		||||
    public render(): JSX.Element {
 | 
			
		||||
        const brand = SdkConfig.get().brand;
 | 
			
		||||
 | 
			
		||||
        let crawlerState;
 | 
			
		||||
 
 | 
			
		||||
@@ -125,7 +125,7 @@ export default class CreateKeyBackupDialog extends React.PureComponent<IProps, I
 | 
			
		||||
        let info;
 | 
			
		||||
        try {
 | 
			
		||||
            if (secureSecretStorage) {
 | 
			
		||||
                await accessSecretStorage(async () => {
 | 
			
		||||
                await accessSecretStorage(async (): Promise<void> => {
 | 
			
		||||
                    info = await MatrixClientPeg.get().prepareKeyBackupVersion(null /* random key */, {
 | 
			
		||||
                        secureSecretStorage: true,
 | 
			
		||||
                    });
 | 
			
		||||
 
 | 
			
		||||
@@ -350,7 +350,7 @@ export default class CreateSecretStorageDialog extends React.PureComponent<IProp
 | 
			
		||||
                    createSecretStorageKey: async () => this.recoveryKey,
 | 
			
		||||
                    keyBackupInfo: this.state.backupInfo,
 | 
			
		||||
                    setupNewKeyBackup: !this.state.backupInfo,
 | 
			
		||||
                    getKeyBackupPassphrase: async () => {
 | 
			
		||||
                    getKeyBackupPassphrase: async (): Promise<Uint8Array> => {
 | 
			
		||||
                        // We may already have the backup key if we earlier went
 | 
			
		||||
                        // through the restore backup path, so pass it along
 | 
			
		||||
                        // rather than prompting again.
 | 
			
		||||
@@ -383,7 +383,9 @@ export default class CreateSecretStorageDialog extends React.PureComponent<IProp
 | 
			
		||||
    private restoreBackup = async (): Promise<void> => {
 | 
			
		||||
        // It's possible we'll need the backup key later on for bootstrapping,
 | 
			
		||||
        // so let's stash it here, rather than prompting for it twice.
 | 
			
		||||
        const keyCallback = (k) => (this.backupKey = k);
 | 
			
		||||
        const keyCallback = (k: Uint8Array): void => {
 | 
			
		||||
            this.backupKey = k;
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        const { finished } = Modal.createDialog(
 | 
			
		||||
            RestoreKeyBackupDialog,
 | 
			
		||||
@@ -420,7 +422,7 @@ export default class CreateSecretStorageDialog extends React.PureComponent<IProp
 | 
			
		||||
        this.setState({ phase: Phase.ChooseKeyPassphrase });
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    private onPassPhraseNextClick = async (e: React.FormEvent) => {
 | 
			
		||||
    private onPassPhraseNextClick = async (e: React.FormEvent): Promise<void> => {
 | 
			
		||||
        e.preventDefault();
 | 
			
		||||
        if (!this.passphraseField.current) return; // unmounting
 | 
			
		||||
 | 
			
		||||
@@ -434,7 +436,7 @@ export default class CreateSecretStorageDialog extends React.PureComponent<IProp
 | 
			
		||||
        this.setState({ phase: Phase.PassphraseConfirm });
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    private onPassPhraseConfirmNextClick = async (e: React.FormEvent) => {
 | 
			
		||||
    private onPassPhraseConfirmNextClick = async (e: React.FormEvent): Promise<void> => {
 | 
			
		||||
        e.preventDefault();
 | 
			
		||||
 | 
			
		||||
        if (this.state.passPhrase !== this.state.passPhraseConfirm) return;
 | 
			
		||||
 
 | 
			
		||||
@@ -121,7 +121,7 @@ export default class ExportE2eKeysDialog extends React.Component<IProps, IState>
 | 
			
		||||
        return false;
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    private onPassphraseChange = (ev: React.ChangeEvent<HTMLInputElement>, phrase: AnyPassphrase) => {
 | 
			
		||||
    private onPassphraseChange = (ev: React.ChangeEvent<HTMLInputElement>, phrase: AnyPassphrase): void => {
 | 
			
		||||
        this.setState({
 | 
			
		||||
            [phrase]: ev.target.value,
 | 
			
		||||
        } as Pick<IState, AnyPassphrase>);
 | 
			
		||||
 
 | 
			
		||||
@@ -91,7 +91,7 @@ export default class ImportE2eKeysDialog extends React.Component<IProps, IState>
 | 
			
		||||
        return false;
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    private startImport(file: File, passphrase: string) {
 | 
			
		||||
    private startImport(file: File, passphrase: string): Promise<void> {
 | 
			
		||||
        this.setState({
 | 
			
		||||
            errStr: null,
 | 
			
		||||
            phase: Phase.Importing,
 | 
			
		||||
 
 | 
			
		||||
@@ -30,7 +30,7 @@ export class ManagedPlayback extends Playback {
 | 
			
		||||
        return super.play();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public destroy() {
 | 
			
		||||
    public destroy(): void {
 | 
			
		||||
        this.manager.destroyPlaybackInstance(this);
 | 
			
		||||
        super.destroy();
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -145,7 +145,7 @@ export class Playback extends EventEmitter implements IDestroyable, PlaybackInte
 | 
			
		||||
        return true; // we don't ever care if the event had listeners, so just return "yes"
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public destroy() {
 | 
			
		||||
    public destroy(): void {
 | 
			
		||||
        // Dev note: It's critical that we call stop() during cleanup to ensure that downstream callers
 | 
			
		||||
        // are aware of the final clock position before the user triggered an unload.
 | 
			
		||||
        // noinspection JSIgnoredPromiseFromCall - not concerned about being called async here
 | 
			
		||||
@@ -159,7 +159,7 @@ export class Playback extends EventEmitter implements IDestroyable, PlaybackInte
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public async prepare() {
 | 
			
		||||
    public async prepare(): Promise<void> {
 | 
			
		||||
        // don't attempt to decode the media again
 | 
			
		||||
        // AudioContext.decodeAudioData detaches the array buffer `this.buf`
 | 
			
		||||
        // meaning it cannot be re-read
 | 
			
		||||
@@ -190,7 +190,7 @@ export class Playback extends EventEmitter implements IDestroyable, PlaybackInte
 | 
			
		||||
                this.context.decodeAudioData(
 | 
			
		||||
                    this.buf,
 | 
			
		||||
                    (b) => resolve(b),
 | 
			
		||||
                    async (e) => {
 | 
			
		||||
                    async (e): Promise<void> => {
 | 
			
		||||
                        try {
 | 
			
		||||
                            // This error handler is largely for Safari as well, which doesn't support Opus/Ogg
 | 
			
		||||
                            // very well.
 | 
			
		||||
@@ -232,12 +232,12 @@ export class Playback extends EventEmitter implements IDestroyable, PlaybackInte
 | 
			
		||||
        this.emit(PlaybackState.Stopped); // signal that we're not decoding anymore
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private onPlaybackEnd = async () => {
 | 
			
		||||
    private onPlaybackEnd = async (): Promise<void> => {
 | 
			
		||||
        await this.context.suspend();
 | 
			
		||||
        this.emit(PlaybackState.Stopped);
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    public async play() {
 | 
			
		||||
    public async play(): Promise<void> {
 | 
			
		||||
        // We can't restart a buffer source, so we need to create a new one if we hit the end
 | 
			
		||||
        if (this.state === PlaybackState.Stopped) {
 | 
			
		||||
            this.disconnectSource();
 | 
			
		||||
@@ -256,13 +256,13 @@ export class Playback extends EventEmitter implements IDestroyable, PlaybackInte
 | 
			
		||||
        this.emit(PlaybackState.Playing);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private disconnectSource() {
 | 
			
		||||
    private disconnectSource(): void {
 | 
			
		||||
        if (this.element) return; // leave connected, we can (and must) re-use it
 | 
			
		||||
        this.source?.disconnect();
 | 
			
		||||
        this.source?.removeEventListener("ended", this.onPlaybackEnd);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private makeNewSourceBuffer() {
 | 
			
		||||
    private makeNewSourceBuffer(): void {
 | 
			
		||||
        if (this.element && this.source) return; // leave connected, we can (and must) re-use it
 | 
			
		||||
 | 
			
		||||
        if (this.element) {
 | 
			
		||||
@@ -276,22 +276,22 @@ export class Playback extends EventEmitter implements IDestroyable, PlaybackInte
 | 
			
		||||
        this.source.connect(this.context.destination);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public async pause() {
 | 
			
		||||
    public async pause(): Promise<void> {
 | 
			
		||||
        await this.context.suspend();
 | 
			
		||||
        this.emit(PlaybackState.Paused);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public async stop() {
 | 
			
		||||
    public async stop(): Promise<void> {
 | 
			
		||||
        await this.onPlaybackEnd();
 | 
			
		||||
        this.clock.flagStop();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public async toggle() {
 | 
			
		||||
    public async toggle(): Promise<void> {
 | 
			
		||||
        if (this.isPlaying) await this.pause();
 | 
			
		||||
        else await this.play();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public async skipTo(timeSeconds: number) {
 | 
			
		||||
    public async skipTo(timeSeconds: number): Promise<void> {
 | 
			
		||||
        // Dev note: this function talks a lot about clock desyncs. There is a clock running
 | 
			
		||||
        // independently to the audio context and buffer so that accurate human-perceptible
 | 
			
		||||
        // time can be exposed. The PlaybackClock class has more information, but the short
 | 
			
		||||
 
 | 
			
		||||
@@ -89,7 +89,7 @@ export class PlaybackClock implements IDestroyable {
 | 
			
		||||
        return this.observable;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private checkTime = (force = false) => {
 | 
			
		||||
    private checkTime = (force = false): void => {
 | 
			
		||||
        const now = this.timeSeconds; // calculated dynamically
 | 
			
		||||
        if (this.lastCheck !== now || force) {
 | 
			
		||||
            this.observable.update([now, this.durationSeconds]);
 | 
			
		||||
@@ -102,7 +102,7 @@ export class PlaybackClock implements IDestroyable {
 | 
			
		||||
     * The placeholders will be overridden once known.
 | 
			
		||||
     * @param {MatrixEvent} event The event to use for placeholders.
 | 
			
		||||
     */
 | 
			
		||||
    public populatePlaceholdersFrom(event: MatrixEvent) {
 | 
			
		||||
    public populatePlaceholdersFrom(event: MatrixEvent): void {
 | 
			
		||||
        const durationMs = Number(event.getContent()["info"]?.["duration"]);
 | 
			
		||||
        if (Number.isFinite(durationMs)) this.placeholderDuration = durationMs / 1000;
 | 
			
		||||
    }
 | 
			
		||||
@@ -112,11 +112,11 @@ export class PlaybackClock implements IDestroyable {
 | 
			
		||||
     * This is to ensure the clock isn't skewed into thinking it is ~0.5s into
 | 
			
		||||
     * a clip when the duration is set.
 | 
			
		||||
     */
 | 
			
		||||
    public flagLoadTime() {
 | 
			
		||||
    public flagLoadTime(): void {
 | 
			
		||||
        this.clipStart = this.context.currentTime;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public flagStart() {
 | 
			
		||||
    public flagStart(): void {
 | 
			
		||||
        if (this.stopped) {
 | 
			
		||||
            this.clipStart = this.context.currentTime;
 | 
			
		||||
            this.stopped = false;
 | 
			
		||||
@@ -128,7 +128,7 @@ export class PlaybackClock implements IDestroyable {
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public flagStop() {
 | 
			
		||||
    public flagStop(): void {
 | 
			
		||||
        this.stopped = true;
 | 
			
		||||
 | 
			
		||||
        // Reset the clock time now so that the update going out will trigger components
 | 
			
		||||
@@ -136,13 +136,13 @@ export class PlaybackClock implements IDestroyable {
 | 
			
		||||
        this.clipStart = this.context.currentTime;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public syncTo(contextTime: number, clipTime: number) {
 | 
			
		||||
    public syncTo(contextTime: number, clipTime: number): void {
 | 
			
		||||
        this.clipStart = contextTime - clipTime;
 | 
			
		||||
        this.stopped = false; // count as a mid-stream pause (if we were stopped)
 | 
			
		||||
        this.checkTime(true);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public destroy() {
 | 
			
		||||
    public destroy(): void {
 | 
			
		||||
        this.observable.close();
 | 
			
		||||
        if (this.timerId) clearInterval(this.timerId);
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -38,13 +38,13 @@ export class PlaybackManager {
 | 
			
		||||
     * instances are paused.
 | 
			
		||||
     * @param playback Optional. The playback to leave untouched.
 | 
			
		||||
     */
 | 
			
		||||
    public pauseAllExcept(playback?: Playback) {
 | 
			
		||||
    public pauseAllExcept(playback?: Playback): void {
 | 
			
		||||
        this.instances
 | 
			
		||||
            .filter((p) => p !== playback && p.currentState === PlaybackState.Playing)
 | 
			
		||||
            .forEach((p) => p.pause());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public destroyPlaybackInstance(playback: ManagedPlayback) {
 | 
			
		||||
    public destroyPlaybackInstance(playback: ManagedPlayback): void {
 | 
			
		||||
        this.instances = this.instances.filter((p) => p !== playback);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -75,28 +75,28 @@ export class PlaybackQueue {
 | 
			
		||||
        return queue;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private persistClocks() {
 | 
			
		||||
    private persistClocks(): void {
 | 
			
		||||
        localStorage.setItem(
 | 
			
		||||
            `mx_voice_message_clocks_${this.room.roomId}`,
 | 
			
		||||
            JSON.stringify(Array.from(this.clockStates.entries())),
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private loadClocks() {
 | 
			
		||||
    private loadClocks(): void {
 | 
			
		||||
        const val = localStorage.getItem(`mx_voice_message_clocks_${this.room.roomId}`);
 | 
			
		||||
        if (!!val) {
 | 
			
		||||
            this.clockStates = new Map<string, number>(JSON.parse(val));
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public unsortedEnqueue(mxEvent: MatrixEvent, playback: Playback) {
 | 
			
		||||
    public unsortedEnqueue(mxEvent: MatrixEvent, playback: Playback): void {
 | 
			
		||||
        // We don't ever detach our listeners: we expect the Playback to clean up for us
 | 
			
		||||
        this.playbacks.set(mxEvent.getId(), playback);
 | 
			
		||||
        playback.on(UPDATE_EVENT, (state) => this.onPlaybackStateChange(playback, mxEvent, state));
 | 
			
		||||
        playback.clockInfo.liveData.onUpdate((clock) => this.onPlaybackClock(playback, mxEvent, clock));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private onPlaybackStateChange(playback: Playback, mxEvent: MatrixEvent, newState: PlaybackState) {
 | 
			
		||||
    private onPlaybackStateChange(playback: Playback, mxEvent: MatrixEvent, newState: PlaybackState): void {
 | 
			
		||||
        // Remember where the user got to in playback
 | 
			
		||||
        const wasLastPlaying = this.currentPlaybackId === mxEvent.getId();
 | 
			
		||||
        if (newState === PlaybackState.Stopped && this.clockStates.has(mxEvent.getId()) && !wasLastPlaying) {
 | 
			
		||||
@@ -210,7 +210,7 @@ export class PlaybackQueue {
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private onPlaybackClock(playback: Playback, mxEvent: MatrixEvent, clocks: number[]) {
 | 
			
		||||
    private onPlaybackClock(playback: Playback, mxEvent: MatrixEvent, clocks: number[]): void {
 | 
			
		||||
        if (playback.currentState === PlaybackState.Decoding) return; // ignore pre-ready values
 | 
			
		||||
 | 
			
		||||
        if (playback.currentState !== PlaybackState.Stopped) {
 | 
			
		||||
 
 | 
			
		||||
@@ -43,7 +43,7 @@ class MxVoiceWorklet extends AudioWorkletProcessor {
 | 
			
		||||
    private nextAmplitudeSecond = 0;
 | 
			
		||||
    private amplitudeIndex = 0;
 | 
			
		||||
 | 
			
		||||
    public process(inputs, outputs, parameters) {
 | 
			
		||||
    public process(inputs, outputs, parameters): boolean {
 | 
			
		||||
        const currentSecond = roundTimeToTargetFreq(currentTime);
 | 
			
		||||
        // We special case the first ping because there's a fairly good chance that we'll miss the zeroth
 | 
			
		||||
        // update. Firefox for instance takes 0.06 seconds (roughly) to call this function for the first
 | 
			
		||||
 
 | 
			
		||||
@@ -141,7 +141,7 @@ export class VoiceMessageRecording implements IDestroyable {
 | 
			
		||||
        this.voiceRecording.destroy();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private onDataAvailable = (data: ArrayBuffer) => {
 | 
			
		||||
    private onDataAvailable = (data: ArrayBuffer): void => {
 | 
			
		||||
        const buf = new Uint8Array(data);
 | 
			
		||||
        this.buffer = concat(this.buffer, buf);
 | 
			
		||||
    };
 | 
			
		||||
@@ -153,6 +153,6 @@ export class VoiceMessageRecording implements IDestroyable {
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export const createVoiceMessageRecording = (matrixClient: MatrixClient) => {
 | 
			
		||||
export const createVoiceMessageRecording = (matrixClient: MatrixClient): VoiceMessageRecording => {
 | 
			
		||||
    return new VoiceMessageRecording(matrixClient, new VoiceRecording());
 | 
			
		||||
};
 | 
			
		||||
 
 | 
			
		||||
@@ -110,7 +110,7 @@ export class VoiceRecording extends EventEmitter implements IDestroyable {
 | 
			
		||||
        return !MediaDeviceHandler.getAudioNoiseSuppression();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private async makeRecorder() {
 | 
			
		||||
    private async makeRecorder(): Promise<void> {
 | 
			
		||||
        try {
 | 
			
		||||
            this.recorderStream = await navigator.mediaDevices.getUserMedia({
 | 
			
		||||
                audio: {
 | 
			
		||||
@@ -212,14 +212,14 @@ export class VoiceRecording extends EventEmitter implements IDestroyable {
 | 
			
		||||
        return !!Recorder.isRecordingSupported();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private onAudioProcess = (ev: AudioProcessingEvent) => {
 | 
			
		||||
    private onAudioProcess = (ev: AudioProcessingEvent): void => {
 | 
			
		||||
        this.processAudioUpdate(ev.playbackTime);
 | 
			
		||||
 | 
			
		||||
        // We skip the functionality of the worklet regarding waveform calculations: we
 | 
			
		||||
        // should get that information pretty quick during the playback info.
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    private processAudioUpdate = (timeSeconds: number) => {
 | 
			
		||||
    private processAudioUpdate = (timeSeconds: number): void => {
 | 
			
		||||
        if (!this.recording) return;
 | 
			
		||||
 | 
			
		||||
        this.observable.update({
 | 
			
		||||
@@ -260,7 +260,7 @@ export class VoiceRecording extends EventEmitter implements IDestroyable {
 | 
			
		||||
    /**
 | 
			
		||||
     * {@link https://github.com/chris-rudmin/opus-recorder#instance-fields ref for recorderSeconds}
 | 
			
		||||
     */
 | 
			
		||||
    public get recorderSeconds() {
 | 
			
		||||
    public get recorderSeconds(): number {
 | 
			
		||||
        return this.recorder.encodedSamplePosition / 48000;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -279,7 +279,7 @@ export class VoiceRecording extends EventEmitter implements IDestroyable {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public async stop(): Promise<void> {
 | 
			
		||||
        return Singleflight.for(this, "stop").do(async () => {
 | 
			
		||||
        return Singleflight.for(this, "stop").do(async (): Promise<void> => {
 | 
			
		||||
            if (!this.recording) {
 | 
			
		||||
                throw new Error("No recording to stop");
 | 
			
		||||
            }
 | 
			
		||||
@@ -307,7 +307,7 @@ export class VoiceRecording extends EventEmitter implements IDestroyable {
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public destroy() {
 | 
			
		||||
    public destroy(): void {
 | 
			
		||||
        // noinspection JSIgnoredPromiseFromCall - not concerned about stop() being called async here
 | 
			
		||||
        this.stop();
 | 
			
		||||
        this.removeAllListeners();
 | 
			
		||||
 
 | 
			
		||||
@@ -22,7 +22,7 @@ import { TimelineRenderingType } from "../contexts/RoomContext";
 | 
			
		||||
import type { ICompletion, ISelectionRange } from "./Autocompleter";
 | 
			
		||||
 | 
			
		||||
export interface ICommand {
 | 
			
		||||
    command: string | null;
 | 
			
		||||
    command: RegExpExecArray | null;
 | 
			
		||||
    range: {
 | 
			
		||||
        start: number;
 | 
			
		||||
        end: number;
 | 
			
		||||
@@ -59,7 +59,7 @@ export default abstract class AutocompleteProvider {
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public destroy() {
 | 
			
		||||
    public destroy(): void {
 | 
			
		||||
        // stub
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -70,7 +70,7 @@ export default abstract class AutocompleteProvider {
 | 
			
		||||
     * @param {boolean} force True if the user is forcing completion
 | 
			
		||||
     * @return {object} { command, range } where both objects fields are null if no match
 | 
			
		||||
     */
 | 
			
		||||
    public getCurrentCommand(query: string, selection: ISelectionRange, force = false) {
 | 
			
		||||
    public getCurrentCommand(query: string, selection: ISelectionRange, force = false): ICommand {
 | 
			
		||||
        let commandRegex = this.commandRegex;
 | 
			
		||||
 | 
			
		||||
        if (force && this.shouldForceComplete()) {
 | 
			
		||||
@@ -83,7 +83,7 @@ export default abstract class AutocompleteProvider {
 | 
			
		||||
 | 
			
		||||
        commandRegex.lastIndex = 0;
 | 
			
		||||
 | 
			
		||||
        let match;
 | 
			
		||||
        let match: RegExpExecArray;
 | 
			
		||||
        while ((match = commandRegex.exec(query)) !== null) {
 | 
			
		||||
            const start = match.index;
 | 
			
		||||
            const end = start + match[0].length;
 | 
			
		||||
 
 | 
			
		||||
@@ -69,7 +69,7 @@ export default class Autocompleter {
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public destroy() {
 | 
			
		||||
    public destroy(): void {
 | 
			
		||||
        this.providers.forEach((p) => {
 | 
			
		||||
            p.destroy();
 | 
			
		||||
        });
 | 
			
		||||
@@ -88,7 +88,7 @@ export default class Autocompleter {
 | 
			
		||||
        */
 | 
			
		||||
        // list of results from each provider, each being a list of completions or null if it times out
 | 
			
		||||
        const completionsList: ICompletion[][] = await Promise.all(
 | 
			
		||||
            this.providers.map(async (provider) => {
 | 
			
		||||
            this.providers.map(async (provider): Promise<ICompletion[] | null> => {
 | 
			
		||||
                return timeout(
 | 
			
		||||
                    provider.getCompletions(query, selection, force, limit),
 | 
			
		||||
                    null,
 | 
			
		||||
 
 | 
			
		||||
@@ -100,7 +100,7 @@ export default class CommandProvider extends AutocompleteProvider {
 | 
			
		||||
            });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public getName() {
 | 
			
		||||
    public getName(): string {
 | 
			
		||||
        return "*️⃣ " + _t("Commands");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -55,7 +55,7 @@ const SORTED_EMOJI: ISortedEmoji[] = EMOJI.sort((a, b) => {
 | 
			
		||||
    _orderBy: index,
 | 
			
		||||
}));
 | 
			
		||||
 | 
			
		||||
function score(query, space) {
 | 
			
		||||
function score(query: string, space: string): number {
 | 
			
		||||
    const index = space.indexOf(query);
 | 
			
		||||
    if (index === -1) {
 | 
			
		||||
        return Infinity;
 | 
			
		||||
@@ -154,7 +154,7 @@ export default class EmojiProvider extends AutocompleteProvider {
 | 
			
		||||
        return [];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public getName() {
 | 
			
		||||
    public getName(): string {
 | 
			
		||||
        return "😃 " + _t("Emoji");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -65,7 +65,7 @@ export default class NotifProvider extends AutocompleteProvider {
 | 
			
		||||
        return [];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public getName() {
 | 
			
		||||
    public getName(): string {
 | 
			
		||||
        return "❗️ " + _t("Room Notification");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -61,7 +61,7 @@ export default class QueryMatcher<T extends {}> {
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public setObjects(objects: T[]) {
 | 
			
		||||
    public setObjects(objects: T[]): void {
 | 
			
		||||
        this._items = new Map();
 | 
			
		||||
 | 
			
		||||
        for (const object of objects) {
 | 
			
		||||
 
 | 
			
		||||
@@ -37,7 +37,15 @@ function canonicalScore(displayedAlias: string, room: Room): number {
 | 
			
		||||
    return displayedAlias === room.getCanonicalAlias() ? 0 : 1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function matcherObject(room: Room, displayedAlias: string, matchName = "") {
 | 
			
		||||
function matcherObject(
 | 
			
		||||
    room: Room,
 | 
			
		||||
    displayedAlias: string,
 | 
			
		||||
    matchName = "",
 | 
			
		||||
): {
 | 
			
		||||
    room: Room;
 | 
			
		||||
    matchName: string;
 | 
			
		||||
    displayedAlias: string;
 | 
			
		||||
} {
 | 
			
		||||
    return {
 | 
			
		||||
        room,
 | 
			
		||||
        matchName,
 | 
			
		||||
@@ -46,7 +54,7 @@ function matcherObject(room: Room, displayedAlias: string, matchName = "") {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export default class RoomProvider extends AutocompleteProvider {
 | 
			
		||||
    protected matcher: QueryMatcher<Room>;
 | 
			
		||||
    protected matcher: QueryMatcher<ReturnType<typeof matcherObject>>;
 | 
			
		||||
 | 
			
		||||
    public constructor(room: Room, renderingType?: TimelineRenderingType) {
 | 
			
		||||
        super({ commandRegex: ROOM_REGEX, renderingType });
 | 
			
		||||
@@ -55,7 +63,7 @@ export default class RoomProvider extends AutocompleteProvider {
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    protected getRooms() {
 | 
			
		||||
    protected getRooms(): Room[] {
 | 
			
		||||
        const cli = MatrixClientPeg.get();
 | 
			
		||||
 | 
			
		||||
        // filter out spaces here as they get their own autocomplete provider
 | 
			
		||||
@@ -68,7 +76,6 @@ export default class RoomProvider extends AutocompleteProvider {
 | 
			
		||||
        force = false,
 | 
			
		||||
        limit = -1,
 | 
			
		||||
    ): Promise<ICompletion[]> {
 | 
			
		||||
        let completions = [];
 | 
			
		||||
        const { command, range } = this.getCurrentCommand(query, selection, force);
 | 
			
		||||
        if (command) {
 | 
			
		||||
            // the only reason we need to do this is because Fuse only matches on properties
 | 
			
		||||
@@ -96,15 +103,15 @@ export default class RoomProvider extends AutocompleteProvider {
 | 
			
		||||
 | 
			
		||||
            this.matcher.setObjects(matcherObjects);
 | 
			
		||||
            const matchedString = command[0];
 | 
			
		||||
            completions = this.matcher.match(matchedString, limit);
 | 
			
		||||
            let completions = this.matcher.match(matchedString, limit);
 | 
			
		||||
            completions = sortBy(completions, [
 | 
			
		||||
                (c) => canonicalScore(c.displayedAlias, c.room),
 | 
			
		||||
                (c) => c.displayedAlias.length,
 | 
			
		||||
            ]);
 | 
			
		||||
            completions = uniqBy(completions, (match) => match.room);
 | 
			
		||||
            completions = completions
 | 
			
		||||
                .map((room) => {
 | 
			
		||||
                    return {
 | 
			
		||||
            return completions
 | 
			
		||||
                .map(
 | 
			
		||||
                    (room): ICompletion => ({
 | 
			
		||||
                        completion: room.displayedAlias,
 | 
			
		||||
                        completionId: room.room.roomId,
 | 
			
		||||
                        type: "room",
 | 
			
		||||
@@ -116,14 +123,14 @@ export default class RoomProvider extends AutocompleteProvider {
 | 
			
		||||
                            </PillCompletion>
 | 
			
		||||
                        ),
 | 
			
		||||
                        range,
 | 
			
		||||
                    };
 | 
			
		||||
                })
 | 
			
		||||
                    }),
 | 
			
		||||
                )
 | 
			
		||||
                .filter((completion) => !!completion.completion && completion.completion.length > 0);
 | 
			
		||||
        }
 | 
			
		||||
        return completions;
 | 
			
		||||
        return [];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public getName() {
 | 
			
		||||
    public getName(): string {
 | 
			
		||||
        return _t("Rooms");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -14,6 +14,7 @@ See the License for the specific language governing permissions and
 | 
			
		||||
limitations under the License.
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
import { Room } from "matrix-js-sdk/src/models/room";
 | 
			
		||||
import React from "react";
 | 
			
		||||
 | 
			
		||||
import { _t } from "../languageHandler";
 | 
			
		||||
@@ -21,13 +22,13 @@ import { MatrixClientPeg } from "../MatrixClientPeg";
 | 
			
		||||
import RoomProvider from "./RoomProvider";
 | 
			
		||||
 | 
			
		||||
export default class SpaceProvider extends RoomProvider {
 | 
			
		||||
    protected getRooms() {
 | 
			
		||||
    protected getRooms(): Room[] {
 | 
			
		||||
        return MatrixClientPeg.get()
 | 
			
		||||
            .getVisibleRooms()
 | 
			
		||||
            .filter((r) => r.isSpaceRoom());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public getName() {
 | 
			
		||||
    public getName(): string {
 | 
			
		||||
        return _t("Spaces");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -64,7 +64,7 @@ export default class UserProvider extends AutocompleteProvider {
 | 
			
		||||
        MatrixClientPeg.get().on(RoomStateEvent.Update, this.onRoomStateUpdate);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public destroy() {
 | 
			
		||||
    public destroy(): void {
 | 
			
		||||
        if (MatrixClientPeg.get()) {
 | 
			
		||||
            MatrixClientPeg.get().removeListener(RoomEvent.Timeline, this.onRoomTimeline);
 | 
			
		||||
            MatrixClientPeg.get().removeListener(RoomStateEvent.Update, this.onRoomStateUpdate);
 | 
			
		||||
@@ -77,7 +77,7 @@ export default class UserProvider extends AutocompleteProvider {
 | 
			
		||||
        toStartOfTimeline: boolean,
 | 
			
		||||
        removed: boolean,
 | 
			
		||||
        data: IRoomTimelineData,
 | 
			
		||||
    ) => {
 | 
			
		||||
    ): void => {
 | 
			
		||||
        if (!room) return; // notification timeline, we'll get this event again with a room specific timeline
 | 
			
		||||
        if (removed) return;
 | 
			
		||||
        if (room.roomId !== this.room.roomId) return;
 | 
			
		||||
@@ -93,7 +93,7 @@ export default class UserProvider extends AutocompleteProvider {
 | 
			
		||||
        this.onUserSpoke(ev.sender);
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    private onRoomStateUpdate = (state: RoomState) => {
 | 
			
		||||
    private onRoomStateUpdate = (state: RoomState): void => {
 | 
			
		||||
        // ignore updates in other rooms
 | 
			
		||||
        if (state.roomId !== this.room.roomId) return;
 | 
			
		||||
 | 
			
		||||
@@ -150,7 +150,7 @@ export default class UserProvider extends AutocompleteProvider {
 | 
			
		||||
        return _t("Users");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private makeUsers() {
 | 
			
		||||
    private makeUsers(): void {
 | 
			
		||||
        const events = this.room.getLiveTimeline().getEvents();
 | 
			
		||||
        const lastSpoken = {};
 | 
			
		||||
 | 
			
		||||
@@ -167,7 +167,7 @@ export default class UserProvider extends AutocompleteProvider {
 | 
			
		||||
        this.matcher.setObjects(this.users);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public onUserSpoke(user: RoomMember) {
 | 
			
		||||
    public onUserSpoke(user: RoomMember): void {
 | 
			
		||||
        if (!this.users) return;
 | 
			
		||||
        if (!user) return;
 | 
			
		||||
        if (user.userId === MatrixClientPeg.get().credentials.userId) return;
 | 
			
		||||
 
 | 
			
		||||
@@ -22,7 +22,7 @@ type DynamicHtmlElementProps<T extends keyof JSX.IntrinsicElements> =
 | 
			
		||||
    JSX.IntrinsicElements[T] extends HTMLAttributes<{}> ? DynamicElementProps<T> : DynamicElementProps<"div">;
 | 
			
		||||
type DynamicElementProps<T extends keyof JSX.IntrinsicElements> = Partial<Omit<JSX.IntrinsicElements[T], "ref">>;
 | 
			
		||||
 | 
			
		||||
export type IProps<T extends keyof JSX.IntrinsicElements> = DynamicHtmlElementProps<T> & {
 | 
			
		||||
export type IProps<T extends keyof JSX.IntrinsicElements> = Omit<DynamicHtmlElementProps<T>, "onScroll"> & {
 | 
			
		||||
    element?: T;
 | 
			
		||||
    className?: string;
 | 
			
		||||
    onScroll?: (event: Event) => void;
 | 
			
		||||
@@ -39,7 +39,7 @@ export default class AutoHideScrollbar<T extends keyof JSX.IntrinsicElements> ex
 | 
			
		||||
 | 
			
		||||
    public readonly containerRef: React.RefObject<HTMLDivElement> = React.createRef();
 | 
			
		||||
 | 
			
		||||
    public componentDidMount() {
 | 
			
		||||
    public componentDidMount(): void {
 | 
			
		||||
        if (this.containerRef.current && this.props.onScroll) {
 | 
			
		||||
            // Using the passive option to not block the main thread
 | 
			
		||||
            // https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener#improving_scrolling_performance_with_passive_listeners
 | 
			
		||||
@@ -49,13 +49,13 @@ export default class AutoHideScrollbar<T extends keyof JSX.IntrinsicElements> ex
 | 
			
		||||
        this.props.wrappedRef?.(this.containerRef.current);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public componentWillUnmount() {
 | 
			
		||||
    public componentWillUnmount(): void {
 | 
			
		||||
        if (this.containerRef.current && this.props.onScroll) {
 | 
			
		||||
            this.containerRef.current.removeEventListener("scroll", this.props.onScroll);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public render() {
 | 
			
		||||
    public render(): JSX.Element {
 | 
			
		||||
        // eslint-disable-next-line @typescript-eslint/no-unused-vars
 | 
			
		||||
        const { element, className, onScroll, tabIndex, wrappedRef, children, ...otherProps } = this.props;
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -52,11 +52,11 @@ export const AutocompleteInput: React.FC<AutocompleteInputProps> = ({
 | 
			
		||||
    const editorContainerRef = useRef<HTMLDivElement>(null);
 | 
			
		||||
    const editorRef = useRef<HTMLInputElement>(null);
 | 
			
		||||
 | 
			
		||||
    const focusEditor = () => {
 | 
			
		||||
    const focusEditor = (): void => {
 | 
			
		||||
        editorRef?.current?.focus();
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    const onQueryChange = async (e: ChangeEvent<HTMLInputElement>) => {
 | 
			
		||||
    const onQueryChange = async (e: ChangeEvent<HTMLInputElement>): Promise<void> => {
 | 
			
		||||
        const value = e.target.value.trim();
 | 
			
		||||
        setQuery(value);
 | 
			
		||||
 | 
			
		||||
@@ -74,11 +74,11 @@ export const AutocompleteInput: React.FC<AutocompleteInputProps> = ({
 | 
			
		||||
        setSuggestions(matches);
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    const onClickInputArea = () => {
 | 
			
		||||
    const onClickInputArea = (): void => {
 | 
			
		||||
        focusEditor();
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    const onKeyDown = (e: KeyboardEvent) => {
 | 
			
		||||
    const onKeyDown = (e: KeyboardEvent): void => {
 | 
			
		||||
        const hasModifiers = e.ctrlKey || e.shiftKey || e.metaKey;
 | 
			
		||||
 | 
			
		||||
        // when the field is empty and the user hits backspace remove the right-most target
 | 
			
		||||
@@ -87,7 +87,7 @@ export const AutocompleteInput: React.FC<AutocompleteInputProps> = ({
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    const toggleSelection = (completion: ICompletion) => {
 | 
			
		||||
    const toggleSelection = (completion: ICompletion): void => {
 | 
			
		||||
        const newSelection = [...selection];
 | 
			
		||||
        const index = selection.findIndex((selection) => selection.completionId === completion.completionId);
 | 
			
		||||
 | 
			
		||||
@@ -101,7 +101,7 @@ export const AutocompleteInput: React.FC<AutocompleteInputProps> = ({
 | 
			
		||||
        focusEditor();
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    const removeSelection = (completion: ICompletion) => {
 | 
			
		||||
    const removeSelection = (completion: ICompletion): void => {
 | 
			
		||||
        const newSelection = [...selection];
 | 
			
		||||
        const index = selection.findIndex((selection) => selection.completionId === completion.completionId);
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -64,7 +64,7 @@ export enum ChevronFace {
 | 
			
		||||
    None = "none",
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export interface IProps extends IPosition {
 | 
			
		||||
export interface MenuProps extends IPosition {
 | 
			
		||||
    menuWidth?: number;
 | 
			
		||||
    menuHeight?: number;
 | 
			
		||||
 | 
			
		||||
@@ -77,7 +77,9 @@ export interface IProps extends IPosition {
 | 
			
		||||
    menuPaddingRight?: number;
 | 
			
		||||
 | 
			
		||||
    zIndex?: number;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export interface IProps extends MenuProps {
 | 
			
		||||
    // If true, insert an invisible screen-sized element behind the menu that when clicked will close it.
 | 
			
		||||
    hasBackground?: boolean;
 | 
			
		||||
    // whether this context menu should be focus managed. If false it must handle itself
 | 
			
		||||
@@ -128,21 +130,21 @@ export default class ContextMenu extends React.PureComponent<IProps, IState> {
 | 
			
		||||
        this.initialFocus = document.activeElement as HTMLElement;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public componentDidMount() {
 | 
			
		||||
    public componentDidMount(): void {
 | 
			
		||||
        Modal.on(ModalManagerEvent.Opened, this.onModalOpen);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public componentWillUnmount() {
 | 
			
		||||
    public componentWillUnmount(): void {
 | 
			
		||||
        Modal.off(ModalManagerEvent.Opened, this.onModalOpen);
 | 
			
		||||
        // return focus to the thing which had it before us
 | 
			
		||||
        this.initialFocus.focus();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private onModalOpen = () => {
 | 
			
		||||
    private onModalOpen = (): void => {
 | 
			
		||||
        this.props.onFinished?.();
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    private collectContextMenuRect = (element: HTMLDivElement) => {
 | 
			
		||||
    private collectContextMenuRect = (element: HTMLDivElement): void => {
 | 
			
		||||
        // We don't need to clean up when unmounting, so ignore
 | 
			
		||||
        if (!element) return;
 | 
			
		||||
 | 
			
		||||
@@ -159,7 +161,7 @@ export default class ContextMenu extends React.PureComponent<IProps, IState> {
 | 
			
		||||
        });
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    private onContextMenu = (e) => {
 | 
			
		||||
    private onContextMenu = (e: React.MouseEvent): void => {
 | 
			
		||||
        if (this.props.onFinished) {
 | 
			
		||||
            this.props.onFinished();
 | 
			
		||||
 | 
			
		||||
@@ -184,20 +186,20 @@ export default class ContextMenu extends React.PureComponent<IProps, IState> {
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    private onContextMenuPreventBubbling = (e) => {
 | 
			
		||||
    private onContextMenuPreventBubbling = (e: React.MouseEvent): void => {
 | 
			
		||||
        // stop propagation so that any context menu handlers don't leak out of this context menu
 | 
			
		||||
        // but do not inhibit the default browser menu
 | 
			
		||||
        e.stopPropagation();
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    // Prevent clicks on the background from going through to the component which opened the menu.
 | 
			
		||||
    private onFinished = (ev: React.MouseEvent) => {
 | 
			
		||||
    private onFinished = (ev: React.MouseEvent): void => {
 | 
			
		||||
        ev.stopPropagation();
 | 
			
		||||
        ev.preventDefault();
 | 
			
		||||
        this.props.onFinished?.();
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    private onClick = (ev: React.MouseEvent) => {
 | 
			
		||||
    private onClick = (ev: React.MouseEvent): void => {
 | 
			
		||||
        // Don't allow clicks to escape the context menu wrapper
 | 
			
		||||
        ev.stopPropagation();
 | 
			
		||||
 | 
			
		||||
@@ -208,7 +210,7 @@ export default class ContextMenu extends React.PureComponent<IProps, IState> {
 | 
			
		||||
 | 
			
		||||
    // We now only handle closing the ContextMenu in this keyDown handler.
 | 
			
		||||
    // All of the item/option navigation is delegated to RovingTabIndex.
 | 
			
		||||
    private onKeyDown = (ev: React.KeyboardEvent) => {
 | 
			
		||||
    private onKeyDown = (ev: React.KeyboardEvent): void => {
 | 
			
		||||
        ev.stopPropagation(); // prevent keyboard propagating out of the context menu, we're focus-locked
 | 
			
		||||
 | 
			
		||||
        const action = getKeyBindingsManager().getAccessibilityAction(ev);
 | 
			
		||||
@@ -243,7 +245,7 @@ export default class ContextMenu extends React.PureComponent<IProps, IState> {
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    protected renderMenu(hasBackground = this.props.hasBackground) {
 | 
			
		||||
    protected renderMenu(hasBackground = this.props.hasBackground): JSX.Element {
 | 
			
		||||
        const position: Partial<Writeable<DOMRect>> = {};
 | 
			
		||||
        const {
 | 
			
		||||
            top,
 | 
			
		||||
@@ -501,17 +503,13 @@ export const toLeftOrRightOf = (elementRect: DOMRect, chevronOffset = 12): ToRig
 | 
			
		||||
    return toRightOf(elementRect, chevronOffset);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export type AboveLeftOf = IPosition & {
 | 
			
		||||
    chevronFace: ChevronFace;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
// Placement method for <ContextMenu /> to position context menu right-aligned and flowing to the left of elementRect,
 | 
			
		||||
// and either above or below: wherever there is more space (maybe this should be aboveOrBelowLeftOf?)
 | 
			
		||||
export const aboveLeftOf = (
 | 
			
		||||
    elementRect: Pick<DOMRect, "right" | "top" | "bottom">,
 | 
			
		||||
    chevronFace = ChevronFace.None,
 | 
			
		||||
    vPadding = 0,
 | 
			
		||||
): AboveLeftOf => {
 | 
			
		||||
): MenuProps => {
 | 
			
		||||
    const menuOptions: IPosition & { chevronFace: ChevronFace } = { chevronFace };
 | 
			
		||||
 | 
			
		||||
    const buttonRight = elementRect.right + window.scrollX;
 | 
			
		||||
@@ -535,7 +533,7 @@ export const aboveRightOf = (
 | 
			
		||||
    elementRect: Pick<DOMRect, "left" | "top" | "bottom">,
 | 
			
		||||
    chevronFace = ChevronFace.None,
 | 
			
		||||
    vPadding = 0,
 | 
			
		||||
): AboveLeftOf => {
 | 
			
		||||
): MenuProps => {
 | 
			
		||||
    const menuOptions: IPosition & { chevronFace: ChevronFace } = { chevronFace };
 | 
			
		||||
 | 
			
		||||
    const buttonLeft = elementRect.left + window.scrollX;
 | 
			
		||||
@@ -555,11 +553,11 @@ export const aboveRightOf = (
 | 
			
		||||
 | 
			
		||||
// Placement method for <ContextMenu /> to position context menu right-aligned and flowing to the left of elementRect
 | 
			
		||||
// and always above elementRect
 | 
			
		||||
export const alwaysAboveLeftOf = (
 | 
			
		||||
export const alwaysMenuProps = (
 | 
			
		||||
    elementRect: Pick<DOMRect, "right" | "bottom" | "top">,
 | 
			
		||||
    chevronFace = ChevronFace.None,
 | 
			
		||||
    vPadding = 0,
 | 
			
		||||
) => {
 | 
			
		||||
): IPosition & { chevronFace: ChevronFace } => {
 | 
			
		||||
    const menuOptions: IPosition & { chevronFace: ChevronFace } = { chevronFace };
 | 
			
		||||
 | 
			
		||||
    const buttonRight = elementRect.right + window.scrollX;
 | 
			
		||||
@@ -578,7 +576,7 @@ export const alwaysAboveRightOf = (
 | 
			
		||||
    elementRect: Pick<DOMRect, "left" | "top">,
 | 
			
		||||
    chevronFace = ChevronFace.None,
 | 
			
		||||
    vPadding = 0,
 | 
			
		||||
) => {
 | 
			
		||||
): IPosition & { chevronFace: ChevronFace } => {
 | 
			
		||||
    const menuOptions: IPosition & { chevronFace: ChevronFace } = { chevronFace };
 | 
			
		||||
 | 
			
		||||
    const buttonLeft = elementRect.left + window.scrollX;
 | 
			
		||||
@@ -607,12 +605,12 @@ export const useContextMenu = <T extends any = HTMLElement>(inputRef?: RefObject
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const [isOpen, setIsOpen] = useState(false);
 | 
			
		||||
    const open = (ev?: SyntheticEvent) => {
 | 
			
		||||
    const open = (ev?: SyntheticEvent): void => {
 | 
			
		||||
        ev?.preventDefault();
 | 
			
		||||
        ev?.stopPropagation();
 | 
			
		||||
        setIsOpen(true);
 | 
			
		||||
    };
 | 
			
		||||
    const close = (ev?: SyntheticEvent) => {
 | 
			
		||||
    const close = (ev?: SyntheticEvent): void => {
 | 
			
		||||
        ev?.preventDefault();
 | 
			
		||||
        ev?.stopPropagation();
 | 
			
		||||
        setIsOpen(false);
 | 
			
		||||
@@ -622,8 +620,11 @@ export const useContextMenu = <T extends any = HTMLElement>(inputRef?: RefObject
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
// XXX: Deprecated, used only for dynamic Tooltips. Avoid using at all costs.
 | 
			
		||||
export function createMenu(ElementClass, props) {
 | 
			
		||||
    const onFinished = function (...args) {
 | 
			
		||||
export function createMenu(
 | 
			
		||||
    ElementClass: typeof React.Component,
 | 
			
		||||
    props: Record<string, any>,
 | 
			
		||||
): { close: (...args: any[]) => void } {
 | 
			
		||||
    const onFinished = function (...args): void {
 | 
			
		||||
        ReactDOM.unmountComponentAtNode(getOrCreateContainer());
 | 
			
		||||
        props?.onFinished?.apply(null, args);
 | 
			
		||||
    };
 | 
			
		||||
 
 | 
			
		||||
@@ -60,7 +60,7 @@ export default class EmbeddedPage extends React.PureComponent<IProps, IState> {
 | 
			
		||||
        return sanitizeHtml(_t(s));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private async fetchEmbed() {
 | 
			
		||||
    private async fetchEmbed(): Promise<void> {
 | 
			
		||||
        let res: Response;
 | 
			
		||||
 | 
			
		||||
        try {
 | 
			
		||||
 
 | 
			
		||||
@@ -37,7 +37,7 @@ const FileDropTarget: React.FC<IProps> = ({ parent, onFileDrop }) => {
 | 
			
		||||
    useEffect(() => {
 | 
			
		||||
        if (!parent || parent.ondrop) return;
 | 
			
		||||
 | 
			
		||||
        const onDragEnter = (ev: DragEvent) => {
 | 
			
		||||
        const onDragEnter = (ev: DragEvent): void => {
 | 
			
		||||
            ev.stopPropagation();
 | 
			
		||||
            ev.preventDefault();
 | 
			
		||||
 | 
			
		||||
@@ -55,7 +55,7 @@ const FileDropTarget: React.FC<IProps> = ({ parent, onFileDrop }) => {
 | 
			
		||||
            }));
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        const onDragLeave = (ev: DragEvent) => {
 | 
			
		||||
        const onDragLeave = (ev: DragEvent): void => {
 | 
			
		||||
            ev.stopPropagation();
 | 
			
		||||
            ev.preventDefault();
 | 
			
		||||
 | 
			
		||||
@@ -65,7 +65,7 @@ const FileDropTarget: React.FC<IProps> = ({ parent, onFileDrop }) => {
 | 
			
		||||
            }));
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        const onDragOver = (ev: DragEvent) => {
 | 
			
		||||
        const onDragOver = (ev: DragEvent): void => {
 | 
			
		||||
            ev.stopPropagation();
 | 
			
		||||
            ev.preventDefault();
 | 
			
		||||
 | 
			
		||||
@@ -79,7 +79,7 @@ const FileDropTarget: React.FC<IProps> = ({ parent, onFileDrop }) => {
 | 
			
		||||
            }
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        const onDrop = (ev: DragEvent) => {
 | 
			
		||||
        const onDrop = (ev: DragEvent): void => {
 | 
			
		||||
            ev.stopPropagation();
 | 
			
		||||
            ev.preventDefault();
 | 
			
		||||
            onFileDrop(ev.dataTransfer);
 | 
			
		||||
 
 | 
			
		||||
@@ -223,7 +223,7 @@ class FilePanel extends React.Component<IProps, IState> {
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public render() {
 | 
			
		||||
    public render(): JSX.Element {
 | 
			
		||||
        if (MatrixClientPeg.get().isGuest()) {
 | 
			
		||||
            return (
 | 
			
		||||
                <BaseCard className="mx_FilePanel mx_RoomView_messageListWrapper" onClose={this.props.onClose}>
 | 
			
		||||
 
 | 
			
		||||
@@ -22,7 +22,7 @@ interface IProps {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export default class GenericErrorPage extends React.PureComponent<IProps> {
 | 
			
		||||
    public render() {
 | 
			
		||||
    public render(): JSX.Element {
 | 
			
		||||
        return (
 | 
			
		||||
            <div className="mx_GenericErrorPage">
 | 
			
		||||
                <div className="mx_GenericErrorPage_box">
 | 
			
		||||
 
 | 
			
		||||
@@ -33,17 +33,17 @@ import MiniAvatarUploader, { AVATAR_SIZE } from "../views/elements/MiniAvatarUpl
 | 
			
		||||
import PosthogTrackers from "../../PosthogTrackers";
 | 
			
		||||
import EmbeddedPage from "./EmbeddedPage";
 | 
			
		||||
 | 
			
		||||
const onClickSendDm = (ev: ButtonEvent) => {
 | 
			
		||||
const onClickSendDm = (ev: ButtonEvent): void => {
 | 
			
		||||
    PosthogTrackers.trackInteraction("WebHomeCreateChatButton", ev);
 | 
			
		||||
    dis.dispatch({ action: "view_create_chat" });
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const onClickExplore = (ev: ButtonEvent) => {
 | 
			
		||||
const onClickExplore = (ev: ButtonEvent): void => {
 | 
			
		||||
    PosthogTrackers.trackInteraction("WebHomeExploreRoomsButton", ev);
 | 
			
		||||
    dis.fire(Action.ViewRoomDirectory);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const onClickNewRoom = (ev: ButtonEvent) => {
 | 
			
		||||
const onClickNewRoom = (ev: ButtonEvent): void => {
 | 
			
		||||
    PosthogTrackers.trackInteraction("WebHomeCreateRoomButton", ev);
 | 
			
		||||
    dis.dispatch({ action: "view_create_room" });
 | 
			
		||||
};
 | 
			
		||||
@@ -52,12 +52,17 @@ interface IProps {
 | 
			
		||||
    justRegistered?: boolean;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const getOwnProfile = (userId: string) => ({
 | 
			
		||||
const getOwnProfile = (
 | 
			
		||||
    userId: string,
 | 
			
		||||
): {
 | 
			
		||||
    displayName: string;
 | 
			
		||||
    avatarUrl: string;
 | 
			
		||||
} => ({
 | 
			
		||||
    displayName: OwnProfileStore.instance.displayName || userId,
 | 
			
		||||
    avatarUrl: OwnProfileStore.instance.getHttpAvatarUrl(AVATAR_SIZE),
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
const UserWelcomeTop = () => {
 | 
			
		||||
const UserWelcomeTop: React.FC = () => {
 | 
			
		||||
    const cli = useContext(MatrixClientContext);
 | 
			
		||||
    const userId = cli.getUserId();
 | 
			
		||||
    const [ownProfile, setOwnProfile] = useState(getOwnProfile(userId));
 | 
			
		||||
 
 | 
			
		||||
@@ -28,7 +28,7 @@ interface IProps {
 | 
			
		||||
interface IState {}
 | 
			
		||||
 | 
			
		||||
export default class HostSignupAction extends React.PureComponent<IProps, IState> {
 | 
			
		||||
    private openDialog = async () => {
 | 
			
		||||
    private openDialog = async (): Promise<void> => {
 | 
			
		||||
        this.props.onClick?.();
 | 
			
		||||
        await HostSignupStore.instance.setHostSignupActive(true);
 | 
			
		||||
    };
 | 
			
		||||
 
 | 
			
		||||
@@ -130,7 +130,7 @@ export default class InteractiveAuthComponent extends React.Component<IProps, IS
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public componentDidMount() {
 | 
			
		||||
    public componentDidMount(): void {
 | 
			
		||||
        this.authLogic
 | 
			
		||||
            .attemptAuth()
 | 
			
		||||
            .then((result) => {
 | 
			
		||||
@@ -155,7 +155,7 @@ export default class InteractiveAuthComponent extends React.Component<IProps, IS
 | 
			
		||||
            });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public componentWillUnmount() {
 | 
			
		||||
    public componentWillUnmount(): void {
 | 
			
		||||
        this.unmounted = true;
 | 
			
		||||
 | 
			
		||||
        if (this.intervalId !== null) {
 | 
			
		||||
@@ -249,7 +249,7 @@ export default class InteractiveAuthComponent extends React.Component<IProps, IS
 | 
			
		||||
        this.authLogic.setEmailSid(sid);
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    public render() {
 | 
			
		||||
    public render(): JSX.Element {
 | 
			
		||||
        const stage = this.state.authStage;
 | 
			
		||||
        if (!stage) {
 | 
			
		||||
            if (this.state.busy) {
 | 
			
		||||
 
 | 
			
		||||
@@ -89,7 +89,7 @@ export default class LeftPanel extends React.Component<IProps, IState> {
 | 
			
		||||
        return SettingsStore.getValue("feature_breadcrumbs_v2") ? BreadcrumbsMode.Labs : BreadcrumbsMode.Legacy;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public componentDidMount() {
 | 
			
		||||
    public componentDidMount(): void {
 | 
			
		||||
        UIStore.instance.trackElementDimensions("ListContainer", this.listContainerRef.current);
 | 
			
		||||
        UIStore.instance.on("ListContainer", this.refreshStickyHeaders);
 | 
			
		||||
        // Using the passive option to not block the main thread
 | 
			
		||||
@@ -97,7 +97,7 @@ export default class LeftPanel extends React.Component<IProps, IState> {
 | 
			
		||||
        this.listContainerRef.current?.addEventListener("scroll", this.onScroll, { passive: true });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public componentWillUnmount() {
 | 
			
		||||
    public componentWillUnmount(): void {
 | 
			
		||||
        BreadcrumbsStore.instance.off(UPDATE_EVENT, this.onBreadcrumbsUpdate);
 | 
			
		||||
        RoomListStore.instance.off(LISTS_UPDATE_EVENT, this.onBreadcrumbsUpdate);
 | 
			
		||||
        SpaceStore.instance.off(UPDATE_SELECTED_SPACE, this.updateActiveSpace);
 | 
			
		||||
@@ -112,25 +112,25 @@ export default class LeftPanel extends React.Component<IProps, IState> {
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private updateActiveSpace = (activeSpace: SpaceKey) => {
 | 
			
		||||
    private updateActiveSpace = (activeSpace: SpaceKey): void => {
 | 
			
		||||
        this.setState({ activeSpace });
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    private onDialPad = () => {
 | 
			
		||||
    private onDialPad = (): void => {
 | 
			
		||||
        dis.fire(Action.OpenDialPad);
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    private onExplore = (ev: ButtonEvent) => {
 | 
			
		||||
    private onExplore = (ev: ButtonEvent): void => {
 | 
			
		||||
        dis.fire(Action.ViewRoomDirectory);
 | 
			
		||||
        PosthogTrackers.trackInteraction("WebLeftPanelExploreRoomsButton", ev);
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    private refreshStickyHeaders = () => {
 | 
			
		||||
    private refreshStickyHeaders = (): void => {
 | 
			
		||||
        if (!this.listContainerRef.current) return; // ignore: no headers to sticky
 | 
			
		||||
        this.handleStickyHeaders(this.listContainerRef.current);
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    private onBreadcrumbsUpdate = () => {
 | 
			
		||||
    private onBreadcrumbsUpdate = (): void => {
 | 
			
		||||
        const newVal = LeftPanel.breadcrumbsMode;
 | 
			
		||||
        if (newVal !== this.state.showBreadcrumbs) {
 | 
			
		||||
            this.setState({ showBreadcrumbs: newVal });
 | 
			
		||||
@@ -141,7 +141,7 @@ export default class LeftPanel extends React.Component<IProps, IState> {
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    private handleStickyHeaders(list: HTMLDivElement) {
 | 
			
		||||
    private handleStickyHeaders(list: HTMLDivElement): void {
 | 
			
		||||
        if (this.isDoingStickyHeaders) return;
 | 
			
		||||
        this.isDoingStickyHeaders = true;
 | 
			
		||||
        window.requestAnimationFrame(() => {
 | 
			
		||||
@@ -150,7 +150,7 @@ export default class LeftPanel extends React.Component<IProps, IState> {
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private doStickyHeaders(list: HTMLDivElement) {
 | 
			
		||||
    private doStickyHeaders(list: HTMLDivElement): void {
 | 
			
		||||
        const topEdge = list.scrollTop;
 | 
			
		||||
        const bottomEdge = list.offsetHeight + list.scrollTop;
 | 
			
		||||
        const sublists = list.querySelectorAll<HTMLDivElement>(".mx_RoomSublist:not(.mx_RoomSublist_hidden)");
 | 
			
		||||
@@ -282,20 +282,20 @@ export default class LeftPanel extends React.Component<IProps, IState> {
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private onScroll = (ev: Event) => {
 | 
			
		||||
    private onScroll = (ev: Event): void => {
 | 
			
		||||
        const list = ev.target as HTMLDivElement;
 | 
			
		||||
        this.handleStickyHeaders(list);
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    private onFocus = (ev: React.FocusEvent) => {
 | 
			
		||||
    private onFocus = (ev: React.FocusEvent): void => {
 | 
			
		||||
        this.focusedElement = ev.target;
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    private onBlur = () => {
 | 
			
		||||
    private onBlur = (): void => {
 | 
			
		||||
        this.focusedElement = null;
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    private onKeyDown = (ev: React.KeyboardEvent, state?: IRovingTabIndexState) => {
 | 
			
		||||
    private onKeyDown = (ev: React.KeyboardEvent, state?: IRovingTabIndexState): void => {
 | 
			
		||||
        if (!this.focusedElement) return;
 | 
			
		||||
 | 
			
		||||
        const action = getKeyBindingsManager().getRoomListAction(ev);
 | 
			
		||||
 
 | 
			
		||||
@@ -142,7 +142,7 @@ export default class LegacyCallEventGrouper extends EventEmitter {
 | 
			
		||||
        return [...this.events][0]?.getRoomId();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private onSilencedCallsChanged = () => {
 | 
			
		||||
    private onSilencedCallsChanged = (): void => {
 | 
			
		||||
        const newState = LegacyCallHandler.instance.isCallSilenced(this.callId);
 | 
			
		||||
        this.emit(LegacyCallEventGrouperEvent.SilencedChanged, newState);
 | 
			
		||||
    };
 | 
			
		||||
@@ -163,20 +163,20 @@ export default class LegacyCallEventGrouper extends EventEmitter {
 | 
			
		||||
        LegacyCallHandler.instance.placeCall(this.roomId, this.isVoice ? CallType.Voice : CallType.Video);
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    public toggleSilenced = () => {
 | 
			
		||||
    public toggleSilenced = (): void => {
 | 
			
		||||
        const silenced = LegacyCallHandler.instance.isCallSilenced(this.callId);
 | 
			
		||||
        silenced
 | 
			
		||||
            ? LegacyCallHandler.instance.unSilenceCall(this.callId)
 | 
			
		||||
            : LegacyCallHandler.instance.silenceCall(this.callId);
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    private setCallListeners() {
 | 
			
		||||
    private setCallListeners(): void {
 | 
			
		||||
        if (!this.call) return;
 | 
			
		||||
        this.call.addListener(CallEvent.State, this.setState);
 | 
			
		||||
        this.call.addListener(CallEvent.LengthChanged, this.onLengthChanged);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private setState = () => {
 | 
			
		||||
    private setState = (): void => {
 | 
			
		||||
        if (CONNECTING_STATES.includes(this.call?.state)) {
 | 
			
		||||
            this.state = CallState.Connecting;
 | 
			
		||||
        } else if (SUPPORTED_STATES.includes(this.call?.state)) {
 | 
			
		||||
@@ -190,7 +190,7 @@ export default class LegacyCallEventGrouper extends EventEmitter {
 | 
			
		||||
        this.emit(LegacyCallEventGrouperEvent.StateChanged, this.state);
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    private setCall = () => {
 | 
			
		||||
    private setCall = (): void => {
 | 
			
		||||
        if (this.call) return;
 | 
			
		||||
 | 
			
		||||
        this.call = LegacyCallHandler.instance.getCallById(this.callId);
 | 
			
		||||
@@ -198,7 +198,7 @@ export default class LegacyCallEventGrouper extends EventEmitter {
 | 
			
		||||
        this.setState();
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    public add(event: MatrixEvent) {
 | 
			
		||||
    public add(event: MatrixEvent): void {
 | 
			
		||||
        if (this.events.has(event)) return; // nothing to do
 | 
			
		||||
        this.events.add(event);
 | 
			
		||||
        this.setCall();
 | 
			
		||||
 
 | 
			
		||||
@@ -159,7 +159,7 @@ class LoggedInView extends React.Component<IProps, IState> {
 | 
			
		||||
        this.resizeHandler = React.createRef();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public componentDidMount() {
 | 
			
		||||
    public componentDidMount(): void {
 | 
			
		||||
        document.addEventListener("keydown", this.onNativeKeyDown, false);
 | 
			
		||||
        LegacyCallHandler.instance.addListener(LegacyCallHandlerEvent.CallState, this.onCallState);
 | 
			
		||||
 | 
			
		||||
@@ -191,7 +191,7 @@ class LoggedInView extends React.Component<IProps, IState> {
 | 
			
		||||
        this.refreshBackgroundImage();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public componentWillUnmount() {
 | 
			
		||||
    public componentWillUnmount(): void {
 | 
			
		||||
        document.removeEventListener("keydown", this.onNativeKeyDown, false);
 | 
			
		||||
        LegacyCallHandler.instance.removeListener(LegacyCallHandlerEvent.CallState, this.onCallState);
 | 
			
		||||
        this._matrixClient.removeListener(ClientEvent.AccountData, this.onAccountData);
 | 
			
		||||
@@ -221,14 +221,14 @@ class LoggedInView extends React.Component<IProps, IState> {
 | 
			
		||||
        this.setState({ backgroundImage });
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    public canResetTimelineInRoom = (roomId: string) => {
 | 
			
		||||
    public canResetTimelineInRoom = (roomId: string): boolean => {
 | 
			
		||||
        if (!this._roomView.current) {
 | 
			
		||||
            return true;
 | 
			
		||||
        }
 | 
			
		||||
        return this._roomView.current.canResetTimeline();
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    private createResizer() {
 | 
			
		||||
    private createResizer(): Resizer {
 | 
			
		||||
        let panelSize;
 | 
			
		||||
        let panelCollapsed;
 | 
			
		||||
        const collapseConfig: ICollapseConfig = {
 | 
			
		||||
@@ -268,7 +268,7 @@ class LoggedInView extends React.Component<IProps, IState> {
 | 
			
		||||
        return resizer;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private loadResizerPreferences() {
 | 
			
		||||
    private loadResizerPreferences(): void {
 | 
			
		||||
        let lhsSize = parseInt(window.localStorage.getItem("mx_lhs_size"), 10);
 | 
			
		||||
        if (isNaN(lhsSize)) {
 | 
			
		||||
            lhsSize = 350;
 | 
			
		||||
@@ -276,13 +276,13 @@ class LoggedInView extends React.Component<IProps, IState> {
 | 
			
		||||
        this.resizer.forHandleWithId("lp-resizer").resize(lhsSize);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private onAccountData = (event: MatrixEvent) => {
 | 
			
		||||
    private onAccountData = (event: MatrixEvent): void => {
 | 
			
		||||
        if (event.getType() === "m.ignored_user_list") {
 | 
			
		||||
            dis.dispatch({ action: "ignore_state_changed" });
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    private onCompactLayoutChanged = () => {
 | 
			
		||||
    private onCompactLayoutChanged = (): void => {
 | 
			
		||||
        this.setState({
 | 
			
		||||
            useCompactLayout: SettingsStore.getValue("useCompactLayout"),
 | 
			
		||||
        });
 | 
			
		||||
@@ -311,13 +311,13 @@ class LoggedInView extends React.Component<IProps, IState> {
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    private onUsageLimitDismissed = () => {
 | 
			
		||||
    private onUsageLimitDismissed = (): void => {
 | 
			
		||||
        this.setState({
 | 
			
		||||
            usageLimitDismissed: true,
 | 
			
		||||
        });
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    private calculateServerLimitToast(syncError: IState["syncErrorData"], usageLimitEventContent?: IUsageLimit) {
 | 
			
		||||
    private calculateServerLimitToast(syncError: IState["syncErrorData"], usageLimitEventContent?: IUsageLimit): void {
 | 
			
		||||
        const error = (syncError?.error as MatrixError)?.errcode === "M_RESOURCE_LIMIT_EXCEEDED";
 | 
			
		||||
        if (error) {
 | 
			
		||||
            usageLimitEventContent = (syncError?.error as MatrixError).data as IUsageLimit;
 | 
			
		||||
@@ -337,9 +337,9 @@ class LoggedInView extends React.Component<IProps, IState> {
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private updateServerNoticeEvents = async () => {
 | 
			
		||||
    private updateServerNoticeEvents = async (): Promise<void> => {
 | 
			
		||||
        const serverNoticeList = RoomListStore.instance.orderedLists[DefaultTagID.ServerNotice];
 | 
			
		||||
        if (!serverNoticeList) return [];
 | 
			
		||||
        if (!serverNoticeList) return;
 | 
			
		||||
 | 
			
		||||
        const events = [];
 | 
			
		||||
        let pinnedEventTs = 0;
 | 
			
		||||
@@ -379,7 +379,7 @@ class LoggedInView extends React.Component<IProps, IState> {
 | 
			
		||||
        });
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    private onPaste = (ev: ClipboardEvent) => {
 | 
			
		||||
    private onPaste = (ev: ClipboardEvent): void => {
 | 
			
		||||
        const element = ev.target as HTMLElement;
 | 
			
		||||
        const inputableElement = getInputableElement(element);
 | 
			
		||||
        if (inputableElement === document.activeElement) return; // nothing to do
 | 
			
		||||
@@ -422,13 +422,13 @@ class LoggedInView extends React.Component<IProps, IState> {
 | 
			
		||||
    We also listen with a native listener on the document to get keydown events when no element is focused.
 | 
			
		||||
    Bubbling is irrelevant here as the target is the body element.
 | 
			
		||||
    */
 | 
			
		||||
    private onReactKeyDown = (ev) => {
 | 
			
		||||
    private onReactKeyDown = (ev): void => {
 | 
			
		||||
        // events caught while bubbling up on the root element
 | 
			
		||||
        // of this component, so something must be focused.
 | 
			
		||||
        this.onKeyDown(ev);
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    private onNativeKeyDown = (ev) => {
 | 
			
		||||
    private onNativeKeyDown = (ev): void => {
 | 
			
		||||
        // only pass this if there is no focused element.
 | 
			
		||||
        // if there is, onKeyDown will be called by the
 | 
			
		||||
        // react keydown handler that respects the react bubbling order.
 | 
			
		||||
@@ -437,7 +437,7 @@ class LoggedInView extends React.Component<IProps, IState> {
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    private onKeyDown = (ev) => {
 | 
			
		||||
    private onKeyDown = (ev): void => {
 | 
			
		||||
        let handled = false;
 | 
			
		||||
 | 
			
		||||
        const roomAction = getKeyBindingsManager().getRoomAction(ev);
 | 
			
		||||
@@ -615,13 +615,13 @@ class LoggedInView extends React.Component<IProps, IState> {
 | 
			
		||||
     * dispatch a page-up/page-down/etc to the appropriate component
 | 
			
		||||
     * @param {Object} ev The key event
 | 
			
		||||
     */
 | 
			
		||||
    private onScrollKeyPressed = (ev) => {
 | 
			
		||||
    private onScrollKeyPressed = (ev): void => {
 | 
			
		||||
        if (this._roomView.current) {
 | 
			
		||||
            this._roomView.current.handleScrollKey(ev);
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    public render() {
 | 
			
		||||
    public render(): JSX.Element {
 | 
			
		||||
        let pageElement;
 | 
			
		||||
 | 
			
		||||
        switch (this.props.page_type) {
 | 
			
		||||
 
 | 
			
		||||
@@ -216,7 +216,7 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
 | 
			
		||||
        realQueryParams: {},
 | 
			
		||||
        startingFragmentQueryParams: {},
 | 
			
		||||
        config: {},
 | 
			
		||||
        onTokenLoginCompleted: () => {},
 | 
			
		||||
        onTokenLoginCompleted: (): void => {},
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    private firstSyncComplete = false;
 | 
			
		||||
@@ -317,7 +317,7 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
 | 
			
		||||
                this.props.realQueryParams,
 | 
			
		||||
                this.props.defaultDeviceDisplayName,
 | 
			
		||||
                this.getFragmentAfterLogin(),
 | 
			
		||||
            ).then(async (loggedIn) => {
 | 
			
		||||
            ).then(async (loggedIn): Promise<boolean | void> => {
 | 
			
		||||
                if (this.props.realQueryParams?.loginToken) {
 | 
			
		||||
                    // remove the loginToken from the URL regardless
 | 
			
		||||
                    this.props.onTokenLoginCompleted();
 | 
			
		||||
@@ -353,7 +353,7 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
 | 
			
		||||
        initSentry(SdkConfig.get("sentry"));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private async postLoginSetup() {
 | 
			
		||||
    private async postLoginSetup(): Promise<void> {
 | 
			
		||||
        const cli = MatrixClientPeg.get();
 | 
			
		||||
        const cryptoEnabled = cli.isCryptoEnabled();
 | 
			
		||||
        if (!cryptoEnabled) {
 | 
			
		||||
@@ -367,7 +367,7 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
 | 
			
		||||
            // as a proxy to figure out if it's worth prompting the user to verify
 | 
			
		||||
            // from another device.
 | 
			
		||||
            promisesList.push(
 | 
			
		||||
                (async () => {
 | 
			
		||||
                (async (): Promise<void> => {
 | 
			
		||||
                    crossSigningIsSetUp = await cli.userHasCrossSigningKeys();
 | 
			
		||||
                })(),
 | 
			
		||||
            );
 | 
			
		||||
@@ -417,7 +417,7 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
 | 
			
		||||
        window.addEventListener("resize", this.onWindowResized);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public componentDidUpdate(prevProps, prevState) {
 | 
			
		||||
    public componentDidUpdate(prevProps, prevState): void {
 | 
			
		||||
        if (this.shouldTrackPageChange(prevState, this.state)) {
 | 
			
		||||
            const durationMs = this.stopPageChangeTimer();
 | 
			
		||||
            PosthogTrackers.instance.trackPageChange(this.state.view, this.state.page_type, durationMs);
 | 
			
		||||
@@ -428,7 +428,7 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public componentWillUnmount() {
 | 
			
		||||
    public componentWillUnmount(): void {
 | 
			
		||||
        Lifecycle.stopMatrixClient();
 | 
			
		||||
        dis.unregister(this.dispatcherRef);
 | 
			
		||||
        this.themeWatcher.stop();
 | 
			
		||||
@@ -477,7 +477,7 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private getServerProperties() {
 | 
			
		||||
    private getServerProperties(): { serverConfig: ValidatedServerConfig } {
 | 
			
		||||
        let props = this.state.serverConfig;
 | 
			
		||||
        if (!props) props = this.props.serverConfig; // for unit tests
 | 
			
		||||
        if (!props) props = SdkConfig.get("validated_server_config");
 | 
			
		||||
@@ -513,11 +513,11 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
 | 
			
		||||
        // to try logging out.
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private startPageChangeTimer() {
 | 
			
		||||
    private startPageChangeTimer(): void {
 | 
			
		||||
        PerformanceMonitor.instance.start(PerformanceEntryNames.PAGE_CHANGE);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private stopPageChangeTimer() {
 | 
			
		||||
    private stopPageChangeTimer(): number | null {
 | 
			
		||||
        const perfMonitor = PerformanceMonitor.instance;
 | 
			
		||||
 | 
			
		||||
        perfMonitor.stop(PerformanceEntryNames.PAGE_CHANGE);
 | 
			
		||||
@@ -876,13 +876,13 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    private setPage(pageType: PageType) {
 | 
			
		||||
    private setPage(pageType: PageType): void {
 | 
			
		||||
        this.setState({
 | 
			
		||||
            page_type: pageType,
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private async startRegistration(params: { [key: string]: string }) {
 | 
			
		||||
    private async startRegistration(params: { [key: string]: string }): Promise<void> {
 | 
			
		||||
        const newState: Partial<IState> = {
 | 
			
		||||
            view: Views.REGISTER,
 | 
			
		||||
        };
 | 
			
		||||
@@ -916,7 +916,7 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // switch view to the given room
 | 
			
		||||
    private async viewRoom(roomInfo: ViewRoomPayload) {
 | 
			
		||||
    private async viewRoom(roomInfo: ViewRoomPayload): Promise<void> {
 | 
			
		||||
        this.focusComposer = true;
 | 
			
		||||
 | 
			
		||||
        if (roomInfo.room_alias) {
 | 
			
		||||
@@ -992,7 +992,7 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private viewSomethingBehindModal() {
 | 
			
		||||
    private viewSomethingBehindModal(): void {
 | 
			
		||||
        if (this.state.view !== Views.LOGGED_IN) {
 | 
			
		||||
            this.viewWelcome();
 | 
			
		||||
            return;
 | 
			
		||||
@@ -1002,7 +1002,7 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private viewWelcome() {
 | 
			
		||||
    private viewWelcome(): void {
 | 
			
		||||
        if (shouldUseLoginForWelcome(SdkConfig.get())) {
 | 
			
		||||
            return this.viewLogin();
 | 
			
		||||
        }
 | 
			
		||||
@@ -1014,7 +1014,7 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
 | 
			
		||||
        this.themeWatcher.recheck();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private viewLogin(otherState?: any) {
 | 
			
		||||
    private viewLogin(otherState?: any): void {
 | 
			
		||||
        this.setStateForNewView({
 | 
			
		||||
            view: Views.LOGIN,
 | 
			
		||||
            ...otherState,
 | 
			
		||||
@@ -1024,7 +1024,7 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
 | 
			
		||||
        this.themeWatcher.recheck();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private viewHome(justRegistered = false) {
 | 
			
		||||
    private viewHome(justRegistered = false): void {
 | 
			
		||||
        // The home page requires the "logged in" view, so we'll set that.
 | 
			
		||||
        this.setStateForNewView({
 | 
			
		||||
            view: Views.LOGGED_IN,
 | 
			
		||||
@@ -1037,7 +1037,7 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
 | 
			
		||||
        this.themeWatcher.recheck();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private viewUser(userId: string, subAction: string) {
 | 
			
		||||
    private viewUser(userId: string, subAction: string): void {
 | 
			
		||||
        // Wait for the first sync so that `getRoom` gives us a room object if it's
 | 
			
		||||
        // in the sync response
 | 
			
		||||
        const waitForSync = this.firstSyncPromise ? this.firstSyncPromise.promise : Promise.resolve();
 | 
			
		||||
@@ -1052,7 +1052,7 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private async createRoom(defaultPublic = false, defaultName?: string, type?: RoomType) {
 | 
			
		||||
    private async createRoom(defaultPublic = false, defaultName?: string, type?: RoomType): Promise<void> {
 | 
			
		||||
        const modal = Modal.createDialog(CreateRoomDialog, {
 | 
			
		||||
            type,
 | 
			
		||||
            defaultPublic,
 | 
			
		||||
@@ -1065,7 +1065,7 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private chatCreateOrReuse(userId: string) {
 | 
			
		||||
    private chatCreateOrReuse(userId: string): void {
 | 
			
		||||
        const snakedConfig = new SnakedObject<IConfigOptions>(this.props.config);
 | 
			
		||||
        // Use a deferred action to reshow the dialog once the user has registered
 | 
			
		||||
        if (MatrixClientPeg.get().isGuest()) {
 | 
			
		||||
@@ -1115,11 +1115,11 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private leaveRoomWarnings(roomId: string) {
 | 
			
		||||
    private leaveRoomWarnings(roomId: string): JSX.Element[] {
 | 
			
		||||
        const roomToLeave = MatrixClientPeg.get().getRoom(roomId);
 | 
			
		||||
        const isSpace = roomToLeave?.isSpaceRoom();
 | 
			
		||||
        // Show a warning if there are additional complications.
 | 
			
		||||
        const warnings = [];
 | 
			
		||||
        const warnings: JSX.Element[] = [];
 | 
			
		||||
 | 
			
		||||
        const memberCount = roomToLeave.currentState.getJoinedMemberCount();
 | 
			
		||||
        if (memberCount === 1) {
 | 
			
		||||
@@ -1153,7 +1153,7 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
 | 
			
		||||
        return warnings;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private leaveRoom(roomId: string) {
 | 
			
		||||
    private leaveRoom(roomId: string): void {
 | 
			
		||||
        const roomToLeave = MatrixClientPeg.get().getRoom(roomId);
 | 
			
		||||
        const warnings = this.leaveRoomWarnings(roomId);
 | 
			
		||||
 | 
			
		||||
@@ -1184,7 +1184,7 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private forgetRoom(roomId: string) {
 | 
			
		||||
    private forgetRoom(roomId: string): void {
 | 
			
		||||
        const room = MatrixClientPeg.get().getRoom(roomId);
 | 
			
		||||
        MatrixClientPeg.get()
 | 
			
		||||
            .forget(roomId)
 | 
			
		||||
@@ -1208,7 +1208,7 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
 | 
			
		||||
            });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private async copyRoom(roomId: string) {
 | 
			
		||||
    private async copyRoom(roomId: string): Promise<void> {
 | 
			
		||||
        const roomLink = makeRoomPermalink(roomId);
 | 
			
		||||
        const success = await copyPlaintext(roomLink);
 | 
			
		||||
        if (!success) {
 | 
			
		||||
@@ -1223,13 +1223,13 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
 | 
			
		||||
     * Starts a chat with the welcome user, if the user doesn't already have one
 | 
			
		||||
     * @returns {string} The room ID of the new room, or null if no room was created
 | 
			
		||||
     */
 | 
			
		||||
    private async startWelcomeUserChat() {
 | 
			
		||||
    private async startWelcomeUserChat(): Promise<string | null> {
 | 
			
		||||
        // We can end up with multiple tabs post-registration where the user
 | 
			
		||||
        // might then end up with a session and we don't want them all making
 | 
			
		||||
        // a chat with the welcome user: try to de-dupe.
 | 
			
		||||
        // We need to wait for the first sync to complete for this to
 | 
			
		||||
        // work though.
 | 
			
		||||
        let waitFor;
 | 
			
		||||
        let waitFor: Promise<void>;
 | 
			
		||||
        if (!this.firstSyncComplete) {
 | 
			
		||||
            waitFor = this.firstSyncPromise.promise;
 | 
			
		||||
        } else {
 | 
			
		||||
@@ -1254,7 +1254,7 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
 | 
			
		||||
            // run without the update to m.direct, making another welcome
 | 
			
		||||
            // user room (it doesn't wait for new data from the server, just
 | 
			
		||||
            // the saved sync to be loaded).
 | 
			
		||||
            const saveWelcomeUser = (ev: MatrixEvent) => {
 | 
			
		||||
            const saveWelcomeUser = (ev: MatrixEvent): void => {
 | 
			
		||||
                if (ev.getType() === EventType.Direct && ev.getContent()[snakedConfig.get("welcome_user_id")]) {
 | 
			
		||||
                    MatrixClientPeg.get().store.save(true);
 | 
			
		||||
                    MatrixClientPeg.get().removeListener(ClientEvent.AccountData, saveWelcomeUser);
 | 
			
		||||
@@ -1270,7 +1270,7 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
 | 
			
		||||
    /**
 | 
			
		||||
     * Called when a new logged in session has started
 | 
			
		||||
     */
 | 
			
		||||
    private async onLoggedIn() {
 | 
			
		||||
    private async onLoggedIn(): Promise<void> {
 | 
			
		||||
        ThemeController.isLogin = false;
 | 
			
		||||
        this.themeWatcher.recheck();
 | 
			
		||||
        StorageManager.tryPersistStorage();
 | 
			
		||||
@@ -1301,7 +1301,7 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private async onShowPostLoginScreen(useCase?: UseCase) {
 | 
			
		||||
    private async onShowPostLoginScreen(useCase?: UseCase): Promise<void> {
 | 
			
		||||
        if (useCase) {
 | 
			
		||||
            PosthogAnalytics.instance.setProperty("ftueUseCaseSelection", useCase);
 | 
			
		||||
            SettingsStore.setValue("FTUE.useCaseSelection", null, SettingLevel.ACCOUNT, useCase);
 | 
			
		||||
@@ -1370,7 +1370,7 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private initPosthogAnalyticsToast() {
 | 
			
		||||
    private initPosthogAnalyticsToast(): void {
 | 
			
		||||
        // Show the analytics toast if necessary
 | 
			
		||||
        if (SettingsStore.getValue("pseudonymousAnalyticsOptIn") === null) {
 | 
			
		||||
            showAnalyticsToast();
 | 
			
		||||
@@ -1397,7 +1397,7 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private showScreenAfterLogin() {
 | 
			
		||||
    private showScreenAfterLogin(): void {
 | 
			
		||||
        // If screenAfterLogin is set, use that, then null it so that a second login will
 | 
			
		||||
        // result in view_home_page, _user_settings or _room_directory
 | 
			
		||||
        if (this.screenAfterLogin && this.screenAfterLogin.screen) {
 | 
			
		||||
@@ -1415,7 +1415,7 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private viewLastRoom() {
 | 
			
		||||
    private viewLastRoom(): void {
 | 
			
		||||
        dis.dispatch<ViewRoomPayload>({
 | 
			
		||||
            action: Action.ViewRoom,
 | 
			
		||||
            room_id: localStorage.getItem("mx_last_room_id"),
 | 
			
		||||
@@ -1426,7 +1426,7 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
 | 
			
		||||
    /**
 | 
			
		||||
     * Called when the session is logged out
 | 
			
		||||
     */
 | 
			
		||||
    private onLoggedOut() {
 | 
			
		||||
    private onLoggedOut(): void {
 | 
			
		||||
        this.viewLogin({
 | 
			
		||||
            ready: false,
 | 
			
		||||
            collapseLhs: false,
 | 
			
		||||
@@ -1439,7 +1439,7 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
 | 
			
		||||
    /**
 | 
			
		||||
     * Called when the session is softly logged out
 | 
			
		||||
     */
 | 
			
		||||
    private onSoftLogout() {
 | 
			
		||||
    private onSoftLogout(): void {
 | 
			
		||||
        this.notifyNewScreen("soft_logout");
 | 
			
		||||
        this.setStateForNewView({
 | 
			
		||||
            view: Views.SOFT_LOGOUT,
 | 
			
		||||
@@ -1455,7 +1455,7 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
 | 
			
		||||
     * Called just before the matrix client is started
 | 
			
		||||
     * (useful for setting listeners)
 | 
			
		||||
     */
 | 
			
		||||
    private onWillStartClient() {
 | 
			
		||||
    private onWillStartClient(): void {
 | 
			
		||||
        // reset the 'have completed first sync' flag,
 | 
			
		||||
        // since we're about to start the client and therefore about
 | 
			
		||||
        // to do the first sync
 | 
			
		||||
@@ -1610,7 +1610,7 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
 | 
			
		||||
                    break;
 | 
			
		||||
            }
 | 
			
		||||
        });
 | 
			
		||||
        cli.on(CryptoEvent.KeyBackupFailed, async (errcode) => {
 | 
			
		||||
        cli.on(CryptoEvent.KeyBackupFailed, async (errcode): Promise<void> => {
 | 
			
		||||
            let haveNewVersion;
 | 
			
		||||
            let newVersionInfo;
 | 
			
		||||
            // if key backup is still enabled, there must be a new backup in place
 | 
			
		||||
@@ -1678,7 +1678,7 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
 | 
			
		||||
     * setting up anything that requires the client to be started.
 | 
			
		||||
     * @private
 | 
			
		||||
     */
 | 
			
		||||
    private onClientStarted() {
 | 
			
		||||
    private onClientStarted(): void {
 | 
			
		||||
        const cli = MatrixClientPeg.get();
 | 
			
		||||
 | 
			
		||||
        if (cli.isCryptoEnabled()) {
 | 
			
		||||
@@ -1700,7 +1700,7 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public showScreen(screen: string, params?: { [key: string]: any }) {
 | 
			
		||||
    public showScreen(screen: string, params?: { [key: string]: any }): void {
 | 
			
		||||
        const cli = MatrixClientPeg.get();
 | 
			
		||||
        const isLoggedOutOrGuest = !cli || cli.isGuest();
 | 
			
		||||
        if (!isLoggedOutOrGuest && AUTH_SCREENS.includes(screen)) {
 | 
			
		||||
@@ -1861,14 +1861,14 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private notifyNewScreen(screen: string, replaceLast = false) {
 | 
			
		||||
    private notifyNewScreen(screen: string, replaceLast = false): void {
 | 
			
		||||
        if (this.props.onNewScreen) {
 | 
			
		||||
            this.props.onNewScreen(screen, replaceLast);
 | 
			
		||||
        }
 | 
			
		||||
        this.setPageSubtitle();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private onLogoutClick(event: React.MouseEvent<HTMLAnchorElement, MouseEvent>) {
 | 
			
		||||
    private onLogoutClick(event: React.MouseEvent<HTMLAnchorElement, MouseEvent>): void {
 | 
			
		||||
        dis.dispatch({
 | 
			
		||||
            action: "logout",
 | 
			
		||||
        });
 | 
			
		||||
@@ -1876,7 +1876,7 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
 | 
			
		||||
        event.preventDefault();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private handleResize = () => {
 | 
			
		||||
    private handleResize = (): void => {
 | 
			
		||||
        const LHS_THRESHOLD = 1000;
 | 
			
		||||
        const width = UIStore.instance.windowWidth;
 | 
			
		||||
 | 
			
		||||
@@ -1892,19 +1892,19 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
 | 
			
		||||
        this.state.resizeNotifier.notifyWindowResized();
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    private dispatchTimelineResize() {
 | 
			
		||||
    private dispatchTimelineResize(): void {
 | 
			
		||||
        dis.dispatch({ action: "timeline_resize" });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private onRegisterClick = () => {
 | 
			
		||||
    private onRegisterClick = (): void => {
 | 
			
		||||
        this.showScreen("register");
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    private onLoginClick = () => {
 | 
			
		||||
    private onLoginClick = (): void => {
 | 
			
		||||
        this.showScreen("login");
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    private onForgotPasswordClick = () => {
 | 
			
		||||
    private onForgotPasswordClick = (): void => {
 | 
			
		||||
        this.showScreen("forgot_password");
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
@@ -1926,7 +1926,7 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private setPageSubtitle(subtitle = "") {
 | 
			
		||||
    private setPageSubtitle(subtitle = ""): void {
 | 
			
		||||
        if (this.state.currentRoomId) {
 | 
			
		||||
            const client = MatrixClientPeg.get();
 | 
			
		||||
            const room = client && client.getRoom(this.state.currentRoomId);
 | 
			
		||||
@@ -1963,11 +1963,11 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
 | 
			
		||||
        this.setPageSubtitle();
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    private onServerConfigChange = (serverConfig: ValidatedServerConfig) => {
 | 
			
		||||
    private onServerConfigChange = (serverConfig: ValidatedServerConfig): void => {
 | 
			
		||||
        this.setState({ serverConfig });
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    private makeRegistrationUrl = (params: QueryDict) => {
 | 
			
		||||
    private makeRegistrationUrl = (params: QueryDict): string => {
 | 
			
		||||
        if (this.props.startingFragmentQueryParams.referrer) {
 | 
			
		||||
            params.referrer = this.props.startingFragmentQueryParams.referrer;
 | 
			
		||||
        }
 | 
			
		||||
@@ -2016,7 +2016,7 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
 | 
			
		||||
        return fragmentAfterLogin;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public render() {
 | 
			
		||||
    public render(): JSX.Element {
 | 
			
		||||
        const fragmentAfterLogin = this.getFragmentAfterLogin();
 | 
			
		||||
        let view = null;
 | 
			
		||||
 | 
			
		||||
@@ -2132,7 +2132,7 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
 | 
			
		||||
                />
 | 
			
		||||
            );
 | 
			
		||||
        } else if (this.state.view === Views.USE_CASE_SELECTION) {
 | 
			
		||||
            view = <UseCaseSelection onFinished={(useCase) => this.onShowPostLoginScreen(useCase)} />;
 | 
			
		||||
            view = <UseCaseSelection onFinished={(useCase): Promise<void> => this.onShowPostLoginScreen(useCase)} />;
 | 
			
		||||
        } else {
 | 
			
		||||
            logger.error(`Unknown view ${this.state.view}`);
 | 
			
		||||
        }
 | 
			
		||||
 
 | 
			
		||||
@@ -296,19 +296,19 @@ export default class MessagePanel extends React.Component<IProps, IState> {
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public componentDidMount() {
 | 
			
		||||
    public componentDidMount(): void {
 | 
			
		||||
        this.calculateRoomMembersCount();
 | 
			
		||||
        this.props.room?.currentState.on(RoomStateEvent.Update, this.calculateRoomMembersCount);
 | 
			
		||||
        this.isMounted = true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public componentWillUnmount() {
 | 
			
		||||
    public componentWillUnmount(): void {
 | 
			
		||||
        this.isMounted = false;
 | 
			
		||||
        this.props.room?.currentState.off(RoomStateEvent.Update, this.calculateRoomMembersCount);
 | 
			
		||||
        SettingsStore.unwatchSetting(this.showTypingNotificationsWatcherRef);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public componentDidUpdate(prevProps, prevState) {
 | 
			
		||||
    public componentDidUpdate(prevProps, prevState): void {
 | 
			
		||||
        if (prevProps.layout !== this.props.layout) {
 | 
			
		||||
            this.calculateRoomMembersCount();
 | 
			
		||||
        }
 | 
			
		||||
@@ -752,7 +752,7 @@ export default class MessagePanel extends React.Component<IProps, IState> {
 | 
			
		||||
        const readReceipts = this.readReceiptsByEvent[eventId];
 | 
			
		||||
 | 
			
		||||
        let isLastSuccessful = false;
 | 
			
		||||
        const isSentState = (s) => !s || s === "sent";
 | 
			
		||||
        const isSentState = (s): boolean => !s || s === "sent";
 | 
			
		||||
        const isSent = isSentState(mxEv.getAssociatedStatus());
 | 
			
		||||
        const hasNextEvent = nextEvent && this.shouldShowEvent(nextEvent);
 | 
			
		||||
        if (!hasNextEvent && isSent) {
 | 
			
		||||
@@ -982,7 +982,7 @@ export default class MessagePanel extends React.Component<IProps, IState> {
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public render() {
 | 
			
		||||
    public render(): JSX.Element {
 | 
			
		||||
        let topSpinner;
 | 
			
		||||
        let bottomSpinner;
 | 
			
		||||
        if (this.props.backPaginating) {
 | 
			
		||||
 
 | 
			
		||||
@@ -37,15 +37,15 @@ export default class NonUrgentToastContainer extends React.PureComponent<IProps,
 | 
			
		||||
        NonUrgentToastStore.instance.on(UPDATE_EVENT, this.onUpdateToasts);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public componentWillUnmount() {
 | 
			
		||||
    public componentWillUnmount(): void {
 | 
			
		||||
        NonUrgentToastStore.instance.off(UPDATE_EVENT, this.onUpdateToasts);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private onUpdateToasts = () => {
 | 
			
		||||
    private onUpdateToasts = (): void => {
 | 
			
		||||
        this.setState({ toasts: NonUrgentToastStore.instance.components });
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    public render() {
 | 
			
		||||
    public render(): JSX.Element {
 | 
			
		||||
        const toasts = this.state.toasts.map((t, i) => {
 | 
			
		||||
            return (
 | 
			
		||||
                <div className="mx_NonUrgentToastContainer_toast" key={`toast-${i}`}>
 | 
			
		||||
 
 | 
			
		||||
@@ -55,7 +55,7 @@ export default class NotificationPanel extends React.PureComponent<IProps, IStat
 | 
			
		||||
        this.setState({ narrow });
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    public render() {
 | 
			
		||||
    public render(): JSX.Element {
 | 
			
		||||
        const emptyState = (
 | 
			
		||||
            <div className="mx_RightPanel_empty mx_NotificationPanel_empty">
 | 
			
		||||
                <h2>{_t("You're all caught up")}</h2>
 | 
			
		||||
 
 | 
			
		||||
@@ -79,7 +79,7 @@ export default class PictureInPictureDragger extends React.Component<IProps> {
 | 
			
		||||
        this._moving = value;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public componentDidMount() {
 | 
			
		||||
    public componentDidMount(): void {
 | 
			
		||||
        document.addEventListener("mousemove", this.onMoving);
 | 
			
		||||
        document.addEventListener("mouseup", this.onEndMoving);
 | 
			
		||||
        UIStore.instance.on(UI_EVENTS.Resize, this.onResize);
 | 
			
		||||
@@ -87,7 +87,7 @@ export default class PictureInPictureDragger extends React.Component<IProps> {
 | 
			
		||||
        this.snap();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public componentWillUnmount() {
 | 
			
		||||
    public componentWillUnmount(): void {
 | 
			
		||||
        document.removeEventListener("mousemove", this.onMoving);
 | 
			
		||||
        document.removeEventListener("mouseup", this.onEndMoving);
 | 
			
		||||
        UIStore.instance.off(UI_EVENTS.Resize, this.onResize);
 | 
			
		||||
@@ -97,7 +97,7 @@ export default class PictureInPictureDragger extends React.Component<IProps> {
 | 
			
		||||
        if (prevProps.children !== this.props.children) this.snap(true);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private animationCallback = () => {
 | 
			
		||||
    private animationCallback = (): void => {
 | 
			
		||||
        if (
 | 
			
		||||
            !this.moving &&
 | 
			
		||||
            Math.abs(this.translationX - this.desiredTranslationX) <= 1 &&
 | 
			
		||||
@@ -119,13 +119,13 @@ export default class PictureInPictureDragger extends React.Component<IProps> {
 | 
			
		||||
        this.props.onMove?.();
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    private setStyle = () => {
 | 
			
		||||
    private setStyle = (): void => {
 | 
			
		||||
        if (!this.callViewWrapper.current) return;
 | 
			
		||||
        // Set the element's style directly, bypassing React for efficiency
 | 
			
		||||
        this.callViewWrapper.current.style.transform = `translateX(${this.translationX}px) translateY(${this.translationY}px)`;
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    private setTranslation(inTranslationX: number, inTranslationY: number) {
 | 
			
		||||
    private setTranslation(inTranslationX: number, inTranslationY: number): void {
 | 
			
		||||
        const width = this.callViewWrapper.current?.clientWidth || PIP_VIEW_WIDTH;
 | 
			
		||||
        const height = this.callViewWrapper.current?.clientHeight || PIP_VIEW_HEIGHT;
 | 
			
		||||
 | 
			
		||||
@@ -152,7 +152,7 @@ export default class PictureInPictureDragger extends React.Component<IProps> {
 | 
			
		||||
        this.snap(false);
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    private snap = (animate = false) => {
 | 
			
		||||
    private snap = (animate = false): void => {
 | 
			
		||||
        const translationX = this.desiredTranslationX;
 | 
			
		||||
        const translationY = this.desiredTranslationY;
 | 
			
		||||
        // We subtract the PiP size from the window size in order to calculate
 | 
			
		||||
@@ -187,14 +187,14 @@ export default class PictureInPictureDragger extends React.Component<IProps> {
 | 
			
		||||
        this.scheduledUpdate.mark();
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    private onStartMoving = (event: React.MouseEvent | MouseEvent) => {
 | 
			
		||||
    private onStartMoving = (event: React.MouseEvent | MouseEvent): void => {
 | 
			
		||||
        event.preventDefault();
 | 
			
		||||
        event.stopPropagation();
 | 
			
		||||
 | 
			
		||||
        this.mouseHeld = true;
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    private onMoving = (event: MouseEvent) => {
 | 
			
		||||
    private onMoving = (event: MouseEvent): void => {
 | 
			
		||||
        if (!this.mouseHeld) return;
 | 
			
		||||
 | 
			
		||||
        event.preventDefault();
 | 
			
		||||
@@ -210,7 +210,7 @@ export default class PictureInPictureDragger extends React.Component<IProps> {
 | 
			
		||||
        this.setTranslation(event.pageX - this.initX, event.pageY - this.initY);
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    private onEndMoving = (event: MouseEvent) => {
 | 
			
		||||
    private onEndMoving = (event: MouseEvent): void => {
 | 
			
		||||
        if (!this.mouseHeld) return;
 | 
			
		||||
 | 
			
		||||
        event.preventDefault();
 | 
			
		||||
@@ -223,7 +223,7 @@ export default class PictureInPictureDragger extends React.Component<IProps> {
 | 
			
		||||
        this.snap(true);
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    private onClickCapture = (event: React.MouseEvent) => {
 | 
			
		||||
    private onClickCapture = (event: React.MouseEvent): void => {
 | 
			
		||||
        // To prevent mouse up events during dragging from being double-counted
 | 
			
		||||
        // as clicks, we cancel clicks before they ever reach the target
 | 
			
		||||
        if (this.moving) {
 | 
			
		||||
@@ -232,7 +232,7 @@ export default class PictureInPictureDragger extends React.Component<IProps> {
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    public render() {
 | 
			
		||||
    public render(): JSX.Element {
 | 
			
		||||
        const style = {
 | 
			
		||||
            transform: `translateX(${this.translationX}px) translateY(${this.translationY}px)`,
 | 
			
		||||
        };
 | 
			
		||||
 
 | 
			
		||||
@@ -135,7 +135,7 @@ class PipContainerInner extends React.Component<IProps, IState> {
 | 
			
		||||
        };
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public componentDidMount() {
 | 
			
		||||
    public componentDidMount(): void {
 | 
			
		||||
        LegacyCallHandler.instance.addListener(LegacyCallHandlerEvent.CallChangeRoom, this.updateCalls);
 | 
			
		||||
        LegacyCallHandler.instance.addListener(LegacyCallHandlerEvent.CallState, this.updateCalls);
 | 
			
		||||
        SdkContextClass.instance.roomViewStore.addListener(UPDATE_EVENT, this.onRoomViewStoreUpdate);
 | 
			
		||||
@@ -149,7 +149,7 @@ class PipContainerInner extends React.Component<IProps, IState> {
 | 
			
		||||
        ActiveWidgetStore.instance.on(ActiveWidgetStoreEvent.Undock, this.onWidgetDockChanges);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public componentWillUnmount() {
 | 
			
		||||
    public componentWillUnmount(): void {
 | 
			
		||||
        LegacyCallHandler.instance.removeListener(LegacyCallHandlerEvent.CallChangeRoom, this.updateCalls);
 | 
			
		||||
        LegacyCallHandler.instance.removeListener(LegacyCallHandlerEvent.CallState, this.updateCalls);
 | 
			
		||||
        const cli = MatrixClientPeg.get();
 | 
			
		||||
@@ -164,9 +164,9 @@ class PipContainerInner extends React.Component<IProps, IState> {
 | 
			
		||||
        ActiveWidgetStore.instance.off(ActiveWidgetStoreEvent.Undock, this.onWidgetDockChanges);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private onMove = () => this.props.movePersistedElement.current?.();
 | 
			
		||||
    private onMove = (): void => this.props.movePersistedElement.current?.();
 | 
			
		||||
 | 
			
		||||
    private onRoomViewStoreUpdate = () => {
 | 
			
		||||
    private onRoomViewStoreUpdate = (): void => {
 | 
			
		||||
        const newRoomId = SdkContextClass.instance.roomViewStore.getRoomId();
 | 
			
		||||
        const oldRoomId = this.state.viewedRoomId;
 | 
			
		||||
        if (newRoomId === oldRoomId) return;
 | 
			
		||||
@@ -213,7 +213,7 @@ class PipContainerInner extends React.Component<IProps, IState> {
 | 
			
		||||
        this.updateShowWidgetInPip();
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    private onCallRemoteHold = () => {
 | 
			
		||||
    private onCallRemoteHold = (): void => {
 | 
			
		||||
        if (!this.state.viewedRoomId) return;
 | 
			
		||||
        const [primaryCall, secondaryCalls] = getPrimarySecondaryCallsForPip(this.state.viewedRoomId);
 | 
			
		||||
 | 
			
		||||
@@ -238,7 +238,7 @@ class PipContainerInner extends React.Component<IProps, IState> {
 | 
			
		||||
    public updateShowWidgetInPip(
 | 
			
		||||
        persistentWidgetId = this.state.persistentWidgetId,
 | 
			
		||||
        persistentRoomId = this.state.persistentRoomId,
 | 
			
		||||
    ) {
 | 
			
		||||
    ): void {
 | 
			
		||||
        let fromAnotherRoom = false;
 | 
			
		||||
        let notDocked = false;
 | 
			
		||||
        // Sanity check the room - the widget may have been destroyed between render cycles, and
 | 
			
		||||
@@ -293,7 +293,7 @@ class PipContainerInner extends React.Component<IProps, IState> {
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public render() {
 | 
			
		||||
    public render(): JSX.Element {
 | 
			
		||||
        const pipMode = true;
 | 
			
		||||
        let pipContent: Array<CreatePipChildren> = [];
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -101,7 +101,7 @@ export default class RightPanel extends React.Component<IProps, IState> {
 | 
			
		||||
        };
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private onRoomStateMember = (ev: MatrixEvent, state: RoomState, member: RoomMember) => {
 | 
			
		||||
    private onRoomStateMember = (ev: MatrixEvent, state: RoomState, member: RoomMember): void => {
 | 
			
		||||
        if (!this.props.room || member.roomId !== this.props.room.roomId) {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
@@ -118,11 +118,11 @@ export default class RightPanel extends React.Component<IProps, IState> {
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    private onRightPanelStoreUpdate = () => {
 | 
			
		||||
    private onRightPanelStoreUpdate = (): void => {
 | 
			
		||||
        this.setState({ ...(RightPanel.getDerivedStateFromProps(this.props) as IState) });
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    private onClose = () => {
 | 
			
		||||
    private onClose = (): void => {
 | 
			
		||||
        // XXX: There are three different ways of 'closing' this panel depending on what state
 | 
			
		||||
        // things are in... this knows far more than it should do about the state of the rest
 | 
			
		||||
        // of the app and is generally a bit silly.
 | 
			
		||||
 
 | 
			
		||||
@@ -39,15 +39,15 @@ export default class RoomSearch extends React.PureComponent<IProps> {
 | 
			
		||||
        this.dispatcherRef = defaultDispatcher.register(this.onAction);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public componentWillUnmount() {
 | 
			
		||||
    public componentWillUnmount(): void {
 | 
			
		||||
        defaultDispatcher.unregister(this.dispatcherRef);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private openSpotlight() {
 | 
			
		||||
    private openSpotlight(): void {
 | 
			
		||||
        Modal.createDialog(SpotlightDialog, {}, "mx_SpotlightDialog_wrapper", false, true);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private onAction = (payload: ActionPayload) => {
 | 
			
		||||
    private onAction = (payload: ActionPayload): void => {
 | 
			
		||||
        if (payload.action === "focus_room_filter") {
 | 
			
		||||
            this.openSpotlight();
 | 
			
		||||
        }
 | 
			
		||||
 
 | 
			
		||||
@@ -37,7 +37,7 @@ import RoomContext from "../../contexts/RoomContext";
 | 
			
		||||
import SettingsStore from "../../settings/SettingsStore";
 | 
			
		||||
 | 
			
		||||
const DEBUG = false;
 | 
			
		||||
let debuglog = function (msg: string) {};
 | 
			
		||||
let debuglog = function (msg: string): void {};
 | 
			
		||||
 | 
			
		||||
/* istanbul ignore next */
 | 
			
		||||
if (DEBUG) {
 | 
			
		||||
@@ -76,7 +76,7 @@ export const RoomSearchView = forwardRef<ScrollPanel, Props>(
 | 
			
		||||
 | 
			
		||||
                return searchPromise
 | 
			
		||||
                    .then(
 | 
			
		||||
                        async (results) => {
 | 
			
		||||
                        async (results): Promise<boolean> => {
 | 
			
		||||
                            debuglog("search complete");
 | 
			
		||||
                            if (aborted.current) {
 | 
			
		||||
                                logger.error("Discarding stale search results");
 | 
			
		||||
@@ -209,7 +209,7 @@ export const RoomSearchView = forwardRef<ScrollPanel, Props>(
 | 
			
		||||
 | 
			
		||||
        // once dynamic content in the search results load, make the scrollPanel check
 | 
			
		||||
        // the scroll offsets.
 | 
			
		||||
        const onHeightChanged = () => {
 | 
			
		||||
        const onHeightChanged = (): void => {
 | 
			
		||||
            const scrollPanel = ref.current;
 | 
			
		||||
            scrollPanel?.checkScroll();
 | 
			
		||||
        };
 | 
			
		||||
 
 | 
			
		||||
@@ -146,7 +146,7 @@ export default class RoomStatusBar extends React.PureComponent<IProps, IState> {
 | 
			
		||||
        dis.fire(Action.FocusSendMessageComposer);
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    private onRoomLocalEchoUpdated = (ev: MatrixEvent, room: Room) => {
 | 
			
		||||
    private onRoomLocalEchoUpdated = (ev: MatrixEvent, room: Room): void => {
 | 
			
		||||
        if (room.roomId !== this.props.room.roomId) return;
 | 
			
		||||
        const messages = getUnsentMessages(this.props.room);
 | 
			
		||||
        this.setState({
 | 
			
		||||
 
 | 
			
		||||
@@ -115,7 +115,7 @@ import VoipUserMapper from "../../VoipUserMapper";
 | 
			
		||||
import { isCallEvent } from "./LegacyCallEventGrouper";
 | 
			
		||||
 | 
			
		||||
const DEBUG = false;
 | 
			
		||||
let debuglog = function (msg: string) {};
 | 
			
		||||
let debuglog = function (msg: string): void {};
 | 
			
		||||
 | 
			
		||||
const BROWSER_SUPPORTS_SANDBOX = "sandbox" in document.createElement("iframe");
 | 
			
		||||
 | 
			
		||||
@@ -248,7 +248,7 @@ function LocalRoomView(props: LocalRoomViewProps): ReactElement {
 | 
			
		||||
        encryptionTile = <EncryptionEvent mxEvent={encryptionEvent} />;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const onRetryClicked = () => {
 | 
			
		||||
    const onRetryClicked = (): void => {
 | 
			
		||||
        room.state = LocalRoomState.NEW;
 | 
			
		||||
        defaultDispatcher.dispatch({
 | 
			
		||||
            action: "local_room_event",
 | 
			
		||||
@@ -470,21 +470,21 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
 | 
			
		||||
        ];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private onIsResizing = (resizing: boolean) => {
 | 
			
		||||
    private onIsResizing = (resizing: boolean): void => {
 | 
			
		||||
        this.setState({ resizing });
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    private onWidgetStoreUpdate = () => {
 | 
			
		||||
    private onWidgetStoreUpdate = (): void => {
 | 
			
		||||
        if (!this.state.room) return;
 | 
			
		||||
        this.checkWidgets(this.state.room);
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    private onWidgetEchoStoreUpdate = () => {
 | 
			
		||||
    private onWidgetEchoStoreUpdate = (): void => {
 | 
			
		||||
        if (!this.state.room) return;
 | 
			
		||||
        this.checkWidgets(this.state.room);
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    private onWidgetLayoutChange = () => {
 | 
			
		||||
    private onWidgetLayoutChange = (): void => {
 | 
			
		||||
        if (!this.state.room) return;
 | 
			
		||||
        dis.dispatch({
 | 
			
		||||
            action: "appsDrawer",
 | 
			
		||||
@@ -505,7 +505,7 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
 | 
			
		||||
        });
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    private getMainSplitContentType = (room: Room) => {
 | 
			
		||||
    private getMainSplitContentType = (room: Room): MainSplitContentType => {
 | 
			
		||||
        if (
 | 
			
		||||
            (SettingsStore.getValue("feature_group_calls") && this.context.roomViewStore.isViewingCall()) ||
 | 
			
		||||
            isVideoRoom(room)
 | 
			
		||||
@@ -707,7 +707,7 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    private onActiveCalls = () => {
 | 
			
		||||
    private onActiveCalls = (): void => {
 | 
			
		||||
        if (this.state.roomId === undefined) return;
 | 
			
		||||
        const activeCall = CallStore.instance.getActiveCall(this.state.roomId);
 | 
			
		||||
 | 
			
		||||
@@ -727,7 +727,7 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
 | 
			
		||||
        this.setState({ activeCall });
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    private getRoomId = () => {
 | 
			
		||||
    private getRoomId = (): string => {
 | 
			
		||||
        // According to `onRoomViewStoreUpdate`, `state.roomId` can be null
 | 
			
		||||
        // if we have a room alias we haven't resolved yet. To work around this,
 | 
			
		||||
        // first we'll try the room object if it's there, and then fallback to
 | 
			
		||||
@@ -736,7 +736,7 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
 | 
			
		||||
        return this.state.room ? this.state.room.roomId : this.state.roomId;
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    private getPermalinkCreatorForRoom(room: Room) {
 | 
			
		||||
    private getPermalinkCreatorForRoom(room: Room): RoomPermalinkCreator {
 | 
			
		||||
        if (this.permalinkCreators[room.roomId]) return this.permalinkCreators[room.roomId];
 | 
			
		||||
 | 
			
		||||
        this.permalinkCreators[room.roomId] = new RoomPermalinkCreator(room);
 | 
			
		||||
@@ -750,14 +750,14 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
 | 
			
		||||
        return this.permalinkCreators[room.roomId];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private stopAllPermalinkCreators() {
 | 
			
		||||
    private stopAllPermalinkCreators(): void {
 | 
			
		||||
        if (!this.permalinkCreators) return;
 | 
			
		||||
        for (const roomId of Object.keys(this.permalinkCreators)) {
 | 
			
		||||
            this.permalinkCreators[roomId].stop();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private setupRoom(room: Room, roomId: string, joining: boolean, shouldPeek: boolean) {
 | 
			
		||||
    private setupRoom(room: Room, roomId: string, joining: boolean, shouldPeek: boolean): void {
 | 
			
		||||
        // if this is an unknown room then we're in one of three states:
 | 
			
		||||
        // - This is a room we can peek into (search engine) (we can /peek)
 | 
			
		||||
        // - This is a room we can publicly join or were invited to. (we can /join)
 | 
			
		||||
@@ -822,7 +822,7 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private shouldShowApps(room: Room) {
 | 
			
		||||
    private shouldShowApps(room: Room): boolean {
 | 
			
		||||
        if (!BROWSER_SUPPORTS_SANDBOX || !room) return false;
 | 
			
		||||
 | 
			
		||||
        // Check if user has previously chosen to hide the app drawer for this
 | 
			
		||||
@@ -838,7 +838,7 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
 | 
			
		||||
        return isManuallyShown && widgets.length > 0;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public componentDidMount() {
 | 
			
		||||
    public componentDidMount(): void {
 | 
			
		||||
        this.onRoomViewStoreUpdate(true);
 | 
			
		||||
 | 
			
		||||
        const call = this.getCallForRoom();
 | 
			
		||||
@@ -851,7 +851,7 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
 | 
			
		||||
        window.addEventListener("beforeunload", this.onPageUnload);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public shouldComponentUpdate(nextProps, nextState) {
 | 
			
		||||
    public shouldComponentUpdate(nextProps, nextState): boolean {
 | 
			
		||||
        const hasPropsDiff = objectHasDiff(this.props, nextProps);
 | 
			
		||||
 | 
			
		||||
        const { upgradeRecommendation, ...state } = this.state;
 | 
			
		||||
@@ -864,7 +864,7 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
 | 
			
		||||
        return hasPropsDiff || hasStateDiff;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public componentDidUpdate() {
 | 
			
		||||
    public componentDidUpdate(): void {
 | 
			
		||||
        // Note: We check the ref here with a flag because componentDidMount, despite
 | 
			
		||||
        // documentation, does not define our messagePanel ref. It looks like our spinner
 | 
			
		||||
        // in render() prevents the ref from being set on first mount, so we try and
 | 
			
		||||
@@ -877,7 +877,7 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public componentWillUnmount() {
 | 
			
		||||
    public componentWillUnmount(): void {
 | 
			
		||||
        // set a boolean to say we've been unmounted, which any pending
 | 
			
		||||
        // promises can use to throw away their results.
 | 
			
		||||
        //
 | 
			
		||||
@@ -947,13 +947,13 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private onRightPanelStoreUpdate = () => {
 | 
			
		||||
    private onRightPanelStoreUpdate = (): void => {
 | 
			
		||||
        this.setState({
 | 
			
		||||
            showRightPanel: this.context.rightPanelStore.isOpenForRoom(this.state.roomId),
 | 
			
		||||
        });
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    private onPageUnload = (event) => {
 | 
			
		||||
    private onPageUnload = (event): string => {
 | 
			
		||||
        if (ContentMessages.sharedInstance().getCurrentUploads().length > 0) {
 | 
			
		||||
            return (event.returnValue = _t("You seem to be uploading files, are you sure you want to quit?"));
 | 
			
		||||
        } else if (this.getCallForRoom() && this.state.callState !== "ended") {
 | 
			
		||||
@@ -961,7 +961,7 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    private onReactKeyDown = (ev) => {
 | 
			
		||||
    private onReactKeyDown = (ev): void => {
 | 
			
		||||
        let handled = false;
 | 
			
		||||
 | 
			
		||||
        const action = getKeyBindingsManager().getRoomAction(ev);
 | 
			
		||||
@@ -1120,12 +1120,12 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    private onLocalRoomEvent(roomId: string) {
 | 
			
		||||
    private onLocalRoomEvent(roomId: string): void {
 | 
			
		||||
        if (roomId !== this.state.room.roomId) return;
 | 
			
		||||
        createRoomFromLocalRoom(this.context.client, this.state.room as LocalRoom);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private onRoomTimeline = (ev: MatrixEvent, room: Room | null, toStartOfTimeline: boolean, removed, data) => {
 | 
			
		||||
    private onRoomTimeline = (ev: MatrixEvent, room: Room | null, toStartOfTimeline: boolean, removed, data): void => {
 | 
			
		||||
        if (this.unmounted) return;
 | 
			
		||||
 | 
			
		||||
        // ignore events for other rooms or the notification timeline set
 | 
			
		||||
@@ -1167,7 +1167,7 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    private onEventDecrypted = (ev: MatrixEvent) => {
 | 
			
		||||
    private onEventDecrypted = (ev: MatrixEvent): void => {
 | 
			
		||||
        if (!this.state.room || !this.state.matrixClientIsReady) return; // not ready at all
 | 
			
		||||
        if (ev.getRoomId() !== this.state.room.roomId) return; // not for us
 | 
			
		||||
        this.updateVisibleDecryptionFailures();
 | 
			
		||||
@@ -1175,7 +1175,7 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
 | 
			
		||||
        this.handleEffects(ev);
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    private handleEffects = (ev: MatrixEvent) => {
 | 
			
		||||
    private handleEffects = (ev: MatrixEvent): void => {
 | 
			
		||||
        const notifState = this.context.roomNotificationStateStore.getRoomState(this.state.room);
 | 
			
		||||
        if (!notifState.isUnread) return;
 | 
			
		||||
 | 
			
		||||
@@ -1189,19 +1189,19 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
 | 
			
		||||
        });
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    private onRoomName = (room: Room) => {
 | 
			
		||||
    private onRoomName = (room: Room): void => {
 | 
			
		||||
        if (this.state.room && room.roomId == this.state.room.roomId) {
 | 
			
		||||
            this.forceUpdate();
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    private onKeyBackupStatus = () => {
 | 
			
		||||
    private onKeyBackupStatus = (): void => {
 | 
			
		||||
        // Key backup status changes affect whether the in-room recovery
 | 
			
		||||
        // reminder is displayed.
 | 
			
		||||
        this.forceUpdate();
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    public canResetTimeline = () => {
 | 
			
		||||
    public canResetTimeline = (): boolean => {
 | 
			
		||||
        if (!this.messagePanel) {
 | 
			
		||||
            return true;
 | 
			
		||||
        }
 | 
			
		||||
@@ -1216,7 +1216,7 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
 | 
			
		||||
 | 
			
		||||
    // called when state.room is first initialised (either at initial load,
 | 
			
		||||
    // after a successful peek, or after we join the room).
 | 
			
		||||
    private onRoomLoaded = (room: Room) => {
 | 
			
		||||
    private onRoomLoaded = (room: Room): void => {
 | 
			
		||||
        if (this.unmounted) return;
 | 
			
		||||
        // Attach a widget store listener only when we get a room
 | 
			
		||||
        this.context.widgetLayoutStore.on(WidgetLayoutStore.emissionForRoom(room), this.onWidgetLayoutChange);
 | 
			
		||||
@@ -1251,17 +1251,17 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    private getRoomTombstone(room = this.state.room) {
 | 
			
		||||
    private getRoomTombstone(room = this.state.room): MatrixEvent | undefined {
 | 
			
		||||
        return room?.currentState.getStateEvents(EventType.RoomTombstone, "");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private async calculateRecommendedVersion(room: Room) {
 | 
			
		||||
    private async calculateRecommendedVersion(room: Room): Promise<void> {
 | 
			
		||||
        const upgradeRecommendation = await room.getRecommendedVersion();
 | 
			
		||||
        if (this.unmounted) return;
 | 
			
		||||
        this.setState({ upgradeRecommendation });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private async loadMembersIfJoined(room: Room) {
 | 
			
		||||
    private async loadMembersIfJoined(room: Room): Promise<void> {
 | 
			
		||||
        // lazy load members if enabled
 | 
			
		||||
        if (this.context.client.hasLazyLoadMembersEnabled()) {
 | 
			
		||||
            if (room && room.getMyMembership() === "join") {
 | 
			
		||||
@@ -1280,14 +1280,14 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private calculatePeekRules(room: Room) {
 | 
			
		||||
    private calculatePeekRules(room: Room): void {
 | 
			
		||||
        const historyVisibility = room.currentState.getStateEvents(EventType.RoomHistoryVisibility, "");
 | 
			
		||||
        this.setState({
 | 
			
		||||
            canPeek: historyVisibility?.getContent().history_visibility === HistoryVisibility.WorldReadable,
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private updatePreviewUrlVisibility({ roomId }: Room) {
 | 
			
		||||
    private updatePreviewUrlVisibility({ roomId }: Room): void {
 | 
			
		||||
        // URL Previews in E2EE rooms can be a privacy leak so use a different setting which is per-room explicit
 | 
			
		||||
        const key = this.context.client.isRoomEncrypted(roomId) ? "urlPreviewsEnabled_e2ee" : "urlPreviewsEnabled";
 | 
			
		||||
        this.setState({
 | 
			
		||||
@@ -1295,7 +1295,7 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private onRoom = (room: Room) => {
 | 
			
		||||
    private onRoom = (room: Room): void => {
 | 
			
		||||
        if (!room || room.roomId !== this.state.roomId) {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
@@ -1318,7 +1318,7 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
 | 
			
		||||
        );
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    private onDeviceVerificationChanged = (userId: string) => {
 | 
			
		||||
    private onDeviceVerificationChanged = (userId: string): void => {
 | 
			
		||||
        const room = this.state.room;
 | 
			
		||||
        if (!room?.currentState.getMember(userId)) {
 | 
			
		||||
            return;
 | 
			
		||||
@@ -1326,7 +1326,7 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
 | 
			
		||||
        this.updateE2EStatus(room);
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    private onUserVerificationChanged = (userId: string) => {
 | 
			
		||||
    private onUserVerificationChanged = (userId: string): void => {
 | 
			
		||||
        const room = this.state.room;
 | 
			
		||||
        if (!room || !room.currentState.getMember(userId)) {
 | 
			
		||||
            return;
 | 
			
		||||
@@ -1334,14 +1334,14 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
 | 
			
		||||
        this.updateE2EStatus(room);
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    private onCrossSigningKeysChanged = () => {
 | 
			
		||||
    private onCrossSigningKeysChanged = (): void => {
 | 
			
		||||
        const room = this.state.room;
 | 
			
		||||
        if (room) {
 | 
			
		||||
            this.updateE2EStatus(room);
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    private async updateE2EStatus(room: Room) {
 | 
			
		||||
    private async updateE2EStatus(room: Room): Promise<void> {
 | 
			
		||||
        if (!this.context.client.isRoomEncrypted(room.roomId)) return;
 | 
			
		||||
 | 
			
		||||
        // If crypto is not currently enabled, we aren't tracking devices at all,
 | 
			
		||||
@@ -1357,13 +1357,13 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
 | 
			
		||||
        this.setState({ e2eStatus });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private onUrlPreviewsEnabledChange = () => {
 | 
			
		||||
    private onUrlPreviewsEnabledChange = (): void => {
 | 
			
		||||
        if (this.state.room) {
 | 
			
		||||
            this.updatePreviewUrlVisibility(this.state.room);
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    private onRoomStateEvents = (ev: MatrixEvent, state: RoomState) => {
 | 
			
		||||
    private onRoomStateEvents = (ev: MatrixEvent, state: RoomState): void => {
 | 
			
		||||
        // ignore if we don't have a room yet
 | 
			
		||||
        if (!this.state.room || this.state.room.roomId !== state.roomId) return;
 | 
			
		||||
 | 
			
		||||
@@ -1377,7 +1377,7 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    private onRoomStateUpdate = (state: RoomState) => {
 | 
			
		||||
    private onRoomStateUpdate = (state: RoomState): void => {
 | 
			
		||||
        // ignore members in other rooms
 | 
			
		||||
        if (state.roomId !== this.state.room?.roomId) {
 | 
			
		||||
            return;
 | 
			
		||||
@@ -1386,7 +1386,7 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
 | 
			
		||||
        this.updateRoomMembers();
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    private onMyMembership = (room: Room, membership: string, oldMembership: string) => {
 | 
			
		||||
    private onMyMembership = (room: Room, membership: string, oldMembership: string): void => {
 | 
			
		||||
        if (room.roomId === this.state.roomId) {
 | 
			
		||||
            this.forceUpdate();
 | 
			
		||||
            this.loadMembersIfJoined(room);
 | 
			
		||||
@@ -1394,7 +1394,7 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    private updatePermissions(room: Room) {
 | 
			
		||||
    private updatePermissions(room: Room): void {
 | 
			
		||||
        if (room) {
 | 
			
		||||
            const me = this.context.client.getUserId();
 | 
			
		||||
            const canReact =
 | 
			
		||||
@@ -1420,7 +1420,7 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
 | 
			
		||||
        { leading: true, trailing: true },
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    private checkDesktopNotifications() {
 | 
			
		||||
    private checkDesktopNotifications(): void {
 | 
			
		||||
        const memberCount = this.state.room.getJoinedMemberCount() + this.state.room.getInvitedMemberCount();
 | 
			
		||||
        // if they are not alone prompt the user about notifications so they don't miss replies
 | 
			
		||||
        if (memberCount > 1 && Notifier.shouldShowPrompt()) {
 | 
			
		||||
@@ -1428,7 +1428,7 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private updateDMState() {
 | 
			
		||||
    private updateDMState(): void {
 | 
			
		||||
        const room = this.state.room;
 | 
			
		||||
        if (room.getMyMembership() != "join") {
 | 
			
		||||
            return;
 | 
			
		||||
@@ -1439,7 +1439,7 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private onInviteClick = () => {
 | 
			
		||||
    private onInviteClick = (): void => {
 | 
			
		||||
        // open the room inviter
 | 
			
		||||
        dis.dispatch({
 | 
			
		||||
            action: "view_invite",
 | 
			
		||||
@@ -1447,7 +1447,7 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
 | 
			
		||||
        });
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    private onJoinButtonClicked = () => {
 | 
			
		||||
    private onJoinButtonClicked = (): void => {
 | 
			
		||||
        // If the user is a ROU, allow them to transition to a PWLU
 | 
			
		||||
        if (this.context.client?.isGuest()) {
 | 
			
		||||
            // Join this room once the user has registered and logged in
 | 
			
		||||
@@ -1489,7 +1489,7 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
 | 
			
		||||
        { leading: false, trailing: true },
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    private onMessageListScroll = () => {
 | 
			
		||||
    private onMessageListScroll = (): void => {
 | 
			
		||||
        if (this.messagePanel.isAtEndOfLiveTimeline()) {
 | 
			
		||||
            this.setState({
 | 
			
		||||
                numUnreadMessages: 0,
 | 
			
		||||
@@ -1504,7 +1504,7 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
 | 
			
		||||
        this.updateVisibleDecryptionFailures();
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    private resetJumpToEvent = (eventId?: string) => {
 | 
			
		||||
    private resetJumpToEvent = (eventId?: string): void => {
 | 
			
		||||
        if (
 | 
			
		||||
            this.state.initialEventId &&
 | 
			
		||||
            this.state.initialEventScrollIntoView &&
 | 
			
		||||
@@ -1523,7 +1523,7 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    private injectSticker(url: string, info: object, text: string, threadId: string | null) {
 | 
			
		||||
    private injectSticker(url: string, info: object, text: string, threadId: string | null): void {
 | 
			
		||||
        if (this.context.client.isGuest()) {
 | 
			
		||||
            dis.dispatch({ action: "require_registration" });
 | 
			
		||||
            return;
 | 
			
		||||
@@ -1539,7 +1539,7 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
 | 
			
		||||
            });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private onSearch = (term: string, scope: SearchScope) => {
 | 
			
		||||
    private onSearch = (term: string, scope: SearchScope): void => {
 | 
			
		||||
        const roomId = scope === SearchScope.Room ? this.state.room.roomId : undefined;
 | 
			
		||||
        debuglog("sending search request");
 | 
			
		||||
        const abortController = new AbortController();
 | 
			
		||||
@@ -1569,21 +1569,21 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
 | 
			
		||||
        });
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    private onAppsClick = () => {
 | 
			
		||||
    private onAppsClick = (): void => {
 | 
			
		||||
        dis.dispatch({
 | 
			
		||||
            action: "appsDrawer",
 | 
			
		||||
            show: !this.state.showApps,
 | 
			
		||||
        });
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    private onForgetClick = () => {
 | 
			
		||||
    private onForgetClick = (): void => {
 | 
			
		||||
        dis.dispatch({
 | 
			
		||||
            action: "forget_room",
 | 
			
		||||
            room_id: this.state.room.roomId,
 | 
			
		||||
        });
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    private onRejectButtonClicked = () => {
 | 
			
		||||
    private onRejectButtonClicked = (): void => {
 | 
			
		||||
        this.setState({
 | 
			
		||||
            rejecting: true,
 | 
			
		||||
        });
 | 
			
		||||
@@ -1611,7 +1611,7 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
 | 
			
		||||
        );
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    private onRejectAndIgnoreClick = async () => {
 | 
			
		||||
    private onRejectAndIgnoreClick = async (): Promise<void> => {
 | 
			
		||||
        this.setState({
 | 
			
		||||
            rejecting: true,
 | 
			
		||||
        });
 | 
			
		||||
@@ -1644,7 +1644,7 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    private onRejectThreepidInviteButtonClicked = () => {
 | 
			
		||||
    private onRejectThreepidInviteButtonClicked = (): void => {
 | 
			
		||||
        // We can reject 3pid invites in the same way that we accept them,
 | 
			
		||||
        // using /leave rather than /join. In the short term though, we
 | 
			
		||||
        // just ignore them.
 | 
			
		||||
@@ -1652,7 +1652,7 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
 | 
			
		||||
        dis.fire(Action.ViewRoomDirectory);
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    private onSearchClick = () => {
 | 
			
		||||
    private onSearchClick = (): void => {
 | 
			
		||||
        this.setState({
 | 
			
		||||
            timelineRenderingType:
 | 
			
		||||
                this.state.timelineRenderingType === TimelineRenderingType.Search
 | 
			
		||||
@@ -1674,7 +1674,7 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    // jump down to the bottom of this room, where new events are arriving
 | 
			
		||||
    private jumpToLiveTimeline = () => {
 | 
			
		||||
    private jumpToLiveTimeline = (): void => {
 | 
			
		||||
        if (this.state.initialEventId && this.state.isInitialEventHighlighted) {
 | 
			
		||||
            // If we were viewing a highlighted event, firing view_room without
 | 
			
		||||
            // an event will take care of both clearing the URL fragment and
 | 
			
		||||
@@ -1692,18 +1692,18 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    // jump up to wherever our read marker is
 | 
			
		||||
    private jumpToReadMarker = () => {
 | 
			
		||||
    private jumpToReadMarker = (): void => {
 | 
			
		||||
        this.messagePanel.jumpToReadMarker();
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    // update the read marker to match the read-receipt
 | 
			
		||||
    private forgetReadMarker = (ev) => {
 | 
			
		||||
    private forgetReadMarker = (ev): void => {
 | 
			
		||||
        ev.stopPropagation();
 | 
			
		||||
        this.messagePanel.forgetReadMarker();
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    // decide whether or not the top 'unread messages' bar should be shown
 | 
			
		||||
    private updateTopUnreadMessagesBar = () => {
 | 
			
		||||
    private updateTopUnreadMessagesBar = (): void => {
 | 
			
		||||
        if (!this.messagePanel) {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
@@ -1754,12 +1754,12 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
 | 
			
		||||
        };
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private onStatusBarVisible = () => {
 | 
			
		||||
    private onStatusBarVisible = (): void => {
 | 
			
		||||
        if (this.unmounted || this.state.statusBarVisible) return;
 | 
			
		||||
        this.setState({ statusBarVisible: true });
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    private onStatusBarHidden = () => {
 | 
			
		||||
    private onStatusBarHidden = (): void => {
 | 
			
		||||
        // This is currently not desired as it is annoying if it keeps expanding and collapsing
 | 
			
		||||
        if (this.unmounted || !this.state.statusBarVisible) return;
 | 
			
		||||
        this.setState({ statusBarVisible: false });
 | 
			
		||||
@@ -1770,7 +1770,7 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
 | 
			
		||||
     *
 | 
			
		||||
     * We pass it down to the scroll panel.
 | 
			
		||||
     */
 | 
			
		||||
    public handleScrollKey = (ev) => {
 | 
			
		||||
    public handleScrollKey = (ev): void => {
 | 
			
		||||
        let panel: ScrollPanel | TimelinePanel;
 | 
			
		||||
        if (this.searchResultsPanel.current) {
 | 
			
		||||
            panel = this.searchResultsPanel.current;
 | 
			
		||||
@@ -1793,24 +1793,24 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
 | 
			
		||||
 | 
			
		||||
    // this has to be a proper method rather than an unnamed function,
 | 
			
		||||
    // otherwise react calls it with null on each update.
 | 
			
		||||
    private gatherTimelinePanelRef = (r) => {
 | 
			
		||||
    private gatherTimelinePanelRef = (r): void => {
 | 
			
		||||
        this.messagePanel = r;
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    private getOldRoom() {
 | 
			
		||||
    private getOldRoom(): Room | null {
 | 
			
		||||
        const createEvent = this.state.room.currentState.getStateEvents(EventType.RoomCreate, "");
 | 
			
		||||
        if (!createEvent || !createEvent.getContent()["predecessor"]) return null;
 | 
			
		||||
 | 
			
		||||
        return this.context.client.getRoom(createEvent.getContent()["predecessor"]["room_id"]);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public getHiddenHighlightCount() {
 | 
			
		||||
    public getHiddenHighlightCount(): number {
 | 
			
		||||
        const oldRoom = this.getOldRoom();
 | 
			
		||||
        if (!oldRoom) return 0;
 | 
			
		||||
        return oldRoom.getUnreadNotificationCount(NotificationCountType.Highlight);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public onHiddenHighlightsClick = () => {
 | 
			
		||||
    public onHiddenHighlightsClick = (): void => {
 | 
			
		||||
        const oldRoom = this.getOldRoom();
 | 
			
		||||
        if (!oldRoom) return;
 | 
			
		||||
        dis.dispatch<ViewRoomPayload>({
 | 
			
		||||
@@ -1826,7 +1826,7 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private onFileDrop = (dataTransfer: DataTransfer) =>
 | 
			
		||||
    private onFileDrop = (dataTransfer: DataTransfer): Promise<void> =>
 | 
			
		||||
        ContentMessages.sharedInstance().sendContentListToRoom(
 | 
			
		||||
            Array.from(dataTransfer.files),
 | 
			
		||||
            this.state.room?.roomId ?? this.state.roomId,
 | 
			
		||||
@@ -1869,7 +1869,7 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public render() {
 | 
			
		||||
    public render(): JSX.Element {
 | 
			
		||||
        if (this.state.room instanceof LocalRoom) {
 | 
			
		||||
            if (this.state.room.state === LocalRoomState.CREATING) {
 | 
			
		||||
                return this.renderLocalRoomCreateLoader();
 | 
			
		||||
 
 | 
			
		||||
@@ -35,7 +35,7 @@ const UNFILL_REQUEST_DEBOUNCE_MS = 200;
 | 
			
		||||
// much while the content loads.
 | 
			
		||||
const PAGE_SIZE = 400;
 | 
			
		||||
 | 
			
		||||
const debuglog = (...args: any[]) => {
 | 
			
		||||
const debuglog = (...args: any[]): void => {
 | 
			
		||||
    if (SettingsStore.getValue("debug_scroll_panel")) {
 | 
			
		||||
        logger.log.call(console, "ScrollPanel debuglog:", ...args);
 | 
			
		||||
    }
 | 
			
		||||
@@ -227,14 +227,14 @@ export default class ScrollPanel extends React.Component<IProps> {
 | 
			
		||||
        this.props.resizeNotifier?.removeListener("middlePanelResizedNoisy", this.onResize);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private onScroll = (ev: Event | React.UIEvent): void => {
 | 
			
		||||
    private onScroll = (ev: Event): void => {
 | 
			
		||||
        // skip scroll events caused by resizing
 | 
			
		||||
        if (this.props.resizeNotifier && this.props.resizeNotifier.isResizing) return;
 | 
			
		||||
        debuglog("onScroll called past resize gate; scroll node top:", this.getScrollNode().scrollTop);
 | 
			
		||||
        this.scrollTimeout.restart();
 | 
			
		||||
        this.saveScrollState();
 | 
			
		||||
        this.updatePreventShrinking();
 | 
			
		||||
        this.props.onScroll?.(ev as Event);
 | 
			
		||||
        this.props.onScroll?.(ev);
 | 
			
		||||
        // noinspection JSIgnoredPromiseFromCall
 | 
			
		||||
        this.checkFillState();
 | 
			
		||||
    };
 | 
			
		||||
@@ -587,7 +587,7 @@ export default class ScrollPanel extends React.Component<IProps> {
 | 
			
		||||
     * Scroll up/down in response to a scroll key
 | 
			
		||||
     * @param {object} ev the keyboard event
 | 
			
		||||
     */
 | 
			
		||||
    public handleScrollKey = (ev: KeyboardEvent) => {
 | 
			
		||||
    public handleScrollKey = (ev: KeyboardEvent): void => {
 | 
			
		||||
        const roomAction = getKeyBindingsManager().getRoomAction(ev);
 | 
			
		||||
        switch (roomAction) {
 | 
			
		||||
            case KeyBindingAction.ScrollUp:
 | 
			
		||||
@@ -853,7 +853,7 @@ export default class ScrollPanel extends React.Component<IProps> {
 | 
			
		||||
        return this.divScroll;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private collectScroll = (divScroll: HTMLDivElement) => {
 | 
			
		||||
    private collectScroll = (divScroll: HTMLDivElement): void => {
 | 
			
		||||
        this.divScroll = divScroll;
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -114,12 +114,12 @@ const Tile: React.FC<ITileProps> = ({
 | 
			
		||||
    const [onFocus, isActive, ref] = useRovingTabIndex();
 | 
			
		||||
    const [busy, setBusy] = useState(false);
 | 
			
		||||
 | 
			
		||||
    const onPreviewClick = (ev: ButtonEvent) => {
 | 
			
		||||
    const onPreviewClick = (ev: ButtonEvent): void => {
 | 
			
		||||
        ev.preventDefault();
 | 
			
		||||
        ev.stopPropagation();
 | 
			
		||||
        onViewRoomClick();
 | 
			
		||||
    };
 | 
			
		||||
    const onJoinClick = async (ev: ButtonEvent) => {
 | 
			
		||||
    const onJoinClick = async (ev: ButtonEvent): Promise<void> => {
 | 
			
		||||
        setBusy(true);
 | 
			
		||||
        ev.preventDefault();
 | 
			
		||||
        ev.stopPropagation();
 | 
			
		||||
@@ -271,7 +271,7 @@ const Tile: React.FC<ITileProps> = ({
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        if (showChildren) {
 | 
			
		||||
            const onChildrenKeyDown = (e) => {
 | 
			
		||||
            const onChildrenKeyDown = (e): void => {
 | 
			
		||||
                const action = getKeyBindingsManager().getAccessibilityAction(e);
 | 
			
		||||
                switch (action) {
 | 
			
		||||
                    case KeyBindingAction.ArrowLeft:
 | 
			
		||||
@@ -439,7 +439,7 @@ const toLocalRoom = (cli: MatrixClient, room: IHierarchyRoom): IHierarchyRoom =>
 | 
			
		||||
    return room;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export const HierarchyLevel = ({
 | 
			
		||||
export const HierarchyLevel: React.FC<IHierarchyLevelProps> = ({
 | 
			
		||||
    root,
 | 
			
		||||
    roomSet,
 | 
			
		||||
    hierarchy,
 | 
			
		||||
@@ -448,7 +448,7 @@ export const HierarchyLevel = ({
 | 
			
		||||
    onViewRoomClick,
 | 
			
		||||
    onJoinRoomClick,
 | 
			
		||||
    onToggleClick,
 | 
			
		||||
}: IHierarchyLevelProps) => {
 | 
			
		||||
}) => {
 | 
			
		||||
    const cli = useContext(MatrixClientContext);
 | 
			
		||||
    const space = cli.getRoom(root.room_id);
 | 
			
		||||
    const hasPermissions = space?.currentState.maySendStateEvent(EventType.SpaceChild, cli.getUserId());
 | 
			
		||||
@@ -553,7 +553,7 @@ export const useRoomHierarchy = (
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    const loadMore = useCallback(
 | 
			
		||||
        async (pageSize?: number) => {
 | 
			
		||||
        async (pageSize?: number): Promise<void> => {
 | 
			
		||||
            if (hierarchy.loading || !hierarchy.canLoadMore || hierarchy.noSupport || error) return;
 | 
			
		||||
            await hierarchy.load(pageSize).catch(setError);
 | 
			
		||||
            setRooms(hierarchy.rooms);
 | 
			
		||||
@@ -578,8 +578,8 @@ export const useRoomHierarchy = (
 | 
			
		||||
    };
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const useIntersectionObserver = (callback: () => void) => {
 | 
			
		||||
    const handleObserver = (entries: IntersectionObserverEntry[]) => {
 | 
			
		||||
const useIntersectionObserver = (callback: () => void): ((element: HTMLDivElement) => void) => {
 | 
			
		||||
    const handleObserver = (entries: IntersectionObserverEntry[]): void => {
 | 
			
		||||
        const target = entries[0];
 | 
			
		||||
        if (target.isIntersecting) {
 | 
			
		||||
            callback();
 | 
			
		||||
@@ -610,7 +610,7 @@ interface IManageButtonsProps {
 | 
			
		||||
    setError: Dispatch<SetStateAction<string>>;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const ManageButtons = ({ hierarchy, selected, setSelected, setError }: IManageButtonsProps) => {
 | 
			
		||||
const ManageButtons: React.FC<IManageButtonsProps> = ({ hierarchy, selected, setSelected, setError }) => {
 | 
			
		||||
    const cli = useContext(MatrixClientContext);
 | 
			
		||||
 | 
			
		||||
    const [removing, setRemoving] = useState(false);
 | 
			
		||||
@@ -645,7 +645,7 @@ const ManageButtons = ({ hierarchy, selected, setSelected, setError }: IManageBu
 | 
			
		||||
        <>
 | 
			
		||||
            <Button
 | 
			
		||||
                {...props}
 | 
			
		||||
                onClick={async () => {
 | 
			
		||||
                onClick={async (): Promise<void> => {
 | 
			
		||||
                    setRemoving(true);
 | 
			
		||||
                    try {
 | 
			
		||||
                        const userId = cli.getUserId();
 | 
			
		||||
@@ -680,7 +680,7 @@ const ManageButtons = ({ hierarchy, selected, setSelected, setError }: IManageBu
 | 
			
		||||
            </Button>
 | 
			
		||||
            <Button
 | 
			
		||||
                {...props}
 | 
			
		||||
                onClick={async () => {
 | 
			
		||||
                onClick={async (): Promise<void> => {
 | 
			
		||||
                    setSaving(true);
 | 
			
		||||
                    try {
 | 
			
		||||
                        for (const [parentId, childId] of selectedRelations) {
 | 
			
		||||
@@ -713,7 +713,7 @@ const ManageButtons = ({ hierarchy, selected, setSelected, setError }: IManageBu
 | 
			
		||||
    );
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const SpaceHierarchy = ({ space, initialText = "", showRoom, additionalButtons }: IProps) => {
 | 
			
		||||
const SpaceHierarchy: React.FC<IProps> = ({ space, initialText = "", showRoom, additionalButtons }) => {
 | 
			
		||||
    const cli = useContext(MatrixClientContext);
 | 
			
		||||
    const [query, setQuery] = useState(initialText);
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -103,7 +103,7 @@ enum Phase {
 | 
			
		||||
    PrivateExistingRooms,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const SpaceLandingAddButton = ({ space }) => {
 | 
			
		||||
const SpaceLandingAddButton: React.FC<{ space: Room }> = ({ space }) => {
 | 
			
		||||
    const [menuDisplayed, handle, openMenu, closeMenu] = useContextMenu();
 | 
			
		||||
    const canCreateRoom = shouldShowComponent(UIComponent.CreateRooms);
 | 
			
		||||
    const canCreateSpace = shouldShowComponent(UIComponent.CreateSpaces);
 | 
			
		||||
@@ -128,7 +128,7 @@ const SpaceLandingAddButton = ({ space }) => {
 | 
			
		||||
                            <IconizedContextMenuOption
 | 
			
		||||
                                label={_t("New room")}
 | 
			
		||||
                                iconClassName="mx_RoomList_iconNewRoom"
 | 
			
		||||
                                onClick={async (e) => {
 | 
			
		||||
                                onClick={async (e): Promise<void> => {
 | 
			
		||||
                                    e.preventDefault();
 | 
			
		||||
                                    e.stopPropagation();
 | 
			
		||||
                                    closeMenu();
 | 
			
		||||
@@ -143,7 +143,7 @@ const SpaceLandingAddButton = ({ space }) => {
 | 
			
		||||
                                <IconizedContextMenuOption
 | 
			
		||||
                                    label={_t("New video room")}
 | 
			
		||||
                                    iconClassName="mx_RoomList_iconNewVideoRoom"
 | 
			
		||||
                                    onClick={async (e) => {
 | 
			
		||||
                                    onClick={async (e): Promise<void> => {
 | 
			
		||||
                                        e.preventDefault();
 | 
			
		||||
                                        e.stopPropagation();
 | 
			
		||||
                                        closeMenu();
 | 
			
		||||
@@ -210,7 +210,7 @@ const SpaceLandingAddButton = ({ space }) => {
 | 
			
		||||
    );
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const SpaceLanding = ({ space }: { space: Room }) => {
 | 
			
		||||
const SpaceLanding: React.FC<{ space: Room }> = ({ space }) => {
 | 
			
		||||
    const cli = useContext(MatrixClientContext);
 | 
			
		||||
    const myMembership = useMyRoomMembership(space);
 | 
			
		||||
    const userId = cli.getUserId();
 | 
			
		||||
@@ -259,7 +259,7 @@ const SpaceLanding = ({ space }: { space: Room }) => {
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const onMembersClick = () => {
 | 
			
		||||
    const onMembersClick = (): void => {
 | 
			
		||||
        RightPanelStore.instance.setCard({ phase: RightPanelPhases.SpaceMemberList });
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
@@ -297,7 +297,12 @@ const SpaceLanding = ({ space }: { space: Room }) => {
 | 
			
		||||
    );
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const SpaceSetupFirstRooms = ({ space, title, description, onFinished }) => {
 | 
			
		||||
const SpaceSetupFirstRooms: React.FC<{
 | 
			
		||||
    space: Room;
 | 
			
		||||
    title: string;
 | 
			
		||||
    description: JSX.Element;
 | 
			
		||||
    onFinished(firstRoomId?: string): void;
 | 
			
		||||
}> = ({ space, title, description, onFinished }) => {
 | 
			
		||||
    const [busy, setBusy] = useState(false);
 | 
			
		||||
    const [error, setError] = useState("");
 | 
			
		||||
    const numFields = 3;
 | 
			
		||||
@@ -321,7 +326,7 @@ const SpaceSetupFirstRooms = ({ space, title, description, onFinished }) => {
 | 
			
		||||
        );
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    const onNextClick = async (ev: ButtonEvent) => {
 | 
			
		||||
    const onNextClick = async (ev: ButtonEvent): Promise<void> => {
 | 
			
		||||
        ev.preventDefault();
 | 
			
		||||
        if (busy) return;
 | 
			
		||||
        setError("");
 | 
			
		||||
@@ -354,7 +359,7 @@ const SpaceSetupFirstRooms = ({ space, title, description, onFinished }) => {
 | 
			
		||||
        setBusy(false);
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    let onClick = (ev: ButtonEvent) => {
 | 
			
		||||
    let onClick = (ev: ButtonEvent): void => {
 | 
			
		||||
        ev.preventDefault();
 | 
			
		||||
        onFinished();
 | 
			
		||||
    };
 | 
			
		||||
@@ -389,7 +394,10 @@ const SpaceSetupFirstRooms = ({ space, title, description, onFinished }) => {
 | 
			
		||||
    );
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const SpaceAddExistingRooms = ({ space, onFinished }) => {
 | 
			
		||||
const SpaceAddExistingRooms: React.FC<{
 | 
			
		||||
    space: Room;
 | 
			
		||||
    onFinished(): void;
 | 
			
		||||
}> = ({ space, onFinished }) => {
 | 
			
		||||
    return (
 | 
			
		||||
        <div>
 | 
			
		||||
            <h1>{_t("What do you want to organise?")}</h1>
 | 
			
		||||
@@ -420,7 +428,12 @@ interface ISpaceSetupPublicShareProps extends Pick<IProps & IState, "justCreated
 | 
			
		||||
    onFinished(): void;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const SpaceSetupPublicShare = ({ justCreatedOpts, space, onFinished, firstRoomId }: ISpaceSetupPublicShareProps) => {
 | 
			
		||||
const SpaceSetupPublicShare: React.FC<ISpaceSetupPublicShareProps> = ({
 | 
			
		||||
    justCreatedOpts,
 | 
			
		||||
    space,
 | 
			
		||||
    onFinished,
 | 
			
		||||
    firstRoomId,
 | 
			
		||||
}) => {
 | 
			
		||||
    return (
 | 
			
		||||
        <div className="mx_SpaceRoomView_publicShare">
 | 
			
		||||
            <h1>
 | 
			
		||||
@@ -443,7 +456,11 @@ const SpaceSetupPublicShare = ({ justCreatedOpts, space, onFinished, firstRoomId
 | 
			
		||||
    );
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const SpaceSetupPrivateScope = ({ space, justCreatedOpts, onFinished }) => {
 | 
			
		||||
const SpaceSetupPrivateScope: React.FC<{
 | 
			
		||||
    space: Room;
 | 
			
		||||
    justCreatedOpts: IOpts;
 | 
			
		||||
    onFinished(createRooms: boolean): void;
 | 
			
		||||
}> = ({ space, justCreatedOpts, onFinished }) => {
 | 
			
		||||
    return (
 | 
			
		||||
        <div className="mx_SpaceRoomView_privateScope">
 | 
			
		||||
            <h1>{_t("Who are you working with?")}</h1>
 | 
			
		||||
@@ -485,7 +502,10 @@ const validateEmailRules = withValidation({
 | 
			
		||||
    ],
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
const SpaceSetupPrivateInvite = ({ space, onFinished }) => {
 | 
			
		||||
const SpaceSetupPrivateInvite: React.FC<{
 | 
			
		||||
    space: Room;
 | 
			
		||||
    onFinished(): void;
 | 
			
		||||
}> = ({ space, onFinished }) => {
 | 
			
		||||
    const [busy, setBusy] = useState(false);
 | 
			
		||||
    const [error, setError] = useState("");
 | 
			
		||||
    const numFields = 3;
 | 
			
		||||
@@ -501,7 +521,7 @@ const SpaceSetupPrivateInvite = ({ space, onFinished }) => {
 | 
			
		||||
                label={_t("Email address")}
 | 
			
		||||
                placeholder={_t("Email")}
 | 
			
		||||
                value={emailAddresses[i]}
 | 
			
		||||
                onChange={(ev) => setEmailAddress(i, ev.target.value)}
 | 
			
		||||
                onChange={(ev: React.ChangeEvent<HTMLInputElement>) => setEmailAddress(i, ev.target.value)}
 | 
			
		||||
                ref={fieldRefs[i]}
 | 
			
		||||
                onValidate={validateEmailRules}
 | 
			
		||||
                autoFocus={i === 0}
 | 
			
		||||
@@ -510,7 +530,7 @@ const SpaceSetupPrivateInvite = ({ space, onFinished }) => {
 | 
			
		||||
        );
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    const onNextClick = async (ev) => {
 | 
			
		||||
    const onNextClick = async (ev: ButtonEvent): Promise<void> => {
 | 
			
		||||
        ev.preventDefault();
 | 
			
		||||
        if (busy) return;
 | 
			
		||||
        setError("");
 | 
			
		||||
@@ -548,7 +568,7 @@ const SpaceSetupPrivateInvite = ({ space, onFinished }) => {
 | 
			
		||||
        setBusy(false);
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    let onClick = (ev) => {
 | 
			
		||||
    let onClick = (ev: ButtonEvent): void => {
 | 
			
		||||
        ev.preventDefault();
 | 
			
		||||
        onFinished();
 | 
			
		||||
    };
 | 
			
		||||
@@ -642,29 +662,29 @@ export default class SpaceRoomView extends React.PureComponent<IProps, IState> {
 | 
			
		||||
        RightPanelStore.instance.on(UPDATE_EVENT, this.onRightPanelStoreUpdate);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public componentDidMount() {
 | 
			
		||||
    public componentDidMount(): void {
 | 
			
		||||
        this.context.on(RoomEvent.MyMembership, this.onMyMembership);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public componentWillUnmount() {
 | 
			
		||||
    public componentWillUnmount(): void {
 | 
			
		||||
        defaultDispatcher.unregister(this.dispatcherRef);
 | 
			
		||||
        RightPanelStore.instance.off(UPDATE_EVENT, this.onRightPanelStoreUpdate);
 | 
			
		||||
        this.context.off(RoomEvent.MyMembership, this.onMyMembership);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private onMyMembership = (room: Room, myMembership: string) => {
 | 
			
		||||
    private onMyMembership = (room: Room, myMembership: string): void => {
 | 
			
		||||
        if (room.roomId === this.props.space.roomId) {
 | 
			
		||||
            this.setState({ myMembership });
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    private onRightPanelStoreUpdate = () => {
 | 
			
		||||
    private onRightPanelStoreUpdate = (): void => {
 | 
			
		||||
        this.setState({
 | 
			
		||||
            showRightPanel: RightPanelStore.instance.isOpenForRoom(this.props.space.roomId),
 | 
			
		||||
        });
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    private onAction = (payload: ActionPayload) => {
 | 
			
		||||
    private onAction = (payload: ActionPayload): void => {
 | 
			
		||||
        if (payload.action === Action.ViewRoom && payload.room_id === this.props.space.roomId) {
 | 
			
		||||
            this.setState({ phase: Phase.Landing });
 | 
			
		||||
            return;
 | 
			
		||||
@@ -698,7 +718,7 @@ export default class SpaceRoomView extends React.PureComponent<IProps, IState> {
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    private goToFirstRoom = async () => {
 | 
			
		||||
    private goToFirstRoom = async (): Promise<void> => {
 | 
			
		||||
        if (this.state.firstRoomId) {
 | 
			
		||||
            defaultDispatcher.dispatch<ViewRoomPayload>({
 | 
			
		||||
                action: Action.ViewRoom,
 | 
			
		||||
@@ -711,7 +731,7 @@ export default class SpaceRoomView extends React.PureComponent<IProps, IState> {
 | 
			
		||||
        this.setState({ phase: Phase.Landing });
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    private renderBody() {
 | 
			
		||||
    private renderBody(): JSX.Element {
 | 
			
		||||
        switch (this.state.phase) {
 | 
			
		||||
            case Phase.Landing:
 | 
			
		||||
                if (this.state.myMembership === "join") {
 | 
			
		||||
@@ -794,7 +814,7 @@ export default class SpaceRoomView extends React.PureComponent<IProps, IState> {
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public render() {
 | 
			
		||||
    public render(): JSX.Element {
 | 
			
		||||
        const rightPanel =
 | 
			
		||||
            this.state.showRightPanel && this.state.phase === Phase.Landing ? (
 | 
			
		||||
                <RightPanel room={this.props.space} resizeNotifier={this.props.resizeNotifier} />
 | 
			
		||||
 
 | 
			
		||||
@@ -19,7 +19,7 @@ interface Props extends DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLEleme
 | 
			
		||||
    children?: ReactNode;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export default function SplashPage({ children, className, ...other }: Props) {
 | 
			
		||||
export default function SplashPage({ children, className, ...other }: Props): JSX.Element {
 | 
			
		||||
    const classes = classNames(className, "mx_SplashPage");
 | 
			
		||||
    return (
 | 
			
		||||
        <main {...other} className={classes}>
 | 
			
		||||
 
 | 
			
		||||
@@ -86,7 +86,7 @@ export default class TabbedView extends React.Component<IProps, IState> {
 | 
			
		||||
     * @param {Tab} tab the tab to show
 | 
			
		||||
     * @private
 | 
			
		||||
     */
 | 
			
		||||
    private setActiveTab(tab: Tab) {
 | 
			
		||||
    private setActiveTab(tab: Tab): void {
 | 
			
		||||
        // make sure this tab is still in available tabs
 | 
			
		||||
        if (!!this.getTabById(tab.id)) {
 | 
			
		||||
            if (this.props.onChange) this.props.onChange(tab.id);
 | 
			
		||||
@@ -96,7 +96,7 @@ export default class TabbedView extends React.Component<IProps, IState> {
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private renderTabLabel(tab: Tab) {
 | 
			
		||||
    private renderTabLabel(tab: Tab): JSX.Element {
 | 
			
		||||
        let classes = "mx_TabbedView_tabLabel ";
 | 
			
		||||
 | 
			
		||||
        if (this.state.activeTabId === tab.id) classes += "mx_TabbedView_tabLabel_active";
 | 
			
		||||
@@ -106,7 +106,7 @@ export default class TabbedView extends React.Component<IProps, IState> {
 | 
			
		||||
            tabIcon = <span className={`mx_TabbedView_maskedIcon ${tab.icon}`} />;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        const onClickHandler = () => this.setActiveTab(tab);
 | 
			
		||||
        const onClickHandler = (): void => this.setActiveTab(tab);
 | 
			
		||||
 | 
			
		||||
        const label = _t(tab.label);
 | 
			
		||||
        return (
 | 
			
		||||
 
 | 
			
		||||
@@ -61,15 +61,12 @@ type ThreadPanelHeaderOption = {
 | 
			
		||||
    key: ThreadFilterType;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export const ThreadPanelHeaderFilterOptionItem = ({
 | 
			
		||||
    label,
 | 
			
		||||
    description,
 | 
			
		||||
    onClick,
 | 
			
		||||
    isSelected,
 | 
			
		||||
}: ThreadPanelHeaderOption & {
 | 
			
		||||
    onClick: () => void;
 | 
			
		||||
    isSelected: boolean;
 | 
			
		||||
}) => {
 | 
			
		||||
export const ThreadPanelHeaderFilterOptionItem: React.FC<
 | 
			
		||||
    ThreadPanelHeaderOption & {
 | 
			
		||||
        onClick: () => void;
 | 
			
		||||
        isSelected: boolean;
 | 
			
		||||
    }
 | 
			
		||||
> = ({ label, description, onClick, isSelected }) => {
 | 
			
		||||
    return (
 | 
			
		||||
        <MenuItemRadio active={isSelected} className="mx_ThreadPanel_Header_FilterOptionItem" onClick={onClick}>
 | 
			
		||||
            <span>{label}</span>
 | 
			
		||||
@@ -78,15 +75,11 @@ export const ThreadPanelHeaderFilterOptionItem = ({
 | 
			
		||||
    );
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export const ThreadPanelHeader = ({
 | 
			
		||||
    filterOption,
 | 
			
		||||
    setFilterOption,
 | 
			
		||||
    empty,
 | 
			
		||||
}: {
 | 
			
		||||
export const ThreadPanelHeader: React.FC<{
 | 
			
		||||
    filterOption: ThreadFilterType;
 | 
			
		||||
    setFilterOption: (filterOption: ThreadFilterType) => void;
 | 
			
		||||
    empty: boolean;
 | 
			
		||||
}) => {
 | 
			
		||||
}> = ({ filterOption, setFilterOption, empty }) => {
 | 
			
		||||
    const [menuDisplayed, button, openMenu, closeMenu] = useContextMenu<HTMLElement>();
 | 
			
		||||
    const options: readonly ThreadPanelHeaderOption[] = [
 | 
			
		||||
        {
 | 
			
		||||
 
 | 
			
		||||
@@ -137,7 +137,7 @@ export default class ThreadView extends React.Component<IProps, IState> {
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public componentDidUpdate(prevProps) {
 | 
			
		||||
    public componentDidUpdate(prevProps): void {
 | 
			
		||||
        if (prevProps.mxEvent !== this.props.mxEvent) {
 | 
			
		||||
            this.setupThread(this.props.mxEvent);
 | 
			
		||||
        }
 | 
			
		||||
@@ -192,7 +192,7 @@ export default class ThreadView extends React.Component<IProps, IState> {
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    private setupThread = (mxEv: MatrixEvent) => {
 | 
			
		||||
    private setupThread = (mxEv: MatrixEvent): void => {
 | 
			
		||||
        let thread = this.props.room.getThread(mxEv.getId());
 | 
			
		||||
        if (!thread) {
 | 
			
		||||
            thread = this.props.room.createThread(mxEv.getId(), mxEv, [mxEv], true);
 | 
			
		||||
@@ -200,7 +200,7 @@ export default class ThreadView extends React.Component<IProps, IState> {
 | 
			
		||||
        this.updateThread(thread);
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    private onNewThread = (thread: Thread) => {
 | 
			
		||||
    private onNewThread = (thread: Thread): void => {
 | 
			
		||||
        if (thread.id === this.props.mxEvent.getId()) {
 | 
			
		||||
            this.setupThread(this.props.mxEvent);
 | 
			
		||||
        }
 | 
			
		||||
@@ -218,7 +218,7 @@ export default class ThreadView extends React.Component<IProps, IState> {
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private updateThread = (thread?: Thread) => {
 | 
			
		||||
    private updateThread = (thread?: Thread): void => {
 | 
			
		||||
        if (this.state.thread === thread) return;
 | 
			
		||||
 | 
			
		||||
        this.setupThreadListeners(thread, this.state.thread);
 | 
			
		||||
@@ -276,7 +276,7 @@ export default class ThreadView extends React.Component<IProps, IState> {
 | 
			
		||||
        this.setState({ narrow });
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    private onKeyDown = (ev: KeyboardEvent) => {
 | 
			
		||||
    private onKeyDown = (ev: KeyboardEvent): void => {
 | 
			
		||||
        let handled = false;
 | 
			
		||||
 | 
			
		||||
        const action = getKeyBindingsManager().getRoomAction(ev);
 | 
			
		||||
@@ -300,7 +300,7 @@ export default class ThreadView extends React.Component<IProps, IState> {
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    private onFileDrop = (dataTransfer: DataTransfer) => {
 | 
			
		||||
    private onFileDrop = (dataTransfer: DataTransfer): void => {
 | 
			
		||||
        const roomId = this.props.mxEvent.getRoomId();
 | 
			
		||||
        if (roomId) {
 | 
			
		||||
            ContentMessages.sharedInstance().sendContentListToRoom(
 | 
			
		||||
 
 | 
			
		||||
@@ -31,6 +31,7 @@ import { Thread, ThreadEvent } from "matrix-js-sdk/src/models/thread";
 | 
			
		||||
import { ReceiptType } from "matrix-js-sdk/src/@types/read_receipts";
 | 
			
		||||
import { MatrixError } from "matrix-js-sdk/src/http-api";
 | 
			
		||||
import { ReadReceipt } from "matrix-js-sdk/src/models/read-receipt";
 | 
			
		||||
import { Relations } from "matrix-js-sdk/src/models/relations";
 | 
			
		||||
 | 
			
		||||
import SettingsStore from "../../settings/SettingsStore";
 | 
			
		||||
import { Layout } from "../../settings/enums/Layout";
 | 
			
		||||
@@ -67,7 +68,7 @@ const READ_MARKER_DEBOUNCE_MS = 100;
 | 
			
		||||
// How far off-screen a decryption failure can be for it to still count as "visible"
 | 
			
		||||
const VISIBLE_DECRYPTION_FAILURE_MARGIN = 100;
 | 
			
		||||
 | 
			
		||||
const debuglog = (...args: any[]) => {
 | 
			
		||||
const debuglog = (...args: any[]): void => {
 | 
			
		||||
    if (SettingsStore.getValue("debug_timeline_panel")) {
 | 
			
		||||
        logger.log.call(console, "TimelinePanel debuglog:", ...args);
 | 
			
		||||
    }
 | 
			
		||||
@@ -315,7 +316,7 @@ class TimelinePanel extends React.Component<IProps, IState> {
 | 
			
		||||
        this.props.timelineSet.room?.on(ThreadEvent.Update, this.onThreadUpdate);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public componentDidMount() {
 | 
			
		||||
    public componentDidMount(): void {
 | 
			
		||||
        if (this.props.manageReadReceipts) {
 | 
			
		||||
            this.updateReadReceiptOnUserActivity();
 | 
			
		||||
        }
 | 
			
		||||
@@ -325,7 +326,7 @@ class TimelinePanel extends React.Component<IProps, IState> {
 | 
			
		||||
        this.initTimeline(this.props);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public componentDidUpdate(prevProps) {
 | 
			
		||||
    public componentDidUpdate(prevProps: Readonly<IProps>): void {
 | 
			
		||||
        if (prevProps.timelineSet !== this.props.timelineSet) {
 | 
			
		||||
            // throw new Error("changing timelineSet on a TimelinePanel is not supported");
 | 
			
		||||
 | 
			
		||||
@@ -360,7 +361,7 @@ class TimelinePanel extends React.Component<IProps, IState> {
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public componentWillUnmount() {
 | 
			
		||||
    public componentWillUnmount(): void {
 | 
			
		||||
        // set a boolean to say we've been unmounted, which any pending
 | 
			
		||||
        // promises can use to throw away their results.
 | 
			
		||||
        //
 | 
			
		||||
@@ -616,7 +617,7 @@ class TimelinePanel extends React.Component<IProps, IState> {
 | 
			
		||||
        });
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    private onMessageListScroll = (e) => {
 | 
			
		||||
    private onMessageListScroll = (e: Event): void => {
 | 
			
		||||
        this.props.onScroll?.(e);
 | 
			
		||||
        if (this.props.manageReadMarkers) {
 | 
			
		||||
            this.doManageReadMarkers();
 | 
			
		||||
@@ -777,7 +778,7 @@ class TimelinePanel extends React.Component<IProps, IState> {
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    public canResetTimeline = () => this.messagePanel?.current?.isAtBottom();
 | 
			
		||||
    public canResetTimeline = (): boolean => this.messagePanel?.current?.isAtBottom();
 | 
			
		||||
 | 
			
		||||
    private onRoomRedaction = (ev: MatrixEvent, room: Room): void => {
 | 
			
		||||
        if (this.unmounted) return;
 | 
			
		||||
@@ -1060,7 +1061,7 @@ class TimelinePanel extends React.Component<IProps, IState> {
 | 
			
		||||
                    this.state.readMarkerEventId ?? "",
 | 
			
		||||
                    sendRRs ? lastReadEvent ?? undefined : undefined, // Public read receipt (could be null)
 | 
			
		||||
                    lastReadEvent ?? undefined, // Private read receipt (could be null)
 | 
			
		||||
                ).catch(async (e) => {
 | 
			
		||||
                ).catch(async (e): Promise<void> => {
 | 
			
		||||
                    // /read_markers API is not implemented on this HS, fallback to just RR
 | 
			
		||||
                    if (e.errcode === "M_UNRECOGNIZED" && lastReadEvent) {
 | 
			
		||||
                        if (
 | 
			
		||||
@@ -1070,10 +1071,11 @@ class TimelinePanel extends React.Component<IProps, IState> {
 | 
			
		||||
                        )
 | 
			
		||||
                            return;
 | 
			
		||||
                        try {
 | 
			
		||||
                            return await cli.sendReadReceipt(
 | 
			
		||||
                            await cli.sendReadReceipt(
 | 
			
		||||
                                lastReadEvent,
 | 
			
		||||
                                sendRRs ? ReceiptType.Read : ReceiptType.ReadPrivate,
 | 
			
		||||
                            );
 | 
			
		||||
                            return;
 | 
			
		||||
                        } catch (error) {
 | 
			
		||||
                            logger.error(e);
 | 
			
		||||
                            this.lastRRSentEventId = undefined;
 | 
			
		||||
@@ -1314,7 +1316,7 @@ class TimelinePanel extends React.Component<IProps, IState> {
 | 
			
		||||
     *
 | 
			
		||||
     * We pass it down to the scroll panel.
 | 
			
		||||
     */
 | 
			
		||||
    public handleScrollKey = (ev) => {
 | 
			
		||||
    public handleScrollKey = (ev: React.KeyboardEvent): void => {
 | 
			
		||||
        if (!this.messagePanel.current) return;
 | 
			
		||||
 | 
			
		||||
        // jump to the live timeline on ctrl-end, rather than the end of the
 | 
			
		||||
@@ -1342,7 +1344,7 @@ class TimelinePanel extends React.Component<IProps, IState> {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private scrollIntoView(eventId?: string, pixelOffset?: number, offsetBase?: number): void {
 | 
			
		||||
        const doScroll = () => {
 | 
			
		||||
        const doScroll = (): void => {
 | 
			
		||||
            if (!this.messagePanel.current) return;
 | 
			
		||||
            if (eventId) {
 | 
			
		||||
                debuglog(
 | 
			
		||||
@@ -1401,7 +1403,7 @@ class TimelinePanel extends React.Component<IProps, IState> {
 | 
			
		||||
            ? new TimelineWindow(cli, this.props.overlayTimelineSet, { windowLimit: this.props.timelineCap })
 | 
			
		||||
            : undefined;
 | 
			
		||||
 | 
			
		||||
        const onLoaded = () => {
 | 
			
		||||
        const onLoaded = (): void => {
 | 
			
		||||
            if (this.unmounted) return;
 | 
			
		||||
 | 
			
		||||
            // clear the timeline min-height when (re)loading the timeline
 | 
			
		||||
@@ -1441,7 +1443,7 @@ class TimelinePanel extends React.Component<IProps, IState> {
 | 
			
		||||
            );
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        const onError = (error: MatrixError) => {
 | 
			
		||||
        const onError = (error: MatrixError): void => {
 | 
			
		||||
            if (this.unmounted) return;
 | 
			
		||||
 | 
			
		||||
            this.setState({ timelineLoading: false });
 | 
			
		||||
@@ -1504,7 +1506,7 @@ class TimelinePanel extends React.Component<IProps, IState> {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        const prom = this.timelineWindow.load(eventId, INITIAL_SIZE).then(async () => {
 | 
			
		||||
        const prom = this.timelineWindow.load(eventId, INITIAL_SIZE).then(async (): Promise<void> => {
 | 
			
		||||
            if (this.overlayTimelineWindow) {
 | 
			
		||||
                // @TODO(kerrya) use timestampToEvent to load the overlay timeline
 | 
			
		||||
                // with more correct position when main TL eventId is truthy
 | 
			
		||||
@@ -1746,7 +1748,7 @@ class TimelinePanel extends React.Component<IProps, IState> {
 | 
			
		||||
        const wrapperRect = messagePanelNode.getBoundingClientRect();
 | 
			
		||||
        const myUserId = MatrixClientPeg.get().credentials.userId;
 | 
			
		||||
 | 
			
		||||
        const isNodeInView = (node: HTMLElement) => {
 | 
			
		||||
        const isNodeInView = (node: HTMLElement): boolean => {
 | 
			
		||||
            if (node) {
 | 
			
		||||
                const boundingRect = node.getBoundingClientRect();
 | 
			
		||||
                if (
 | 
			
		||||
@@ -1877,13 +1879,14 @@ class TimelinePanel extends React.Component<IProps, IState> {
 | 
			
		||||
        eventId: string,
 | 
			
		||||
        relationType: RelationType | string,
 | 
			
		||||
        eventType: EventType | string,
 | 
			
		||||
    ) => this.props.timelineSet.relations?.getChildEventsForEvent(eventId, relationType, eventType);
 | 
			
		||||
    ): Relations | undefined =>
 | 
			
		||||
        this.props.timelineSet.relations?.getChildEventsForEvent(eventId, relationType, eventType);
 | 
			
		||||
 | 
			
		||||
    private buildLegacyCallEventGroupers(events?: MatrixEvent[]): void {
 | 
			
		||||
        this.callEventGroupers = buildLegacyCallEventGroupers(this.callEventGroupers, events);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public render() {
 | 
			
		||||
    public render(): JSX.Element {
 | 
			
		||||
        // just show a spinner while the timeline loads.
 | 
			
		||||
        //
 | 
			
		||||
        // put it in a div of the right class (mx_RoomView_messagePanel) so
 | 
			
		||||
 
 | 
			
		||||
@@ -39,18 +39,18 @@ export default class ToastContainer extends React.Component<{}, IState> {
 | 
			
		||||
        ToastStore.sharedInstance().on("update", this.onToastStoreUpdate);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public componentWillUnmount() {
 | 
			
		||||
    public componentWillUnmount(): void {
 | 
			
		||||
        ToastStore.sharedInstance().removeListener("update", this.onToastStoreUpdate);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private onToastStoreUpdate = () => {
 | 
			
		||||
    private onToastStoreUpdate = (): void => {
 | 
			
		||||
        this.setState({
 | 
			
		||||
            toasts: ToastStore.sharedInstance().getToasts(),
 | 
			
		||||
            countSeen: ToastStore.sharedInstance().getCountSeen(),
 | 
			
		||||
        });
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    public render() {
 | 
			
		||||
    public render(): JSX.Element {
 | 
			
		||||
        const totalCount = this.state.toasts.length;
 | 
			
		||||
        const isStacked = totalCount > 1;
 | 
			
		||||
        let toast;
 | 
			
		||||
 
 | 
			
		||||
@@ -65,12 +65,12 @@ export default class UploadBar extends React.PureComponent<IProps, IState> {
 | 
			
		||||
        this.state = this.calculateState();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public componentDidMount() {
 | 
			
		||||
    public componentDidMount(): void {
 | 
			
		||||
        this.dispatcherRef = dis.register(this.onAction);
 | 
			
		||||
        this.mounted = true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public componentWillUnmount() {
 | 
			
		||||
    public componentWillUnmount(): void {
 | 
			
		||||
        this.mounted = false;
 | 
			
		||||
        dis.unregister(this.dispatcherRef!);
 | 
			
		||||
    }
 | 
			
		||||
@@ -91,19 +91,19 @@ export default class UploadBar extends React.PureComponent<IProps, IState> {
 | 
			
		||||
        };
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private onAction = (payload: ActionPayload) => {
 | 
			
		||||
    private onAction = (payload: ActionPayload): void => {
 | 
			
		||||
        if (!this.mounted) return;
 | 
			
		||||
        if (isUploadPayload(payload)) {
 | 
			
		||||
            this.setState(this.calculateState());
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    private onCancelClick = (ev: ButtonEvent) => {
 | 
			
		||||
    private onCancelClick = (ev: ButtonEvent): void => {
 | 
			
		||||
        ev.preventDefault();
 | 
			
		||||
        ContentMessages.sharedInstance().cancelUpload(this.state.currentUpload!);
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    public render() {
 | 
			
		||||
    public render(): JSX.Element {
 | 
			
		||||
        if (!this.state.currentFile) {
 | 
			
		||||
            return null;
 | 
			
		||||
        }
 | 
			
		||||
 
 | 
			
		||||
@@ -22,7 +22,7 @@ import defaultDispatcher from "../../dispatcher/dispatcher";
 | 
			
		||||
import { ActionPayload } from "../../dispatcher/payloads";
 | 
			
		||||
import { Action } from "../../dispatcher/actions";
 | 
			
		||||
import { _t } from "../../languageHandler";
 | 
			
		||||
import { ChevronFace, ContextMenuButton } from "./ContextMenu";
 | 
			
		||||
import { ChevronFace, ContextMenuButton, MenuProps } from "./ContextMenu";
 | 
			
		||||
import { UserTab } from "../views/dialogs/UserTab";
 | 
			
		||||
import { OpenToTabPayload } from "../../dispatcher/payloads/OpenToTabPayload";
 | 
			
		||||
import FeedbackDialog from "../views/dialogs/FeedbackDialog";
 | 
			
		||||
@@ -67,7 +67,7 @@ interface IState {
 | 
			
		||||
    showLiveAvatarAddon: boolean;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const toRightOf = (rect: PartialDOMRect) => {
 | 
			
		||||
const toRightOf = (rect: PartialDOMRect): MenuProps => {
 | 
			
		||||
    return {
 | 
			
		||||
        left: rect.width + rect.left + 8,
 | 
			
		||||
        top: rect.top,
 | 
			
		||||
@@ -75,7 +75,7 @@ const toRightOf = (rect: PartialDOMRect) => {
 | 
			
		||||
    };
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const below = (rect: PartialDOMRect) => {
 | 
			
		||||
const below = (rect: PartialDOMRect): MenuProps => {
 | 
			
		||||
    return {
 | 
			
		||||
        left: rect.left,
 | 
			
		||||
        top: rect.top + rect.height,
 | 
			
		||||
@@ -118,7 +118,7 @@ export default class UserMenu extends React.Component<IProps, IState> {
 | 
			
		||||
        });
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    public componentDidMount() {
 | 
			
		||||
    public componentDidMount(): void {
 | 
			
		||||
        this.context.voiceBroadcastRecordingsStore.on(
 | 
			
		||||
            VoiceBroadcastRecordingsStoreEvent.CurrentChanged,
 | 
			
		||||
            this.onCurrentVoiceBroadcastRecordingChanged,
 | 
			
		||||
@@ -127,7 +127,7 @@ export default class UserMenu extends React.Component<IProps, IState> {
 | 
			
		||||
        this.themeWatcherRef = SettingsStore.watchSetting("theme", null, this.onThemeChanged);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public componentWillUnmount() {
 | 
			
		||||
    public componentWillUnmount(): void {
 | 
			
		||||
        if (this.themeWatcherRef) SettingsStore.unwatchSetting(this.themeWatcherRef);
 | 
			
		||||
        if (this.dndWatcherRef) SettingsStore.unwatchSetting(this.dndWatcherRef);
 | 
			
		||||
        if (this.dispatcherRef) defaultDispatcher.unregister(this.dispatcherRef);
 | 
			
		||||
@@ -163,26 +163,26 @@ export default class UserMenu extends React.Component<IProps, IState> {
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private onProfileUpdate = async () => {
 | 
			
		||||
    private onProfileUpdate = async (): Promise<void> => {
 | 
			
		||||
        // the store triggered an update, so force a layout update. We don't
 | 
			
		||||
        // have any state to store here for that to magically happen.
 | 
			
		||||
        this.forceUpdate();
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    private onSelectedSpaceUpdate = async () => {
 | 
			
		||||
    private onSelectedSpaceUpdate = async (): Promise<void> => {
 | 
			
		||||
        this.setState({
 | 
			
		||||
            selectedSpace: SpaceStore.instance.activeSpaceRoom,
 | 
			
		||||
        });
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    private onThemeChanged = () => {
 | 
			
		||||
    private onThemeChanged = (): void => {
 | 
			
		||||
        this.setState({
 | 
			
		||||
            isDarkTheme: this.isUserOnDarkTheme(),
 | 
			
		||||
            isHighContrast: this.isUserOnHighContrastTheme(),
 | 
			
		||||
        });
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    private onAction = (payload: ActionPayload) => {
 | 
			
		||||
    private onAction = (payload: ActionPayload): void => {
 | 
			
		||||
        switch (payload.action) {
 | 
			
		||||
            case Action.ToggleUserMenu:
 | 
			
		||||
                if (this.state.contextMenuPosition) {
 | 
			
		||||
@@ -194,13 +194,13 @@ export default class UserMenu extends React.Component<IProps, IState> {
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    private onOpenMenuClick = (ev: React.MouseEvent) => {
 | 
			
		||||
    private onOpenMenuClick = (ev: React.MouseEvent): void => {
 | 
			
		||||
        ev.preventDefault();
 | 
			
		||||
        ev.stopPropagation();
 | 
			
		||||
        this.setState({ contextMenuPosition: ev.currentTarget.getBoundingClientRect() });
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    private onContextMenu = (ev: React.MouseEvent) => {
 | 
			
		||||
    private onContextMenu = (ev: React.MouseEvent): void => {
 | 
			
		||||
        ev.preventDefault();
 | 
			
		||||
        ev.stopPropagation();
 | 
			
		||||
        this.setState({
 | 
			
		||||
@@ -213,11 +213,11 @@ export default class UserMenu extends React.Component<IProps, IState> {
 | 
			
		||||
        });
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    private onCloseMenu = () => {
 | 
			
		||||
    private onCloseMenu = (): void => {
 | 
			
		||||
        this.setState({ contextMenuPosition: null });
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    private onSwitchThemeClick = (ev: React.MouseEvent) => {
 | 
			
		||||
    private onSwitchThemeClick = (ev: React.MouseEvent): void => {
 | 
			
		||||
        ev.preventDefault();
 | 
			
		||||
        ev.stopPropagation();
 | 
			
		||||
 | 
			
		||||
@@ -236,7 +236,7 @@ export default class UserMenu extends React.Component<IProps, IState> {
 | 
			
		||||
        SettingsStore.setValue("theme", null, SettingLevel.DEVICE, newTheme); // set at same level as Appearance tab
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    private onSettingsOpen = (ev: ButtonEvent, tabId: string) => {
 | 
			
		||||
    private onSettingsOpen = (ev: ButtonEvent, tabId: string): void => {
 | 
			
		||||
        ev.preventDefault();
 | 
			
		||||
        ev.stopPropagation();
 | 
			
		||||
 | 
			
		||||
@@ -245,7 +245,7 @@ export default class UserMenu extends React.Component<IProps, IState> {
 | 
			
		||||
        this.setState({ contextMenuPosition: null }); // also close the menu
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    private onProvideFeedback = (ev: ButtonEvent) => {
 | 
			
		||||
    private onProvideFeedback = (ev: ButtonEvent): void => {
 | 
			
		||||
        ev.preventDefault();
 | 
			
		||||
        ev.stopPropagation();
 | 
			
		||||
 | 
			
		||||
@@ -253,7 +253,7 @@ export default class UserMenu extends React.Component<IProps, IState> {
 | 
			
		||||
        this.setState({ contextMenuPosition: null }); // also close the menu
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    private onSignOutClick = async (ev: ButtonEvent) => {
 | 
			
		||||
    private onSignOutClick = async (ev: ButtonEvent): Promise<void> => {
 | 
			
		||||
        ev.preventDefault();
 | 
			
		||||
        ev.stopPropagation();
 | 
			
		||||
 | 
			
		||||
@@ -268,17 +268,17 @@ export default class UserMenu extends React.Component<IProps, IState> {
 | 
			
		||||
        this.setState({ contextMenuPosition: null }); // also close the menu
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    private onSignInClick = () => {
 | 
			
		||||
    private onSignInClick = (): void => {
 | 
			
		||||
        defaultDispatcher.dispatch({ action: "start_login" });
 | 
			
		||||
        this.setState({ contextMenuPosition: null }); // also close the menu
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    private onRegisterClick = () => {
 | 
			
		||||
    private onRegisterClick = (): void => {
 | 
			
		||||
        defaultDispatcher.dispatch({ action: "start_registration" });
 | 
			
		||||
        this.setState({ contextMenuPosition: null }); // also close the menu
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    private onHomeClick = (ev: ButtonEvent) => {
 | 
			
		||||
    private onHomeClick = (ev: ButtonEvent): void => {
 | 
			
		||||
        ev.preventDefault();
 | 
			
		||||
        ev.stopPropagation();
 | 
			
		||||
 | 
			
		||||
@@ -429,7 +429,7 @@ export default class UserMenu extends React.Component<IProps, IState> {
 | 
			
		||||
        );
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    public render() {
 | 
			
		||||
    public render(): JSX.Element {
 | 
			
		||||
        const avatarSize = 32; // should match border-radius of the avatar
 | 
			
		||||
 | 
			
		||||
        const userId = MatrixClientPeg.get().getUserId();
 | 
			
		||||
 
 | 
			
		||||
@@ -57,7 +57,7 @@ export default class CompleteSecurity extends React.Component<IProps, IState> {
 | 
			
		||||
        store.stop();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public render() {
 | 
			
		||||
    public render(): JSX.Element {
 | 
			
		||||
        const { phase, lostKeys } = this.state;
 | 
			
		||||
        let icon;
 | 
			
		||||
        let title;
 | 
			
		||||
 
 | 
			
		||||
@@ -27,7 +27,7 @@ interface IProps {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export default class E2eSetup extends React.Component<IProps> {
 | 
			
		||||
    public render() {
 | 
			
		||||
    public render(): JSX.Element {
 | 
			
		||||
        return (
 | 
			
		||||
            <AuthPage>
 | 
			
		||||
                <CompleteSecurityBody>
 | 
			
		||||
 
 | 
			
		||||
@@ -110,11 +110,11 @@ export default class ForgotPassword extends React.Component<Props, State> {
 | 
			
		||||
        this.reset = new PasswordReset(this.props.serverConfig.hsUrl, this.props.serverConfig.isUrl);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public componentDidMount() {
 | 
			
		||||
    public componentDidMount(): void {
 | 
			
		||||
        this.checkServerCapabilities(this.props.serverConfig);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public componentDidUpdate(prevProps: Readonly<Props>) {
 | 
			
		||||
    public componentDidUpdate(prevProps: Readonly<Props>): void {
 | 
			
		||||
        if (
 | 
			
		||||
            prevProps.serverConfig.hsUrl !== this.props.serverConfig.hsUrl ||
 | 
			
		||||
            prevProps.serverConfig.isUrl !== this.props.serverConfig.isUrl
 | 
			
		||||
@@ -159,7 +159,7 @@ export default class ForgotPassword extends React.Component<Props, State> {
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private async onPhaseEmailInputSubmit() {
 | 
			
		||||
    private async onPhaseEmailInputSubmit(): Promise<void> {
 | 
			
		||||
        this.phase = Phase.SendingEmail;
 | 
			
		||||
 | 
			
		||||
        if (await this.sendVerificationMail()) {
 | 
			
		||||
@@ -213,7 +213,7 @@ export default class ForgotPassword extends React.Component<Props, State> {
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private async onPhaseEmailSentSubmit() {
 | 
			
		||||
    private async onPhaseEmailSentSubmit(): Promise<void> {
 | 
			
		||||
        this.setState({
 | 
			
		||||
            phase: Phase.PasswordInput,
 | 
			
		||||
        });
 | 
			
		||||
@@ -288,7 +288,7 @@ export default class ForgotPassword extends React.Component<Props, State> {
 | 
			
		||||
            false,
 | 
			
		||||
            false,
 | 
			
		||||
            {
 | 
			
		||||
                onBeforeClose: async (reason?: string) => {
 | 
			
		||||
                onBeforeClose: async (reason?: string): Promise<boolean> => {
 | 
			
		||||
                    if (reason === "backgroundClick") {
 | 
			
		||||
                        // Modal dismissed by clicking the background.
 | 
			
		||||
                        // Go one phase back.
 | 
			
		||||
@@ -342,7 +342,7 @@ export default class ForgotPassword extends React.Component<Props, State> {
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    private onInputChanged = (stateKey: string, ev: React.FormEvent<HTMLInputElement>) => {
 | 
			
		||||
    private onInputChanged = (stateKey: string, ev: React.FormEvent<HTMLInputElement>): void => {
 | 
			
		||||
        let value = ev.currentTarget.value;
 | 
			
		||||
        if (stateKey === "email") value = value.trim();
 | 
			
		||||
        this.setState({
 | 
			
		||||
@@ -460,7 +460,7 @@ export default class ForgotPassword extends React.Component<Props, State> {
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public renderDone() {
 | 
			
		||||
    public renderDone(): JSX.Element {
 | 
			
		||||
        return (
 | 
			
		||||
            <>
 | 
			
		||||
                <CheckboxIcon className="mx_Icon mx_Icon_32 mx_Icon_accent" />
 | 
			
		||||
@@ -484,7 +484,7 @@ export default class ForgotPassword extends React.Component<Props, State> {
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public render() {
 | 
			
		||||
    public render(): JSX.Element {
 | 
			
		||||
        let resetPasswordJsx: JSX.Element;
 | 
			
		||||
 | 
			
		||||
        switch (this.state.phase) {
 | 
			
		||||
 
 | 
			
		||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user