diff --git a/src/AddThreepid.ts b/src/AddThreepid.ts index db74eaad31..51779042f9 100644 --- a/src/AddThreepid.ts +++ b/src/AddThreepid.ts @@ -184,7 +184,7 @@ export default class AddThreepid { * with a "message" property which contains a human-readable message detailing why * the request failed. */ - public async checkEmailLinkClicked(): Promise<[boolean, IAuthData | Error | null] | undefined> { + public async checkEmailLinkClicked(): Promise<[success?: boolean, result?: IAuthData | Error | null]> { try { if (await MatrixClientPeg.get().doesServerSupportSeparateAddAndBind()) { if (this.bind) { @@ -202,7 +202,7 @@ export default class AddThreepid { // The spec has always required this to use UI auth but synapse briefly // implemented it without, so this may just succeed and that's OK. - return; + return [true]; } catch (e) { if (e.httpStatus !== 401 || !e.data || !e.data.flows) { // doesn't look like an interactive-auth failure @@ -225,19 +225,16 @@ export default class AddThreepid { continueKind: "primary", }, }; - const { finished } = Modal.createDialog<[boolean, IAuthData | Error | null]>( - InteractiveAuthDialog, - { - title: _t("Add Email Address"), - matrixClient: MatrixClientPeg.get(), - authData: e.data, - makeRequest: this.makeAddThreepidOnlyRequest, - aestheticsForStagePhases: { - [SSOAuthEntry.LOGIN_TYPE]: dialogAesthetics, - [SSOAuthEntry.UNSTABLE_LOGIN_TYPE]: dialogAesthetics, - }, + const { finished } = Modal.createDialog(InteractiveAuthDialog, { + title: _t("Add Email Address"), + matrixClient: MatrixClientPeg.get(), + authData: e.data, + makeRequest: this.makeAddThreepidOnlyRequest, + aestheticsForStagePhases: { + [SSOAuthEntry.LOGIN_TYPE]: dialogAesthetics, + [SSOAuthEntry.UNSTABLE_LOGIN_TYPE]: dialogAesthetics, }, - ); + }); return finished; } } @@ -259,6 +256,7 @@ export default class AddThreepid { } throw err; } + return []; } /** diff --git a/src/AsyncWrapper.tsx b/src/AsyncWrapper.tsx index e4e12bedfe..4ee10ca22d 100644 --- a/src/AsyncWrapper.tsx +++ b/src/AsyncWrapper.tsx @@ -18,16 +18,16 @@ import React, { ComponentType } from "react"; import { logger } from "matrix-js-sdk/src/logger"; import { _t } from "./languageHandler"; -import { IDialogProps } from "./components/views/dialogs/IDialogProps"; import BaseDialog from "./components/views/dialogs/BaseDialog"; import DialogButtons from "./components/views/elements/DialogButtons"; import Spinner from "./components/views/elements/Spinner"; type AsyncImport = { default: T }; -interface IProps extends IDialogProps { +interface IProps { // A promise which resolves with the real component - prom: Promise>; + prom: Promise | AsyncImport>>; + onFinished(): void; } interface IState { @@ -71,7 +71,7 @@ export default class AsyncWrapper extends React.Component { } private onWrapperCancelClick = (): void => { - this.props.onFinished(false); + this.props.onFinished(); }; public render(): React.ReactNode { diff --git a/src/ContentMessages.ts b/src/ContentMessages.ts index 382214a1b6..41bd0361a2 100644 --- a/src/ContentMessages.ts +++ b/src/ContentMessages.ts @@ -389,7 +389,7 @@ export default class ContentMessages { } if (tooBigFiles.length > 0) { - const { finished } = Modal.createDialog<[boolean]>(UploadFailureDialog, { + const { finished } = Modal.createDialog(UploadFailureDialog, { badFiles: tooBigFiles, totalFiles: files.length, contentMessages: this, @@ -407,7 +407,7 @@ export default class ContentMessages { const loopPromiseBefore = promBefore; if (!uploadAll) { - const { finished } = Modal.createDialog<[boolean, boolean]>(UploadConfirmDialog, { + const { finished } = Modal.createDialog(UploadConfirmDialog, { file, currentIndex: i, totalFiles: okFiles.length, diff --git a/src/LegacyCallHandler.tsx b/src/LegacyCallHandler.tsx index 90bded231e..708db7a32d 100644 --- a/src/LegacyCallHandler.tsx +++ b/src/LegacyCallHandler.tsx @@ -58,7 +58,7 @@ import IncomingLegacyCallToast, { getIncomingLegacyCallToastKey } from "./toasts import ToastStore from "./stores/ToastStore"; import Resend from "./Resend"; import { ViewRoomPayload } from "./dispatcher/payloads/ViewRoomPayload"; -import { KIND_CALL_TRANSFER } from "./components/views/dialogs/InviteDialogTypes"; +import { InviteKind } from "./components/views/dialogs/InviteDialogTypes"; import { OpenInviteDialogPayload } from "./dispatcher/payloads/OpenInviteDialogPayload"; import { findDMForUser } from "./utils/dm/findDMForUser"; import { getJoinedNonFunctionalMembers } from "./utils/room/getJoinedNonFunctionalMembers"; @@ -1214,7 +1214,7 @@ export default class LegacyCallHandler extends EventEmitter { call.setRemoteOnHold(true); dis.dispatch({ action: Action.OpenInviteDialog, - kind: KIND_CALL_TRANSFER, + kind: InviteKind.CallTransfer, call, analyticsName: "Transfer Call", className: "mx_InviteDialog_transferWrapper", diff --git a/src/Lifecycle.ts b/src/Lifecycle.ts index 04b20adc8e..590b7eb180 100644 --- a/src/Lifecycle.ts +++ b/src/Lifecycle.ts @@ -265,7 +265,7 @@ export function handleInvalidStoreError(e: InvalidStoreError): Promise { .then(() => { const lazyLoadEnabled = e.value; if (lazyLoadEnabled) { - return new Promise((resolve) => { + return new Promise((resolve) => { Modal.createDialog(LazyLoadingResyncDialog, { onFinished: resolve, }); @@ -275,7 +275,7 @@ export function handleInvalidStoreError(e: InvalidStoreError): Promise { // between LL/non-LL version on same host. // as disabling LL when previously enabled // is a strong indicator of this (/develop & /app) - return new Promise((resolve) => { + return new Promise((resolve) => { Modal.createDialog(LazyLoadingDisabledDialog, { onFinished: resolve, host: window.location.host, diff --git a/src/Modal.tsx b/src/Modal.tsx index 3b21c47d3d..54ae185ad1 100644 --- a/src/Modal.tsx +++ b/src/Modal.tsx @@ -27,34 +27,35 @@ import AsyncWrapper from "./AsyncWrapper"; const DIALOG_CONTAINER_ID = "mx_Dialog_Container"; const STATIC_DIALOG_CONTAINER_ID = "mx_Dialog_StaticContainer"; -export interface IModal { +// Type which accepts a React Component which looks like a Modal (accepts an onFinished prop) +export type ComponentType = React.ComponentType<{ + onFinished?(...args: any): void; +}>; + +// Generic type which returns the props of the Modal component with the onFinished being optional. +export type ComponentProps = Omit, "onFinished"> & + Partial, "onFinished">>; + +export interface IModal { elem: React.ReactNode; className?: string; beforeClosePromise?: Promise; closeReason?: string; onBeforeClose?(reason?: string): Promise; - onFinished?(...args: T): void; - close(...args: T): void; + onFinished: ComponentProps["onFinished"]; + close(...args: Parameters["onFinished"]>): void; hidden?: boolean; } -export interface IHandle { - finished: Promise; - close(...args: T): void; +export interface IHandle { + finished: Promise["onFinished"]>>; + close(...args: Parameters["onFinished"]>): void; } -interface IProps { - onFinished?(...args: T): void; - // TODO improve typing here once all Modals are TS and we can exhaustively check the props - [key: string]: any; +interface IOptions { + onBeforeClose?: IModal["onBeforeClose"]; } -interface IOptions { - onBeforeClose?: IModal["onBeforeClose"]; -} - -type ParametersWithoutFirst any> = T extends (a: any, ...args: infer P) => any ? P : never; - export enum ModalManagerEvent { Opened = "opened", } @@ -111,18 +112,30 @@ export class ModalManager extends TypedEventEmitter 0; } - public createDialog( - Element: React.ComponentType, - ...rest: ParametersWithoutFirst - ): IHandle { - return this.createDialogAsync(Promise.resolve(Element), ...rest); + public createDialog( + Element: C, + props?: ComponentProps, + className?: string, + isPriorityModal = false, + isStaticModal = false, + options: IOptions = {}, + ): IHandle { + return this.createDialogAsync( + Promise.resolve(Element), + props, + className, + isPriorityModal, + isStaticModal, + options, + ); } - public appendDialog( + public appendDialog( Element: React.ComponentType, - ...rest: ParametersWithoutFirst - ): IHandle { - return this.appendDialogAsync(Promise.resolve(Element), ...rest); + props?: ComponentProps, + className?: string, + ): IHandle { + return this.appendDialogAsync(Promise.resolve(Element), props, className); } public closeCurrentModal(reason: string): void { @@ -134,15 +147,15 @@ export class ModalManager extends TypedEventEmitter( + private buildModal( prom: Promise, - props?: IProps, + props?: ComponentProps, className?: string, - options?: IOptions, + options?: IOptions, ): { - modal: IModal; - closeDialog: IHandle["close"]; - onFinishedProm: IHandle["finished"]; + modal: IModal; + closeDialog: IHandle["close"]; + onFinishedProm: IHandle["finished"]; } { const modal = { onFinished: props?.onFinished, @@ -151,10 +164,10 @@ export class ModalManager extends TypedEventEmitter; + } as IModal; // never call this from onFinished() otherwise it will loop - const [closeDialog, onFinishedProm] = this.getCloseFn(modal, props); + const [closeDialog, onFinishedProm] = this.getCloseFn(modal, props); // don't attempt to reuse the same AsyncWrapper for different dialogs, // otherwise we'll get confused. @@ -168,13 +181,13 @@ export class ModalManager extends TypedEventEmitter( - modal: IModal, - props?: IProps, - ): [IHandle["close"], IHandle["finished"]] { - const deferred = defer(); + private getCloseFn( + modal: IModal, + props?: ComponentProps, + ): [IHandle["close"], IHandle["finished"]] { + const deferred = defer["onFinished"]>>(); return [ - async (...args: T): Promise => { + async (...args: Parameters["onFinished"]>): Promise => { if (modal.beforeClosePromise) { await modal.beforeClosePromise; } else if (modal.onBeforeClose) { @@ -249,16 +262,16 @@ export class ModalManager extends TypedEventEmitter( - prom: Promise, - props?: IProps, + public createDialogAsync( + prom: Promise, + props?: ComponentProps, className?: string, isPriorityModal = false, isStaticModal = false, - options: IOptions = {}, - ): IHandle { + options: IOptions = {}, + ): IHandle { const beforeModal = this.getCurrentModal(); - const { modal, closeDialog, onFinishedProm } = this.buildModal(prom, props, className, options); + const { modal, closeDialog, onFinishedProm } = this.buildModal(prom, props, className, options); if (isPriorityModal) { // XXX: This is destructive this.priorityModal = modal; @@ -278,13 +291,13 @@ export class ModalManager extends TypedEventEmitter( + private appendDialogAsync( prom: Promise, - props?: IProps, + props?: ComponentProps, className?: string, - ): IHandle { + ): IHandle { const beforeModal = this.getCurrentModal(); - const { modal, closeDialog, onFinishedProm } = this.buildModal(prom, props, className, {}); + const { modal, closeDialog, onFinishedProm } = this.buildModal(prom, props, className, {}); this.modals.push(modal); diff --git a/src/RoomInvite.tsx b/src/RoomInvite.tsx index c92ebcc55e..0d2b64f244 100644 --- a/src/RoomInvite.tsx +++ b/src/RoomInvite.tsx @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -import React from "react"; +import React, { ComponentProps } from "react"; import { Room } from "matrix-js-sdk/src/models/room"; import { MatrixEvent } from "matrix-js-sdk/src/models/event"; import { User } from "matrix-js-sdk/src/models/user"; @@ -29,7 +29,7 @@ import InviteDialog from "./components/views/dialogs/InviteDialog"; import BaseAvatar from "./components/views/avatars/BaseAvatar"; import { mediaFromMxc } from "./customisations/Media"; import ErrorDialog from "./components/views/dialogs/ErrorDialog"; -import { KIND_DM, KIND_INVITE } from "./components/views/dialogs/InviteDialogTypes"; +import { InviteKind } from "./components/views/dialogs/InviteDialogTypes"; import { Member } from "./utils/direct-messages"; export interface IInviteResult { @@ -64,7 +64,7 @@ export function showStartChatInviteDialog(initialText = ""): void { // This dialog handles the room creation internally - we don't need to worry about it. Modal.createDialog( InviteDialog, - { kind: KIND_DM, initialText }, + { kind: InviteKind.Dm, initialText }, /*className=*/ "mx_InviteDialog_flexWrapper", /*isPriority=*/ false, /*isStatic=*/ true, @@ -76,10 +76,10 @@ export function showRoomInviteDialog(roomId: string, initialText = ""): void { Modal.createDialog( InviteDialog, { - kind: KIND_INVITE, + kind: InviteKind.Invite, initialText, roomId, - }, + } as Omit, "onFinished">, /*className=*/ "mx_InviteDialog_flexWrapper", /*isPriority=*/ false, /*isStatic=*/ true, diff --git a/src/SecurityManager.ts b/src/SecurityManager.ts index 2a56de8751..563aac09ad 100644 --- a/src/SecurityManager.ts +++ b/src/SecurityManager.ts @@ -22,13 +22,13 @@ import { decodeRecoveryKey } from "matrix-js-sdk/src/crypto/recoverykey"; import { encodeBase64 } from "matrix-js-sdk/src/crypto/olmlib"; import { DeviceTrustLevel } from "matrix-js-sdk/src/crypto/CrossSigning"; import { logger } from "matrix-js-sdk/src/logger"; -import { ComponentType } from "react"; +import type CreateSecretStorageDialog from "./async-components/views/dialogs/security/CreateSecretStorageDialog"; import Modal from "./Modal"; import { MatrixClientPeg } from "./MatrixClientPeg"; import { _t } from "./languageHandler"; import { isSecureBackupRequired } from "./utils/WellKnownUtils"; -import AccessSecretStorageDialog from "./components/views/dialogs/security/AccessSecretStorageDialog"; +import AccessSecretStorageDialog, { KeyParams } from "./components/views/dialogs/security/AccessSecretStorageDialog"; import RestoreKeyBackupDialog from "./components/views/dialogs/security/RestoreKeyBackupDialog"; import SettingsStore from "./settings/SettingsStore"; import SecurityCustomisations from "./customisations/Security"; @@ -83,8 +83,6 @@ async function confirmToDismiss(): Promise { return !sure; } -type KeyParams = { passphrase: string; recoveryKey: string }; - function makeInputToKey(keyInfo: ISecretStorageKeyInfo): (keyParams: KeyParams) => Promise { return async ({ passphrase, recoveryKey }): Promise => { if (passphrase) { @@ -333,7 +331,7 @@ export async function accessSecretStorage(func = async (): Promise => {}, // passphrase creation. const { finished } = Modal.createDialogAsync( import("./async-components/views/dialogs/security/CreateSecretStorageDialog") as unknown as Promise< - ComponentType<{}> + typeof CreateSecretStorageDialog >, { forceReset, diff --git a/src/SlashCommands.tsx b/src/SlashCommands.tsx index 6628b95fc1..b1df460f80 100644 --- a/src/SlashCommands.tsx +++ b/src/SlashCommands.tsx @@ -551,7 +551,7 @@ export const Commands = [ ) { const defaultIdentityServerUrl = getDefaultIdentityServerUrl(); if (defaultIdentityServerUrl) { - const { finished } = Modal.createDialog<[boolean]>(QuestionDialog, { + const { finished } = Modal.createDialog(QuestionDialog, { title: _t("Use an identity server"), description: (

diff --git a/src/Terms.ts b/src/Terms.ts index f66f543887..ad4386d7aa 100644 --- a/src/Terms.ts +++ b/src/Terms.ts @@ -191,7 +191,7 @@ export async function dialogTermsInteractionCallback( ): Promise { logger.log("Terms that need agreement", policiesAndServicePairs); - const { finished } = Modal.createDialog<[boolean, string[]]>( + const { finished } = Modal.createDialog( TermsDialog, { policiesAndServicePairs, diff --git a/src/async-components/views/dialogs/eventindex/DisableEventIndexDialog.tsx b/src/async-components/views/dialogs/eventindex/DisableEventIndexDialog.tsx index 300e67df82..a1e9485e02 100644 --- a/src/async-components/views/dialogs/eventindex/DisableEventIndexDialog.tsx +++ b/src/async-components/views/dialogs/eventindex/DisableEventIndexDialog.tsx @@ -27,7 +27,7 @@ import { Action } from "../../../../dispatcher/actions"; import { SettingLevel } from "../../../../settings/SettingLevel"; interface IProps { - onFinished: (success: boolean) => void; + onFinished: (success?: boolean) => void; } interface IState { diff --git a/src/async-components/views/dialogs/eventindex/ManageEventIndexDialog.tsx b/src/async-components/views/dialogs/eventindex/ManageEventIndexDialog.tsx index 5513165e69..1022bf5ff2 100644 --- a/src/async-components/views/dialogs/eventindex/ManageEventIndexDialog.tsx +++ b/src/async-components/views/dialogs/eventindex/ManageEventIndexDialog.tsx @@ -27,10 +27,11 @@ import { SettingLevel } from "../../../../settings/SettingLevel"; import Field from "../../../../components/views/elements/Field"; import BaseDialog from "../../../../components/views/dialogs/BaseDialog"; import DialogButtons from "../../../../components/views/elements/DialogButtons"; -import { IDialogProps } from "../../../../components/views/dialogs/IDialogProps"; import { IIndexStats } from "../../../../indexing/BaseEventIndexManager"; -interface IProps extends IDialogProps {} +interface IProps { + onFinished(): void; +} interface IState { eventIndexSize: number; diff --git a/src/async-components/views/dialogs/security/CreateKeyBackupDialog.tsx b/src/async-components/views/dialogs/security/CreateKeyBackupDialog.tsx index a9327f2a46..1669dd3c14 100644 --- a/src/async-components/views/dialogs/security/CreateKeyBackupDialog.tsx +++ b/src/async-components/views/dialogs/security/CreateKeyBackupDialog.tsx @@ -26,7 +26,6 @@ import { accessSecretStorage } from "../../../../SecurityManager"; import AccessibleButton from "../../../../components/views/elements/AccessibleButton"; import { copyNode } from "../../../../utils/strings"; import PassphraseField from "../../../../components/views/auth/PassphraseField"; -import { IDialogProps } from "../../../../components/views/dialogs/IDialogProps"; import Field from "../../../../components/views/elements/Field"; import Spinner from "../../../../components/views/elements/Spinner"; import BaseDialog from "../../../../components/views/dialogs/BaseDialog"; @@ -45,7 +44,9 @@ enum Phase { const PASSWORD_MIN_SCORE = 4; // So secure, many characters, much complex, wow, etc, etc. -interface IProps extends IDialogProps {} +interface IProps { + onFinished(done?: boolean): void; +} interface IState { secureSecretStorage: boolean; diff --git a/src/async-components/views/dialogs/security/CreateSecretStorageDialog.tsx b/src/async-components/views/dialogs/security/CreateSecretStorageDialog.tsx index 1f2e7fd6b9..36ebf3ad8a 100644 --- a/src/async-components/views/dialogs/security/CreateSecretStorageDialog.tsx +++ b/src/async-components/views/dialogs/security/CreateSecretStorageDialog.tsx @@ -43,7 +43,6 @@ import { SecureBackupSetupMethod, } from "../../../../utils/WellKnownUtils"; import SecurityCustomisations from "../../../../customisations/Security"; -import { IDialogProps } from "../../../../components/views/dialogs/IDialogProps"; import Field from "../../../../components/views/elements/Field"; import BaseDialog from "../../../../components/views/dialogs/BaseDialog"; import Spinner from "../../../../components/views/elements/Spinner"; @@ -67,10 +66,11 @@ enum Phase { const PASSWORD_MIN_SCORE = 4; // So secure, many characters, much complex, wow, etc, etc. -interface IProps extends IDialogProps { - hasCancel: boolean; - accountPassword: string; - forceReset: boolean; +interface IProps { + hasCancel?: boolean; + accountPassword?: string; + forceReset?: boolean; + onFinished(ok?: boolean): void; } interface IState { diff --git a/src/async-components/views/dialogs/security/ExportE2eKeysDialog.tsx b/src/async-components/views/dialogs/security/ExportE2eKeysDialog.tsx index 26c49b40fc..518f985dd8 100644 --- a/src/async-components/views/dialogs/security/ExportE2eKeysDialog.tsx +++ b/src/async-components/views/dialogs/security/ExportE2eKeysDialog.tsx @@ -22,7 +22,6 @@ import { logger } from "matrix-js-sdk/src/logger"; import { _t } from "../../../../languageHandler"; import * as MegolmExportEncryption from "../../../../utils/MegolmExportEncryption"; -import { IDialogProps } from "../../../../components/views/dialogs/IDialogProps"; import BaseDialog from "../../../../components/views/dialogs/BaseDialog"; import Field from "../../../../components/views/elements/Field"; import { KeysStartingWith } from "../../../../@types/common"; @@ -32,8 +31,9 @@ enum Phase { Exporting = "exporting", } -interface IProps extends IDialogProps { +interface IProps { matrixClient: MatrixClient; + onFinished(doExport?: boolean): void; } interface IState { diff --git a/src/async-components/views/dialogs/security/ImportE2eKeysDialog.tsx b/src/async-components/views/dialogs/security/ImportE2eKeysDialog.tsx index 5d3864e811..f6d488716c 100644 --- a/src/async-components/views/dialogs/security/ImportE2eKeysDialog.tsx +++ b/src/async-components/views/dialogs/security/ImportE2eKeysDialog.tsx @@ -21,7 +21,6 @@ import { logger } from "matrix-js-sdk/src/logger"; import * as MegolmExportEncryption from "../../../../utils/MegolmExportEncryption"; import { _t } from "../../../../languageHandler"; -import { IDialogProps } from "../../../../components/views/dialogs/IDialogProps"; import BaseDialog from "../../../../components/views/dialogs/BaseDialog"; import Field from "../../../../components/views/elements/Field"; @@ -42,8 +41,9 @@ enum Phase { Importing = "importing", } -interface IProps extends IDialogProps { +interface IProps { matrixClient: MatrixClient; + onFinished(imported?: boolean): void; } interface IState { diff --git a/src/async-components/views/dialogs/security/NewRecoveryMethodDialog.tsx b/src/async-components/views/dialogs/security/NewRecoveryMethodDialog.tsx index fbb823dd8e..7268f62e1c 100644 --- a/src/async-components/views/dialogs/security/NewRecoveryMethodDialog.tsx +++ b/src/async-components/views/dialogs/security/NewRecoveryMethodDialog.tsx @@ -24,12 +24,12 @@ import { _t } from "../../../../languageHandler"; import Modal from "../../../../Modal"; import RestoreKeyBackupDialog from "../../../../components/views/dialogs/security/RestoreKeyBackupDialog"; import { Action } from "../../../../dispatcher/actions"; -import { IDialogProps } from "../../../../components/views/dialogs/IDialogProps"; import DialogButtons from "../../../../components/views/elements/DialogButtons"; import BaseDialog from "../../../../components/views/dialogs/BaseDialog"; -interface IProps extends IDialogProps { +interface IProps { newVersionInfo: IKeyBackupInfo; + onFinished(): void; } export default class NewRecoveryMethodDialog extends React.PureComponent { diff --git a/src/async-components/views/dialogs/security/RecoveryMethodRemovedDialog.tsx b/src/async-components/views/dialogs/security/RecoveryMethodRemovedDialog.tsx index f795a3e534..cb318e61fb 100644 --- a/src/async-components/views/dialogs/security/RecoveryMethodRemovedDialog.tsx +++ b/src/async-components/views/dialogs/security/RecoveryMethodRemovedDialog.tsx @@ -21,11 +21,12 @@ import dis from "../../../../dispatcher/dispatcher"; import { _t } from "../../../../languageHandler"; import Modal from "../../../../Modal"; import { Action } from "../../../../dispatcher/actions"; -import { IDialogProps } from "../../../../components/views/dialogs/IDialogProps"; import BaseDialog from "../../../../components/views/dialogs/BaseDialog"; import DialogButtons from "../../../../components/views/elements/DialogButtons"; -interface IProps extends IDialogProps {} +interface IProps { + onFinished(): void; +} export default class RecoveryMethodRemovedDialog extends React.PureComponent { private onGoToSettingsClick = (): void => { diff --git a/src/components/structures/MatrixChat.tsx b/src/components/structures/MatrixChat.tsx index 43c82b61ff..18c61240c9 100644 --- a/src/components/structures/MatrixChat.tsx +++ b/src/components/structures/MatrixChat.tsx @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -import React, { ComponentType, createRef } from "react"; +import React, { createRef } from "react"; import { ClientEvent, createClient, @@ -38,6 +38,8 @@ import "focus-visible"; // what-input helps improve keyboard accessibility import "what-input"; +import type NewRecoveryMethodDialog from "../../async-components/views/dialogs/security/NewRecoveryMethodDialog"; +import type RecoveryMethodRemovedDialog from "../../async-components/views/dialogs/security/RecoveryMethodRemovedDialog"; import PosthogTrackers from "../../PosthogTrackers"; import { DecryptionFailureTracker } from "../../DecryptionFailureTracker"; import { IMatrixClientCreds, MatrixClientPeg } from "../../MatrixClientPeg"; @@ -140,6 +142,7 @@ import RovingSpotlightDialog, { Filter } from "../views/dialogs/spotlight/Spotli import { findDMForUser } from "../../utils/dm/findDMForUser"; import { Linkify } from "../../HtmlUtils"; import { NotificationColor } from "../../stores/notifications/NotificationColor"; +import { UserTab } from "../views/dialogs/UserTab"; // legacy export export { default as Views } from "../../Views"; @@ -705,7 +708,7 @@ export default class MatrixChat extends React.PureComponent { const tabPayload = payload as OpenToTabPayload; Modal.createDialog( UserSettingsDialog, - { initialTabId: tabPayload.initialTabId }, + { initialTabId: tabPayload.initialTabId as UserTab }, /*className=*/ null, /*isPriority=*/ false, /*isStatic=*/ true, @@ -1629,14 +1632,14 @@ export default class MatrixChat extends React.PureComponent { Modal.createDialogAsync( import( "../../async-components/views/dialogs/security/NewRecoveryMethodDialog" - ) as unknown as Promise>, + ) as unknown as Promise, { newVersionInfo }, ); } else { Modal.createDialogAsync( import( "../../async-components/views/dialogs/security/RecoveryMethodRemovedDialog" - ) as unknown as Promise>, + ) as unknown as Promise, ); } }); diff --git a/src/components/structures/ViewSource.tsx b/src/components/structures/ViewSource.tsx index c9191a23a8..f804eb2ce8 100644 --- a/src/components/structures/ViewSource.tsx +++ b/src/components/structures/ViewSource.tsx @@ -23,15 +23,15 @@ import { _t } from "../../languageHandler"; import MatrixClientContext from "../../contexts/MatrixClientContext"; import { canEditContent } from "../../utils/EventUtils"; import { MatrixClientPeg } from "../../MatrixClientPeg"; -import { IDialogProps } from "../views/dialogs/IDialogProps"; import BaseDialog from "../views/dialogs/BaseDialog"; import { DevtoolsContext } from "../views/dialogs/devtools/BaseTool"; import { StateEventEditor } from "../views/dialogs/devtools/RoomState"; import { stringify, TimelineEventEditor } from "../views/dialogs/devtools/Event"; import CopyableText from "../views/elements/CopyableText"; -interface IProps extends IDialogProps { +interface IProps { mxEvent: MatrixEvent; // the MatrixEvent associated with the context menu + onFinished(): void; } interface IState { diff --git a/src/components/structures/auth/ForgotPassword.tsx b/src/components/structures/auth/ForgotPassword.tsx index c3a6201252..d7a51956f4 100644 --- a/src/components/structures/auth/ForgotPassword.tsx +++ b/src/components/structures/auth/ForgotPassword.tsx @@ -368,7 +368,7 @@ export default class ForgotPassword extends React.Component { } public async renderConfirmLogoutDevicesDialog(): Promise { - const { finished } = Modal.createDialog<[boolean]>(QuestionDialog, { + const { finished } = Modal.createDialog(QuestionDialog, { title: _t("Warning!"), description: (

diff --git a/src/components/structures/auth/forgot-password/VerifyEmailModal.tsx b/src/components/structures/auth/forgot-password/VerifyEmailModal.tsx index d1de4ba4b3..72792a1999 100644 --- a/src/components/structures/auth/forgot-password/VerifyEmailModal.tsx +++ b/src/components/structures/auth/forgot-password/VerifyEmailModal.tsx @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -import React from "react"; +import React, { ReactNode } from "react"; import { _t } from "../../../../languageHandler"; import AccessibleButton from "../../../views/elements/AccessibleButton"; @@ -26,7 +26,8 @@ import { ErrorMessage } from "../../ErrorMessage"; interface Props { email: string; - errorText: string | null; + errorText: ReactNode | null; + onFinished(): void; // This modal is weird in that the way you close it signals intent onCloseClick: () => void; onReEnterEmailClick: () => void; onResendClick: () => Promise; diff --git a/src/components/views/beacon/BeaconViewDialog.tsx b/src/components/views/beacon/BeaconViewDialog.tsx index 6c91cd1f39..b164168a2d 100644 --- a/src/components/views/beacon/BeaconViewDialog.tsx +++ b/src/components/views/beacon/BeaconViewDialog.tsx @@ -23,7 +23,6 @@ import { Icon as LiveLocationIcon } from "../../../../res/img/location/live-loca import { useLiveBeacons } from "../../../utils/beacon/useLiveBeacons"; import MatrixClientContext from "../../../contexts/MatrixClientContext"; import BaseDialog from "../dialogs/BaseDialog"; -import { IDialogProps } from "../dialogs/IDialogProps"; import Map from "../location/Map"; import ZoomButtons from "../location/ZoomButtons"; import BeaconMarker from "./BeaconMarker"; @@ -38,11 +37,12 @@ import MapFallback from "../location/MapFallback"; import { MapError } from "../location/MapError"; import { LocationShareError } from "../../../utils/location"; -interface IProps extends IDialogProps { +interface IProps { roomId: Room["roomId"]; matrixClient: MatrixClient; // open the map centered on this beacon's location initialFocusedBeacon?: Beacon; + onFinished(): void; } // track the 'focused time' as ts diff --git a/src/components/views/dialogs/AppDownloadDialog.tsx b/src/components/views/dialogs/AppDownloadDialog.tsx index 46b7e455c2..dae321e683 100644 --- a/src/components/views/dialogs/AppDownloadDialog.tsx +++ b/src/components/views/dialogs/AppDownloadDialog.tsx @@ -25,13 +25,16 @@ import AccessibleButton from "../elements/AccessibleButton"; import QRCode from "../elements/QRCode"; import Heading from "../typography/Heading"; import BaseDialog from "./BaseDialog"; -import { IDialogProps } from "./IDialogProps"; const fallbackAppStore = "https://apps.apple.com/app/vector/id1083446067"; const fallbackGooglePlay = "https://play.google.com/store/apps/details?id=im.vector.app"; const fallbackFDroid = "https://f-droid.org/repository/browse/?fdid=im.vector.app"; -export const AppDownloadDialog: FC = ({ onFinished }: IDialogProps) => { +interface Props { + onFinished(): void; +} + +export const AppDownloadDialog: FC = ({ onFinished }) => { const brand = SdkConfig.get("brand"); const desktopBuilds = SdkConfig.getObject("desktop_builds"); const mobileBuilds = SdkConfig.getObject("mobile_builds"); diff --git a/src/components/views/dialogs/BaseDialog.tsx b/src/components/views/dialogs/BaseDialog.tsx index 2feb7aac33..0def4f1943 100644 --- a/src/components/views/dialogs/BaseDialog.tsx +++ b/src/components/views/dialogs/BaseDialog.tsx @@ -21,17 +21,16 @@ import FocusLock from "react-focus-lock"; import classNames from "classnames"; import { MatrixClient } from "matrix-js-sdk/src/client"; -import AccessibleButton, { ButtonEvent } from "../elements/AccessibleButton"; +import AccessibleButton from "../elements/AccessibleButton"; import { MatrixClientPeg } from "../../../MatrixClientPeg"; import { _t } from "../../../languageHandler"; import MatrixClientContext from "../../../contexts/MatrixClientContext"; import Heading from "../typography/Heading"; -import { IDialogProps } from "./IDialogProps"; import { PosthogScreenTracker, ScreenName } from "../../../PosthogTrackers"; import { getKeyBindingsManager } from "../../../KeyBindingsManager"; import { KeyBindingAction } from "../../../accessibility/KeyboardShortcuts"; -interface IProps extends IDialogProps { +interface IProps { // Whether the dialog should have a 'close' button that will // cause the dialog to be cancelled. This should only be set // to false if there is nothing the app can sensibly do if the @@ -75,6 +74,7 @@ interface IProps extends IDialogProps { // optional Posthog ScreenName to supply during the lifetime of this dialog "screenName"?: ScreenName; + onFinished(): void; } /* @@ -86,7 +86,7 @@ interface IProps extends IDialogProps { export default class BaseDialog extends React.Component { private matrixClient: MatrixClient; - public static defaultProps = { + public static defaultProps: Partial = { hasCancel: true, fixedWidth: true, }; @@ -98,9 +98,7 @@ export default class BaseDialog extends React.Component { } private onKeyDown = (e: KeyboardEvent | React.KeyboardEvent): void => { - if (this.props.onKeyDown) { - this.props.onKeyDown(e); - } + this.props.onKeyDown?.(e); const action = getKeyBindingsManager().getAccessibilityAction(e); switch (action) { @@ -109,13 +107,13 @@ export default class BaseDialog extends React.Component { e.stopPropagation(); e.preventDefault(); - this.props.onFinished(false); + this.props.onFinished(); break; } }; - private onCancelClick = (e: ButtonEvent): void => { - this.props.onFinished(false); + private onCancelClick = (): void => { + this.props.onFinished(); }; public render(): React.ReactNode { diff --git a/src/components/views/dialogs/BetaFeedbackDialog.tsx b/src/components/views/dialogs/BetaFeedbackDialog.tsx index 86298fb970..8223fde929 100644 --- a/src/components/views/dialogs/BetaFeedbackDialog.tsx +++ b/src/components/views/dialogs/BetaFeedbackDialog.tsx @@ -17,7 +17,6 @@ limitations under the License. import React from "react"; import { _t } from "../../../languageHandler"; -import { IDialogProps } from "./IDialogProps"; import SettingsStore from "../../../settings/SettingsStore"; import AccessibleButton from "../elements/AccessibleButton"; import defaultDispatcher from "../../../dispatcher/dispatcher"; @@ -27,8 +26,9 @@ import GenericFeatureFeedbackDialog from "./GenericFeatureFeedbackDialog"; // XXX: Keep this around for re-use in future Betas -interface IProps extends IDialogProps { +interface IProps { featureId: string; + onFinished(sendFeedback?: boolean): void; } const BetaFeedbackDialog: React.FC = ({ featureId, onFinished }) => { @@ -49,7 +49,7 @@ const BetaFeedbackDialog: React.FC = ({ featureId, onFinished }) => { { - onFinished(false); + onFinished(); defaultDispatcher.dispatch({ action: Action.ViewUserSettings, initialTabId: UserTab.Labs, diff --git a/src/components/views/dialogs/BulkRedactDialog.tsx b/src/components/views/dialogs/BulkRedactDialog.tsx index e1f9a11cd2..ba12f21039 100644 --- a/src/components/views/dialogs/BulkRedactDialog.tsx +++ b/src/components/views/dialogs/BulkRedactDialog.tsx @@ -26,19 +26,19 @@ import { MatrixEvent } from "matrix-js-sdk/src/models/event"; import { _t } from "../../../languageHandler"; import dis from "../../../dispatcher/dispatcher"; import { Action } from "../../../dispatcher/actions"; -import { IDialogProps } from "./IDialogProps"; import BaseDialog from "../dialogs/BaseDialog"; import InfoDialog from "../dialogs/InfoDialog"; import DialogButtons from "../elements/DialogButtons"; import StyledCheckbox from "../elements/StyledCheckbox"; -interface IBulkRedactDialogProps extends IDialogProps { +interface Props { matrixClient: MatrixClient; room: Room; member: RoomMember; + onFinished(redact?: boolean): void; } -const BulkRedactDialog: React.FC = (props) => { +const BulkRedactDialog: React.FC = (props) => { const { matrixClient: cli, room, member, onFinished } = props; const [keepStateEvents, setKeepStateEvents] = useState(true); diff --git a/src/components/views/dialogs/ConfirmAndWaitRedactDialog.tsx b/src/components/views/dialogs/ConfirmAndWaitRedactDialog.tsx index 60e18eb263..250b90e61d 100644 --- a/src/components/views/dialogs/ConfirmAndWaitRedactDialog.tsx +++ b/src/components/views/dialogs/ConfirmAndWaitRedactDialog.tsx @@ -24,7 +24,7 @@ import Spinner from "../elements/Spinner"; interface IProps { redact: () => Promise; - onFinished: (success: boolean) => void; + onFinished: (success?: boolean) => void; } interface IState { diff --git a/src/components/views/dialogs/ConfirmUserActionDialog.tsx b/src/components/views/dialogs/ConfirmUserActionDialog.tsx index 0ee73f871a..2fa684eda8 100644 --- a/src/components/views/dialogs/ConfirmUserActionDialog.tsx +++ b/src/components/views/dialogs/ConfirmUserActionDialog.tsx @@ -39,7 +39,7 @@ interface IProps { children?: ReactNode; className?: string; roomId?: string; - onFinished: (success: boolean, reason?: string) => void; + onFinished: (success?: boolean, reason?: string) => void; } interface IState { @@ -55,7 +55,7 @@ interface IState { * Also tweaks the style for 'dangerous' actions (albeit only with colour) */ export default class ConfirmUserActionDialog extends React.Component { - public static defaultProps = { + public static defaultProps: Partial = { danger: false, askReason: false, }; diff --git a/src/components/views/dialogs/ConfirmWipeDeviceDialog.tsx b/src/components/views/dialogs/ConfirmWipeDeviceDialog.tsx index 0f530a4fd7..e9ab1bc3cc 100644 --- a/src/components/views/dialogs/ConfirmWipeDeviceDialog.tsx +++ b/src/components/views/dialogs/ConfirmWipeDeviceDialog.tsx @@ -21,7 +21,7 @@ import BaseDialog from "./BaseDialog"; import DialogButtons from "../elements/DialogButtons"; interface IProps { - onFinished: (success: boolean) => void; + onFinished: (success?: boolean) => void; } export default class ConfirmWipeDeviceDialog extends React.Component { diff --git a/src/components/views/dialogs/CreateRoomDialog.tsx b/src/components/views/dialogs/CreateRoomDialog.tsx index 915ca9f249..b15fb800d2 100644 --- a/src/components/views/dialogs/CreateRoomDialog.tsx +++ b/src/components/views/dialogs/CreateRoomDialog.tsx @@ -41,7 +41,7 @@ interface IProps { defaultName?: string; parentSpace?: Room; defaultEncrypted?: boolean; - onFinished(proceed: boolean, opts?: IOpts): void; + onFinished(proceed?: boolean, opts?: IOpts): void; } interface IState { diff --git a/src/components/views/dialogs/CryptoStoreTooNewDialog.tsx b/src/components/views/dialogs/CryptoStoreTooNewDialog.tsx index 59b697621f..fe9f04af18 100644 --- a/src/components/views/dialogs/CryptoStoreTooNewDialog.tsx +++ b/src/components/views/dialogs/CryptoStoreTooNewDialog.tsx @@ -24,9 +24,10 @@ import Modal from "../../../Modal"; import BaseDialog from "./BaseDialog"; import DialogButtons from "../elements/DialogButtons"; import QuestionDialog from "./QuestionDialog"; -import { IDialogProps } from "./IDialogProps"; -interface IProps extends IDialogProps {} +interface IProps { + onFinished(logout?: boolean): void; +} const CryptoStoreTooNewDialog: React.FC = (props: IProps) => { const brand = SdkConfig.get().brand; @@ -72,7 +73,7 @@ const CryptoStoreTooNewDialog: React.FC = (props: IProps) => { props.onFinished(false)} > diff --git a/src/components/views/dialogs/DeactivateAccountDialog.tsx b/src/components/views/dialogs/DeactivateAccountDialog.tsx index bdc1d54aba..5c1269033d 100644 --- a/src/components/views/dialogs/DeactivateAccountDialog.tsx +++ b/src/components/views/dialogs/DeactivateAccountDialog.tsx @@ -39,7 +39,7 @@ type DialogAesthetics = Partial<{ }>; interface IProps { - onFinished: (success: boolean) => void; + onFinished: (success?: boolean) => void; } interface IState { diff --git a/src/components/views/dialogs/DevtoolsDialog.tsx b/src/components/views/dialogs/DevtoolsDialog.tsx index 2b74667ba5..3fd45c17a1 100644 --- a/src/components/views/dialogs/DevtoolsDialog.tsx +++ b/src/components/views/dialogs/DevtoolsDialog.tsx @@ -65,7 +65,7 @@ const Tools: Record = { interface IProps { roomId: string; - onFinished(finished: boolean): void; + onFinished(finished?: boolean): void; } type ToolInfo = [label: string, tool: Tool]; diff --git a/src/components/views/dialogs/EndPollDialog.tsx b/src/components/views/dialogs/EndPollDialog.tsx index 1951f881bb..0d4a66dfe7 100644 --- a/src/components/views/dialogs/EndPollDialog.tsx +++ b/src/components/views/dialogs/EndPollDialog.tsx @@ -20,17 +20,16 @@ import { MatrixClient } from "matrix-js-sdk/src/client"; import { PollEndEvent } from "matrix-js-sdk/src/extensible_events_v1/PollEndEvent"; import { _t } from "../../../languageHandler"; -import { IDialogProps } from "./IDialogProps"; import QuestionDialog from "./QuestionDialog"; import { findTopAnswer } from "../messages/MPollBody"; import Modal from "../../../Modal"; import ErrorDialog from "./ErrorDialog"; import { GetRelationsForEvent } from "../rooms/EventTile"; -interface IProps extends IDialogProps { +interface IProps { matrixClient: MatrixClient; event: MatrixEvent; - onFinished: (success: boolean) => void; + onFinished: (success?: boolean) => void; getRelationsForEvent?: GetRelationsForEvent; } diff --git a/src/components/views/dialogs/ErrorDialog.tsx b/src/components/views/dialogs/ErrorDialog.tsx index 62ce55cdf3..4a82941767 100644 --- a/src/components/views/dialogs/ErrorDialog.tsx +++ b/src/components/views/dialogs/ErrorDialog.tsx @@ -31,7 +31,7 @@ import { _t } from "../../../languageHandler"; import BaseDialog from "./BaseDialog"; interface IProps { - onFinished: (success: boolean) => void; + onFinished: (success?: boolean) => void; title?: string; description?: React.ReactNode; button?: string; @@ -44,7 +44,7 @@ interface IState { } export default class ErrorDialog extends React.Component { - public static defaultProps = { + public static defaultProps: Partial = { focus: true, }; diff --git a/src/components/views/dialogs/ExportDialog.tsx b/src/components/views/dialogs/ExportDialog.tsx index 406df1fd8e..e7b93c50f4 100644 --- a/src/components/views/dialogs/ExportDialog.tsx +++ b/src/components/views/dialogs/ExportDialog.tsx @@ -19,7 +19,6 @@ import { Room } from "matrix-js-sdk/src/matrix"; import { logger } from "matrix-js-sdk/src/logger"; import { _t } from "../../../languageHandler"; -import { IDialogProps } from "./IDialogProps"; import BaseDialog from "./BaseDialog"; import DialogButtons from "../elements/DialogButtons"; import Field from "../elements/Field"; @@ -44,8 +43,9 @@ import InfoDialog from "./InfoDialog"; import ChatExport from "../../../customisations/ChatExport"; import { validateNumberInRange } from "../../../utils/validate"; -interface IProps extends IDialogProps { +interface IProps { room: Room; + onFinished(doExport?: boolean): void; } interface ExportConfig { diff --git a/src/components/views/dialogs/FeedbackDialog.tsx b/src/components/views/dialogs/FeedbackDialog.tsx index b2f8dcfe00..9996e65d2e 100644 --- a/src/components/views/dialogs/FeedbackDialog.tsx +++ b/src/components/views/dialogs/FeedbackDialog.tsx @@ -24,7 +24,6 @@ import SdkConfig from "../../../SdkConfig"; import Modal from "../../../Modal"; import BugReportDialog from "./BugReportDialog"; import InfoDialog from "./InfoDialog"; -import { IDialogProps } from "./IDialogProps"; import { submitFeedback } from "../../../rageshake/submit-rageshake"; import { useStateToggle } from "../../../hooks/useStateToggle"; import StyledCheckbox from "../elements/StyledCheckbox"; @@ -33,8 +32,9 @@ const existingIssuesUrl = "https://github.com/vector-im/element-web/issues" + "?q=is%3Aopen+is%3Aissue+sort%3Areactions-%2B1-desc"; const newIssueUrl = "https://github.com/vector-im/element-web/issues/new/choose"; -interface IProps extends IDialogProps { +interface IProps { feature?: string; + onFinished(): void; } const FeedbackDialog: React.FC = (props: IProps) => { diff --git a/src/components/views/dialogs/ForwardDialog.tsx b/src/components/views/dialogs/ForwardDialog.tsx index 0bd462c65a..8b84c9380a 100644 --- a/src/components/views/dialogs/ForwardDialog.tsx +++ b/src/components/views/dialogs/ForwardDialog.tsx @@ -29,7 +29,6 @@ import { _t } from "../../../languageHandler"; import dis from "../../../dispatcher/dispatcher"; import { useSettingValue } from "../../../hooks/useSettings"; import { Layout } from "../../../settings/enums/Layout"; -import { IDialogProps } from "./IDialogProps"; import BaseDialog from "./BaseDialog"; import { avatarUrlForUser } from "../../../Avatar"; import EventTile from "../rooms/EventTile"; @@ -55,13 +54,14 @@ import { RoomContextDetails } from "../rooms/RoomContextDetails"; const AVATAR_SIZE = 30; -interface IProps extends IDialogProps { +interface IProps { matrixClient: MatrixClient; // The event to forward event: MatrixEvent; // We need a permalink creator for the source room to pass through to EventTile // in case the event is a reply (even though the user can't get at the link) permalinkCreator: RoomPermalinkCreator; + onFinished(): void; } interface IEntryProps { diff --git a/src/components/views/dialogs/GenericFeatureFeedbackDialog.tsx b/src/components/views/dialogs/GenericFeatureFeedbackDialog.tsx index 2bdc3b5b3f..48e95f0244 100644 --- a/src/components/views/dialogs/GenericFeatureFeedbackDialog.tsx +++ b/src/components/views/dialogs/GenericFeatureFeedbackDialog.tsx @@ -20,17 +20,17 @@ import QuestionDialog from "./QuestionDialog"; import { _t } from "../../../languageHandler"; import Field from "../elements/Field"; import SdkConfig from "../../../SdkConfig"; -import { IDialogProps } from "./IDialogProps"; import { submitFeedback } from "../../../rageshake/submit-rageshake"; import StyledCheckbox from "../elements/StyledCheckbox"; import Modal from "../../../Modal"; import InfoDialog from "./InfoDialog"; -interface IProps extends IDialogProps { +interface IProps { title: string; subheading: string; rageshakeLabel: string; rageshakeData?: Record; + onFinished(sendFeedback?: boolean): void; } const GenericFeatureFeedbackDialog: React.FC = ({ diff --git a/src/components/views/dialogs/IDialogProps.ts b/src/components/views/dialogs/IDialogProps.ts deleted file mode 100644 index b294fdafe1..0000000000 --- a/src/components/views/dialogs/IDialogProps.ts +++ /dev/null @@ -1,19 +0,0 @@ -/* -Copyright 2020 The Matrix.org Foundation C.I.C. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -export interface IDialogProps { - onFinished(...args: any): void; -} diff --git a/src/components/views/dialogs/IncomingSasDialog.tsx b/src/components/views/dialogs/IncomingSasDialog.tsx index a9540ef95c..1edb3d9a53 100644 --- a/src/components/views/dialogs/IncomingSasDialog.tsx +++ b/src/components/views/dialogs/IncomingSasDialog.tsx @@ -29,7 +29,6 @@ import Spinner from "../elements/Spinner"; import VerificationShowSas from "../verification/VerificationShowSas"; import BaseDialog from "./BaseDialog"; import DialogButtons from "../elements/DialogButtons"; -import { IDialogProps } from "./IDialogProps"; const PHASE_START = 0; const PHASE_SHOW_SAS = 1; @@ -37,8 +36,9 @@ const PHASE_WAIT_FOR_PARTNER_TO_CONFIRM = 2; const PHASE_VERIFIED = 3; const PHASE_CANCELLED = 4; -interface IProps extends IDialogProps { +interface IProps { verifier: VerificationBase; + onFinished(verified?: boolean): void; } interface IState { diff --git a/src/components/views/dialogs/InfoDialog.tsx b/src/components/views/dialogs/InfoDialog.tsx index 3e975de6e3..790c77d418 100644 --- a/src/components/views/dialogs/InfoDialog.tsx +++ b/src/components/views/dialogs/InfoDialog.tsx @@ -19,11 +19,10 @@ import React, { ReactNode, KeyboardEvent } from "react"; import classNames from "classnames"; import { _t } from "../../../languageHandler"; -import { IDialogProps } from "./IDialogProps"; import BaseDialog from "./BaseDialog"; import DialogButtons from "../elements/DialogButtons"; -interface IProps extends IDialogProps { +interface IProps { top?: ReactNode; title?: string; description?: ReactNode; @@ -32,10 +31,11 @@ interface IProps extends IDialogProps { hasCloseButton?: boolean; fixedWidth?: boolean; onKeyDown?(event: KeyboardEvent): void; + onFinished(): void; } export default class InfoDialog extends React.Component { - public static defaultProps = { + public static defaultProps: Partial = { title: "", description: "", hasCloseButton: false, diff --git a/src/components/views/dialogs/IntegrationsDisabledDialog.tsx b/src/components/views/dialogs/IntegrationsDisabledDialog.tsx index 1daae2f8a8..f8098f3d41 100644 --- a/src/components/views/dialogs/IntegrationsDisabledDialog.tsx +++ b/src/components/views/dialogs/IntegrationsDisabledDialog.tsx @@ -21,9 +21,10 @@ import dis from "../../../dispatcher/dispatcher"; import { Action } from "../../../dispatcher/actions"; import BaseDialog from "./BaseDialog"; import DialogButtons from "../elements/DialogButtons"; -import { IDialogProps } from "./IDialogProps"; -interface IProps extends IDialogProps {} +interface IProps { + onFinished(): void; +} export default class IntegrationsDisabledDialog extends React.Component { private onAcknowledgeClick = (): void => { diff --git a/src/components/views/dialogs/IntegrationsImpossibleDialog.tsx b/src/components/views/dialogs/IntegrationsImpossibleDialog.tsx index a0b9246ce4..5b158402e8 100644 --- a/src/components/views/dialogs/IntegrationsImpossibleDialog.tsx +++ b/src/components/views/dialogs/IntegrationsImpossibleDialog.tsx @@ -18,11 +18,12 @@ import React from "react"; import { _t } from "../../../languageHandler"; import SdkConfig from "../../../SdkConfig"; -import { IDialogProps } from "./IDialogProps"; import BaseDialog from "./BaseDialog"; import DialogButtons from "../elements/DialogButtons"; -interface IProps extends IDialogProps {} +interface IProps { + onFinished(): void; +} export default class IntegrationsImpossibleDialog extends React.Component { private onAcknowledgeClick = (): void => { diff --git a/src/components/views/dialogs/InteractiveAuthDialog.tsx b/src/components/views/dialogs/InteractiveAuthDialog.tsx index 13e921a4f9..dbc6c5fda7 100644 --- a/src/components/views/dialogs/InteractiveAuthDialog.tsx +++ b/src/components/views/dialogs/InteractiveAuthDialog.tsx @@ -25,7 +25,6 @@ import AccessibleButton from "../elements/AccessibleButton"; import InteractiveAuth, { ERROR_USER_CANCELLED, InteractiveAuthCallback } from "../../structures/InteractiveAuth"; import { SSOAuthEntry } from "../auth/InteractiveAuthEntryComponents"; import BaseDialog from "./BaseDialog"; -import { IDialogProps } from "./IDialogProps"; type DialogAesthetics = Partial<{ [x in AuthType]: { @@ -38,7 +37,7 @@ type DialogAesthetics = Partial<{ }; }>; -export interface InteractiveAuthDialogProps extends IDialogProps { +export interface InteractiveAuthDialogProps { // matrix client to use for UI auth requests matrixClient: MatrixClient; @@ -72,6 +71,8 @@ export interface InteractiveAuthDialogProps extends IDialogProps { // // Default is defined in _getDefaultDialogAesthetics() aestheticsForStagePhases?: DialogAesthetics; + + onFinished(success?: boolean, result?: IAuthData | Error | null): void; } interface IState { @@ -152,14 +153,14 @@ export default class InteractiveAuthDialog extends React.Component diff --git a/src/components/views/dialogs/InviteDialog.tsx b/src/components/views/dialogs/InviteDialog.tsx index 2d1c967472..db88615b24 100644 --- a/src/components/views/dialogs/InviteDialog.tsx +++ b/src/components/views/dialogs/InviteDialog.tsx @@ -70,7 +70,7 @@ import { startDmOnFirstMessage, ThreepidMember, } from "../../../utils/direct-messages"; -import { KIND_CALL_TRANSFER, KIND_DM, KIND_INVITE } from "./InviteDialogTypes"; +import { InviteKind } from "./InviteDialogTypes"; import Modal from "../../../Modal"; import dis from "../../../dispatcher/dispatcher"; @@ -253,32 +253,32 @@ interface BaseProps { // Takes a boolean which is true if a user / users were invited / // a call transfer was initiated or false if the dialog was cancelled // with no action taken. - onFinished: (success: boolean) => void; + onFinished: (success?: boolean) => void; // Initial value to populate the filter with initialText?: string; } interface InviteDMProps extends BaseProps { - // The kind of invite being performed. Assumed to be KIND_DM if not provided. - kind?: typeof KIND_DM; + // The kind of invite being performed. Assumed to be InviteKind.Dm if not provided. + kind?: InviteKind.Dm; } interface InviteRoomProps extends BaseProps { - kind: typeof KIND_INVITE; + kind: InviteKind.Invite; - // The room ID this dialog is for. Only required for KIND_INVITE. + // The room ID this dialog is for. Only required for InviteKind.Invite. roomId: string; } function isRoomInvite(props: Props): props is InviteRoomProps { - return props.kind === KIND_INVITE; + return props.kind === InviteKind.Invite; } interface InviteCallProps extends BaseProps { - kind: typeof KIND_CALL_TRANSFER; + kind: InviteKind.CallTransfer; - // The call to transfer. Only required for KIND_CALL_TRANSFER. + // The call to transfer. Only required for InviteKind.CallTransfer. call: MatrixCall; } @@ -305,8 +305,8 @@ interface IInviteDialogState { } export default class InviteDialog extends React.PureComponent { - public static defaultProps = { - kind: KIND_DM, + public static defaultProps: Partial = { + kind: InviteKind.Dm, initialText: "", }; @@ -318,10 +318,10 @@ export default class InviteDialog extends React.PureComponent => { - if (this.props.kind !== KIND_INVITE) return; + if (this.props.kind !== InviteKind.Invite) return; this.setState({ busy: true }); this.convertFilter(); const targets = this.convertFilter(); @@ -528,7 +528,7 @@ export default class InviteDialog extends React.PureComponent => { - if (this.props.kind !== KIND_CALL_TRANSFER) return; + if (this.props.kind !== InviteKind.CallTransfer) return; if (this.state.currentTabId == TabId.UserDirectory) { this.convertFilter(); const targets = this.convertFilter(); @@ -737,7 +737,7 @@ export default class InviteDialog extends React.PureComponent= 0) { targets.splice(idx, 1); } else { - if (this.props.kind === KIND_CALL_TRANSFER && targets.length > 0) { + if (this.props.kind === InviteKind.CallTransfer && targets.length > 0) { targets = []; } targets.push(member); @@ -869,7 +869,7 @@ export default class InviteDialog extends React.PureComponent (kind === "recents" ? m.lastActive : undefined); let sectionName = kind === "recents" ? _t("Recent Conversations") : _t("Suggestions"); - if (this.props.kind === KIND_INVITE) { + if (this.props.kind === InviteKind.Invite) { sectionName = kind === "recents" ? _t("Recently Direct Messaged") : _t("Suggestions"); } @@ -959,7 +959,7 @@ export default class InviteDialog extends React.PureComponent ( @@ -974,7 +974,9 @@ export default class InviteDialog extends React.PureComponent 0)} + disabled={ + this.state.busy || (this.props.kind == InviteKind.CallTransfer && this.state.targets.length > 0) + } autoComplete="off" placeholder={hasPlaceholder ? _t("Search") : undefined} data-testid="invite-dialog-input" @@ -1085,7 +1087,7 @@ export default class InviteDialog extends React.PureComponent
); - } else if (this.props.kind === KIND_INVITE) { + } else if (this.props.kind === InviteKind.Invite) { const roomId = this.props.roomId; const room = MatrixClientPeg.get()?.getRoom(roomId); const isSpace = room?.isSpaceRoom(); @@ -1235,7 +1237,7 @@ export default class InviteDialog extends React.PureComponent >; source: string; - continuation: () => Promise; + continuation: (opts: { shouldEmit: boolean }) => Promise; + onFinished(): void; } const KeySignatureUploadFailedDialog: React.FC = ({ failures, source, continuation, onFinished }) => { @@ -61,7 +61,7 @@ const KeySignatureUploadFailedDialog: React.FC = ({ failures, source, co }).finally(() => { setCancelled(true); }); - await Promise.race([continuation(), cancel]); + await Promise.race([continuation({ shouldEmit: false }), cancel]); setSuccess(true); } catch (e) { setRetry((r) => r - 1); diff --git a/src/components/views/dialogs/LazyLoadingDisabledDialog.tsx b/src/components/views/dialogs/LazyLoadingDisabledDialog.tsx index cdd54b56bf..e1d5374fb3 100644 --- a/src/components/views/dialogs/LazyLoadingDisabledDialog.tsx +++ b/src/components/views/dialogs/LazyLoadingDisabledDialog.tsx @@ -20,10 +20,10 @@ import React from "react"; import QuestionDialog from "./QuestionDialog"; import { _t } from "../../../languageHandler"; import SdkConfig from "../../../SdkConfig"; -import { IDialogProps } from "./IDialogProps"; -interface IProps extends IDialogProps { +interface IProps { host: string; + onFinished(): void; } const LazyLoadingDisabledDialog: React.FC = (props) => { diff --git a/src/components/views/dialogs/LazyLoadingResyncDialog.tsx b/src/components/views/dialogs/LazyLoadingResyncDialog.tsx index b96b1b4a9e..c5bd2e0227 100644 --- a/src/components/views/dialogs/LazyLoadingResyncDialog.tsx +++ b/src/components/views/dialogs/LazyLoadingResyncDialog.tsx @@ -20,9 +20,10 @@ import React from "react"; import QuestionDialog from "./QuestionDialog"; import { _t } from "../../../languageHandler"; import SdkConfig from "../../../SdkConfig"; -import { IDialogProps } from "./IDialogProps"; -interface IProps extends IDialogProps {} +interface IProps { + onFinished(): void; +} const LazyLoadingResyncDialog: React.FC = (props) => { const brand = SdkConfig.get().brand; diff --git a/src/components/views/dialogs/LogoutDialog.tsx b/src/components/views/dialogs/LogoutDialog.tsx index 84ecc97863..a475fd53f0 100644 --- a/src/components/views/dialogs/LogoutDialog.tsx +++ b/src/components/views/dialogs/LogoutDialog.tsx @@ -15,10 +15,12 @@ See the License for the specific language governing permissions and limitations under the License. */ -import React, { ComponentType } from "react"; +import React from "react"; import { IKeyBackupInfo } from "matrix-js-sdk/src/crypto/keybackup"; import { logger } from "matrix-js-sdk/src/logger"; +import type CreateKeyBackupDialog from "../../../async-components/views/dialogs/security/CreateKeyBackupDialog"; +import type ExportE2eKeysDialog from "../../../async-components/views/dialogs/security/ExportE2eKeysDialog"; import Modal from "../../../Modal"; import dis from "../../../dispatcher/dispatcher"; import { _t } from "../../../languageHandler"; @@ -81,7 +83,7 @@ export default class LogoutDialog extends React.Component { private onExportE2eKeysClicked = (): void => { Modal.createDialogAsync( import("../../../async-components/views/dialogs/security/ExportE2eKeysDialog") as unknown as Promise< - ComponentType<{}> + typeof ExportE2eKeysDialog >, { matrixClient: MatrixClientPeg.get(), @@ -89,7 +91,7 @@ export default class LogoutDialog extends React.Component { ); }; - private onFinished = (confirmed: boolean): void => { + private onFinished = (confirmed?: boolean): void => { if (confirmed) { dis.dispatch({ action: "logout" }); } @@ -112,7 +114,7 @@ export default class LogoutDialog extends React.Component { } else { Modal.createDialogAsync( import("../../../async-components/views/dialogs/security/CreateKeyBackupDialog") as unknown as Promise< - ComponentType<{}> + typeof CreateKeyBackupDialog >, undefined, undefined, diff --git a/src/components/views/dialogs/ManageRestrictedJoinRuleDialog.tsx b/src/components/views/dialogs/ManageRestrictedJoinRuleDialog.tsx index d2ce8358fc..97c43ce891 100644 --- a/src/components/views/dialogs/ManageRestrictedJoinRuleDialog.tsx +++ b/src/components/views/dialogs/ManageRestrictedJoinRuleDialog.tsx @@ -18,7 +18,6 @@ import React, { useMemo, useState } from "react"; import { Room } from "matrix-js-sdk/src/models/room"; import { _t } from "../../../languageHandler"; -import { IDialogProps } from "./IDialogProps"; import BaseDialog from "./BaseDialog"; import SearchBox from "../../structures/SearchBox"; import SpaceStore from "../../../stores/spaces/SpaceStore"; @@ -28,9 +27,10 @@ import AutoHideScrollbar from "../../structures/AutoHideScrollbar"; import StyledCheckbox from "../elements/StyledCheckbox"; import MatrixClientContext from "../../../contexts/MatrixClientContext"; -interface IProps extends IDialogProps { +interface IProps { room: Room; selected?: string[]; + onFinished(rooms?: string[]): void; } const Entry: React.FC<{ diff --git a/src/components/views/dialogs/ManualDeviceKeyVerificationDialog.tsx b/src/components/views/dialogs/ManualDeviceKeyVerificationDialog.tsx index 0819d72d7e..e3bc1dc469 100644 --- a/src/components/views/dialogs/ManualDeviceKeyVerificationDialog.tsx +++ b/src/components/views/dialogs/ManualDeviceKeyVerificationDialog.tsx @@ -25,11 +25,11 @@ import { MatrixClientPeg } from "../../../MatrixClientPeg"; import * as FormattingUtils from "../../../utils/FormattingUtils"; import { _t } from "../../../languageHandler"; import QuestionDialog from "./QuestionDialog"; -import { IDialogProps } from "./IDialogProps"; -interface IProps extends IDialogProps { +interface IProps { userId: string; device: DeviceInfo; + onFinished(confirm?: boolean): void; } export default class ManualDeviceKeyVerificationDialog extends React.Component { diff --git a/src/components/views/dialogs/MessageEditHistoryDialog.tsx b/src/components/views/dialogs/MessageEditHistoryDialog.tsx index 2fcad2fe8c..d558774bfd 100644 --- a/src/components/views/dialogs/MessageEditHistoryDialog.tsx +++ b/src/components/views/dialogs/MessageEditHistoryDialog.tsx @@ -30,10 +30,10 @@ import ScrollPanel from "../../structures/ScrollPanel"; import Spinner from "../elements/Spinner"; import EditHistoryMessage from "../messages/EditHistoryMessage"; import DateSeparator from "../messages/DateSeparator"; -import { IDialogProps } from "./IDialogProps"; -interface IProps extends IDialogProps { +interface IProps { mxEvent: MatrixEvent; + onFinished(): void; } interface IState { diff --git a/src/components/views/dialogs/ModalWidgetDialog.tsx b/src/components/views/dialogs/ModalWidgetDialog.tsx index dde301729c..e62e8b53f8 100644 --- a/src/components/views/dialogs/ModalWidgetDialog.tsx +++ b/src/components/views/dialogs/ModalWidgetDialog.tsx @@ -46,7 +46,7 @@ interface IProps { widgetDefinition: IModalWidgetOpenRequestData; widgetRoomId?: string; sourceWidgetId: string; - onFinished(success: boolean, data?: IModalWidgetReturnData): void; + onFinished(success?: boolean, data?: IModalWidgetReturnData): void; } interface IState { diff --git a/src/components/views/dialogs/ModuleUiDialog.tsx b/src/components/views/dialogs/ModuleUiDialog.tsx index c92c08e66f..cced3907bb 100644 --- a/src/components/views/dialogs/ModuleUiDialog.tsx +++ b/src/components/views/dialogs/ModuleUiDialog.tsx @@ -19,13 +19,13 @@ import { DialogContent, DialogProps } from "@matrix-org/react-sdk-module-api/lib import { logger } from "matrix-js-sdk/src/logger"; import ScrollableBaseModal, { IScrollableBaseState } from "./ScrollableBaseModal"; -import { IDialogProps } from "./IDialogProps"; import { _t } from "../../../languageHandler"; -interface IProps extends IDialogProps { - contentFactory: (props: DialogProps, ref: React.Ref) => React.ReactNode; +interface IProps { + contentFactory: (props: DialogProps, ref: React.Ref) => React.ReactNode; contentProps: DialogProps; title: string; + onFinished(ok?: boolean, model?: Awaited>): void; } interface IState extends IScrollableBaseState { diff --git a/src/components/views/dialogs/QuestionDialog.tsx b/src/components/views/dialogs/QuestionDialog.tsx index 43d62dcb68..866b541206 100644 --- a/src/components/views/dialogs/QuestionDialog.tsx +++ b/src/components/views/dialogs/QuestionDialog.tsx @@ -19,11 +19,10 @@ import React from "react"; import classNames from "classnames"; import { _t } from "../../../languageHandler"; -import { IDialogProps } from "./IDialogProps"; import BaseDialog from "./BaseDialog"; import DialogButtons from "../elements/DialogButtons"; -export interface IQuestionDialogProps extends IDialogProps { +export interface IQuestionDialogProps { title?: string; description?: React.ReactNode; extraButtons?: React.ReactNode; @@ -37,6 +36,7 @@ export interface IQuestionDialogProps extends IDialogProps { className?: string; hasCancelButton?: boolean; cancelButton?: React.ReactNode; + onFinished(ok?: boolean): void; } export default class QuestionDialog extends React.Component { diff --git a/src/components/views/dialogs/RegistrationEmailPromptDialog.tsx b/src/components/views/dialogs/RegistrationEmailPromptDialog.tsx index 1e6c9901ee..ec80e720b3 100644 --- a/src/components/views/dialogs/RegistrationEmailPromptDialog.tsx +++ b/src/components/views/dialogs/RegistrationEmailPromptDialog.tsx @@ -18,13 +18,12 @@ import * as React from "react"; import { SyntheticEvent, useRef, useState } from "react"; import { _t, _td } from "../../../languageHandler"; -import { IDialogProps } from "./IDialogProps"; import Field from "../elements/Field"; import BaseDialog from "./BaseDialog"; import DialogButtons from "../elements/DialogButtons"; import EmailField from "../auth/EmailField"; -interface IProps extends IDialogProps { +interface IProps { onFinished(continued: boolean, email?: string): void; } diff --git a/src/components/views/dialogs/ReportEventDialog.tsx b/src/components/views/dialogs/ReportEventDialog.tsx index f26dcf08ad..6814ce55f3 100644 --- a/src/components/views/dialogs/ReportEventDialog.tsx +++ b/src/components/views/dialogs/ReportEventDialog.tsx @@ -21,7 +21,6 @@ import { logger } from "matrix-js-sdk/src/logger"; import { _t } from "../../../languageHandler"; import { ensureDMExists } from "../../../createRoom"; -import { IDialogProps } from "./IDialogProps"; import { MatrixClientPeg } from "../../../MatrixClientPeg"; import SdkConfig from "../../../SdkConfig"; import Markdown from "../../../Markdown"; @@ -33,8 +32,9 @@ import Field from "../elements/Field"; import Spinner from "../elements/Spinner"; import LabelledCheckbox from "../elements/LabelledCheckbox"; -interface IProps extends IDialogProps { +interface IProps { mxEvent: MatrixEvent; + onFinished(report?: boolean): void; } interface IState { diff --git a/src/components/views/dialogs/RoomSettingsDialog.tsx b/src/components/views/dialogs/RoomSettingsDialog.tsx index d451d4a658..b8e31c1540 100644 --- a/src/components/views/dialogs/RoomSettingsDialog.tsx +++ b/src/components/views/dialogs/RoomSettingsDialog.tsx @@ -45,7 +45,7 @@ export const ROOM_ADVANCED_TAB = "ROOM_ADVANCED_TAB"; interface IProps { roomId: string; - onFinished: (success: boolean) => void; + onFinished: (success?: boolean) => void; initialTabId?: string; } diff --git a/src/components/views/dialogs/RoomUpgradeDialog.tsx b/src/components/views/dialogs/RoomUpgradeDialog.tsx index 86d2493442..9acf8b6ef8 100644 --- a/src/components/views/dialogs/RoomUpgradeDialog.tsx +++ b/src/components/views/dialogs/RoomUpgradeDialog.tsx @@ -20,14 +20,14 @@ import { Room } from "matrix-js-sdk/src/models/room"; import Modal from "../../../Modal"; import { _t } from "../../../languageHandler"; import { upgradeRoom } from "../../../utils/RoomUpgrade"; -import { IDialogProps } from "./IDialogProps"; import BaseDialog from "./BaseDialog"; import ErrorDialog from "./ErrorDialog"; import DialogButtons from "../elements/DialogButtons"; import Spinner from "../elements/Spinner"; -interface IProps extends IDialogProps { +interface IProps { room: Room; + onFinished(upgrade?: boolean): void; } interface IState { diff --git a/src/components/views/dialogs/RoomUpgradeWarningDialog.tsx b/src/components/views/dialogs/RoomUpgradeWarningDialog.tsx index 65652cec43..c0d97aa5ce 100644 --- a/src/components/views/dialogs/RoomUpgradeWarningDialog.tsx +++ b/src/components/views/dialogs/RoomUpgradeWarningDialog.tsx @@ -23,7 +23,6 @@ import SdkConfig from "../../../SdkConfig"; import LabelledToggleSwitch from "../elements/LabelledToggleSwitch"; import { MatrixClientPeg } from "../../../MatrixClientPeg"; import Modal from "../../../Modal"; -import { IDialogProps } from "./IDialogProps"; import BugReportDialog from "./BugReportDialog"; import BaseDialog from "./BaseDialog"; import DialogButtons from "../elements/DialogButtons"; @@ -35,11 +34,12 @@ export interface IFinishedOpts { invite: boolean; } -interface IProps extends IDialogProps { +interface IProps { roomId: string; targetVersion: string; description?: ReactNode; doUpgrade?(opts: IFinishedOpts, fn: (progressText: string, progress: number, total: number) => void): Promise; + onFinished(opts?: IFinishedOpts): void; } interface IState { @@ -70,19 +70,14 @@ export default class RoomUpgradeWarningDialog extends React.Component { + private onContinue = async (): Promise => { const opts = { continue: true, invite: this.isPrivate && this.state.inviteUsersToNewRoom, }; - if (this.props.doUpgrade) { - this.props.doUpgrade(opts, this.onProgressCallback).then(() => { - this.props.onFinished(opts); - }); - } else { - this.props.onFinished(opts); - } + await this.props.doUpgrade?.(opts, this.onProgressCallback); + this.props.onFinished(opts); }; private onCancel = (): void => { diff --git a/src/components/views/dialogs/ScrollableBaseModal.tsx b/src/components/views/dialogs/ScrollableBaseModal.tsx index ca3d8591c3..88c85053aa 100644 --- a/src/components/views/dialogs/ScrollableBaseModal.tsx +++ b/src/components/views/dialogs/ScrollableBaseModal.tsx @@ -19,7 +19,6 @@ import { MatrixClient } from "matrix-js-sdk/src/client"; import FocusLock from "react-focus-lock"; import { MatrixClientPeg } from "../../../MatrixClientPeg"; -import { IDialogProps } from "./IDialogProps"; import MatrixClientContext from "../../../contexts/MatrixClientContext"; import { _t } from "../../../languageHandler"; import AccessibleButton from "../elements/AccessibleButton"; @@ -36,7 +35,7 @@ export interface IScrollableBaseState { * Scrollable dialog base from Compound (Web Components). */ export default abstract class ScrollableBaseModal< - TProps extends IDialogProps, + TProps extends { onFinished?: (...args: any[]) => void }, TState extends IScrollableBaseState, > extends React.PureComponent { protected constructor(props: TProps) { diff --git a/src/components/views/dialogs/ServerOfflineDialog.tsx b/src/components/views/dialogs/ServerOfflineDialog.tsx index 6945edc254..c6a384c7f9 100644 --- a/src/components/views/dialogs/ServerOfflineDialog.tsx +++ b/src/components/views/dialogs/ServerOfflineDialog.tsx @@ -28,9 +28,10 @@ import Spinner from "../elements/Spinner"; import AccessibleButton from "../elements/AccessibleButton"; import { UPDATE_EVENT } from "../../../stores/AsyncStore"; import { MatrixClientPeg } from "../../../MatrixClientPeg"; -import { IDialogProps } from "./IDialogProps"; -interface IProps extends IDialogProps {} +interface IProps { + onFinished(): void; +} export default class ServerOfflineDialog extends React.PureComponent { public componentDidMount(): void { diff --git a/src/components/views/dialogs/SeshatResetDialog.tsx b/src/components/views/dialogs/SeshatResetDialog.tsx index e63a1569a0..badd808ac9 100644 --- a/src/components/views/dialogs/SeshatResetDialog.tsx +++ b/src/components/views/dialogs/SeshatResetDialog.tsx @@ -19,9 +19,12 @@ import React from "react"; import { _t } from "../../../languageHandler"; import BaseDialog from "./BaseDialog"; import DialogButtons from "../elements/DialogButtons"; -import { IDialogProps } from "./IDialogProps"; -export default class SeshatResetDialog extends React.PureComponent { +interface Props { + onFinished(reset?: boolean): void; +} + +export default class SeshatResetDialog extends React.PureComponent { public render(): React.ReactNode { return ( { diff --git a/src/components/views/dialogs/SetEmailDialog.tsx b/src/components/views/dialogs/SetEmailDialog.tsx index 6455517961..ec41240c46 100644 --- a/src/components/views/dialogs/SetEmailDialog.tsx +++ b/src/components/views/dialogs/SetEmailDialog.tsx @@ -27,10 +27,10 @@ import ErrorDialog from "./ErrorDialog"; import QuestionDialog from "./QuestionDialog"; import BaseDialog from "./BaseDialog"; import EditableText from "../elements/EditableText"; -import { IDialogProps } from "./IDialogProps"; -interface IProps extends IDialogProps { +interface IProps { title: string; + onFinished(ok?: boolean): void; } interface IState { diff --git a/src/components/views/dialogs/ShareDialog.tsx b/src/components/views/dialogs/ShareDialog.tsx index 353ad347e0..9fcfee0dc2 100644 --- a/src/components/views/dialogs/ShareDialog.tsx +++ b/src/components/views/dialogs/ShareDialog.tsx @@ -26,7 +26,6 @@ import QRCode from "../elements/QRCode"; import { RoomPermalinkCreator, makeUserPermalink } from "../../../utils/permalinks/Permalinks"; import { selectText } from "../../../utils/strings"; import StyledCheckbox from "../elements/StyledCheckbox"; -import { IDialogProps } from "./IDialogProps"; import SettingsStore from "../../../settings/SettingsStore"; import { UIFeature } from "../../../settings/UIFeature"; import BaseDialog from "./BaseDialog"; @@ -64,9 +63,10 @@ const socials = [ }, ]; -interface IProps extends IDialogProps { +interface IProps { target: Room | User | RoomMember | MatrixEvent; - permalinkCreator: RoomPermalinkCreator; + permalinkCreator?: RoomPermalinkCreator; + onFinished(): void; } interface IState { diff --git a/src/components/views/dialogs/SlashCommandHelpDialog.tsx b/src/components/views/dialogs/SlashCommandHelpDialog.tsx index 1324babbeb..c650e78f41 100644 --- a/src/components/views/dialogs/SlashCommandHelpDialog.tsx +++ b/src/components/views/dialogs/SlashCommandHelpDialog.tsx @@ -18,10 +18,11 @@ import React from "react"; import { _t } from "../../../languageHandler"; import { Command, CommandCategories, Commands } from "../../../SlashCommands"; -import { IDialogProps } from "./IDialogProps"; import InfoDialog from "./InfoDialog"; -interface IProps extends IDialogProps {} +interface IProps { + onFinished(): void; +} const SlashCommandHelpDialog: React.FC = ({ onFinished }) => { const categories: Record = {}; diff --git a/src/components/views/dialogs/SlidingSyncOptionsDialog.tsx b/src/components/views/dialogs/SlidingSyncOptionsDialog.tsx index b74ad67b75..d79bfab73a 100644 --- a/src/components/views/dialogs/SlidingSyncOptionsDialog.tsx +++ b/src/components/views/dialogs/SlidingSyncOptionsDialog.tsx @@ -19,7 +19,6 @@ import { MatrixClient, Method } from "matrix-js-sdk/src/matrix"; import { logger } from "matrix-js-sdk/src/logger"; import { _t } from "../../../languageHandler"; -import { IDialogProps } from "./IDialogProps"; import SettingsStore from "../../../settings/SettingsStore"; import TextInputDialog from "./TextInputDialog"; import withValidation from "../elements/Validation"; @@ -63,7 +62,7 @@ async function proxyHealthCheck(endpoint: string, hsUrl?: string): Promise logger.info("sliding sync proxy is OK"); } -export const SlidingSyncOptionsDialog: React.FC = ({ onFinished }) => { +export const SlidingSyncOptionsDialog: React.FC<{ onFinished(enabled: boolean): void }> = ({ onFinished }) => { const cli = MatrixClientPeg.get(); const currentProxy = SettingsStore.getValue("feature_sliding_sync_proxy_url"); const hasNativeSupport = useAsyncMemo( diff --git a/src/components/views/dialogs/SpacePreferencesDialog.tsx b/src/components/views/dialogs/SpacePreferencesDialog.tsx index 2a32e049f2..a66d0aad66 100644 --- a/src/components/views/dialogs/SpacePreferencesDialog.tsx +++ b/src/components/views/dialogs/SpacePreferencesDialog.tsx @@ -19,7 +19,6 @@ import { Room } from "matrix-js-sdk/src/models/room"; import { _t, _td } from "../../../languageHandler"; import BaseDialog from "../dialogs/BaseDialog"; -import { IDialogProps } from "./IDialogProps"; import TabbedView, { Tab } from "../../structures/TabbedView"; import StyledCheckbox from "../elements/StyledCheckbox"; import { useSettingValue } from "../../../hooks/useSettings"; @@ -28,9 +27,10 @@ import { SettingLevel } from "../../../settings/SettingLevel"; import RoomName from "../elements/RoomName"; import { SpacePreferenceTab } from "../../../dispatcher/payloads/OpenSpacePreferencesPayload"; -interface IProps extends IDialogProps { +interface IProps { space: Room; initialTabId?: SpacePreferenceTab; + onFinished(): void; } const SpacePreferencesAppearanceTab: React.FC> = ({ space }) => { diff --git a/src/components/views/dialogs/SpaceSettingsDialog.tsx b/src/components/views/dialogs/SpaceSettingsDialog.tsx index ef46371dcf..76cb883068 100644 --- a/src/components/views/dialogs/SpaceSettingsDialog.tsx +++ b/src/components/views/dialogs/SpaceSettingsDialog.tsx @@ -19,7 +19,6 @@ import { Room } from "matrix-js-sdk/src/models/room"; import { MatrixClient } from "matrix-js-sdk/src/client"; import { _t, _td } from "../../../languageHandler"; -import { IDialogProps } from "./IDialogProps"; import BaseDialog from "./BaseDialog"; import defaultDispatcher from "../../../dispatcher/dispatcher"; import { useDispatcher } from "../../../hooks/useDispatcher"; @@ -39,15 +38,16 @@ export enum SpaceSettingsTab { Advanced = "SPACE_ADVANCED_TAB", } -interface IProps extends IDialogProps { +interface IProps { matrixClient: MatrixClient; space: Room; + onFinished(): void; } const SpaceSettingsDialog: React.FC = ({ matrixClient: cli, space, onFinished }) => { useDispatcher(defaultDispatcher, (payload) => { if (payload.action === Action.AfterLeaveRoom && payload.room_id === space.roomId) { - onFinished(false); + onFinished(); } }); @@ -57,7 +57,7 @@ const SpaceSettingsDialog: React.FC = ({ matrixClient: cli, space, onFin SpaceSettingsTab.General, _td("General"), "mx_SpaceSettingsDialog_generalIcon", - , + , ), new Tab( SpaceSettingsTab.Visibility, diff --git a/src/components/views/dialogs/StorageEvictedDialog.tsx b/src/components/views/dialogs/StorageEvictedDialog.tsx index 68c5ee72f3..df27c4a31d 100644 --- a/src/components/views/dialogs/StorageEvictedDialog.tsx +++ b/src/components/views/dialogs/StorageEvictedDialog.tsx @@ -22,10 +22,11 @@ import { _t } from "../../../languageHandler"; import BaseDialog from "./BaseDialog"; import DialogButtons from "../elements/DialogButtons"; import BugReportDialog from "./BugReportDialog"; -import { IDialogProps } from "./IDialogProps"; import AccessibleButton from "../elements/AccessibleButton"; -interface IProps extends IDialogProps {} +interface IProps { + onFinished(signOut?: boolean): void; +} export default class StorageEvictedDialog extends React.Component { private sendBugReport = (ev: React.MouseEvent): void => { diff --git a/src/components/views/dialogs/TextInputDialog.tsx b/src/components/views/dialogs/TextInputDialog.tsx index 4dfa3c0c3b..073b2760d6 100644 --- a/src/components/views/dialogs/TextInputDialog.tsx +++ b/src/components/views/dialogs/TextInputDialog.tsx @@ -21,9 +21,8 @@ import { _t, _td } from "../../../languageHandler"; import { IFieldState, IValidationResult } from "../elements/Validation"; import BaseDialog from "./BaseDialog"; import DialogButtons from "../elements/DialogButtons"; -import { IDialogProps } from "./IDialogProps"; -interface IProps extends IDialogProps { +interface IProps { title?: string; description?: React.ReactNode; value?: string; @@ -34,6 +33,7 @@ interface IProps extends IDialogProps { hasCancel?: boolean; validator?: (fieldState: IFieldState) => Promise; // result of withValidation fixedWidth?: boolean; + onFinished(ok?: boolean, text?: string): void; } interface IState { @@ -45,7 +45,7 @@ interface IState { export default class TextInputDialog extends React.Component { private field = createRef(); - public static defaultProps = { + public static defaultProps: Partial = { title: "", value: "", description: "", diff --git a/src/components/views/dialogs/UntrustedDeviceDialog.tsx b/src/components/views/dialogs/UntrustedDeviceDialog.tsx index eac0f8b69a..0d70e3b71f 100644 --- a/src/components/views/dialogs/UntrustedDeviceDialog.tsx +++ b/src/components/views/dialogs/UntrustedDeviceDialog.tsx @@ -22,12 +22,12 @@ import { MatrixClientPeg } from "../../../MatrixClientPeg"; import E2EIcon, { E2EState } from "../rooms/E2EIcon"; import AccessibleButton from "../elements/AccessibleButton"; import BaseDialog from "./BaseDialog"; -import { IDialogProps } from "./IDialogProps"; import { IDevice } from "../right_panel/UserInfo"; -interface IProps extends IDialogProps { +interface IProps { user: User; device: IDevice; + onFinished(mode?: "legacy" | "sas" | false): void; } const UntrustedDeviceDialog: React.FC = ({ device, user, onFinished }) => { diff --git a/src/components/views/dialogs/UploadConfirmDialog.tsx b/src/components/views/dialogs/UploadConfirmDialog.tsx index 025540b9dc..584015e160 100644 --- a/src/components/views/dialogs/UploadConfirmDialog.tsx +++ b/src/components/views/dialogs/UploadConfirmDialog.tsx @@ -26,7 +26,7 @@ import DialogButtons from "../elements/DialogButtons"; interface IProps { file: File; - currentIndex: number; + currentIndex?: number; totalFiles?: number; onFinished: (uploadConfirmed: boolean, uploadAll?: boolean) => void; } @@ -35,7 +35,7 @@ export default class UploadConfirmDialog extends React.Component { private readonly objectUrl: string; private readonly mimeType: string; - public static defaultProps = { + public static defaultProps: Partial = { totalFiles: 1, }; diff --git a/src/components/views/dialogs/UploadFailureDialog.tsx b/src/components/views/dialogs/UploadFailureDialog.tsx index 2dd99e88a5..2bec57dd0d 100644 --- a/src/components/views/dialogs/UploadFailureDialog.tsx +++ b/src/components/views/dialogs/UploadFailureDialog.tsx @@ -21,12 +21,12 @@ import { _t } from "../../../languageHandler"; import ContentMessages from "../../../ContentMessages"; import BaseDialog from "./BaseDialog"; import DialogButtons from "../elements/DialogButtons"; -import { IDialogProps } from "./IDialogProps"; -interface IProps extends IDialogProps { +interface IProps { badFiles: File[]; totalFiles: number; contentMessages: ContentMessages; + onFinished(upload?: boolean): void; } /* diff --git a/src/components/views/dialogs/UserSettingsDialog.tsx b/src/components/views/dialogs/UserSettingsDialog.tsx index 5beae8ccaa..07a795f04d 100644 --- a/src/components/views/dialogs/UserSettingsDialog.tsx +++ b/src/components/views/dialogs/UserSettingsDialog.tsx @@ -32,14 +32,14 @@ import SdkConfig from "../../../SdkConfig"; import MjolnirUserSettingsTab from "../settings/tabs/user/MjolnirUserSettingsTab"; import { UIFeature } from "../../../settings/UIFeature"; import BaseDialog from "./BaseDialog"; -import { IDialogProps } from "./IDialogProps"; import SidebarUserSettingsTab from "../settings/tabs/user/SidebarUserSettingsTab"; import KeyboardUserSettingsTab from "../settings/tabs/user/KeyboardUserSettingsTab"; import SessionManagerTab from "../settings/tabs/user/SessionManagerTab"; import { UserTab } from "./UserTab"; -interface IProps extends IDialogProps { +interface IProps { initialTabId?: UserTab; + onFinished(): void; } interface IState { @@ -202,7 +202,7 @@ export default class UserSettingsDialog extends React.Component UserTab.Help, _td("Help & About"), "mx_UserSettingsDialog_helpIcon", - this.props.onFinished(true)} />, + this.props.onFinished()} />, "UserSettingsHelpAbout", ), ); diff --git a/src/components/views/dialogs/VerificationRequestDialog.tsx b/src/components/views/dialogs/VerificationRequestDialog.tsx index 84a8ac92ea..53ac14f6c0 100644 --- a/src/components/views/dialogs/VerificationRequestDialog.tsx +++ b/src/components/views/dialogs/VerificationRequestDialog.tsx @@ -24,14 +24,14 @@ import BaseDialog from "./BaseDialog"; import EncryptionPanel from "../right_panel/EncryptionPanel"; interface IProps { - verificationRequest: VerificationRequest; - verificationRequestPromise: Promise; + verificationRequest?: VerificationRequest; + verificationRequestPromise?: Promise; onFinished: () => void; - member: User; + member?: User; } interface IState { - verificationRequest: VerificationRequest; + verificationRequest?: VerificationRequest; } export default class VerificationRequestDialog extends React.Component { @@ -40,18 +40,16 @@ export default class VerificationRequestDialog extends React.Component { - this.setState({ verificationRequest: r }); - }); - } + this.props.verificationRequestPromise?.then((r) => { + this.setState({ verificationRequest: r }); + }); } public render(): React.ReactNode { const request = this.state.verificationRequest; - const otherUserId = request && request.otherUserId; - const member = this.props.member || (otherUserId && MatrixClientPeg.get().getUser(otherUserId)); - const title = request && request.isSelfVerification ? _t("Verify other device") : _t("Verification Request"); + const otherUserId = request?.otherUserId; + const member = this.props.member || (otherUserId ? MatrixClientPeg.get().getUser(otherUserId) : null); + const title = request?.isSelfVerification ? _t("Verify other device") : _t("Verification Request"); return ( ; widget: Widget; widgetKind: WidgetKind; // TODO: Refactor into the Widget class + onFinished(result?: { approved: Capability[]; remember: boolean }): void; } type BooleanStates = Partial<{ diff --git a/src/components/views/dialogs/WidgetOpenIDPermissionsDialog.tsx b/src/components/views/dialogs/WidgetOpenIDPermissionsDialog.tsx index f185c5b7e6..52b392d38f 100644 --- a/src/components/views/dialogs/WidgetOpenIDPermissionsDialog.tsx +++ b/src/components/views/dialogs/WidgetOpenIDPermissionsDialog.tsx @@ -22,15 +22,15 @@ import { logger } from "matrix-js-sdk/src/logger"; import { _t } from "../../../languageHandler"; import LabelledToggleSwitch from "../elements/LabelledToggleSwitch"; import { OIDCState } from "../../../stores/widgets/WidgetPermissionStore"; -import { IDialogProps } from "./IDialogProps"; import BaseDialog from "./BaseDialog"; import DialogButtons from "../elements/DialogButtons"; import { SdkContextClass } from "../../../contexts/SDKContext"; -interface IProps extends IDialogProps { +interface IProps { widget: Widget; widgetKind: WidgetKind; inRoomId?: string; + onFinished(allowed?: boolean): void; } interface IState { diff --git a/src/components/views/dialogs/polls/PollHistoryDialog.tsx b/src/components/views/dialogs/polls/PollHistoryDialog.tsx index a1cc273bf9..879ca8f4da 100644 --- a/src/components/views/dialogs/polls/PollHistoryDialog.tsx +++ b/src/components/views/dialogs/polls/PollHistoryDialog.tsx @@ -20,7 +20,6 @@ import { MatrixEvent, Poll, Room } from "matrix-js-sdk/src/matrix"; import { _t } from "../../../../languageHandler"; import BaseDialog from "../BaseDialog"; -import { IDialogProps } from "../IDialogProps"; import { PollHistoryList } from "./PollHistoryList"; import { PollHistoryFilter } from "./types"; import { PollDetailHeader } from "./PollDetailHeader"; @@ -29,10 +28,11 @@ import { RoomPermalinkCreator } from "../../../../utils/permalinks/Permalinks"; import { usePollsWithRelations } from "./usePollHistory"; import { useFetchPastPolls } from "./fetchPastPolls"; -type PollHistoryDialogProps = Pick & { +type PollHistoryDialogProps = { room: Room; matrixClient: MatrixClient; permalinkCreator: RoomPermalinkCreator; + onFinished(): void; }; const sortEventsByLatest = (left: MatrixEvent, right: MatrixEvent): number => right.getTs() - left.getTs(); diff --git a/src/components/views/dialogs/security/AccessSecretStorageDialog.tsx b/src/components/views/dialogs/security/AccessSecretStorageDialog.tsx index 0cf19b4eb2..e6a5c67bfd 100644 --- a/src/components/views/dialogs/security/AccessSecretStorageDialog.tsx +++ b/src/components/views/dialogs/security/AccessSecretStorageDialog.tsx @@ -24,7 +24,6 @@ import { MatrixClientPeg } from "../../../../MatrixClientPeg"; import Field from "../../elements/Field"; import AccessibleButton from "../../elements/AccessibleButton"; import { _t } from "../../../../languageHandler"; -import { IDialogProps } from "../IDialogProps"; import { accessSecretStorage } from "../../../../SecurityManager"; import Modal from "../../../../Modal"; import InteractiveAuthDialog from "../InteractiveAuthDialog"; @@ -40,9 +39,12 @@ const KEY_FILE_MAX_SIZE = 128; // Don't shout at the user that their key is invalid every time they type a key: wait a short time const VALIDATION_THROTTLE_MS = 200; -interface IProps extends IDialogProps { +export type KeyParams = { passphrase?: string; recoveryKey?: string }; + +interface IProps { keyInfo?: ISecretStorageKeyInfo; - checkPrivateKey: (k: { passphrase?: string; recoveryKey?: string }) => boolean; + checkPrivateKey: (k: KeyParams) => Promise; + onFinished(result?: false | KeyParams): void; } interface IState { @@ -246,7 +248,7 @@ export default class AccessSecretStorageDialog extends React.PureComponent void; + onFinished: (success?: boolean) => void; } export default class ConfirmDestroyCrossSigningDialog extends React.Component { diff --git a/src/components/views/dialogs/security/CreateCrossSigningDialog.tsx b/src/components/views/dialogs/security/CreateCrossSigningDialog.tsx index 8413b7cd53..3f44cfe9d6 100644 --- a/src/components/views/dialogs/security/CreateCrossSigningDialog.tsx +++ b/src/components/views/dialogs/security/CreateCrossSigningDialog.tsx @@ -32,7 +32,7 @@ import InteractiveAuthDialog from "../InteractiveAuthDialog"; interface IProps { accountPassword?: string; tokenLogin?: boolean; - onFinished?: (success: boolean) => void; + onFinished?: (success?: boolean) => void; } interface IState { diff --git a/src/components/views/dialogs/security/RestoreKeyBackupDialog.tsx b/src/components/views/dialogs/security/RestoreKeyBackupDialog.tsx index 63b26f9c45..4c740b36d6 100644 --- a/src/components/views/dialogs/security/RestoreKeyBackupDialog.tsx +++ b/src/components/views/dialogs/security/RestoreKeyBackupDialog.tsx @@ -24,7 +24,6 @@ import { logger } from "matrix-js-sdk/src/logger"; import { MatrixClientPeg } from "../../../../MatrixClientPeg"; import { _t } from "../../../../languageHandler"; import { accessSecretStorage } from "../../../../SecurityManager"; -import { IDialogProps } from "../IDialogProps"; import Spinner from "../../elements/Spinner"; import DialogButtons from "../../elements/DialogButtons"; import AccessibleButton from "../../elements/AccessibleButton"; @@ -42,13 +41,14 @@ enum ProgressState { LoadKeys = "load_keys", } -interface IProps extends IDialogProps { +interface IProps { // if false, will close the dialog as soon as the restore completes successfully // default: true showSummary?: boolean; // If specified, gather the key from the user but then call the function with the backup // key rather than actually (necessarily) restoring the backup. keyCallback?: (key: Uint8Array) => void; + onFinished(done?: boolean): void; } interface IState { @@ -77,7 +77,7 @@ interface IState { * Dialog for restoring e2e keys from a backup and the user's recovery key */ export default class RestoreKeyBackupDialog extends React.PureComponent { - public static defaultProps = { + public static defaultProps: Partial = { showSummary: true, }; diff --git a/src/components/views/dialogs/security/SetupEncryptionDialog.tsx b/src/components/views/dialogs/security/SetupEncryptionDialog.tsx index a4cf53b73b..659b9cc9e7 100644 --- a/src/components/views/dialogs/security/SetupEncryptionDialog.tsx +++ b/src/components/views/dialogs/security/SetupEncryptionDialog.tsx @@ -20,7 +20,6 @@ import SetupEncryptionBody from "../../../structures/auth/SetupEncryptionBody"; import BaseDialog from "../BaseDialog"; import { _t } from "../../../../languageHandler"; import { SetupEncryptionStore, Phase } from "../../../../stores/SetupEncryptionStore"; -import { IDialogProps } from "../IDialogProps"; function iconFromPhase(phase: Phase): string { if (phase === Phase.Done) { @@ -30,7 +29,9 @@ function iconFromPhase(phase: Phase): string { } } -interface IProps extends IDialogProps {} +interface IProps { + onFinished(): void; +} interface IState { icon: string; } diff --git a/src/components/views/dialogs/spotlight/SpotlightDialog.tsx b/src/components/views/dialogs/spotlight/SpotlightDialog.tsx index 24a0fb7b52..ab7c5397de 100644 --- a/src/components/views/dialogs/spotlight/SpotlightDialog.tsx +++ b/src/components/views/dialogs/spotlight/SpotlightDialog.tsx @@ -83,7 +83,6 @@ import Spinner from "../../elements/Spinner"; import NotificationBadge from "../../rooms/NotificationBadge"; import BaseDialog from "../BaseDialog"; import FeedbackDialog from "../FeedbackDialog"; -import { IDialogProps } from "../IDialogProps"; import { Option } from "./Option"; import { PublicRoomResultDetails } from "./PublicRoomResultDetails"; import { RoomResultContextMenus } from "./RoomResultContextMenus"; @@ -99,9 +98,10 @@ const MAX_RECENT_SEARCHES = 10; const SECTION_LIMIT = 50; // only show 50 results per section for performance reasons const AVATAR_SIZE = 24; -interface IProps extends IDialogProps { +interface IProps { initialText?: string; initialFilter?: Filter; + onFinished(): void; } function refIsForRecentlyViewed(ref: RefObject): boolean { diff --git a/src/components/views/elements/ImageView.tsx b/src/components/views/elements/ImageView.tsx index 5bc1f0562b..325c54304a 100644 --- a/src/components/views/elements/ImageView.tsx +++ b/src/components/views/elements/ImageView.tsx @@ -33,7 +33,6 @@ import dis from "../../../dispatcher/dispatcher"; import { Action } from "../../../dispatcher/actions"; import { RoomPermalinkCreator } from "../../../utils/permalinks/Permalinks"; import { normalizeWheelEvent } from "../../../utils/Mouse"; -import { IDialogProps } from "../dialogs/IDialogProps"; import UIStore from "../../../stores/UIStore"; import { ViewRoomPayload } from "../../../dispatcher/payloads/ViewRoomPayload"; import { KeyBindingAction } from "../../../accessibility/KeyboardShortcuts"; @@ -56,7 +55,7 @@ const getPanelHeight = (): number => { return parseInt(value.slice(0, value.length - 2)); }; -interface IProps extends IDialogProps { +interface IProps { src: string; // the source of the image being displayed name?: string; // the main title ('name') for the image link?: string; // the link (if any) applied to the name of the image @@ -77,6 +76,7 @@ interface IProps extends IDialogProps { width: number; height: number; }; + onFinished(): void; } interface IState { diff --git a/src/components/views/elements/PollCreateDialog.tsx b/src/components/views/elements/PollCreateDialog.tsx index c4717c6ecc..a30453ed87 100644 --- a/src/components/views/elements/PollCreateDialog.tsx +++ b/src/components/views/elements/PollCreateDialog.tsx @@ -27,7 +27,6 @@ import { MatrixEvent } from "matrix-js-sdk/src/models/event"; import { IPartialEvent } from "matrix-js-sdk/src/@types/extensible_events"; import ScrollableBaseModal, { IScrollableBaseState } from "../dialogs/ScrollableBaseModal"; -import { IDialogProps } from "../dialogs/IDialogProps"; import QuestionDialog from "../dialogs/QuestionDialog"; import Modal from "../../../Modal"; import { _t } from "../../../languageHandler"; @@ -37,10 +36,11 @@ import AccessibleButton from "./AccessibleButton"; import Spinner from "./Spinner"; import { doMaybeLocalRoomAction } from "../../../utils/local-room"; -interface IProps extends IDialogProps { +interface IProps { room: Room; threadId?: string; editingMxEvent?: MatrixEvent; // Truthy if we are editing an existing poll + onFinished(pollCreated?: boolean): void; } enum FocusTarget { diff --git a/src/components/views/elements/Spinner.tsx b/src/components/views/elements/Spinner.tsx index 9dad812edf..943bc29631 100644 --- a/src/components/views/elements/Spinner.tsx +++ b/src/components/views/elements/Spinner.tsx @@ -22,6 +22,7 @@ interface IProps { w?: number; h?: number; message?: string; + onFinished: any; // XXX: Spinner pretends to be a dialog so it must accept an onFinished, but it never calls it } export default class Spinner extends React.PureComponent { diff --git a/src/components/views/location/LocationViewDialog.tsx b/src/components/views/location/LocationViewDialog.tsx index 042e37a111..049c4a89a7 100644 --- a/src/components/views/location/LocationViewDialog.tsx +++ b/src/components/views/location/LocationViewDialog.tsx @@ -19,15 +19,15 @@ import { MatrixEvent } from "matrix-js-sdk/src/models/event"; import { MatrixClient } from "matrix-js-sdk/src/client"; import BaseDialog from "../dialogs/BaseDialog"; -import { IDialogProps } from "../dialogs/IDialogProps"; import { locationEventGeoUri, isSelfLocation } from "../../../utils/location"; import Map from "./Map"; import SmartMarker from "./SmartMarker"; import ZoomButtons from "./ZoomButtons"; -interface IProps extends IDialogProps { +interface IProps { matrixClient: MatrixClient; mxEvent: MatrixEvent; + onFinished(): void; } interface IState { diff --git a/src/components/views/messages/EditHistoryMessage.tsx b/src/components/views/messages/EditHistoryMessage.tsx index 22469a43f7..ec10869a71 100644 --- a/src/components/views/messages/EditHistoryMessage.tsx +++ b/src/components/views/messages/EditHistoryMessage.tsx @@ -80,7 +80,9 @@ export default class EditHistoryMessage extends React.PureComponent cli.redactEvent(event.getRoomId(), event.getId()), + redact: async () => { + await cli.redactEvent(event.getRoomId(), event.getId()); + }, }, "mx_Dialog_confirmredact", ); diff --git a/src/components/views/messages/MBeaconBody.tsx b/src/components/views/messages/MBeaconBody.tsx index 5d8a7a7d65..363b69f7d4 100644 --- a/src/components/views/messages/MBeaconBody.tsx +++ b/src/components/views/messages/MBeaconBody.tsx @@ -162,7 +162,6 @@ const MBeaconBody: React.FC = React.forwardRef(({ mxEvent, getRelati roomId: mxEvent.getRoomId(), matrixClient, initialFocusedBeacon: beacon, - isMapDisplayError, }, "mx_BeaconViewDialog_wrapper", false, // isPriority diff --git a/src/components/views/right_panel/EncryptionPanel.tsx b/src/components/views/right_panel/EncryptionPanel.tsx index 283a644382..b61f3b7c7e 100644 --- a/src/components/views/right_panel/EncryptionPanel.tsx +++ b/src/components/views/right_panel/EncryptionPanel.tsx @@ -41,7 +41,7 @@ const MISMATCHES = ["m.key_mismatch", "m.user_error", "m.mismatched_sas"]; interface IProps { member: RoomMember | User; onClose: () => void; - verificationRequest: VerificationRequest; + verificationRequest?: VerificationRequest; verificationRequestPromise?: Promise; layout: string; isRoomEncrypted: boolean; diff --git a/src/components/views/right_panel/UserInfo.tsx b/src/components/views/right_panel/UserInfo.tsx index 72df34290d..41cd87e107 100644 --- a/src/components/views/right_panel/UserInfo.tsx +++ b/src/components/views/right_panel/UserInfo.tsx @@ -30,6 +30,7 @@ import { logger } from "matrix-js-sdk/src/logger"; import { CryptoEvent } from "matrix-js-sdk/src/crypto"; import { RoomStateEvent } from "matrix-js-sdk/src/models/room-state"; import { UserTrustLevel } from "matrix-js-sdk/src/crypto/CrossSigning"; +import { DeviceInfo } from "matrix-js-sdk/src/crypto/deviceinfo"; import dis from "../../../dispatcher/dispatcher"; import Modal from "../../../Modal"; @@ -79,10 +80,8 @@ import { ViewRoomPayload } from "../../../dispatcher/payloads/ViewRoomPayload"; import { DirectoryMember, startDmOnFirstMessage } from "../../../utils/direct-messages"; import { SdkContextClass } from "../../../contexts/SDKContext"; -export interface IDevice { - deviceId: string; +export interface IDevice extends DeviceInfo { ambiguous?: boolean; - getDisplayName(): string; } export const disambiguateDevices = (devices: IDevice[]): void => { @@ -239,8 +238,8 @@ function DevicesSection({ const isMe = userId === cli.getUserId(); const deviceTrusts = devices.map((d) => cli.checkDeviceTrust(userId, d.deviceId)); - let expandSectionDevices = []; - const unverifiedDevices = []; + let expandSectionDevices: IDevice[] = []; + const unverifiedDevices: IDevice[] = []; let expandCountCaption; let expandHideCaption; @@ -487,7 +486,7 @@ export const UserOptionsSection: React.FC<{ }; const warnSelfDemote = async (isSpace: boolean): Promise => { - const { finished } = Modal.createDialog<[boolean]>(QuestionDialog, { + const { finished } = Modal.createDialog(QuestionDialog, { title: _t("Demote yourself?"), description: (
@@ -592,43 +591,52 @@ export const RoomKickButton = ({ if (member.membership !== "invite" && member.membership !== "join") return null; const onKick = async (): Promise => { - const { finished } = Modal.createDialog( - room.isSpaceRoom() ? ConfirmSpaceUserActionDialog : ConfirmUserActionDialog, - { - member, - action: room.isSpaceRoom() - ? member.membership === "invite" - ? _t("Disinvite from space") - : _t("Remove from space") - : member.membership === "invite" - ? _t("Disinvite from room") - : _t("Remove from room"), - title: - member.membership === "invite" - ? _t("Disinvite from %(roomName)s", { roomName: room.name }) - : _t("Remove from %(roomName)s", { roomName: room.name }), - askReason: member.membership === "join", - danger: true, - // space-specific props - space: room, - spaceChildFilter: (child: Room) => { - // Return true if the target member is not banned and we have sufficient PL to ban them - const myMember = child.getMember(cli.credentials.userId || ""); - const theirMember = child.getMember(member.userId); - return ( - myMember && - theirMember && - theirMember.membership === member.membership && - myMember.powerLevel > theirMember.powerLevel && - child.currentState.hasSufficientPowerLevelFor("kick", myMember.powerLevel) - ); + const commonProps = { + member, + action: room.isSpaceRoom() + ? member.membership === "invite" + ? _t("Disinvite from space") + : _t("Remove from space") + : member.membership === "invite" + ? _t("Disinvite from room") + : _t("Remove from room"), + title: + member.membership === "invite" + ? _t("Disinvite from %(roomName)s", { roomName: room.name }) + : _t("Remove from %(roomName)s", { roomName: room.name }), + askReason: member.membership === "join", + danger: true, + }; + + let finished: Promise<[success?: boolean, reason?: string, rooms?: Room[]]>; + + if (room.isSpaceRoom()) { + ({ finished } = Modal.createDialog( + ConfirmSpaceUserActionDialog, + { + ...commonProps, + space: room, + spaceChildFilter: (child: Room) => { + // Return true if the target member is not banned and we have sufficient PL to ban them + const myMember = child.getMember(cli.credentials.userId || ""); + const theirMember = child.getMember(member.userId); + return ( + myMember && + theirMember && + theirMember.membership === member.membership && + myMember.powerLevel > theirMember.powerLevel && + child.currentState.hasSufficientPowerLevelFor("kick", myMember.powerLevel) + ); + }, + allLabel: _t("Remove them from everything I'm able to"), + specificLabel: _t("Remove them from specific things I'm able to"), + warningMessage: _t("They'll still be able to access whatever you're not an admin of."), }, - allLabel: _t("Remove them from everything I'm able to"), - specificLabel: _t("Remove them from specific things I'm able to"), - warningMessage: _t("They'll still be able to access whatever you're not an admin of."), - }, - room.isSpaceRoom() ? "mx_ConfirmSpaceUserActionDialog_wrapper" : undefined, - ); + "mx_ConfirmSpaceUserActionDialog_wrapper", + )); + } else { + ({ finished } = Modal.createDialog(ConfirmUserActionDialog, commonProps)); + } const [proceed, reason, rooms = []] = await finished; if (!proceed) return; @@ -705,61 +713,70 @@ export const BanToggleButton = ({ const isBanned = member.membership === "ban"; const onBanOrUnban = async (): Promise => { - const { finished } = Modal.createDialog( - room.isSpaceRoom() ? ConfirmSpaceUserActionDialog : ConfirmUserActionDialog, - { - member, - action: room.isSpaceRoom() - ? isBanned - ? _t("Unban from space") - : _t("Ban from space") - : isBanned - ? _t("Unban from room") - : _t("Ban from room"), - title: isBanned - ? _t("Unban from %(roomName)s", { roomName: room.name }) - : _t("Ban from %(roomName)s", { roomName: room.name }), - askReason: !isBanned, - danger: !isBanned, - // space-specific props - space: room, - spaceChildFilter: isBanned - ? (child: Room) => { - // Return true if the target member is banned and we have sufficient PL to unban - const myMember = child.getMember(cli.credentials.userId || ""); - const theirMember = child.getMember(member.userId); - return ( - myMember && - theirMember && - theirMember.membership === "ban" && - myMember.powerLevel > theirMember.powerLevel && - child.currentState.hasSufficientPowerLevelFor("ban", myMember.powerLevel) - ); - } - : (child: Room) => { - // Return true if the target member isn't banned and we have sufficient PL to ban - const myMember = child.getMember(cli.credentials.userId || ""); - const theirMember = child.getMember(member.userId); - return ( - myMember && - theirMember && - theirMember.membership !== "ban" && - myMember.powerLevel > theirMember.powerLevel && - child.currentState.hasSufficientPowerLevelFor("ban", myMember.powerLevel) - ); - }, - allLabel: isBanned - ? _t("Unban them from everything I'm able to") - : _t("Ban them from everything I'm able to"), - specificLabel: isBanned - ? _t("Unban them from specific things I'm able to") - : _t("Ban them from specific things I'm able to"), - warningMessage: isBanned - ? _t("They won't be able to access whatever you're not an admin of.") - : _t("They'll still be able to access whatever you're not an admin of."), - }, - room.isSpaceRoom() ? "mx_ConfirmSpaceUserActionDialog_wrapper" : undefined, - ); + const commonProps = { + member, + action: room.isSpaceRoom() + ? isBanned + ? _t("Unban from space") + : _t("Ban from space") + : isBanned + ? _t("Unban from room") + : _t("Ban from room"), + title: isBanned + ? _t("Unban from %(roomName)s", { roomName: room.name }) + : _t("Ban from %(roomName)s", { roomName: room.name }), + askReason: !isBanned, + danger: !isBanned, + }; + + let finished: Promise<[success?: boolean, reason?: string, rooms?: Room[]]>; + + if (room.isSpaceRoom()) { + ({ finished } = Modal.createDialog( + ConfirmSpaceUserActionDialog, + { + ...commonProps, + space: room, + spaceChildFilter: isBanned + ? (child: Room) => { + // Return true if the target member is banned and we have sufficient PL to unban + const myMember = child.getMember(cli.credentials.userId || ""); + const theirMember = child.getMember(member.userId); + return ( + myMember && + theirMember && + theirMember.membership === "ban" && + myMember.powerLevel > theirMember.powerLevel && + child.currentState.hasSufficientPowerLevelFor("ban", myMember.powerLevel) + ); + } + : (child: Room) => { + // Return true if the target member isn't banned and we have sufficient PL to ban + const myMember = child.getMember(cli.credentials.userId || ""); + const theirMember = child.getMember(member.userId); + return ( + myMember && + theirMember && + theirMember.membership !== "ban" && + myMember.powerLevel > theirMember.powerLevel && + child.currentState.hasSufficientPowerLevelFor("ban", myMember.powerLevel) + ); + }, + allLabel: isBanned + ? _t("Unban them from everything I'm able to") + : _t("Ban them from everything I'm able to"), + specificLabel: isBanned + ? _t("Unban them from specific things I'm able to") + : _t("Ban them from specific things I'm able to"), + warningMessage: isBanned + ? _t("They won't be able to access whatever you're not an admin of.") + : _t("They'll still be able to access whatever you're not an admin of."), + }, + "mx_ConfirmSpaceUserActionDialog_wrapper", + )); + } else { + ({ finished } = Modal.createDialog(ConfirmUserActionDialog, commonProps)); + } const [proceed, reason, rooms = []] = await finished; if (!proceed) return; @@ -1173,8 +1190,8 @@ export const useDevices = (userId: string): IDevice[] | undefined | null => { return; } - disambiguateDevices(devices as IDevice[]); - setDevices(devices as IDevice[]); + disambiguateDevices(devices); + setDevices(devices); } catch (err) { setDevices(null); } @@ -1193,7 +1210,7 @@ export const useDevices = (userId: string): IDevice[] | undefined | null => { const updateDevices = async (): Promise => { const newDevices = cli.getStoredDevicesForUser(userId); if (cancel) return; - setDevices(newDevices as IDevice[]); + setDevices(newDevices); }; const onDevicesUpdated = (users: string[]): void => { if (!users.includes(userId)) return; @@ -1603,7 +1620,7 @@ const UserInfo: React.FC = ({ user, room, onClose, phase = RightPanelPha RightPanelStore.instance.popCard(); }; - let content; + let content: JSX.Element | undefined; switch (phase) { case RightPanelPhases.RoomMemberInfo: case RightPanelPhases.SpaceMemberInfo: @@ -1611,7 +1628,7 @@ const UserInfo: React.FC = ({ user, room, onClose, phase = RightPanelPha ); diff --git a/src/components/views/rooms/wysiwyg_composer/components/LinkModal.tsx b/src/components/views/rooms/wysiwyg_composer/components/LinkModal.tsx index 0c459323d7..a852aebe5a 100644 --- a/src/components/views/rooms/wysiwyg_composer/components/LinkModal.tsx +++ b/src/components/views/rooms/wysiwyg_composer/components/LinkModal.tsx @@ -30,12 +30,11 @@ export function openLinkModal( composerContext: ComposerContextState, isEditing: boolean, ): void { - const modal = Modal.createDialog( + Modal.createDialog( LinkModal, { composerContext, composer, - onClose: () => modal.close(), isTextEnabled: isSelectionEmpty(), isEditing, }, @@ -52,18 +51,18 @@ function isEmpty(text: string): boolean { interface LinkModalProps { composer: FormattingFunctions; isTextEnabled: boolean; - onClose: () => void; + onFinished: () => void; composerContext: ComposerContextState; isEditing: boolean; } -export function LinkModal({ +export const LinkModal: React.FC = ({ composer, isTextEnabled, - onClose, + onFinished, composerContext, isEditing, -}: LinkModalProps): JSX.Element { +}) => { const [hasLinkChanged, setHasLinkChanged] = useState(false); const [fields, setFields] = useState({ text: "", link: isEditing ? composer.getLink() : "" }); const hasText = !isEditing && isTextEnabled; @@ -74,7 +73,7 @@ export function LinkModal({ className="mx_LinkModal" title={isEditing ? _t("Edit link") : _t("Create a link")} hasCancel={true} - onFinished={onClose} + onFinished={onFinished} >
{ composer.removeLinks(); - onClose(); + onFinished(); }} > {_t("Remove")} @@ -136,10 +135,10 @@ export function LinkModal({ primaryButton={_t("Save")} primaryDisabled={isSaveDisabled} primaryIsSubmit={true} - onCancel={onClose} + onCancel={onFinished} />
); -} +}; diff --git a/src/components/views/settings/ChangePassword.tsx b/src/components/views/settings/ChangePassword.tsx index 4c84a960f9..9978985ad8 100644 --- a/src/components/views/settings/ChangePassword.tsx +++ b/src/components/views/settings/ChangePassword.tsx @@ -15,9 +15,10 @@ See the License for the specific language governing permissions and limitations under the License. */ -import React, { ComponentType } from "react"; +import React from "react"; import { MatrixClient } from "matrix-js-sdk/src/client"; +import type ExportE2eKeysDialog from "../../../async-components/views/dialogs/security/ExportE2eKeysDialog"; import Field from "../elements/Field"; import { MatrixClientPeg } from "../../../MatrixClientPeg"; import AccessibleButton from "../elements/AccessibleButton"; @@ -100,7 +101,7 @@ export default class ChangePassword extends React.Component { if (userHasOtherDevices && !serverSupportsControlOfDevicesLogout && this.props.confirm) { // warn about logging out all devices - const { finished } = Modal.createDialog<[boolean]>(QuestionDialog, { + const { finished } = Modal.createDialog(QuestionDialog, { title: _t("Warning!"), description: (
@@ -218,7 +219,7 @@ export default class ChangePassword extends React.Component { private onExportE2eKeysClicked = (): void => { Modal.createDialogAsync( import("../../../async-components/views/dialogs/security/ExportE2eKeysDialog") as unknown as Promise< - ComponentType<{}> + typeof ExportE2eKeysDialog >, { matrixClient: MatrixClientPeg.get(), diff --git a/src/components/views/settings/CryptographyPanel.tsx b/src/components/views/settings/CryptographyPanel.tsx index 72d1d9e485..34b52e405e 100644 --- a/src/components/views/settings/CryptographyPanel.tsx +++ b/src/components/views/settings/CryptographyPanel.tsx @@ -14,8 +14,10 @@ See the License for the specific language governing permissions and limitations under the License. */ -import React, { ComponentType } from "react"; +import React from "react"; +import type ExportE2eKeysDialog from "../../../async-components/views/dialogs/security/ExportE2eKeysDialog"; +import type ImportE2eKeysDialog from "../../../async-components/views/dialogs/security/ImportE2eKeysDialog"; import { MatrixClientPeg } from "../../../MatrixClientPeg"; import { _t } from "../../../languageHandler"; import Modal from "../../../Modal"; @@ -44,7 +46,7 @@ export default class CryptographyPanel extends React.Component { identityKey = FormattingUtils.formatCryptoKey(identityKey); } - let importExportButtons = null; + let importExportButtons: JSX.Element | undefined; if (client.isCryptoEnabled()) { importExportButtons = (
@@ -58,7 +60,7 @@ export default class CryptographyPanel extends React.Component { ); } - let noSendUnverifiedSetting; + let noSendUnverifiedSetting: JSX.Element | undefined; if (SettingsStore.isEnabled("blacklistUnverifiedDevices")) { noSendUnverifiedSetting = ( { private onExportE2eKeysClicked = (): void => { Modal.createDialogAsync( import("../../../async-components/views/dialogs/security/ExportE2eKeysDialog") as unknown as Promise< - ComponentType<{}> + typeof ExportE2eKeysDialog >, { matrixClient: MatrixClientPeg.get() }, ); @@ -108,7 +110,7 @@ export default class CryptographyPanel extends React.Component { private onImportE2eKeysClicked = (): void => { Modal.createDialogAsync( import("../../../async-components/views/dialogs/security/ImportE2eKeysDialog") as unknown as Promise< - ComponentType<{}> + typeof ImportE2eKeysDialog >, { matrixClient: MatrixClientPeg.get() }, ); diff --git a/src/components/views/settings/IntegrationManager.tsx b/src/components/views/settings/IntegrationManager.tsx index b07d75d520..ee69c5cd92 100644 --- a/src/components/views/settings/IntegrationManager.tsx +++ b/src/components/views/settings/IntegrationManager.tsx @@ -26,10 +26,10 @@ import Heading from "../typography/Heading"; interface IProps { // false to display an error saying that we couldn't connect to the integration manager - connected: boolean; + connected?: boolean; // true to display a loading spinner - loading: boolean; + loading?: boolean; // The source URL to load url?: string; @@ -45,7 +45,7 @@ interface IState { export default class IntegrationManager extends React.Component { private dispatcherRef: string; - public static defaultProps = { + public static defaultProps: Partial = { connected: true, loading: false, }; diff --git a/src/components/views/settings/JoinRuleSettings.tsx b/src/components/views/settings/JoinRuleSettings.tsx index f4f82bac9f..5b1c5e0bbf 100644 --- a/src/components/views/settings/JoinRuleSettings.tsx +++ b/src/components/views/settings/JoinRuleSettings.tsx @@ -24,7 +24,6 @@ import { _t } from "../../../languageHandler"; import AccessibleButton from "../elements/AccessibleButton"; import RoomAvatar from "../avatars/RoomAvatar"; import SpaceStore from "../../../stores/spaces/SpaceStore"; -import { MatrixClientPeg } from "../../../MatrixClientPeg"; import Modal from "../../../Modal"; import ManageRestrictedJoinRuleDialog from "../dialogs/ManageRestrictedJoinRuleDialog"; import RoomUpgradeWarningDialog, { IFinishedOpts } from "../dialogs/RoomUpgradeWarningDialog"; @@ -80,11 +79,9 @@ const JoinRuleSettings: React.FC = ({ selected = [SpaceStore.instance.activeSpaceRoom.roomId]; } - const matrixClient = MatrixClientPeg.get(); - const { finished } = Modal.createDialog<[string[]]>( + const { finished } = Modal.createDialog( ManageRestrictedJoinRuleDialog, { - matrixClient, room, selected, }, diff --git a/src/components/views/settings/SecureBackupPanel.tsx b/src/components/views/settings/SecureBackupPanel.tsx index 98c241fd0f..7ab22730fd 100644 --- a/src/components/views/settings/SecureBackupPanel.tsx +++ b/src/components/views/settings/SecureBackupPanel.tsx @@ -15,12 +15,13 @@ See the License for the specific language governing permissions and limitations under the License. */ -import React, { ComponentType } from "react"; +import React from "react"; import { IKeyBackupInfo } from "matrix-js-sdk/src/crypto/keybackup"; import { TrustInfo } from "matrix-js-sdk/src/crypto/backup"; import { CryptoEvent } from "matrix-js-sdk/src/crypto"; import { logger } from "matrix-js-sdk/src/logger"; +import type CreateKeyBackupDialog from "../../../async-components/views/dialogs/security/CreateKeyBackupDialog"; import { MatrixClientPeg } from "../../../MatrixClientPeg"; import { _t } from "../../../languageHandler"; import Modal from "../../../Modal"; @@ -166,7 +167,7 @@ export default class SecureBackupPanel extends React.PureComponent<{}, IState> { private startNewBackup = (): void => { Modal.createDialogAsync( import("../../../async-components/views/dialogs/security/CreateKeyBackupDialog") as unknown as Promise< - ComponentType<{}> + typeof CreateKeyBackupDialog >, { onFinished: () => { diff --git a/src/components/views/settings/SetIdServer.tsx b/src/components/views/settings/SetIdServer.tsx index feb1bc0f79..50c1c6c452 100644 --- a/src/components/views/settings/SetIdServer.tsx +++ b/src/components/views/settings/SetIdServer.tsx @@ -127,7 +127,7 @@ export default class SetIdServer extends React.Component { this.setState({ idServer: u }); }; - private getTooltip = (): JSX.Element => { + private getTooltip = (): ReactNode => { if (this.state.checking) { return (
@@ -153,7 +153,7 @@ export default class SetIdServer extends React.Component { }); this.setState({ busy: false, - error: null, + error: undefined, currentClientIdServer: fullUrl, idServer: "", }); @@ -163,7 +163,7 @@ export default class SetIdServer extends React.Component { e.preventDefault(); const { idServer, currentClientIdServer } = this.state; - this.setState({ busy: true, checking: true, error: null }); + this.setState({ busy: true, checking: true, error: undefined }); const fullUrl = unabbreviateUrl(idServer); @@ -183,7 +183,7 @@ export default class SetIdServer extends React.Component { const hasTerms = await doesIdentityServerHaveTerms(fullUrl); if (!hasTerms) { const [confirmed] = await this.showNoTermsWarning(fullUrl); - save = confirmed; + save = !!confirmed; } // Show a general warning, possibly with details about any bound @@ -201,7 +201,7 @@ export default class SetIdServer extends React.Component { ), button: _t("Continue"), }); - save = confirmed; + save = !!confirmed; } if (save) { @@ -215,13 +215,13 @@ export default class SetIdServer extends React.Component { this.setState({ busy: false, checking: false, - error: errStr, + error: errStr ?? undefined, currentClientIdServer: MatrixClientPeg.get().getIdentityServerUrl(), }); }; - private showNoTermsWarning(fullUrl: string): Promise<[boolean]> { - const { finished } = Modal.createDialog<[boolean]>(QuestionDialog, { + private showNoTermsWarning(fullUrl: string): Promise<[ok?: boolean]> { + const { finished } = Modal.createDialog(QuestionDialog, { title: _t("Identity server has no terms of service"), description: (
@@ -264,7 +264,7 @@ export default class SetIdServer extends React.Component { title: string; unboundMessage: ReactNode; button: string; - }): Promise<[boolean]> { + }): Promise<[ok?: boolean]> { const { currentClientIdServer } = this.state; let threepids: IThreepid[] = []; @@ -349,7 +349,7 @@ export default class SetIdServer extends React.Component { message = unboundMessage; } - const { finished } = Modal.createDialog<[boolean]>(QuestionDialog, { + const { finished } = Modal.createDialog(QuestionDialog, { title, description: message, button, @@ -374,7 +374,7 @@ export default class SetIdServer extends React.Component { this.setState({ busy: false, - error: null, + error: undefined, currentClientIdServer: MatrixClientPeg.get().getIdentityServerUrl(), idServer: newFieldVal, }); @@ -452,7 +452,7 @@ export default class SetIdServer extends React.Component { tooltipContent={this.getTooltip()} tooltipClassName="mx_SetIdServer_tooltip" disabled={this.state.busy} - forceValidity={this.state.error ? false : null} + forceValidity={this.state.error ? false : undefined} /> - async (auth?: IAuthData): Promise => { - await matrixClient.deleteMultipleDevices(deviceIds, auth); + async (auth?: IAuthData): Promise => { + return matrixClient.deleteMultipleDevices(deviceIds, auth); }; export const deleteDevicesWithInteractiveAuth = async ( diff --git a/src/components/views/settings/tabs/room/SecurityRoomSettingsTab.tsx b/src/components/views/settings/tabs/room/SecurityRoomSettingsTab.tsx index 4c8cc9d967..78bb1fe782 100644 --- a/src/components/views/settings/tabs/room/SecurityRoomSettingsTab.tsx +++ b/src/components/views/settings/tabs/room/SecurityRoomSettingsTab.tsx @@ -32,7 +32,7 @@ import SettingsStore from "../../../../../settings/SettingsStore"; import { UIFeature } from "../../../../../settings/UIFeature"; import AccessibleButton from "../../../elements/AccessibleButton"; import SettingsFlag from "../../../elements/SettingsFlag"; -import createRoom, { IOpts } from "../../../../../createRoom"; +import createRoom from "../../../../../createRoom"; import CreateRoomDialog from "../../../dialogs/CreateRoomDialog"; import JoinRuleSettings from "../../JoinRuleSettings"; import ErrorDialog from "../../../dialogs/ErrorDialog"; @@ -204,7 +204,7 @@ export default class SecurityRoomSettingsTab extends React.Component => { - const modal = Modal.createDialog<[boolean, IOpts]>(CreateRoomDialog, { defaultPublic, defaultEncrypted }); + const modal = Modal.createDialog(CreateRoomDialog, { defaultPublic, defaultEncrypted }); PosthogTrackers.trackInteraction("WebRoomSettingsSecurityTabCreateNewRoomButton"); diff --git a/src/components/views/spaces/SpaceCreateMenu.tsx b/src/components/views/spaces/SpaceCreateMenu.tsx index 855526a2fe..ec383f6f5e 100644 --- a/src/components/views/spaces/SpaceCreateMenu.tsx +++ b/src/components/views/spaces/SpaceCreateMenu.tsx @@ -139,7 +139,7 @@ export const SpaceFeedbackPrompt: React.FC<{ rageshakeData: Object.fromEntries( ["Spaces.allRoomsInHome", "Spaces.enabledMetaSpaces"].map((k) => [ k, - SettingsStore.getValue(k), + String(SettingsStore.getValue(k)), ]), ), }); diff --git a/src/components/views/spaces/SpaceSettingsGeneralTab.tsx b/src/components/views/spaces/SpaceSettingsGeneralTab.tsx index 2215d148f6..329a580b79 100644 --- a/src/components/views/spaces/SpaceSettingsGeneralTab.tsx +++ b/src/components/views/spaces/SpaceSettingsGeneralTab.tsx @@ -24,17 +24,16 @@ import { _t } from "../../../languageHandler"; import AccessibleButton from "../elements/AccessibleButton"; import SpaceBasicSettings from "./SpaceBasicSettings"; import { avatarUrlForRoom } from "../../../Avatar"; -import { IDialogProps } from "../dialogs/IDialogProps"; import { htmlSerializeFromMdIfNeeded } from "../../../editor/serialize"; import { leaveSpace } from "../../../utils/leave-behaviour"; import { getTopic } from "../../../hooks/room/useTopic"; -interface IProps extends IDialogProps { +interface IProps { matrixClient: MatrixClient; space: Room; } -const SpaceSettingsGeneralTab: React.FC = ({ matrixClient: cli, space, onFinished }) => { +const SpaceSettingsGeneralTab: React.FC = ({ matrixClient: cli, space }) => { const [busy, setBusy] = useState(false); const [error, setError] = useState(""); diff --git a/src/components/views/voip/LegacyCallView.tsx b/src/components/views/voip/LegacyCallView.tsx index 2d989d296a..86be87608d 100644 --- a/src/components/views/voip/LegacyCallView.tsx +++ b/src/components/views/voip/LegacyCallView.tsx @@ -290,7 +290,7 @@ export default class LegacyCallView extends React.Component { isScreensharing = await this.props.call.setScreensharingEnabled(false); } else { if (PlatformPeg.get()?.supportsDesktopCapturer()) { - const { finished } = Modal.createDialog<[string]>(DesktopCapturerSourcePicker); + const { finished } = Modal.createDialog(DesktopCapturerSourcePicker); const [source] = await finished; if (!source) return; diff --git a/src/dispatcher/payloads/OpenInviteDialogPayload.ts b/src/dispatcher/payloads/OpenInviteDialogPayload.ts index eb0b11e20d..add3e37a2c 100644 --- a/src/dispatcher/payloads/OpenInviteDialogPayload.ts +++ b/src/dispatcher/payloads/OpenInviteDialogPayload.ts @@ -19,12 +19,12 @@ import { MatrixCall } from "matrix-js-sdk/src/webrtc/call"; import { ActionPayload } from "../payloads"; import { Action } from "../actions"; -import { AnyInviteKind } from "../../components/views/dialogs/InviteDialogTypes"; +import { InviteKind } from "../../components/views/dialogs/InviteDialogTypes"; export interface OpenInviteDialogPayload extends ActionPayload { action: Action.OpenInviteDialog; - kind: AnyInviteKind; + kind: InviteKind; onFinishedCallback: Optional<(results: boolean[]) => void>; call?: MatrixCall; diff --git a/src/modules/ProxiedModuleApi.ts b/src/modules/ProxiedModuleApi.ts index 56fc3ff121..818a1f9fcd 100644 --- a/src/modules/ProxiedModuleApi.ts +++ b/src/modules/ProxiedModuleApi.ts @@ -88,7 +88,7 @@ export class ProxiedModuleApi implements ModuleApi { }, "mx_CompoundDialog", ).finished.then(([didOkOrSubmit, model]) => { - resolve({ didOkOrSubmit, model }); + resolve({ didOkOrSubmit, model: model as M }); }); }); } diff --git a/src/stores/ModalWidgetStore.ts b/src/stores/ModalWidgetStore.ts index f32d1f1fb7..479ca4fe3f 100644 --- a/src/stores/ModalWidgetStore.ts +++ b/src/stores/ModalWidgetStore.ts @@ -35,7 +35,7 @@ export class ModalWidgetStore extends AsyncStoreWithClient { instance.start(); return instance; })(); - private modalInstance: IHandle | null = null; + private modalInstance: IHandle | null = null; private openSourceWidgetId: string | null = null; private openSourceWidgetRoomId: string | null = null; diff --git a/src/stores/widgets/StopGapWidgetDriver.ts b/src/stores/widgets/StopGapWidgetDriver.ts index 97c93f883f..8e892a05e6 100644 --- a/src/stores/widgets/StopGapWidgetDriver.ts +++ b/src/stores/widgets/StopGapWidgetDriver.ts @@ -197,14 +197,7 @@ export class StopGapWidgetDriver extends WidgetDriver { let rememberApproved = false; if (missing.size > 0) { try { - const [result] = await Modal.createDialog< - [ - { - approved: Capability[]; - remember: boolean; - }, - ] - >(WidgetCapabilitiesPromptDialog, { + const [result] = await Modal.createDialog(WidgetCapabilitiesPromptDialog, { requestedCapabilities: missing, widget: this.forWidget, widgetKind: this.forWidgetKind, diff --git a/src/utils/DialogOpener.ts b/src/utils/DialogOpener.ts index 0121fb7c18..c02ccf2841 100644 --- a/src/utils/DialogOpener.ts +++ b/src/utils/DialogOpener.ts @@ -15,6 +15,7 @@ limitations under the License. */ import classnames from "classnames"; +import { ComponentProps } from "react"; import defaultDispatcher from "../dispatcher/dispatcher"; import { ActionPayload } from "../dispatcher/payloads"; @@ -114,7 +115,7 @@ export class DialogOpener { kind: payload.kind, call: payload.call, roomId: payload.roomId, - }, + } as Omit, "onFinished">, classnames("mx_InviteDialog_flexWrapper", payload.className), false, true, diff --git a/src/utils/UserInteractiveAuth.ts b/src/utils/UserInteractiveAuth.ts index 042fe67eff..c5d1c84c47 100644 --- a/src/utils/UserInteractiveAuth.ts +++ b/src/utils/UserInteractiveAuth.ts @@ -43,7 +43,7 @@ export function wrapRequestWithDialog( makeRequest: (authData?: IAuthData) => boundFunction(authData, ...args), onFinished: (success, result) => { if (success) { - resolve(result); + resolve(result as R); } else { reject(result); } diff --git a/src/utils/space.tsx b/src/utils/space.tsx index fc183a3a7d..2c076436d4 100644 --- a/src/utils/space.tsx +++ b/src/utils/space.tsx @@ -23,7 +23,7 @@ import { ICreateRoomStateEvent } from "matrix-js-sdk/src/matrix"; import { calculateRoomVia } from "./permalinks/Permalinks"; import Modal from "../Modal"; import CreateRoomDialog from "../components/views/dialogs/CreateRoomDialog"; -import createRoom, { IOpts } from "../createRoom"; +import createRoom from "../createRoom"; import { _t } from "../languageHandler"; import SpacePublicShare from "../components/views/spaces/SpacePublicShare"; import InfoDialog from "../components/views/dialogs/InfoDialog"; @@ -75,7 +75,7 @@ export const showAddExistingRooms = (space: Room): void => { }; export const showCreateNewRoom = async (space: Room, type?: RoomType): Promise => { - const modal = Modal.createDialog<[boolean, IOpts]>(CreateRoomDialog, { + const modal = Modal.createDialog(CreateRoomDialog, { type, defaultPublic: space.getJoinRule() === JoinRule.Public, parentSpace: space, diff --git a/src/voice-broadcast/components/molecules/ConfirmListeBroadcastStopCurrent.tsx b/src/voice-broadcast/components/molecules/ConfirmListeBroadcastStopCurrent.tsx index babef3564f..39ab4339b4 100644 --- a/src/voice-broadcast/components/molecules/ConfirmListeBroadcastStopCurrent.tsx +++ b/src/voice-broadcast/components/molecules/ConfirmListeBroadcastStopCurrent.tsx @@ -23,7 +23,7 @@ import { _t } from "../../../languageHandler"; import Modal from "../../../Modal"; interface Props { - onFinished: (confirmed: boolean) => void; + onFinished: (confirmed?: boolean) => void; } export const ConfirmListenBroadcastStopCurrentDialog: React.FC = ({ onFinished }) => { diff --git a/test/components/views/dialogs/InviteDialog-test.tsx b/test/components/views/dialogs/InviteDialog-test.tsx index b117211fbb..7c6a925d86 100644 --- a/test/components/views/dialogs/InviteDialog-test.tsx +++ b/test/components/views/dialogs/InviteDialog-test.tsx @@ -21,7 +21,7 @@ import { RoomType } from "matrix-js-sdk/src/@types/event"; import { Room } from "matrix-js-sdk/src/matrix"; import InviteDialog from "../../../../src/components/views/dialogs/InviteDialog"; -import { KIND_DM, KIND_INVITE } from "../../../../src/components/views/dialogs/InviteDialogTypes"; +import { InviteKind } from "../../../../src/components/views/dialogs/InviteDialogTypes"; import { getMockClientWithEventEmitter, mkMembership, mkMessage, mkRoomCreateEvent } from "../../../test-utils"; import DMRoomMap from "../../../../src/utils/DMRoomMap"; import SdkConfig from "../../../../src/SdkConfig"; @@ -109,20 +109,20 @@ describe("InviteDialog", () => { room.isSpaceRoom = jest.fn().mockReturnValue(true); room.getType = jest.fn().mockReturnValue(RoomType.Space); room.name = "Space"; - render(); + render(); expect(screen.queryByText("Invite to Space")).toBeTruthy(); }); it("should label with room name", () => { - render(); + render(); expect(screen.getByText(`Invite to ${roomId}`)).toBeInTheDocument(); }); it("should suggest valid MXIDs even if unknown", async () => { render( { it("should not suggest invalid MXIDs", () => { render( { expect(screen.queryByText("@localpart:server:tld")).toBeFalsy(); }); - it.each([[KIND_DM], [KIND_INVITE]] as [typeof KIND_DM | typeof KIND_INVITE][])( + it.each([[InviteKind.Dm], [InviteKind.Invite]] as [typeof InviteKind.Dm | typeof InviteKind.Invite][])( "should lookup inputs which look like email addresses (%s)", - async (kind: typeof KIND_DM | typeof KIND_INVITE) => { + async (kind: typeof InviteKind.Dm | typeof InviteKind.Invite) => { mockClient.getIdentityServerUrl.mockReturnValue("https://identity-server"); mockClient.lookupThreePid.mockResolvedValue({ address: aliceEmail, @@ -162,7 +162,7 @@ describe("InviteDialog", () => { render( , @@ -182,7 +182,12 @@ describe("InviteDialog", () => { mockClient.lookupThreePid.mockResolvedValue({}); render( - , + , ); await screen.findByText("foobar@email.com"); @@ -193,7 +198,7 @@ describe("InviteDialog", () => { mockClient.getIdentityServerUrl.mockReturnValue("https://identity-server"); mockClient.lookupThreePid.mockResolvedValue({}); - render(); + render(); const input = screen.getByTestId("invite-dialog-input"); input.focus(); diff --git a/test/components/views/dialogs/UserSettingsDialog-test.tsx b/test/components/views/dialogs/UserSettingsDialog-test.tsx index 190d05a0b6..b5f04c7b78 100644 --- a/test/components/views/dialogs/UserSettingsDialog-test.tsx +++ b/test/components/views/dialogs/UserSettingsDialog-test.tsx @@ -22,7 +22,6 @@ import SettingsStore, { CallbackFn } from "../../../../src/settings/SettingsStor import SdkConfig from "../../../../src/SdkConfig"; import { UserTab } from "../../../../src/components/views/dialogs/UserTab"; import UserSettingsDialog from "../../../../src/components/views/dialogs/UserSettingsDialog"; -import { IDialogProps } from "../../../../src/components/views/dialogs/IDialogProps"; import { getMockClientWithEventEmitter, mockClientMethodsUser, @@ -62,7 +61,7 @@ describe("", () => { }); const defaultProps = { onFinished: jest.fn() }; - const getComponent = (props: Partial = {}): ReactElement => ( + const getComponent = (props: Partial = {}): ReactElement => ( ); diff --git a/test/components/views/right_panel/UserInfo-test.tsx b/test/components/views/right_panel/UserInfo-test.tsx index 049fa6ef28..1047db4478 100644 --- a/test/components/views/right_panel/UserInfo-test.tsx +++ b/test/components/views/right_panel/UserInfo-test.tsx @@ -21,13 +21,13 @@ import { mocked } from "jest-mock"; import { Room, User, MatrixClient, RoomMember, MatrixEvent, EventType } from "matrix-js-sdk/src/matrix"; import { Phase, VerificationRequest } from "matrix-js-sdk/src/crypto/verification/request/VerificationRequest"; import { DeviceTrustLevel, UserTrustLevel } from "matrix-js-sdk/src/crypto/CrossSigning"; +import { DeviceInfo } from "matrix-js-sdk/src/crypto/deviceinfo"; import UserInfo, { BanToggleButton, DeviceItem, disambiguateDevices, getPowerLevels, - IDevice, isMuted, PowerLevelEditor, RoomAdminToolsContainer, @@ -80,6 +80,22 @@ const mockRoom = mocked({ getEventReadUpTo: jest.fn(), } as unknown as Room); +const mockSpace = mocked({ + roomId: "!fkfk", + getType: jest.fn().mockReturnValue("m.space"), + isSpaceRoom: jest.fn().mockReturnValue(true), + getMember: jest.fn().mockReturnValue(undefined), + getMxcAvatarUrl: jest.fn().mockReturnValue("mock-avatar-url"), + name: "test room", + on: jest.fn(), + off: jest.fn(), + currentState: { + getStateEvents: jest.fn(), + on: jest.fn(), + }, + getEventReadUpTo: jest.fn(), +} as unknown as Room); + const mockClient = mocked({ getUser: jest.fn(), isGuest: jest.fn().mockReturnValue(false), @@ -258,7 +274,7 @@ describe("", () => { }); describe("", () => { - const device: IDevice = { deviceId: "deviceId", getDisplayName: () => "deviceName" }; + const device = { deviceId: "deviceId", getDisplayName: () => "deviceName" } as DeviceInfo; const defaultProps = { userId: defaultUserId, device, @@ -722,14 +738,14 @@ describe("", () => { it("clicking the kick button calls Modal.createDialog with the correct arguments", async () => { createDialogSpy.mockReturnValueOnce({ finished: Promise.resolve([]), close: jest.fn() }); - renderComponent({ member: memberWithInviteMembership }); + renderComponent({ room: mockSpace, member: memberWithInviteMembership }); await userEvent.click(screen.getByText(/disinvite from/i)); // check the last call arguments and the presence of the spaceChildFilter callback expect(createDialogSpy).toHaveBeenLastCalledWith( expect.any(Function), expect.objectContaining({ spaceChildFilter: expect.any(Function) }), - undefined, + "mx_ConfirmSpaceUserActionDialog_wrapper", ); // test the spaceChildFilter callback @@ -806,14 +822,14 @@ describe("", () => { it("clicking the ban or unban button calls Modal.createDialog with the correct arguments if user is not banned", async () => { createDialogSpy.mockReturnValueOnce({ finished: Promise.resolve([]), close: jest.fn() }); - renderComponent(); + renderComponent({ room: mockSpace }); await userEvent.click(screen.getByText(/ban from/i)); // check the last call arguments and the presence of the spaceChildFilter callback expect(createDialogSpy).toHaveBeenLastCalledWith( expect.any(Function), expect.objectContaining({ spaceChildFilter: expect.any(Function) }), - undefined, + "mx_ConfirmSpaceUserActionDialog_wrapper", ); // test the spaceChildFilter callback @@ -844,14 +860,14 @@ describe("", () => { it("clicking the ban or unban button calls Modal.createDialog with the correct arguments if user _is_ banned", async () => { createDialogSpy.mockReturnValueOnce({ finished: Promise.resolve([]), close: jest.fn() }); - renderComponent({ member: memberWithBanMembership }); + renderComponent({ room: mockSpace, member: memberWithBanMembership }); await userEvent.click(screen.getByText(/ban from/i)); // check the last call arguments and the presence of the spaceChildFilter callback expect(createDialogSpy).toHaveBeenLastCalledWith( expect.any(Function), expect.objectContaining({ spaceChildFilter: expect.any(Function) }), - undefined, + "mx_ConfirmSpaceUserActionDialog_wrapper", ); // test the spaceChildFilter callback @@ -955,9 +971,9 @@ describe("", () => { describe("disambiguateDevices", () => { it("does not add ambiguous key to unique names", () => { const initialDevices = [ - { deviceId: "id1", getDisplayName: () => "name1" }, - { deviceId: "id2", getDisplayName: () => "name2" }, - { deviceId: "id3", getDisplayName: () => "name3" }, + { deviceId: "id1", getDisplayName: () => "name1" } as DeviceInfo, + { deviceId: "id2", getDisplayName: () => "name2" } as DeviceInfo, + { deviceId: "id3", getDisplayName: () => "name3" } as DeviceInfo, ]; disambiguateDevices(initialDevices); @@ -969,14 +985,14 @@ describe("disambiguateDevices", () => { it("adds ambiguous key to all ids with non-unique names", () => { const uniqueNameDevices = [ - { deviceId: "id3", getDisplayName: () => "name3" }, - { deviceId: "id4", getDisplayName: () => "name4" }, - { deviceId: "id6", getDisplayName: () => "name6" }, + { deviceId: "id3", getDisplayName: () => "name3" } as DeviceInfo, + { deviceId: "id4", getDisplayName: () => "name4" } as DeviceInfo, + { deviceId: "id6", getDisplayName: () => "name6" } as DeviceInfo, ]; const nonUniqueNameDevices = [ - { deviceId: "id1", getDisplayName: () => "nonUnique" }, - { deviceId: "id2", getDisplayName: () => "nonUnique" }, - { deviceId: "id5", getDisplayName: () => "nonUnique" }, + { deviceId: "id1", getDisplayName: () => "nonUnique" } as DeviceInfo, + { deviceId: "id2", getDisplayName: () => "nonUnique" } as DeviceInfo, + { deviceId: "id5", getDisplayName: () => "nonUnique" } as DeviceInfo, ]; const initialDevices = [...uniqueNameDevices, ...nonUniqueNameDevices]; disambiguateDevices(initialDevices); diff --git a/test/components/views/rooms/wysiwyg_composer/components/LinkModal-test.tsx b/test/components/views/rooms/wysiwyg_composer/components/LinkModal-test.tsx index 852354f0ba..a0d8176339 100644 --- a/test/components/views/rooms/wysiwyg_composer/components/LinkModal-test.tsx +++ b/test/components/views/rooms/wysiwyg_composer/components/LinkModal-test.tsx @@ -38,12 +38,12 @@ describe("LinkModal", () => { isForward: true, }; - const customRender = (isTextEnabled: boolean, onClose: () => void, isEditing = false) => { + const customRender = (isTextEnabled: boolean, onFinished: () => void, isEditing = false) => { return render( , @@ -60,8 +60,8 @@ describe("LinkModal", () => { it("Should create a link", async () => { // When - const onClose = jest.fn(); - customRender(false, onClose); + const onFinished = jest.fn(); + customRender(false, onFinished); // Then expect(screen.getByLabelText("Link")).toBeTruthy(); @@ -84,7 +84,7 @@ describe("LinkModal", () => { // Then await waitFor(() => { expect(selectionSpy).toHaveBeenCalledWith(defaultValue); - expect(onClose).toBeCalledTimes(1); + expect(onFinished).toBeCalledTimes(1); }); // Then @@ -93,8 +93,8 @@ describe("LinkModal", () => { it("Should create a link with text", async () => { // When - const onClose = jest.fn(); - customRender(true, onClose); + const onFinished = jest.fn(); + customRender(true, onFinished); // Then expect(screen.getByLabelText("Text")).toBeTruthy(); @@ -127,7 +127,7 @@ describe("LinkModal", () => { // Then await waitFor(() => { expect(selectionSpy).toHaveBeenCalledWith(defaultValue); - expect(onClose).toBeCalledTimes(1); + expect(onFinished).toBeCalledTimes(1); }); // Then @@ -136,13 +136,13 @@ describe("LinkModal", () => { it("Should remove the link", async () => { // When - const onClose = jest.fn(); - customRender(true, onClose, true); + const onFinished = jest.fn(); + customRender(true, onFinished, true); await userEvent.click(screen.getByText("Remove")); // Then expect(formattingFunctions.removeLinks).toHaveBeenCalledTimes(1); - expect(onClose).toBeCalledTimes(1); + expect(onFinished).toBeCalledTimes(1); }); it("Should display the link in editing", async () => { diff --git a/test/components/views/settings/devices/deleteDevices-test.tsx b/test/components/views/settings/devices/deleteDevices-test.tsx index 80e62f423d..bdd0c5a8c2 100644 --- a/test/components/views/settings/devices/deleteDevices-test.tsx +++ b/test/components/views/settings/devices/deleteDevices-test.tsx @@ -28,7 +28,7 @@ describe("deleteDevices()", () => { deleteMultipleDevices: jest.fn(), }); - const modalSpy = jest.spyOn(Modal, "createDialog"); + const modalSpy = jest.spyOn(Modal, "createDialog") as jest.SpyInstance; const interactiveAuthError = { httpStatus: 401, data: { flows: [] as UIAFlow[] } }; diff --git a/test/models/Call-test.ts b/test/models/Call-test.ts index 028cf12018..da10098e53 100644 --- a/test/models/Call-test.ts +++ b/test/models/Call-test.ts @@ -922,7 +922,7 @@ describe("ElementCall", () => { const sourceId = "source_id"; jest.spyOn(Modal, "createDialog").mockReturnValue({ finished: new Promise((r) => r([sourceId])), - } as IHandle); + } as IHandle); jest.spyOn(PlatformPeg.get()!, "supportsDesktopCapturer").mockReturnValue(true); await call.connect(); @@ -950,7 +950,7 @@ describe("ElementCall", () => { it("sends ScreenshareStop if we couldn't get a source id", async () => { jest.spyOn(Modal, "createDialog").mockReturnValue({ finished: new Promise((r) => r([null])), - } as IHandle); + } as IHandle); jest.spyOn(PlatformPeg.get()!, "supportsDesktopCapturer").mockReturnValue(true); await call.connect(); diff --git a/test/utils/MultiInviter-test.ts b/test/utils/MultiInviter-test.ts index ed925227eb..b6091e5908 100644 --- a/test/utils/MultiInviter-test.ts +++ b/test/utils/MultiInviter-test.ts @@ -18,10 +18,11 @@ import { mocked } from "jest-mock"; import { MatrixClient } from "matrix-js-sdk/src/matrix"; import { MatrixClientPeg } from "../../src/MatrixClientPeg"; -import Modal, { ModalManager } from "../../src/Modal"; +import Modal, { ComponentType, ComponentProps } from "../../src/Modal"; import SettingsStore from "../../src/settings/SettingsStore"; import MultiInviter, { CompletionStates } from "../../src/utils/MultiInviter"; import * as TestUtilsMatrix from "../test-utils"; +import AskInviteAnywayDialog from "../../src/components/views/dialogs/AskInviteAnywayDialog"; const ROOMID = "!room:server"; @@ -56,9 +57,11 @@ const mockPromptBeforeInviteUnknownUsers = (value: boolean) => { }; const mockCreateTrackedDialog = (callbackName: "onInviteAnyways" | "onGiveUp") => { - mocked(Modal.createDialog).mockImplementation((...rest: Parameters): any => { - rest[1]![callbackName](); - }); + mocked(Modal.createDialog).mockImplementation( + (Element: ComponentType, props?: ComponentProps): any => { + (props as ComponentProps)[callbackName](); + }, + ); }; const expectAllInvitedResult = (result: CompletionStates) => {