diff --git a/src/components/views/auth/AuthFooter.js b/src/components/views/auth/AuthFooter.js
index 3de5a19350..f167e16283 100644
--- a/src/components/views/auth/AuthFooter.js
+++ b/src/components/views/auth/AuthFooter.js
@@ -18,7 +18,9 @@ limitations under the License.
import { _t } from '../../../languageHandler';
import React from 'react';
+import {replaceableComponent} from "../../../utils/replaceableComponent";
+@replaceableComponent("views.auth.AuthFooter")
export default class AuthFooter extends React.Component {
render() {
return (
diff --git a/src/components/views/auth/AuthHeader.js b/src/components/views/auth/AuthHeader.js
index 57499e397c..323299b3a8 100644
--- a/src/components/views/auth/AuthHeader.js
+++ b/src/components/views/auth/AuthHeader.js
@@ -18,7 +18,9 @@ limitations under the License.
import React from 'react';
import PropTypes from 'prop-types';
import * as sdk from '../../../index';
+import {replaceableComponent} from "../../../utils/replaceableComponent";
+@replaceableComponent("views.auth.AuthHeader")
export default class AuthHeader extends React.Component {
static propTypes = {
disableLanguageSelector: PropTypes.bool,
diff --git a/src/components/views/auth/AuthHeaderLogo.js b/src/components/views/auth/AuthHeaderLogo.js
index 9edf149a83..ea649893c6 100644
--- a/src/components/views/auth/AuthHeaderLogo.js
+++ b/src/components/views/auth/AuthHeaderLogo.js
@@ -17,7 +17,9 @@ limitations under the License.
'use strict';
import React from 'react';
+import {replaceableComponent} from "../../../utils/replaceableComponent";
+@replaceableComponent("views.auth.AuthHeaderLogo")
export default class AuthHeaderLogo extends React.PureComponent {
render() {
return
diff --git a/src/components/views/auth/CaptchaForm.js b/src/components/views/auth/CaptchaForm.js
index e2d7d594fa..50de24d403 100644
--- a/src/components/views/auth/CaptchaForm.js
+++ b/src/components/views/auth/CaptchaForm.js
@@ -18,12 +18,14 @@ import React, {createRef} from 'react';
import PropTypes from 'prop-types';
import { _t } from '../../../languageHandler';
import CountlyAnalytics from "../../../CountlyAnalytics";
+import {replaceableComponent} from "../../../utils/replaceableComponent";
const DIV_ID = 'mx_recaptcha';
/**
* A pure UI component which displays a captcha form.
*/
+@replaceableComponent("views.auth.CaptchaForm")
export default class CaptchaForm extends React.Component {
static propTypes = {
sitePublicKey: PropTypes.string,
diff --git a/src/components/views/auth/CompleteSecurityBody.js b/src/components/views/auth/CompleteSecurityBody.js
index d757de9fe0..91cd66f150 100644
--- a/src/components/views/auth/CompleteSecurityBody.js
+++ b/src/components/views/auth/CompleteSecurityBody.js
@@ -17,7 +17,9 @@ limitations under the License.
'use strict';
import React from 'react';
+import {replaceableComponent} from "../../../utils/replaceableComponent";
+@replaceableComponent("views.auth.CompleteSecurityBody")
export default class CompleteSecurityBody extends React.PureComponent {
render() {
return
diff --git a/src/components/views/auth/CountryDropdown.js b/src/components/views/auth/CountryDropdown.js
index 3296b574a4..e21f112865 100644
--- a/src/components/views/auth/CountryDropdown.js
+++ b/src/components/views/auth/CountryDropdown.js
@@ -22,6 +22,7 @@ import * as sdk from '../../../index';
import {COUNTRIES, getEmojiFlag} from '../../../phonenumber';
import SdkConfig from "../../../SdkConfig";
import { _t } from "../../../languageHandler";
+import {replaceableComponent} from "../../../utils/replaceableComponent";
const COUNTRIES_BY_ISO2 = {};
for (const c of COUNTRIES) {
@@ -40,6 +41,7 @@ function countryMatchesSearchQuery(query, country) {
return false;
}
+@replaceableComponent("views.auth.CountryDropdown")
export default class CountryDropdown extends React.Component {
constructor(props) {
super(props);
diff --git a/src/components/views/auth/InteractiveAuthEntryComponents.js b/src/components/views/auth/InteractiveAuthEntryComponents.js
index 7dc1976641..6cbecd22ee 100644
--- a/src/components/views/auth/InteractiveAuthEntryComponents.js
+++ b/src/components/views/auth/InteractiveAuthEntryComponents.js
@@ -26,6 +26,7 @@ import SettingsStore from "../../../settings/SettingsStore";
import AccessibleButton from "../elements/AccessibleButton";
import Spinner from "../elements/Spinner";
import CountlyAnalytics from "../../../CountlyAnalytics";
+import {replaceableComponent} from "../../../utils/replaceableComponent";
/* This file contains a collection of components which are used by the
* InteractiveAuth to prompt the user to enter the information needed
@@ -75,6 +76,7 @@ import CountlyAnalytics from "../../../CountlyAnalytics";
export const DEFAULT_PHASE = 0;
+@replaceableComponent("views.auth.PasswordAuthEntry")
export class PasswordAuthEntry extends React.Component {
static LOGIN_TYPE = "m.login.password";
@@ -173,6 +175,7 @@ export class PasswordAuthEntry extends React.Component {
}
}
+@replaceableComponent("views.auth.RecaptchaAuthEntry")
export class RecaptchaAuthEntry extends React.Component {
static LOGIN_TYPE = "m.login.recaptcha";
@@ -235,6 +238,7 @@ export class RecaptchaAuthEntry extends React.Component {
}
}
+@replaceableComponent("views.auth.TermsAuthEntry")
export class TermsAuthEntry extends React.Component {
static LOGIN_TYPE = "m.login.terms";
@@ -385,6 +389,7 @@ export class TermsAuthEntry extends React.Component {
}
}
+@replaceableComponent("views.auth.EmailIdentityAuthEntry")
export class EmailIdentityAuthEntry extends React.Component {
static LOGIN_TYPE = "m.login.email.identity";
@@ -432,6 +437,7 @@ export class EmailIdentityAuthEntry extends React.Component {
}
}
+@replaceableComponent("views.auth.MsisdnAuthEntry")
export class MsisdnAuthEntry extends React.Component {
static LOGIN_TYPE = "m.login.msisdn";
@@ -578,6 +584,7 @@ export class MsisdnAuthEntry extends React.Component {
}
}
+@replaceableComponent("views.auth.SSOAuthEntry")
export class SSOAuthEntry extends React.Component {
static propTypes = {
matrixClient: PropTypes.object.isRequired,
@@ -708,6 +715,7 @@ export class SSOAuthEntry extends React.Component {
}
}
+@replaceableComponent("views.auth.FallbackAuthEntry")
export class FallbackAuthEntry extends React.Component {
static propTypes = {
matrixClient: PropTypes.object.isRequired,
diff --git a/src/components/views/auth/PassphraseField.tsx b/src/components/views/auth/PassphraseField.tsx
index e240ad61ca..274c244b2a 100644
--- a/src/components/views/auth/PassphraseField.tsx
+++ b/src/components/views/auth/PassphraseField.tsx
@@ -22,6 +22,7 @@ import SdkConfig from "../../../SdkConfig";
import withValidation, {IFieldState, IValidationResult} from "../elements/Validation";
import {_t, _td} from "../../../languageHandler";
import Field, {IInputProps} from "../elements/Field";
+import {replaceableComponent} from "../../../utils/replaceableComponent";
interface IProps extends Omit {
autoFocus?: boolean;
@@ -40,6 +41,7 @@ interface IProps extends Omit {
onValidate(result: IValidationResult);
}
+@replaceableComponent("views.auth.PassphraseField")
class PassphraseField extends PureComponent {
static defaultProps = {
label: _td("Password"),
diff --git a/src/components/views/auth/PasswordLogin.tsx b/src/components/views/auth/PasswordLogin.tsx
index b2a3d62f55..2a42804a61 100644
--- a/src/components/views/auth/PasswordLogin.tsx
+++ b/src/components/views/auth/PasswordLogin.tsx
@@ -26,6 +26,7 @@ import withValidation from "../elements/Validation";
import * as Email from "../../../email";
import Field from "../elements/Field";
import CountryDropdown from "./CountryDropdown";
+import {replaceableComponent} from "../../../utils/replaceableComponent";
// For validating phone numbers without country codes
const PHONE_NUMBER_REGEX = /^[0-9()\-\s]*$/;
@@ -66,6 +67,7 @@ enum LoginField {
* A pure UI component which displays a username/password form.
* The email/username/phone fields are fully-controlled, the password field is not.
*/
+@replaceableComponent("views.auth.PasswordLogin")
export default class PasswordLogin extends React.PureComponent {
static defaultProps = {
onUsernameChanged: function() {},
diff --git a/src/components/views/auth/RegistrationForm.tsx b/src/components/views/auth/RegistrationForm.tsx
index e42ed88f99..85e0933be9 100644
--- a/src/components/views/auth/RegistrationForm.tsx
+++ b/src/components/views/auth/RegistrationForm.tsx
@@ -30,6 +30,7 @@ import PassphraseField from "./PassphraseField";
import CountlyAnalytics from "../../../CountlyAnalytics";
import Field from '../elements/Field';
import RegistrationEmailPromptDialog from '../dialogs/RegistrationEmailPromptDialog';
+import {replaceableComponent} from "../../../utils/replaceableComponent";
enum RegistrationField {
Email = "field_email",
@@ -80,6 +81,7 @@ interface IState {
/*
* A pure UI component which displays a registration form.
*/
+@replaceableComponent("views.auth.RegistrationForm")
export default class RegistrationForm extends React.PureComponent {
static defaultProps = {
onValidationChange: console.error,
diff --git a/src/components/views/auth/Welcome.js b/src/components/views/auth/Welcome.js
index 0205f4e0b9..fca66fcf9b 100644
--- a/src/components/views/auth/Welcome.js
+++ b/src/components/views/auth/Welcome.js
@@ -24,10 +24,12 @@ import {_td} from "../../../languageHandler";
import SettingsStore from "../../../settings/SettingsStore";
import {UIFeature} from "../../../settings/UIFeature";
import CountlyAnalytics from "../../../CountlyAnalytics";
+import {replaceableComponent} from "../../../utils/replaceableComponent";
// translatable strings for Welcome pages
_td("Sign in with SSO");
+@replaceableComponent("views.auth.Welcome")
export default class Welcome extends React.PureComponent {
constructor(props) {
super(props);
diff --git a/src/components/views/avatars/DecoratedRoomAvatar.tsx b/src/components/views/avatars/DecoratedRoomAvatar.tsx
index d7e012467b..e95022687a 100644
--- a/src/components/views/avatars/DecoratedRoomAvatar.tsx
+++ b/src/components/views/avatars/DecoratedRoomAvatar.tsx
@@ -30,6 +30,7 @@ import {MatrixClientPeg} from "../../../MatrixClientPeg";
import {_t} from "../../../languageHandler";
import TextWithTooltip from "../elements/TextWithTooltip";
import DMRoomMap from "../../../utils/DMRoomMap";
+import {replaceableComponent} from "../../../utils/replaceableComponent";
interface IProps {
room: Room;
@@ -68,6 +69,7 @@ function tooltipText(variant: Icon) {
}
}
+@replaceableComponent("views.avatars.DecoratedRoomAvatar")
export default class DecoratedRoomAvatar extends React.PureComponent {
private _dmUser: User;
private isUnmounted = false;
diff --git a/src/components/views/avatars/GroupAvatar.tsx b/src/components/views/avatars/GroupAvatar.tsx
index 51327605c0..a033257871 100644
--- a/src/components/views/avatars/GroupAvatar.tsx
+++ b/src/components/views/avatars/GroupAvatar.tsx
@@ -17,6 +17,7 @@ limitations under the License.
import React from 'react';
import {MatrixClientPeg} from '../../../MatrixClientPeg';
import BaseAvatar from './BaseAvatar';
+import {replaceableComponent} from "../../../utils/replaceableComponent";
export interface IProps {
groupId?: string;
@@ -28,6 +29,7 @@ export interface IProps {
onClick?: React.MouseEventHandler;
}
+@replaceableComponent("views.avatars.GroupAvatar")
export default class GroupAvatar extends React.Component {
public static defaultProps = {
width: 36,
diff --git a/src/components/views/avatars/MemberAvatar.tsx b/src/components/views/avatars/MemberAvatar.tsx
index 60b043016b..641046aa55 100644
--- a/src/components/views/avatars/MemberAvatar.tsx
+++ b/src/components/views/avatars/MemberAvatar.tsx
@@ -22,6 +22,7 @@ import dis from "../../../dispatcher/dispatcher";
import {Action} from "../../../dispatcher/actions";
import {MatrixClientPeg} from "../../../MatrixClientPeg";
import BaseAvatar from "./BaseAvatar";
+import {replaceableComponent} from "../../../utils/replaceableComponent";
interface IProps extends Omit, "name" | "idName" | "url"> {
member: RoomMember;
@@ -42,6 +43,7 @@ interface IState {
imageUrl?: string;
}
+@replaceableComponent("views.avatars.MemberAvatar")
export default class MemberAvatar extends React.Component {
public static defaultProps = {
width: 40,
diff --git a/src/components/views/avatars/MemberStatusMessageAvatar.js b/src/components/views/avatars/MemberStatusMessageAvatar.js
index d5d927106c..acf190f17f 100644
--- a/src/components/views/avatars/MemberStatusMessageAvatar.js
+++ b/src/components/views/avatars/MemberStatusMessageAvatar.js
@@ -23,7 +23,9 @@ import classNames from 'classnames';
import StatusMessageContextMenu from "../context_menus/StatusMessageContextMenu";
import SettingsStore from "../../../settings/SettingsStore";
import {ContextMenu, ContextMenuButton} from "../../structures/ContextMenu";
+import {replaceableComponent} from "../../../utils/replaceableComponent";
+@replaceableComponent("views.avatars.MemberStatusMessageAvatar")
export default class MemberStatusMessageAvatar extends React.Component {
static propTypes = {
member: PropTypes.object.isRequired,
diff --git a/src/components/views/avatars/RoomAvatar.tsx b/src/components/views/avatars/RoomAvatar.tsx
index 952b9d4cb6..0a59f6e36a 100644
--- a/src/components/views/avatars/RoomAvatar.tsx
+++ b/src/components/views/avatars/RoomAvatar.tsx
@@ -23,6 +23,7 @@ import {MatrixClientPeg} from '../../../MatrixClientPeg';
import Modal from '../../../Modal';
import * as Avatar from '../../../Avatar';
import {ResizeMethod} from "../../../Avatar";
+import {replaceableComponent} from "../../../utils/replaceableComponent";
interface IProps extends Omit, "name" | "idName" | "url" | "onClick"> {
// Room may be left unset here, but if it is,
@@ -42,6 +43,7 @@ interface IState {
urls: string[];
}
+@replaceableComponent("views.avatars.RoomAvatar")
export default class RoomAvatar extends React.Component {
public static defaultProps = {
width: 36,
diff --git a/src/components/views/context_menus/CallContextMenu.tsx b/src/components/views/context_menus/CallContextMenu.tsx
index 3557976326..97473059a6 100644
--- a/src/components/views/context_menus/CallContextMenu.tsx
+++ b/src/components/views/context_menus/CallContextMenu.tsx
@@ -22,11 +22,13 @@ import { MatrixCall } from 'matrix-js-sdk/src/webrtc/call';
import CallHandler from '../../../CallHandler';
import InviteDialog, { KIND_CALL_TRANSFER } from '../dialogs/InviteDialog';
import Modal from '../../../Modal';
+import {replaceableComponent} from "../../../utils/replaceableComponent";
interface IProps extends IContextMenuProps {
call: MatrixCall;
}
+@replaceableComponent("views.context_menus.CallContextMenu")
export default class CallContextMenu extends React.Component {
static propTypes = {
// js-sdk User object. Not required because it might not exist.
diff --git a/src/components/views/context_menus/DialpadContextMenu.tsx b/src/components/views/context_menus/DialpadContextMenu.tsx
index e3aed0179b..17abce0c61 100644
--- a/src/components/views/context_menus/DialpadContextMenu.tsx
+++ b/src/components/views/context_menus/DialpadContextMenu.tsx
@@ -19,6 +19,7 @@ import { _t } from '../../../languageHandler';
import { ContextMenu, IProps as IContextMenuProps } from '../../structures/ContextMenu';
import { MatrixCall } from 'matrix-js-sdk/src/webrtc/call';
import Dialpad from '../voip/DialPad';
+import {replaceableComponent} from "../../../utils/replaceableComponent";
interface IProps extends IContextMenuProps {
call: MatrixCall;
@@ -28,6 +29,7 @@ interface IState {
value: string;
}
+@replaceableComponent("views.context_menus.DialpadContextMenu")
export default class DialpadContextMenu extends React.Component {
constructor(props) {
super(props);
diff --git a/src/components/views/context_menus/GenericElementContextMenu.js b/src/components/views/context_menus/GenericElementContextMenu.js
index cea684b663..e04e3f7695 100644
--- a/src/components/views/context_menus/GenericElementContextMenu.js
+++ b/src/components/views/context_menus/GenericElementContextMenu.js
@@ -16,6 +16,7 @@ limitations under the License.
import React from 'react';
import PropTypes from 'prop-types';
+import {replaceableComponent} from "../../../utils/replaceableComponent";
/*
* This component can be used to display generic HTML content in a contextual
@@ -23,6 +24,7 @@ import PropTypes from 'prop-types';
*/
+@replaceableComponent("views.context_menus.GenericElementContextMenu")
export default class GenericElementContextMenu extends React.Component {
static propTypes = {
element: PropTypes.element.isRequired,
diff --git a/src/components/views/context_menus/GenericTextContextMenu.js b/src/components/views/context_menus/GenericTextContextMenu.js
index 068f83be5f..3d3add006f 100644
--- a/src/components/views/context_menus/GenericTextContextMenu.js
+++ b/src/components/views/context_menus/GenericTextContextMenu.js
@@ -16,7 +16,9 @@ limitations under the License.
import React from 'react';
import PropTypes from 'prop-types';
+import {replaceableComponent} from "../../../utils/replaceableComponent";
+@replaceableComponent("views.context_menus.GenericTextContextMenu")
export default class GenericTextContextMenu extends React.Component {
static propTypes = {
message: PropTypes.string.isRequired,
diff --git a/src/components/views/context_menus/GroupInviteTileContextMenu.js b/src/components/views/context_menus/GroupInviteTileContextMenu.js
index 27ef76452f..11a9d90ac2 100644
--- a/src/components/views/context_menus/GroupInviteTileContextMenu.js
+++ b/src/components/views/context_menus/GroupInviteTileContextMenu.js
@@ -23,7 +23,9 @@ import Modal from '../../../Modal';
import {Group} from 'matrix-js-sdk';
import GroupStore from "../../../stores/GroupStore";
import {MenuItem} from "../../structures/ContextMenu";
+import {replaceableComponent} from "../../../utils/replaceableComponent";
+@replaceableComponent("views.context_menus.GroupInviteTileContextMenu")
export default class GroupInviteTileContextMenu extends React.Component {
static propTypes = {
group: PropTypes.instanceOf(Group).isRequired,
diff --git a/src/components/views/context_menus/MessageContextMenu.js b/src/components/views/context_menus/MessageContextMenu.js
index 6b871e4f24..e19cfab809 100644
--- a/src/components/views/context_menus/MessageContextMenu.js
+++ b/src/components/views/context_menus/MessageContextMenu.js
@@ -32,11 +32,13 @@ import { isUrlPermitted } from '../../../HtmlUtils';
import { isContentActionable } from '../../../utils/EventUtils';
import {MenuItem} from "../../structures/ContextMenu";
import {EventType} from "matrix-js-sdk/src/@types/event";
+import {replaceableComponent} from "../../../utils/replaceableComponent";
function canCancel(eventStatus) {
return eventStatus === EventStatus.QUEUED || eventStatus === EventStatus.NOT_SENT;
}
+@replaceableComponent("views.context_menus.MessageContextMenu")
export default class MessageContextMenu extends React.Component {
static propTypes = {
/* the MatrixEvent associated with the context menu */
diff --git a/src/components/views/context_menus/StatusMessageContextMenu.js b/src/components/views/context_menus/StatusMessageContextMenu.js
index 5e6f06dd5d..41f0e0ba61 100644
--- a/src/components/views/context_menus/StatusMessageContextMenu.js
+++ b/src/components/views/context_menus/StatusMessageContextMenu.js
@@ -20,7 +20,9 @@ import { _t } from '../../../languageHandler';
import {MatrixClientPeg} from '../../../MatrixClientPeg';
import * as sdk from '../../../index';
import AccessibleButton from '../elements/AccessibleButton';
+import {replaceableComponent} from "../../../utils/replaceableComponent";
+@replaceableComponent("views.context_menus.StatusMessageContextMenu")
export default class StatusMessageContextMenu extends React.Component {
static propTypes = {
// js-sdk User object. Not required because it might not exist.
diff --git a/src/components/views/context_menus/TagTileContextMenu.js b/src/components/views/context_menus/TagTileContextMenu.js
index 8d690483a8..8dea62690c 100644
--- a/src/components/views/context_menus/TagTileContextMenu.js
+++ b/src/components/views/context_menus/TagTileContextMenu.js
@@ -22,7 +22,9 @@ import dis from '../../../dispatcher/dispatcher';
import TagOrderActions from '../../../actions/TagOrderActions';
import {MenuItem} from "../../structures/ContextMenu";
import MatrixClientContext from "../../../contexts/MatrixClientContext";
+import {replaceableComponent} from "../../../utils/replaceableComponent";
+@replaceableComponent("views.context_menus.TagTileContextMenu")
export default class TagTileContextMenu extends React.Component {
static propTypes = {
tag: PropTypes.string.isRequired,
diff --git a/src/components/views/dialogs/AddressPickerDialog.js b/src/components/views/dialogs/AddressPickerDialog.js
index 2cd09874b2..929d688e47 100644
--- a/src/components/views/dialogs/AddressPickerDialog.js
+++ b/src/components/views/dialogs/AddressPickerDialog.js
@@ -33,6 +33,7 @@ import { abbreviateUrl } from '../../../utils/UrlUtils';
import {sleep} from "../../../utils/promise";
import {Key} from "../../../Keyboard";
import {Action} from "../../../dispatcher/actions";
+import {replaceableComponent} from "../../../utils/replaceableComponent";
const TRUNCATE_QUERY_LIST = 40;
const QUERY_USER_DIRECTORY_DEBOUNCE_MS = 200;
@@ -43,7 +44,7 @@ const addressTypeName = {
'email': _td("email address"),
};
-
+@replaceableComponent("views.dialogs.AddressPickerDialog")
export default class AddressPickerDialog extends React.Component {
static propTypes = {
title: PropTypes.string.isRequired,
diff --git a/src/components/views/dialogs/AskInviteAnywayDialog.js b/src/components/views/dialogs/AskInviteAnywayDialog.js
index c69400977a..e6cd45ba6b 100644
--- a/src/components/views/dialogs/AskInviteAnywayDialog.js
+++ b/src/components/views/dialogs/AskInviteAnywayDialog.js
@@ -20,7 +20,9 @@ import * as sdk from '../../../index';
import { _t } from '../../../languageHandler';
import SettingsStore from "../../../settings/SettingsStore";
import {SettingLevel} from "../../../settings/SettingLevel";
+import {replaceableComponent} from "../../../utils/replaceableComponent";
+@replaceableComponent("views.dialogs.AskInviteAnywayDialog")
export default class AskInviteAnywayDialog extends React.Component {
static propTypes = {
unknownProfileUsers: PropTypes.array.isRequired, // [ {userId, errorText}... ]
diff --git a/src/components/views/dialogs/BaseDialog.js b/src/components/views/dialogs/BaseDialog.js
index 9ba5368ee5..0858e53e50 100644
--- a/src/components/views/dialogs/BaseDialog.js
+++ b/src/components/views/dialogs/BaseDialog.js
@@ -26,6 +26,7 @@ import AccessibleButton from '../elements/AccessibleButton';
import {MatrixClientPeg} from '../../../MatrixClientPeg';
import { _t } from "../../../languageHandler";
import MatrixClientContext from "../../../contexts/MatrixClientContext";
+import {replaceableComponent} from "../../../utils/replaceableComponent";
/*
* Basic container for modal dialogs.
@@ -33,6 +34,7 @@ import MatrixClientContext from "../../../contexts/MatrixClientContext";
* Includes a div for the title, and a keypress handler which cancels the
* dialog on escape.
*/
+@replaceableComponent("views.dialogs.BaseDialog")
export default class BaseDialog extends React.Component {
static propTypes = {
// onFinished callback to call when Escape is pressed
diff --git a/src/components/views/dialogs/BugReportDialog.js b/src/components/views/dialogs/BugReportDialog.js
index c4dd0a1430..8948c14c7c 100644
--- a/src/components/views/dialogs/BugReportDialog.js
+++ b/src/components/views/dialogs/BugReportDialog.js
@@ -25,7 +25,9 @@ import Modal from '../../../Modal';
import { _t } from '../../../languageHandler';
import sendBugReport, {downloadBugReport} from '../../../rageshake/submit-rageshake';
import AccessibleButton from "../elements/AccessibleButton";
+import {replaceableComponent} from "../../../utils/replaceableComponent";
+@replaceableComponent("views.dialogs.BugReportDialog")
export default class BugReportDialog extends React.Component {
constructor(props) {
super(props);
diff --git a/src/components/views/dialogs/CommunityPrototypeInviteDialog.tsx b/src/components/views/dialogs/CommunityPrototypeInviteDialog.tsx
index 1c8a4ad6f6..d1080566ac 100644
--- a/src/components/views/dialogs/CommunityPrototypeInviteDialog.tsx
+++ b/src/components/views/dialogs/CommunityPrototypeInviteDialog.tsx
@@ -31,6 +31,7 @@ import {inviteMultipleToRoom, showAnyInviteErrors} from "../../../RoomInvite";
import StyledCheckbox from "../elements/StyledCheckbox";
import Modal from "../../../Modal";
import ErrorDialog from "./ErrorDialog";
+import {replaceableComponent} from "../../../utils/replaceableComponent";
interface IProps extends IDialogProps {
roomId: string;
@@ -52,6 +53,7 @@ interface IState {
busy: boolean;
}
+@replaceableComponent("views.dialogs.CommunityPrototypeInviteDialog")
export default class CommunityPrototypeInviteDialog extends React.PureComponent {
constructor(props: IProps) {
super(props);
diff --git a/src/components/views/dialogs/ConfirmAndWaitRedactDialog.js b/src/components/views/dialogs/ConfirmAndWaitRedactDialog.js
index 0622dd7dfb..37d5510756 100644
--- a/src/components/views/dialogs/ConfirmAndWaitRedactDialog.js
+++ b/src/components/views/dialogs/ConfirmAndWaitRedactDialog.js
@@ -17,6 +17,7 @@ limitations under the License.
import React from 'react';
import * as sdk from '../../../index';
import { _t } from '../../../languageHandler';
+import {replaceableComponent} from "../../../utils/replaceableComponent";
/*
* A dialog for confirming a redaction.
@@ -30,6 +31,7 @@ import { _t } from '../../../languageHandler';
*
* To avoid this, we keep the dialog open as long as /redact is in progress.
*/
+@replaceableComponent("views.dialogs.ConfirmAndWaitRedactDialog")
export default class ConfirmAndWaitRedactDialog extends React.PureComponent {
constructor(props) {
super(props);
diff --git a/src/components/views/dialogs/ConfirmRedactDialog.js b/src/components/views/dialogs/ConfirmRedactDialog.js
index 2216f9a93a..bd63d3acc1 100644
--- a/src/components/views/dialogs/ConfirmRedactDialog.js
+++ b/src/components/views/dialogs/ConfirmRedactDialog.js
@@ -17,10 +17,12 @@ limitations under the License.
import React from 'react';
import * as sdk from '../../../index';
import { _t } from '../../../languageHandler';
+import {replaceableComponent} from "../../../utils/replaceableComponent";
/*
* A dialog for confirming a redaction.
*/
+@replaceableComponent("views.dialogs.ConfirmRedactDialog")
export default class ConfirmRedactDialog extends React.Component {
render() {
const TextInputDialog = sdk.getComponent('views.dialogs.TextInputDialog');
diff --git a/src/components/views/dialogs/ConfirmUserActionDialog.js b/src/components/views/dialogs/ConfirmUserActionDialog.js
index 44f57f047e..8827f161f1 100644
--- a/src/components/views/dialogs/ConfirmUserActionDialog.js
+++ b/src/components/views/dialogs/ConfirmUserActionDialog.js
@@ -20,6 +20,7 @@ import { MatrixClient } from 'matrix-js-sdk';
import * as sdk from '../../../index';
import { _t } from '../../../languageHandler';
import { GroupMemberType } from '../../../groups';
+import {replaceableComponent} from "../../../utils/replaceableComponent";
/*
* A dialog for confirming an operation on another user.
@@ -29,6 +30,7 @@ import { GroupMemberType } from '../../../groups';
* to make it obvious what is going to happen.
* Also tweaks the style for 'dangerous' actions (albeit only with colour)
*/
+@replaceableComponent("views.dialogs.ConfirmUserActionDialog")
export default class ConfirmUserActionDialog extends React.Component {
static propTypes = {
// matrix-js-sdk (room) member object. Supply either this or 'groupMember'
diff --git a/src/components/views/dialogs/security/ConfirmDestroyCrossSigningDialog.js b/src/components/views/dialogs/security/ConfirmDestroyCrossSigningDialog.js
index abc1586205..43fb25f152 100644
--- a/src/components/views/dialogs/security/ConfirmDestroyCrossSigningDialog.js
+++ b/src/components/views/dialogs/security/ConfirmDestroyCrossSigningDialog.js
@@ -18,7 +18,9 @@ import React from 'react';
import PropTypes from 'prop-types';
import {_t} from "../../../../languageHandler";
import * as sdk from "../../../../index";
+import {replaceableComponent} from "../../../../utils/replaceableComponent";
+@replaceableComponent("views.dialogs.security.ConfirmDestroyCrossSigningDialog")
export default class ConfirmDestroyCrossSigningDialog extends React.Component {
static propTypes = {
onFinished: PropTypes.func.isRequired,
diff --git a/src/components/views/dialogs/security/CreateCrossSigningDialog.js b/src/components/views/dialogs/security/CreateCrossSigningDialog.js
index be546d2616..fedcc02f89 100644
--- a/src/components/views/dialogs/security/CreateCrossSigningDialog.js
+++ b/src/components/views/dialogs/security/CreateCrossSigningDialog.js
@@ -25,12 +25,14 @@ import DialogButtons from '../../elements/DialogButtons';
import BaseDialog from '../BaseDialog';
import Spinner from '../../elements/Spinner';
import InteractiveAuthDialog from '../InteractiveAuthDialog';
+import {replaceableComponent} from "../../../../utils/replaceableComponent";
/*
* Walks the user through the process of creating a cross-signing keys. In most
* cases, only a spinner is shown, but for more complex auth like SSO, the user
* may need to complete some steps to proceed.
*/
+@replaceableComponent("views.dialogs.security.CreateCrossSigningDialog")
export default class CreateCrossSigningDialog extends React.PureComponent {
static propTypes = {
accountPassword: PropTypes.string,
diff --git a/src/components/views/dialogs/security/SetupEncryptionDialog.js b/src/components/views/dialogs/security/SetupEncryptionDialog.js
index 9ce3144534..3c15ea9f1d 100644
--- a/src/components/views/dialogs/security/SetupEncryptionDialog.js
+++ b/src/components/views/dialogs/security/SetupEncryptionDialog.js
@@ -20,6 +20,7 @@ import SetupEncryptionBody from '../../../structures/auth/SetupEncryptionBody';
import BaseDialog from '../BaseDialog';
import { _t } from '../../../../languageHandler';
import { SetupEncryptionStore, PHASE_DONE } from '../../../../stores/SetupEncryptionStore';
+import {replaceableComponent} from "../../../../utils/replaceableComponent";
function iconFromPhase(phase) {
if (phase === PHASE_DONE) {
@@ -29,6 +30,7 @@ function iconFromPhase(phase) {
}
}
+@replaceableComponent("views.dialogs.security.SetupEncryptionDialog")
export default class SetupEncryptionDialog extends React.Component {
static propTypes = {
onFinished: PropTypes.func.isRequired,