Enable @typescript-eslint/explicit-function-return-type in /src (#9788)

* Enable `@typescript-eslint/explicit-member-accessibility` on /src

* Prettier

* Enable `@typescript-eslint/explicit-function-return-type` in /src

* Fix types

* tsc strict fixes

* Delint

* Fix test

* Fix bad merge
This commit is contained in:
Michael Telatynski 2023-01-12 13:25:14 +00:00 committed by GitHub
parent 7a36ba0fde
commit 030b7e90bf
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
683 changed files with 3459 additions and 3013 deletions

View File

@ -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",

View File

@ -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 {}

View File

@ -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) {

View File

@ -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} />;

View File

@ -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()) {

View File

@ -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;

View File

@ -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) {

View File

@ -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();
}

View File

@ -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());
}

View File

@ -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());

View File

@ -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}`;

View File

@ -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;

View File

@ -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

View File

@ -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 {

View File

@ -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,
{

View File

@ -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();

View File

@ -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, {

View File

@ -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);

View File

@ -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 = "";

View File

@ -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);

View File

@ -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,

View File

@ -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;
}

View File

@ -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
}
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;

View File

@ -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 });
}
}

View File

@ -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,

View File

@ -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;

View File

@ -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>({

View File

@ -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;

View File

@ -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(

View File

@ -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,

View File

@ -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();

View File

@ -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) {

View File

@ -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;

View File

@ -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:

View File

@ -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:

View File

@ -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,

View File

@ -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,

View File

@ -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;

View File

@ -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,
});

View File

@ -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;

View File

@ -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>);

View File

@ -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,

View File

@ -30,7 +30,7 @@ export class ManagedPlayback extends Playback {
return super.play();
}
public destroy() {
public destroy(): void {
this.manager.destroyPlaybackInstance(this);
super.destroy();
}

View File

@ -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

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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) {

View File

@ -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

View File

@ -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());
};

View File

@ -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();

View File

@ -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;

View File

@ -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,

View File

@ -100,7 +100,7 @@ export default class CommandProvider extends AutocompleteProvider {
});
}
public getName() {
public getName(): string {
return "*️⃣ " + _t("Commands");
}

View File

@ -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");
}

View File

@ -65,7 +65,7 @@ export default class NotifProvider extends AutocompleteProvider {
return [];
}
public getName() {
public getName(): string {
return "❗️ " + _t("Room Notification");
}

View File

@ -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) {

View File

@ -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");
}

View File

@ -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");
}

View File

@ -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;

View File

@ -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;

View File

@ -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);

View File

@ -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);
};

View File

@ -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 {

View File

@ -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);

View File

@ -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}>

View File

@ -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">

View File

@ -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));

View File

@ -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);
};

View File

@ -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) {

View File

@ -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);

View File

@ -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();

View File

@ -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) {

View File

@ -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}`);
}

View File

@ -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) {

View File

@ -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}`}>

View File

@ -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>

View File

@ -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)`,
};

View File

@ -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> = [];

View File

@ -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.

View File

@ -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();
}

View File

@ -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();
};

View File

@ -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({

View File

@ -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();

View File

@ -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;
};

View File

@ -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);

View File

@ -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} />

View File

@ -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}>

View File

@ -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 (

View File

@ -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[] = [
{

View File

@ -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(

View File

@ -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

View File

@ -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;

View File

@ -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;
}

View File

@ -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();

View File

@ -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;

View File

@ -27,7 +27,7 @@ interface IProps {
}
export default class E2eSetup extends React.Component<IProps> {
public render() {
public render(): JSX.Element {
return (
<AuthPage>
<CompleteSecurityBody>

View File

@ -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