Merge pull request #6743 from SimonBrandner/task/dialogs-ts
This commit is contained in:
commit
b13fdb698c
23 changed files with 628 additions and 542 deletions
|
@ -30,6 +30,8 @@ const HEARTBEAT_INTERVAL = 5_000; // ms
|
|||
const SESSION_UPDATE_INTERVAL = 60; // seconds
|
||||
const MAX_PENDING_EVENTS = 1000;
|
||||
|
||||
export type Rating = 1 | 2 | 3 | 4 | 5;
|
||||
|
||||
enum Orientation {
|
||||
Landscape = "landscape",
|
||||
Portrait = "portrait",
|
||||
|
@ -451,7 +453,7 @@ export default class CountlyAnalytics {
|
|||
window.removeEventListener("scroll", this.onUserActivity);
|
||||
}
|
||||
|
||||
public reportFeedback(rating: 1 | 2 | 3 | 4 | 5, comment: string) {
|
||||
public reportFeedback(rating: Rating, comment: string) {
|
||||
this.track<IStarRatingEvent>("[CLY]_star_rating", { rating, comment }, null, {}, true);
|
||||
}
|
||||
|
||||
|
|
|
@ -18,15 +18,54 @@ limitations under the License.
|
|||
|
||||
import React from 'react';
|
||||
import FocusLock from 'react-focus-lock';
|
||||
import PropTypes from 'prop-types';
|
||||
import classNames from 'classnames';
|
||||
|
||||
import { Key } from '../../../Keyboard';
|
||||
import AccessibleButton from '../elements/AccessibleButton';
|
||||
import AccessibleButton, { ButtonEvent } from '../elements/AccessibleButton';
|
||||
import { MatrixClientPeg } from '../../../MatrixClientPeg';
|
||||
import { _t } from "../../../languageHandler";
|
||||
import MatrixClientContext from "../../../contexts/MatrixClientContext";
|
||||
import { replaceableComponent } from "../../../utils/replaceableComponent";
|
||||
import { MatrixClient } from "matrix-js-sdk/src/client";
|
||||
import { IDialogProps } from "./IDialogProps";
|
||||
|
||||
interface IProps extends IDialogProps {
|
||||
// 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
|
||||
// dialog is cancelled, eg. "We can't restore your session and
|
||||
// the app cannot work". Default: true.
|
||||
hasCancel?: boolean;
|
||||
|
||||
// called when a key is pressed
|
||||
onKeyDown?: (e: KeyboardEvent | React.KeyboardEvent) => void;
|
||||
|
||||
// CSS class to apply to dialog div
|
||||
className?: string;
|
||||
|
||||
// if true, dialog container is 60% of the viewport width. Otherwise,
|
||||
// the container will have no fixed size, allowing its contents to
|
||||
// determine its size. Default: true.
|
||||
fixedWidth?: boolean;
|
||||
|
||||
// Title for the dialog.
|
||||
title?: JSX.Element | string;
|
||||
|
||||
// Path to an icon to put in the header
|
||||
headerImage?: string;
|
||||
|
||||
// children should be the content of the dialog
|
||||
children?: React.ReactNode;
|
||||
|
||||
// Id of content element
|
||||
// If provided, this is used to add a aria-describedby attribute
|
||||
contentId?: string;
|
||||
|
||||
// optional additional class for the title element (basically anything that can be passed to classnames)
|
||||
titleClass?: string | string[];
|
||||
|
||||
headerButton?: JSX.Element;
|
||||
}
|
||||
|
||||
/*
|
||||
* Basic container for modal dialogs.
|
||||
|
@ -35,54 +74,10 @@ import { replaceableComponent } from "../../../utils/replaceableComponent";
|
|||
* dialog on escape.
|
||||
*/
|
||||
@replaceableComponent("views.dialogs.BaseDialog")
|
||||
export default class BaseDialog extends React.Component {
|
||||
static propTypes = {
|
||||
// onFinished callback to call when Escape is pressed
|
||||
// Take a boolean which is true if the dialog was dismissed
|
||||
// with a positive / confirm action or false if it was
|
||||
// cancelled (BaseDialog itself only calls this with false).
|
||||
onFinished: PropTypes.func.isRequired,
|
||||
export default class BaseDialog extends React.Component<IProps> {
|
||||
private matrixClient: MatrixClient;
|
||||
|
||||
// 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
|
||||
// dialog is cancelled, eg. "We can't restore your session and
|
||||
// the app cannot work". Default: true.
|
||||
hasCancel: PropTypes.bool,
|
||||
|
||||
// called when a key is pressed
|
||||
onKeyDown: PropTypes.func,
|
||||
|
||||
// CSS class to apply to dialog div
|
||||
className: PropTypes.string,
|
||||
|
||||
// if true, dialog container is 60% of the viewport width. Otherwise,
|
||||
// the container will have no fixed size, allowing its contents to
|
||||
// determine its size. Default: true.
|
||||
fixedWidth: PropTypes.bool,
|
||||
|
||||
// Title for the dialog.
|
||||
title: PropTypes.node.isRequired,
|
||||
|
||||
// Path to an icon to put in the header
|
||||
headerImage: PropTypes.string,
|
||||
|
||||
// children should be the content of the dialog
|
||||
children: PropTypes.node,
|
||||
|
||||
// Id of content element
|
||||
// If provided, this is used to add a aria-describedby attribute
|
||||
contentId: PropTypes.string,
|
||||
|
||||
// optional additional class for the title element (basically anything that can be passed to classnames)
|
||||
titleClass: PropTypes.oneOfType([
|
||||
PropTypes.string,
|
||||
PropTypes.object,
|
||||
PropTypes.arrayOf(PropTypes.string),
|
||||
]),
|
||||
};
|
||||
|
||||
static defaultProps = {
|
||||
public static defaultProps = {
|
||||
hasCancel: true,
|
||||
fixedWidth: true,
|
||||
};
|
||||
|
@ -90,10 +85,10 @@ export default class BaseDialog extends React.Component {
|
|||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this._matrixClient = MatrixClientPeg.get();
|
||||
this.matrixClient = MatrixClientPeg.get();
|
||||
}
|
||||
|
||||
_onKeyDown = (e) => {
|
||||
private onKeyDown = (e: KeyboardEvent | React.KeyboardEvent): void => {
|
||||
if (this.props.onKeyDown) {
|
||||
this.props.onKeyDown(e);
|
||||
}
|
||||
|
@ -104,15 +99,15 @@ export default class BaseDialog extends React.Component {
|
|||
}
|
||||
};
|
||||
|
||||
_onCancelClick = (e) => {
|
||||
private onCancelClick = (e: ButtonEvent): void => {
|
||||
this.props.onFinished(false);
|
||||
};
|
||||
|
||||
render() {
|
||||
public render(): JSX.Element {
|
||||
let cancelButton;
|
||||
if (this.props.hasCancel) {
|
||||
cancelButton = (
|
||||
<AccessibleButton onClick={this._onCancelClick} className="mx_Dialog_cancelButton" aria-label={_t("Close dialog")} />
|
||||
<AccessibleButton onClick={this.onCancelClick} className="mx_Dialog_cancelButton" aria-label={_t("Close dialog")} />
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -122,11 +117,11 @@ export default class BaseDialog extends React.Component {
|
|||
}
|
||||
|
||||
return (
|
||||
<MatrixClientContext.Provider value={this._matrixClient}>
|
||||
<MatrixClientContext.Provider value={this.matrixClient}>
|
||||
<FocusLock
|
||||
returnFocus={true}
|
||||
lockProps={{
|
||||
onKeyDown: this._onKeyDown,
|
||||
onKeyDown: this.onKeyDown,
|
||||
role: "dialog",
|
||||
["aria-labelledby"]: "mx_BaseDialog_title",
|
||||
// This should point to a node describing the dialog.
|
|
@ -19,30 +19,33 @@ import QuestionDialog from './QuestionDialog';
|
|||
import { _t } from '../../../languageHandler';
|
||||
import Field from "../elements/Field";
|
||||
import AccessibleButton from "../elements/AccessibleButton";
|
||||
import CountlyAnalytics from "../../../CountlyAnalytics";
|
||||
import CountlyAnalytics, { Rating } from "../../../CountlyAnalytics";
|
||||
import SdkConfig from "../../../SdkConfig";
|
||||
import Modal from "../../../Modal";
|
||||
import BugReportDialog from "./BugReportDialog";
|
||||
import InfoDialog from "./InfoDialog";
|
||||
import StyledRadioGroup from "../elements/StyledRadioGroup";
|
||||
import { IDialogProps } from "./IDialogProps";
|
||||
|
||||
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";
|
||||
|
||||
export default (props) => {
|
||||
const [rating, setRating] = useState("");
|
||||
const [comment, setComment] = useState("");
|
||||
interface IProps extends IDialogProps {}
|
||||
|
||||
const onDebugLogsLinkClick = () => {
|
||||
const FeedbackDialog: React.FC<IProps> = (props: IProps) => {
|
||||
const [rating, setRating] = useState<Rating>();
|
||||
const [comment, setComment] = useState<string>("");
|
||||
|
||||
const onDebugLogsLinkClick = (): void => {
|
||||
props.onFinished();
|
||||
Modal.createTrackedDialog('Bug Report Dialog', '', BugReportDialog, {});
|
||||
};
|
||||
|
||||
const hasFeedback = CountlyAnalytics.instance.canEnable();
|
||||
const onFinished = (sendFeedback) => {
|
||||
const onFinished = (sendFeedback: boolean): void => {
|
||||
if (hasFeedback && sendFeedback) {
|
||||
CountlyAnalytics.instance.reportFeedback(parseInt(rating, 10), comment);
|
||||
CountlyAnalytics.instance.reportFeedback(rating, comment);
|
||||
Modal.createTrackedDialog('Feedback sent', '', InfoDialog, {
|
||||
title: _t('Feedback sent'),
|
||||
description: _t('Thank you!'),
|
||||
|
@ -65,8 +68,8 @@ export default (props) => {
|
|||
|
||||
<StyledRadioGroup
|
||||
name="feedbackRating"
|
||||
value={rating}
|
||||
onChange={setRating}
|
||||
value={String(rating)}
|
||||
onChange={(r) => setRating(parseInt(r, 10) as Rating)}
|
||||
definitions={[
|
||||
{ value: "1", label: "😠" },
|
||||
{ value: "2", label: "😞" },
|
||||
|
@ -138,7 +141,9 @@ export default (props) => {
|
|||
{ countlyFeedbackSection }
|
||||
</React.Fragment>}
|
||||
button={hasFeedback ? _t("Send feedback") : _t("Go back")}
|
||||
buttonDisabled={hasFeedback && rating === ""}
|
||||
buttonDisabled={hasFeedback && !rating}
|
||||
onFinished={onFinished}
|
||||
/>);
|
||||
};
|
||||
|
||||
export default FeedbackDialog;
|
|
@ -15,12 +15,20 @@ limitations under the License.
|
|||
*/
|
||||
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { MatrixClientPeg } from '../../../MatrixClientPeg';
|
||||
import * as sdk from '../../../index';
|
||||
import { _t } from '../../../languageHandler';
|
||||
import { replaceableComponent } from "../../../utils/replaceableComponent";
|
||||
import { mediaFromMxc } from "../../../customisations/Media";
|
||||
import VerificationComplete from "../verification/VerificationComplete";
|
||||
import VerificationCancelled from "../verification/VerificationCancelled";
|
||||
import BaseAvatar from "../avatars/BaseAvatar";
|
||||
import Spinner from "../elements/Spinner";
|
||||
import VerificationShowSas from "../verification/VerificationShowSas";
|
||||
import BaseDialog from "./BaseDialog";
|
||||
import DialogButtons from "../elements/DialogButtons";
|
||||
import { IDialogProps } from "./IDialogProps";
|
||||
import { IGeneratedSas, ISasEvent } from "matrix-js-sdk/src/crypto/verification/SAS";
|
||||
import { VerificationBase } from "matrix-js-sdk/src/crypto/verification/Base";
|
||||
|
||||
import { logger } from "matrix-js-sdk/src/logger";
|
||||
|
||||
|
@ -30,41 +38,56 @@ const PHASE_WAIT_FOR_PARTNER_TO_CONFIRM = 2;
|
|||
const PHASE_VERIFIED = 3;
|
||||
const PHASE_CANCELLED = 4;
|
||||
|
||||
@replaceableComponent("views.dialogs.IncomingSasDialog")
|
||||
export default class IncomingSasDialog extends React.Component {
|
||||
static propTypes = {
|
||||
verifier: PropTypes.object.isRequired,
|
||||
};
|
||||
interface IProps extends IDialogProps {
|
||||
verifier: VerificationBase; // TODO types
|
||||
}
|
||||
|
||||
constructor(props) {
|
||||
interface IState {
|
||||
phase: number;
|
||||
sasVerified: boolean;
|
||||
opponentProfile: {
|
||||
// eslint-disable-next-line camelcase
|
||||
avatar_url?: string;
|
||||
displayname?: string;
|
||||
};
|
||||
opponentProfileError: Error;
|
||||
sas: IGeneratedSas;
|
||||
}
|
||||
|
||||
@replaceableComponent("views.dialogs.IncomingSasDialog")
|
||||
export default class IncomingSasDialog extends React.Component<IProps, IState> {
|
||||
private showSasEvent: ISasEvent;
|
||||
|
||||
constructor(props: IProps) {
|
||||
super(props);
|
||||
|
||||
let phase = PHASE_START;
|
||||
if (this.props.verifier.cancelled) {
|
||||
if (this.props.verifier.hasBeenCancelled) {
|
||||
logger.log("Verifier was cancelled in the background.");
|
||||
phase = PHASE_CANCELLED;
|
||||
}
|
||||
|
||||
this._showSasEvent = null;
|
||||
this.showSasEvent = null;
|
||||
this.state = {
|
||||
phase: phase,
|
||||
sasVerified: false,
|
||||
opponentProfile: null,
|
||||
opponentProfileError: null,
|
||||
sas: null,
|
||||
};
|
||||
this.props.verifier.on('show_sas', this._onVerifierShowSas);
|
||||
this.props.verifier.on('cancel', this._onVerifierCancel);
|
||||
this._fetchOpponentProfile();
|
||||
this.props.verifier.on('show_sas', this.onVerifierShowSas);
|
||||
this.props.verifier.on('cancel', this.onVerifierCancel);
|
||||
this.fetchOpponentProfile();
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
public componentWillUnmount(): void {
|
||||
if (this.state.phase !== PHASE_CANCELLED && this.state.phase !== PHASE_VERIFIED) {
|
||||
this.props.verifier.cancel('User cancel');
|
||||
this.props.verifier.cancel(new Error('User cancel'));
|
||||
}
|
||||
this.props.verifier.removeListener('show_sas', this._onVerifierShowSas);
|
||||
this.props.verifier.removeListener('show_sas', this.onVerifierShowSas);
|
||||
}
|
||||
|
||||
async _fetchOpponentProfile() {
|
||||
private async fetchOpponentProfile(): Promise<void> {
|
||||
try {
|
||||
const prof = await MatrixClientPeg.get().getProfileInfo(
|
||||
this.props.verifier.userId,
|
||||
|
@ -79,53 +102,49 @@ export default class IncomingSasDialog extends React.Component {
|
|||
}
|
||||
}
|
||||
|
||||
_onFinished = () => {
|
||||
private onFinished = (): void => {
|
||||
this.props.onFinished(this.state.phase === PHASE_VERIFIED);
|
||||
}
|
||||
};
|
||||
|
||||
_onCancelClick = () => {
|
||||
private onCancelClick = (): void => {
|
||||
this.props.onFinished(this.state.phase === PHASE_VERIFIED);
|
||||
}
|
||||
};
|
||||
|
||||
_onContinueClick = () => {
|
||||
private onContinueClick = (): void => {
|
||||
this.setState({ phase: PHASE_WAIT_FOR_PARTNER_TO_CONFIRM });
|
||||
this.props.verifier.verify().then(() => {
|
||||
this.setState({ phase: PHASE_VERIFIED });
|
||||
}).catch((e) => {
|
||||
logger.log("Verification failed", e);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
_onVerifierShowSas = (e) => {
|
||||
this._showSasEvent = e;
|
||||
private onVerifierShowSas = (e: ISasEvent): void => {
|
||||
this.showSasEvent = e;
|
||||
this.setState({
|
||||
phase: PHASE_SHOW_SAS,
|
||||
sas: e.sas,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
_onVerifierCancel = (e) => {
|
||||
private onVerifierCancel = (): void => {
|
||||
this.setState({
|
||||
phase: PHASE_CANCELLED,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
_onSasMatchesClick = () => {
|
||||
this._showSasEvent.confirm();
|
||||
private onSasMatchesClick = (): void => {
|
||||
this.showSasEvent.confirm();
|
||||
this.setState({
|
||||
phase: PHASE_WAIT_FOR_PARTNER_TO_CONFIRM,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
_onVerifiedDoneClick = () => {
|
||||
private onVerifiedDoneClick = (): void => {
|
||||
this.props.onFinished(true);
|
||||
}
|
||||
|
||||
_renderPhaseStart() {
|
||||
const DialogButtons = sdk.getComponent('views.elements.DialogButtons');
|
||||
const Spinner = sdk.getComponent("views.elements.Spinner");
|
||||
const BaseAvatar = sdk.getComponent("avatars.BaseAvatar");
|
||||
};
|
||||
|
||||
private renderPhaseStart(): JSX.Element {
|
||||
const isSelf = this.props.verifier.userId === MatrixClientPeg.get().getUserId();
|
||||
|
||||
let profile;
|
||||
|
@ -192,27 +211,24 @@ export default class IncomingSasDialog extends React.Component {
|
|||
<DialogButtons
|
||||
primaryButton={_t('Continue')}
|
||||
hasCancel={true}
|
||||
onPrimaryButtonClick={this._onContinueClick}
|
||||
onCancel={this._onCancelClick}
|
||||
onPrimaryButtonClick={this.onContinueClick}
|
||||
onCancel={this.onCancelClick}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
_renderPhaseShowSas() {
|
||||
const VerificationShowSas = sdk.getComponent('views.verification.VerificationShowSas');
|
||||
private renderPhaseShowSas(): JSX.Element {
|
||||
return <VerificationShowSas
|
||||
sas={this._showSasEvent.sas}
|
||||
onCancel={this._onCancelClick}
|
||||
onDone={this._onSasMatchesClick}
|
||||
sas={this.showSasEvent.sas}
|
||||
onCancel={this.onCancelClick}
|
||||
onDone={this.onSasMatchesClick}
|
||||
isSelf={this.props.verifier.userId === MatrixClientPeg.get().getUserId()}
|
||||
inDialog={true}
|
||||
/>;
|
||||
}
|
||||
|
||||
_renderPhaseWaitForPartnerToConfirm() {
|
||||
const Spinner = sdk.getComponent("views.elements.Spinner");
|
||||
|
||||
private renderPhaseWaitForPartnerToConfirm(): JSX.Element {
|
||||
return (
|
||||
<div>
|
||||
<Spinner />
|
||||
|
@ -221,41 +237,38 @@ export default class IncomingSasDialog extends React.Component {
|
|||
);
|
||||
}
|
||||
|
||||
_renderPhaseVerified() {
|
||||
const VerificationComplete = sdk.getComponent('views.verification.VerificationComplete');
|
||||
return <VerificationComplete onDone={this._onVerifiedDoneClick} />;
|
||||
private renderPhaseVerified(): JSX.Element {
|
||||
return <VerificationComplete onDone={this.onVerifiedDoneClick} />;
|
||||
}
|
||||
|
||||
_renderPhaseCancelled() {
|
||||
const VerificationCancelled = sdk.getComponent('views.verification.VerificationCancelled');
|
||||
return <VerificationCancelled onDone={this._onCancelClick} />;
|
||||
private renderPhaseCancelled(): JSX.Element {
|
||||
return <VerificationCancelled onDone={this.onCancelClick} />;
|
||||
}
|
||||
|
||||
render() {
|
||||
public render(): JSX.Element {
|
||||
let body;
|
||||
switch (this.state.phase) {
|
||||
case PHASE_START:
|
||||
body = this._renderPhaseStart();
|
||||
body = this.renderPhaseStart();
|
||||
break;
|
||||
case PHASE_SHOW_SAS:
|
||||
body = this._renderPhaseShowSas();
|
||||
body = this.renderPhaseShowSas();
|
||||
break;
|
||||
case PHASE_WAIT_FOR_PARTNER_TO_CONFIRM:
|
||||
body = this._renderPhaseWaitForPartnerToConfirm();
|
||||
body = this.renderPhaseWaitForPartnerToConfirm();
|
||||
break;
|
||||
case PHASE_VERIFIED:
|
||||
body = this._renderPhaseVerified();
|
||||
body = this.renderPhaseVerified();
|
||||
break;
|
||||
case PHASE_CANCELLED:
|
||||
body = this._renderPhaseCancelled();
|
||||
body = this.renderPhaseCancelled();
|
||||
break;
|
||||
}
|
||||
|
||||
const BaseDialog = sdk.getComponent("dialogs.BaseDialog");
|
||||
return (
|
||||
<BaseDialog
|
||||
title={_t("Incoming Verification Request")}
|
||||
onFinished={this._onFinished}
|
||||
onFinished={this.onFinished}
|
||||
fixedWidth={false}
|
||||
>
|
||||
{ body }
|
|
@ -15,32 +15,28 @@ limitations under the License.
|
|||
*/
|
||||
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { _t } from "../../../languageHandler";
|
||||
import * as sdk from "../../../index";
|
||||
import dis from '../../../dispatcher/dispatcher';
|
||||
import { Action } from "../../../dispatcher/actions";
|
||||
import { replaceableComponent } from "../../../utils/replaceableComponent";
|
||||
import BaseDialog from "./BaseDialog";
|
||||
import DialogButtons from "../elements/DialogButtons";
|
||||
import { IDialogProps } from "./IDialogProps";
|
||||
|
||||
interface IProps extends IDialogProps {}
|
||||
|
||||
@replaceableComponent("views.dialogs.IntegrationsDisabledDialog")
|
||||
export default class IntegrationsDisabledDialog extends React.Component {
|
||||
static propTypes = {
|
||||
onFinished: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
_onAcknowledgeClick = () => {
|
||||
export default class IntegrationsDisabledDialog extends React.Component<IProps> {
|
||||
private onAcknowledgeClick = (): void => {
|
||||
this.props.onFinished();
|
||||
};
|
||||
|
||||
_onOpenSettingsClick = () => {
|
||||
private onOpenSettingsClick = (): void => {
|
||||
this.props.onFinished();
|
||||
dis.fire(Action.ViewUserSettings);
|
||||
};
|
||||
|
||||
render() {
|
||||
const BaseDialog = sdk.getComponent('views.dialogs.BaseDialog');
|
||||
const DialogButtons = sdk.getComponent('views.elements.DialogButtons');
|
||||
|
||||
public render(): JSX.Element {
|
||||
return (
|
||||
<BaseDialog
|
||||
className='mx_IntegrationsDisabledDialog'
|
||||
|
@ -53,9 +49,9 @@ export default class IntegrationsDisabledDialog extends React.Component {
|
|||
</div>
|
||||
<DialogButtons
|
||||
primaryButton={_t("Settings")}
|
||||
onPrimaryButtonClick={this._onOpenSettingsClick}
|
||||
onPrimaryButtonClick={this.onOpenSettingsClick}
|
||||
cancelButton={_t("OK")}
|
||||
onCancel={this._onAcknowledgeClick}
|
||||
onCancel={this.onAcknowledgeClick}
|
||||
/>
|
||||
</BaseDialog>
|
||||
);
|
|
@ -15,23 +15,21 @@ limitations under the License.
|
|||
*/
|
||||
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { _t } from "../../../languageHandler";
|
||||
import SdkConfig from "../../../SdkConfig";
|
||||
import * as sdk from "../../../index";
|
||||
import { replaceableComponent } from "../../../utils/replaceableComponent";
|
||||
import { IDialogProps } from "./IDialogProps";
|
||||
|
||||
interface IProps extends IDialogProps {}
|
||||
|
||||
@replaceableComponent("views.dialogs.IntegrationsImpossibleDialog")
|
||||
export default class IntegrationsImpossibleDialog extends React.Component {
|
||||
static propTypes = {
|
||||
onFinished: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
_onAcknowledgeClick = () => {
|
||||
export default class IntegrationsImpossibleDialog extends React.Component<IProps> {
|
||||
private onAcknowledgeClick = (): void => {
|
||||
this.props.onFinished();
|
||||
};
|
||||
|
||||
render() {
|
||||
public render(): JSX.Element {
|
||||
const brand = SdkConfig.get().brand;
|
||||
const BaseDialog = sdk.getComponent('views.dialogs.BaseDialog');
|
||||
const DialogButtons = sdk.getComponent('views.elements.DialogButtons');
|
||||
|
@ -54,7 +52,7 @@ export default class IntegrationsImpossibleDialog extends React.Component {
|
|||
</div>
|
||||
<DialogButtons
|
||||
primaryButton={_t("OK")}
|
||||
onPrimaryButtonClick={this._onAcknowledgeClick}
|
||||
onPrimaryButtonClick={this.onAcknowledgeClick}
|
||||
hasCancel={false}
|
||||
/>
|
||||
</BaseDialog>
|
|
@ -17,69 +17,88 @@ limitations under the License.
|
|||
*/
|
||||
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
import * as sdk from '../../../index';
|
||||
import { _t } from '../../../languageHandler';
|
||||
|
||||
import AccessibleButton from '../elements/AccessibleButton';
|
||||
import { ERROR_USER_CANCELLED } from "../../structures/InteractiveAuth";
|
||||
import InteractiveAuth, { ERROR_USER_CANCELLED } from "../../structures/InteractiveAuth";
|
||||
import { SSOAuthEntry } from "../auth/InteractiveAuthEntryComponents";
|
||||
import { replaceableComponent } from "../../../utils/replaceableComponent";
|
||||
import { MatrixClient } from "matrix-js-sdk/src/client";
|
||||
import BaseDialog from "./BaseDialog";
|
||||
import { IAuthData } from "matrix-js-sdk/src/interactive-auth";
|
||||
import { IDialogProps } from "./IDialogProps";
|
||||
|
||||
interface IDialogAesthetics {
|
||||
[x: string]: {
|
||||
[x: number]: {
|
||||
title: string;
|
||||
body: string;
|
||||
continueText: string;
|
||||
continueKind: string;
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
interface IProps extends IDialogProps {
|
||||
// matrix client to use for UI auth requests
|
||||
matrixClient: MatrixClient;
|
||||
|
||||
// response from initial request. If not supplied, will do a request on
|
||||
// mount.
|
||||
authData?: IAuthData;
|
||||
|
||||
// callback
|
||||
makeRequest: (auth: IAuthData) => Promise<IAuthData>;
|
||||
|
||||
// Optional title and body to show when not showing a particular stage
|
||||
title?: string;
|
||||
body?: string;
|
||||
|
||||
// Optional title and body pairs for particular stages and phases within
|
||||
// those stages. Object structure/example is:
|
||||
// {
|
||||
// "org.example.stage_type": {
|
||||
// 1: {
|
||||
// "body": "This is a body for phase 1" of org.example.stage_type,
|
||||
// "title": "Title for phase 1 of org.example.stage_type"
|
||||
// },
|
||||
// 2: {
|
||||
// "body": "This is a body for phase 2 of org.example.stage_type",
|
||||
// "title": "Title for phase 2 of org.example.stage_type"
|
||||
// "continueText": "Confirm identity with Example Auth",
|
||||
// "continueKind": "danger"
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// Default is defined in _getDefaultDialogAesthetics()
|
||||
aestheticsForStagePhases?: IDialogAesthetics;
|
||||
}
|
||||
|
||||
interface IState {
|
||||
authError: Error;
|
||||
|
||||
// See _onUpdateStagePhase()
|
||||
uiaStage: number | string;
|
||||
uiaStagePhase: number | string;
|
||||
}
|
||||
|
||||
@replaceableComponent("views.dialogs.InteractiveAuthDialog")
|
||||
export default class InteractiveAuthDialog extends React.Component {
|
||||
static propTypes = {
|
||||
// matrix client to use for UI auth requests
|
||||
matrixClient: PropTypes.object.isRequired,
|
||||
export default class InteractiveAuthDialog extends React.Component<IProps, IState> {
|
||||
constructor(props: IProps) {
|
||||
super(props);
|
||||
|
||||
// response from initial request. If not supplied, will do a request on
|
||||
// mount.
|
||||
authData: PropTypes.shape({
|
||||
flows: PropTypes.array,
|
||||
params: PropTypes.object,
|
||||
session: PropTypes.string,
|
||||
}),
|
||||
this.state = {
|
||||
authError: null,
|
||||
|
||||
// callback
|
||||
makeRequest: PropTypes.func.isRequired,
|
||||
// See _onUpdateStagePhase()
|
||||
uiaStage: null,
|
||||
uiaStagePhase: null,
|
||||
};
|
||||
}
|
||||
|
||||
onFinished: PropTypes.func.isRequired,
|
||||
|
||||
// Optional title and body to show when not showing a particular stage
|
||||
title: PropTypes.string,
|
||||
body: PropTypes.string,
|
||||
|
||||
// Optional title and body pairs for particular stages and phases within
|
||||
// those stages. Object structure/example is:
|
||||
// {
|
||||
// "org.example.stage_type": {
|
||||
// 1: {
|
||||
// "body": "This is a body for phase 1" of org.example.stage_type,
|
||||
// "title": "Title for phase 1 of org.example.stage_type"
|
||||
// },
|
||||
// 2: {
|
||||
// "body": "This is a body for phase 2 of org.example.stage_type",
|
||||
// "title": "Title for phase 2 of org.example.stage_type"
|
||||
// "continueText": "Confirm identity with Example Auth",
|
||||
// "continueKind": "danger"
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// Default is defined in _getDefaultDialogAesthetics()
|
||||
aestheticsForStagePhases: PropTypes.object,
|
||||
};
|
||||
|
||||
state = {
|
||||
authError: null,
|
||||
|
||||
// See _onUpdateStagePhase()
|
||||
uiaStage: null,
|
||||
uiaStagePhase: null,
|
||||
};
|
||||
|
||||
_getDefaultDialogAesthetics() {
|
||||
private getDefaultDialogAesthetics(): IDialogAesthetics {
|
||||
const ssoAesthetics = {
|
||||
[SSOAuthEntry.PHASE_PREAUTH]: {
|
||||
title: _t("Use Single Sign On to continue"),
|
||||
|
@ -101,7 +120,7 @@ export default class InteractiveAuthDialog extends React.Component {
|
|||
};
|
||||
}
|
||||
|
||||
_onAuthFinished = (success, result) => {
|
||||
private onAuthFinished = (success: boolean, result: Error): void => {
|
||||
if (success) {
|
||||
this.props.onFinished(true, result);
|
||||
} else {
|
||||
|
@ -115,19 +134,16 @@ export default class InteractiveAuthDialog extends React.Component {
|
|||
}
|
||||
};
|
||||
|
||||
_onUpdateStagePhase = (newStage, newPhase) => {
|
||||
private onUpdateStagePhase = (newStage: string | number, newPhase: string | number): void => {
|
||||
// We copy the stage and stage phase params into state for title selection in render()
|
||||
this.setState({ uiaStage: newStage, uiaStagePhase: newPhase });
|
||||
};
|
||||
|
||||
_onDismissClick = () => {
|
||||
private onDismissClick = (): void => {
|
||||
this.props.onFinished(false);
|
||||
};
|
||||
|
||||
render() {
|
||||
const InteractiveAuth = sdk.getComponent("structures.InteractiveAuth");
|
||||
const BaseDialog = sdk.getComponent('views.dialogs.BaseDialog');
|
||||
|
||||
public render(): JSX.Element {
|
||||
// Let's pick a title, body, and other params text that we'll show to the user. The order
|
||||
// is most specific first, so stagePhase > our props > defaults.
|
||||
|
||||
|
@ -135,7 +151,7 @@ export default class InteractiveAuthDialog extends React.Component {
|
|||
let body = this.state.authError ? null : this.props.body;
|
||||
let continueText = null;
|
||||
let continueKind = null;
|
||||
const dialogAesthetics = this.props.aestheticsForStagePhases || this._getDefaultDialogAesthetics();
|
||||
const dialogAesthetics = this.props.aestheticsForStagePhases || this.getDefaultDialogAesthetics();
|
||||
if (!this.state.authError && dialogAesthetics) {
|
||||
if (dialogAesthetics[this.state.uiaStage]) {
|
||||
const aesthetics = dialogAesthetics[this.state.uiaStage][this.state.uiaStagePhase];
|
||||
|
@ -152,9 +168,9 @@ export default class InteractiveAuthDialog extends React.Component {
|
|||
<div id='mx_Dialog_content'>
|
||||
<div role="alert">{ this.state.authError.message || this.state.authError.toString() }</div>
|
||||
<br />
|
||||
<AccessibleButton onClick={this._onDismissClick}
|
||||
<AccessibleButton onClick={this.onDismissClick}
|
||||
className="mx_GeneralButton"
|
||||
autoFocus="true"
|
||||
autoFocus={true}
|
||||
>
|
||||
{ _t("Dismiss") }
|
||||
</AccessibleButton>
|
||||
|
@ -165,12 +181,11 @@ export default class InteractiveAuthDialog extends React.Component {
|
|||
<div id='mx_Dialog_content'>
|
||||
{ body }
|
||||
<InteractiveAuth
|
||||
ref={this._collectInteractiveAuth}
|
||||
matrixClient={this.props.matrixClient}
|
||||
authData={this.props.authData}
|
||||
makeRequest={this.props.makeRequest}
|
||||
onAuthFinished={this._onAuthFinished}
|
||||
onStagePhaseChange={this._onUpdateStagePhase}
|
||||
onAuthFinished={this.onAuthFinished}
|
||||
onStagePhaseChange={this.onUpdateStagePhase}
|
||||
continueText={continueText}
|
||||
continueKind={continueKind}
|
||||
/>
|
|
@ -15,20 +15,29 @@ limitations under the License.
|
|||
*/
|
||||
|
||||
import React, { useState, useCallback, useRef } from 'react';
|
||||
import * as sdk from '../../../index';
|
||||
import { _t } from '../../../languageHandler';
|
||||
import SdkConfig from '../../../SdkConfig';
|
||||
import BaseDialog from "./BaseDialog";
|
||||
import DialogButtons from "../elements/DialogButtons";
|
||||
import Spinner from "../elements/Spinner";
|
||||
import { IDialogProps } from "./IDialogProps";
|
||||
|
||||
export default function KeySignatureUploadFailedDialog({
|
||||
interface IProps extends IDialogProps {
|
||||
failures: Record<string, Record<string, {
|
||||
errcode: string;
|
||||
error: string;
|
||||
}>>;
|
||||
source: string;
|
||||
continuation: () => void;
|
||||
}
|
||||
|
||||
const KeySignatureUploadFailedDialog: React.FC<IProps> = ({
|
||||
failures,
|
||||
source,
|
||||
continuation,
|
||||
onFinished,
|
||||
}) {
|
||||
}) => {
|
||||
const RETRIES = 2;
|
||||
const BaseDialog = sdk.getComponent('dialogs.BaseDialog');
|
||||
const DialogButtons = sdk.getComponent('views.elements.DialogButtons');
|
||||
const Spinner = sdk.getComponent('elements.Spinner');
|
||||
const [retry, setRetry] = useState(RETRIES);
|
||||
const [cancelled, setCancelled] = useState(false);
|
||||
const [retrying, setRetrying] = useState(false);
|
||||
|
@ -107,4 +116,6 @@ export default function KeySignatureUploadFailedDialog({
|
|||
{ body }
|
||||
</BaseDialog>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
export default KeySignatureUploadFailedDialog;
|
|
@ -19,8 +19,13 @@ import React from 'react';
|
|||
import QuestionDialog from './QuestionDialog';
|
||||
import { _t } from '../../../languageHandler';
|
||||
import SdkConfig from '../../../SdkConfig';
|
||||
import { IDialogProps } from "./IDialogProps";
|
||||
|
||||
export default (props) => {
|
||||
interface IProps extends IDialogProps {
|
||||
host: string;
|
||||
}
|
||||
|
||||
const LazyLoadingDisabledDialog: React.FC<IProps> = (props) => {
|
||||
const brand = SdkConfig.get().brand;
|
||||
const description1 = _t(
|
||||
"You've previously used %(brand)s on %(host)s with lazy loading of members enabled. " +
|
||||
|
@ -49,3 +54,5 @@ export default (props) => {
|
|||
onFinished={props.onFinished}
|
||||
/>);
|
||||
};
|
||||
|
||||
export default LazyLoadingDisabledDialog;
|
|
@ -19,8 +19,11 @@ import React from 'react';
|
|||
import QuestionDialog from './QuestionDialog';
|
||||
import { _t } from '../../../languageHandler';
|
||||
import SdkConfig from '../../../SdkConfig';
|
||||
import { IDialogProps } from "./IDialogProps";
|
||||
|
||||
export default (props) => {
|
||||
interface IProps extends IDialogProps {}
|
||||
|
||||
const LazyLoadingResyncDialog: React.FC<IProps> = (props) => {
|
||||
const brand = SdkConfig.get().brand;
|
||||
const description =
|
||||
_t(
|
||||
|
@ -38,3 +41,5 @@ export default (props) => {
|
|||
onFinished={props.onFinished}
|
||||
/>);
|
||||
};
|
||||
|
||||
export default LazyLoadingResyncDialog;
|
|
@ -19,37 +19,31 @@ limitations under the License.
|
|||
*/
|
||||
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { MatrixClientPeg } from '../../../MatrixClientPeg';
|
||||
import * as sdk from '../../../index';
|
||||
import * as FormattingUtils from '../../../utils/FormattingUtils';
|
||||
import { _t } from '../../../languageHandler';
|
||||
import { replaceableComponent } from "../../../utils/replaceableComponent";
|
||||
import QuestionDialog from "./QuestionDialog";
|
||||
import { DeviceInfo } from "matrix-js-sdk/src/crypto/deviceinfo";
|
||||
import { IDialogProps } from "./IDialogProps";
|
||||
|
||||
interface IProps extends IDialogProps {
|
||||
userId: string;
|
||||
device: DeviceInfo;
|
||||
}
|
||||
|
||||
@replaceableComponent("views.dialogs.ManualDeviceKeyVerificationDialog")
|
||||
export default class ManualDeviceKeyVerificationDialog extends React.Component {
|
||||
static propTypes = {
|
||||
userId: PropTypes.string.isRequired,
|
||||
device: PropTypes.object.isRequired,
|
||||
onFinished: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
_onCancelClick = () => {
|
||||
this.props.onFinished(false);
|
||||
}
|
||||
|
||||
_onLegacyFinished = (confirm) => {
|
||||
export default class ManualDeviceKeyVerificationDialog extends React.Component<IProps> {
|
||||
private onLegacyFinished = (confirm: boolean): void => {
|
||||
if (confirm) {
|
||||
MatrixClientPeg.get().setDeviceVerified(
|
||||
this.props.userId, this.props.device.deviceId, true,
|
||||
);
|
||||
}
|
||||
this.props.onFinished(confirm);
|
||||
}
|
||||
|
||||
render() {
|
||||
const QuestionDialog = sdk.getComponent("dialogs.QuestionDialog");
|
||||
};
|
||||
|
||||
public render(): JSX.Element {
|
||||
let text;
|
||||
if (MatrixClientPeg.get().getUserId() === this.props.userId) {
|
||||
text = _t("Confirm by comparing the following with the User Settings in your other session:");
|
||||
|
@ -81,7 +75,7 @@ export default class ManualDeviceKeyVerificationDialog extends React.Component {
|
|||
title={_t("Verify session")}
|
||||
description={body}
|
||||
button={_t("Verify session")}
|
||||
onFinished={this._onLegacyFinished}
|
||||
onFinished={this.onLegacyFinished}
|
||||
/>
|
||||
);
|
||||
}
|
|
@ -15,21 +15,39 @@ limitations under the License.
|
|||
*/
|
||||
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { MatrixClientPeg } from "../../../MatrixClientPeg";
|
||||
import { _t } from '../../../languageHandler';
|
||||
import * as sdk from "../../../index";
|
||||
import { wantsDateSeparator } from '../../../DateUtils';
|
||||
import SettingsStore from '../../../settings/SettingsStore';
|
||||
import { replaceableComponent } from "../../../utils/replaceableComponent";
|
||||
import { MatrixEvent } from "matrix-js-sdk/src/models/event";
|
||||
import BaseDialog from "./BaseDialog";
|
||||
import ScrollPanel from "../../structures/ScrollPanel";
|
||||
import Spinner from "../elements/Spinner";
|
||||
import EditHistoryMessage from "../messages/EditHistoryMessage";
|
||||
import DateSeparator from "../messages/DateSeparator";
|
||||
import { IDialogProps } from "./IDialogProps";
|
||||
import { EventType, RelationType } from "matrix-js-sdk/src/@types/event";
|
||||
import { defer } from "matrix-js-sdk/src/utils";
|
||||
|
||||
interface IProps extends IDialogProps {
|
||||
mxEvent: MatrixEvent;
|
||||
}
|
||||
|
||||
interface IState {
|
||||
originalEvent: MatrixEvent;
|
||||
error: {
|
||||
errcode: string;
|
||||
};
|
||||
events: MatrixEvent[];
|
||||
nextBatch: string;
|
||||
isLoading: boolean;
|
||||
isTwelveHour: boolean;
|
||||
}
|
||||
|
||||
@replaceableComponent("views.dialogs.MessageEditHistoryDialog")
|
||||
export default class MessageEditHistoryDialog extends React.PureComponent {
|
||||
static propTypes = {
|
||||
mxEvent: PropTypes.object.isRequired,
|
||||
};
|
||||
|
||||
constructor(props) {
|
||||
export default class MessageEditHistoryDialog extends React.PureComponent<IProps, IState> {
|
||||
constructor(props: IProps) {
|
||||
super(props);
|
||||
this.state = {
|
||||
originalEvent: null,
|
||||
|
@ -41,7 +59,7 @@ export default class MessageEditHistoryDialog extends React.PureComponent {
|
|||
};
|
||||
}
|
||||
|
||||
loadMoreEdits = async (backwards) => {
|
||||
private loadMoreEdits = async (backwards?: boolean): Promise<boolean> => {
|
||||
if (backwards || (!this.state.nextBatch && !this.state.isLoading)) {
|
||||
// bail out on backwards as we only paginate in one direction
|
||||
return false;
|
||||
|
@ -50,13 +68,13 @@ export default class MessageEditHistoryDialog extends React.PureComponent {
|
|||
const roomId = this.props.mxEvent.getRoomId();
|
||||
const eventId = this.props.mxEvent.getId();
|
||||
const client = MatrixClientPeg.get();
|
||||
|
||||
const { resolve, reject, promise } = defer<boolean>();
|
||||
let result;
|
||||
let resolve;
|
||||
let reject;
|
||||
const promise = new Promise((_resolve, _reject) => {resolve = _resolve; reject = _reject;});
|
||||
|
||||
try {
|
||||
result = await client.relations(
|
||||
roomId, eventId, "m.replace", "m.room.message", opts);
|
||||
roomId, eventId, RelationType.Replace, EventType.RoomMessage, opts);
|
||||
} catch (error) {
|
||||
// log if the server returned an error
|
||||
if (error.errcode) {
|
||||
|
@ -67,7 +85,7 @@ export default class MessageEditHistoryDialog extends React.PureComponent {
|
|||
}
|
||||
|
||||
const newEvents = result.events;
|
||||
this._locallyRedactEventsIfNeeded(newEvents);
|
||||
this.locallyRedactEventsIfNeeded(newEvents);
|
||||
this.setState({
|
||||
originalEvent: this.state.originalEvent || result.originalEvent,
|
||||
events: this.state.events.concat(newEvents),
|
||||
|
@ -78,9 +96,9 @@ export default class MessageEditHistoryDialog extends React.PureComponent {
|
|||
resolve(hasMoreResults);
|
||||
});
|
||||
return promise;
|
||||
}
|
||||
};
|
||||
|
||||
_locallyRedactEventsIfNeeded(newEvents) {
|
||||
private locallyRedactEventsIfNeeded(newEvents: MatrixEvent[]): void {
|
||||
const roomId = this.props.mxEvent.getRoomId();
|
||||
const client = MatrixClientPeg.get();
|
||||
const room = client.getRoom(roomId);
|
||||
|
@ -95,13 +113,11 @@ export default class MessageEditHistoryDialog extends React.PureComponent {
|
|||
}
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
public componentDidMount(): void {
|
||||
this.loadMoreEdits();
|
||||
}
|
||||
|
||||
_renderEdits() {
|
||||
const EditHistoryMessage = sdk.getComponent('messages.EditHistoryMessage');
|
||||
const DateSeparator = sdk.getComponent('messages.DateSeparator');
|
||||
private renderEdits(): JSX.Element[] {
|
||||
const nodes = [];
|
||||
let lastEvent;
|
||||
let allEvents = this.state.events;
|
||||
|
@ -128,7 +144,7 @@ export default class MessageEditHistoryDialog extends React.PureComponent {
|
|||
return nodes;
|
||||
}
|
||||
|
||||
render() {
|
||||
public render(): JSX.Element {
|
||||
let content;
|
||||
if (this.state.error) {
|
||||
const { error } = this.state;
|
||||
|
@ -149,20 +165,17 @@ export default class MessageEditHistoryDialog extends React.PureComponent {
|
|||
</p>);
|
||||
}
|
||||
} else if (this.state.isLoading) {
|
||||
const Spinner = sdk.getComponent("elements.Spinner");
|
||||
content = <Spinner />;
|
||||
} else {
|
||||
const ScrollPanel = sdk.getComponent("structures.ScrollPanel");
|
||||
content = (<ScrollPanel
|
||||
className="mx_MessageEditHistoryDialog_scrollPanel"
|
||||
onFillRequest={this.loadMoreEdits}
|
||||
stickyBottom={false}
|
||||
startAtBottom={false}
|
||||
>
|
||||
<ul className="mx_MessageEditHistoryDialog_edits">{ this._renderEdits() }</ul>
|
||||
<ul className="mx_MessageEditHistoryDialog_edits">{ this.renderEdits() }</ul>
|
||||
</ScrollPanel>);
|
||||
}
|
||||
const BaseDialog = sdk.getComponent('views.dialogs.BaseDialog');
|
||||
return (
|
||||
<BaseDialog
|
||||
className='mx_MessageEditHistoryDialog'
|
|
@ -16,29 +16,30 @@ limitations under the License.
|
|||
*/
|
||||
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import classNames from "classnames";
|
||||
|
||||
import * as sdk from '../../../index';
|
||||
import { _t } from '../../../languageHandler';
|
||||
import { IDialogProps } from "./IDialogProps";
|
||||
|
||||
export default class QuestionDialog extends React.Component {
|
||||
static propTypes = {
|
||||
title: PropTypes.string,
|
||||
description: PropTypes.node,
|
||||
extraButtons: PropTypes.node,
|
||||
button: PropTypes.string,
|
||||
buttonDisabled: PropTypes.bool,
|
||||
danger: PropTypes.bool,
|
||||
focus: PropTypes.bool,
|
||||
onFinished: PropTypes.func.isRequired,
|
||||
headerImage: PropTypes.string,
|
||||
quitOnly: PropTypes.bool, // quitOnly doesn't show the cancel button just the quit [x].
|
||||
fixedWidth: PropTypes.bool,
|
||||
className: PropTypes.string,
|
||||
};
|
||||
interface IProps extends IDialogProps {
|
||||
title?: string;
|
||||
description?: React.ReactNode;
|
||||
extraButtons?: React.ReactNode;
|
||||
button?: string;
|
||||
buttonDisabled?: boolean;
|
||||
danger?: boolean;
|
||||
focus?: boolean;
|
||||
headerImage?: string;
|
||||
quitOnly?: boolean; // quitOnly doesn't show the cancel button just the quit [x].
|
||||
fixedWidth?: boolean;
|
||||
className?: string;
|
||||
hasCancelButton?: boolean;
|
||||
cancelButton?: React.ReactNode;
|
||||
}
|
||||
|
||||
static defaultProps = {
|
||||
export default class QuestionDialog extends React.Component<IProps> {
|
||||
public static defaultProps: Partial<IProps> = {
|
||||
title: "",
|
||||
description: "",
|
||||
extraButtons: null,
|
||||
|
@ -48,17 +49,19 @@ export default class QuestionDialog extends React.Component {
|
|||
quitOnly: false,
|
||||
};
|
||||
|
||||
onOk = () => {
|
||||
private onOk = (): void => {
|
||||
this.props.onFinished(true);
|
||||
};
|
||||
|
||||
onCancel = () => {
|
||||
private onCancel = (): void => {
|
||||
this.props.onFinished(false);
|
||||
};
|
||||
|
||||
render() {
|
||||
public render(): JSX.Element {
|
||||
// Converting these to imports breaks wrench tests
|
||||
const BaseDialog = sdk.getComponent('views.dialogs.BaseDialog');
|
||||
const DialogButtons = sdk.getComponent('views.elements.DialogButtons');
|
||||
|
||||
let primaryButtonClass = "";
|
||||
if (this.props.danger) {
|
||||
primaryButtonClass = "danger";
|
|
@ -17,27 +17,27 @@ limitations under the License.
|
|||
*/
|
||||
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import * as sdk from '../../../index';
|
||||
import SdkConfig from '../../../SdkConfig';
|
||||
import Modal from '../../../Modal';
|
||||
import { _t } from '../../../languageHandler';
|
||||
import { replaceableComponent } from "../../../utils/replaceableComponent";
|
||||
import QuestionDialog from "./QuestionDialog";
|
||||
import BugReportDialog from "./BugReportDialog";
|
||||
import BaseDialog from "./BaseDialog";
|
||||
import DialogButtons from "../elements/DialogButtons";
|
||||
import { IDialogProps } from "./IDialogProps";
|
||||
|
||||
interface IProps extends IDialogProps {
|
||||
error: string;
|
||||
}
|
||||
|
||||
@replaceableComponent("views.dialogs.SessionRestoreErrorDialog")
|
||||
export default class SessionRestoreErrorDialog extends React.Component {
|
||||
static propTypes = {
|
||||
error: PropTypes.string.isRequired,
|
||||
onFinished: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
_sendBugReport = () => {
|
||||
const BugReportDialog = sdk.getComponent("dialogs.BugReportDialog");
|
||||
export default class SessionRestoreErrorDialog extends React.Component<IProps> {
|
||||
private sendBugReport = (): void => {
|
||||
Modal.createTrackedDialog('Session Restore Error', 'Send Bug Report Dialog', BugReportDialog, {});
|
||||
};
|
||||
|
||||
_onClearStorageClick = () => {
|
||||
const QuestionDialog = sdk.getComponent("dialogs.QuestionDialog");
|
||||
private onClearStorageClick = (): void => {
|
||||
Modal.createTrackedDialog('Session Restore Confirm Logout', '', QuestionDialog, {
|
||||
title: _t("Sign out"),
|
||||
description:
|
||||
|
@ -48,19 +48,17 @@ export default class SessionRestoreErrorDialog extends React.Component {
|
|||
});
|
||||
};
|
||||
|
||||
_onRefreshClick = () => {
|
||||
private onRefreshClick = (): void => {
|
||||
// Is this likely to help? Probably not, but giving only one button
|
||||
// that clears your storage seems awful.
|
||||
window.location.reload(true);
|
||||
window.location.reload();
|
||||
};
|
||||
|
||||
render() {
|
||||
public render(): JSX.Element {
|
||||
const brand = SdkConfig.get().brand;
|
||||
const BaseDialog = sdk.getComponent('views.dialogs.BaseDialog');
|
||||
const DialogButtons = sdk.getComponent('views.elements.DialogButtons');
|
||||
|
||||
const clearStorageButton = (
|
||||
<button onClick={this._onClearStorageClick} className="danger">
|
||||
<button onClick={this.onClearStorageClick} className="danger">
|
||||
{ _t("Clear Storage and Sign Out") }
|
||||
</button>
|
||||
);
|
||||
|
@ -68,7 +66,7 @@ export default class SessionRestoreErrorDialog extends React.Component {
|
|||
let dialogButtons;
|
||||
if (SdkConfig.get().bug_report_endpoint_url) {
|
||||
dialogButtons = <DialogButtons primaryButton={_t("Send Logs")}
|
||||
onPrimaryButtonClick={this._sendBugReport}
|
||||
onPrimaryButtonClick={this.sendBugReport}
|
||||
focus={true}
|
||||
hasCancel={false}
|
||||
>
|
||||
|
@ -76,7 +74,7 @@ export default class SessionRestoreErrorDialog extends React.Component {
|
|||
</DialogButtons>;
|
||||
} else {
|
||||
dialogButtons = <DialogButtons primaryButton={_t("Refresh")}
|
||||
onPrimaryButtonClick={this._onRefreshClick}
|
||||
onPrimaryButtonClick={this.onRefreshClick}
|
||||
focus={true}
|
||||
hasCancel={false}
|
||||
>
|
|
@ -16,13 +16,26 @@ limitations under the License.
|
|||
*/
|
||||
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import * as sdk from '../../../index';
|
||||
import * as Email from '../../../email';
|
||||
import AddThreepid from '../../../AddThreepid';
|
||||
import { _t } from '../../../languageHandler';
|
||||
import Modal from '../../../Modal';
|
||||
import { replaceableComponent } from "../../../utils/replaceableComponent";
|
||||
import Spinner from "../elements/Spinner";
|
||||
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 {
|
||||
title: string;
|
||||
}
|
||||
|
||||
interface IState {
|
||||
emailAddress: string;
|
||||
emailBusy: boolean;
|
||||
}
|
||||
|
||||
/*
|
||||
* Prompt the user to set an email address.
|
||||
|
@ -30,26 +43,25 @@ import { replaceableComponent } from "../../../utils/replaceableComponent";
|
|||
* On success, `onFinished(true)` is called.
|
||||
*/
|
||||
@replaceableComponent("views.dialogs.SetEmailDialog")
|
||||
export default class SetEmailDialog extends React.Component {
|
||||
static propTypes = {
|
||||
onFinished: PropTypes.func.isRequired,
|
||||
};
|
||||
export default class SetEmailDialog extends React.Component<IProps, IState> {
|
||||
private addThreepid: AddThreepid;
|
||||
|
||||
state = {
|
||||
emailAddress: '',
|
||||
emailBusy: false,
|
||||
};
|
||||
constructor(props: IProps) {
|
||||
super(props);
|
||||
|
||||
onEmailAddressChanged = value => {
|
||||
this.state = {
|
||||
emailAddress: '',
|
||||
emailBusy: false,
|
||||
};
|
||||
}
|
||||
|
||||
private onEmailAddressChanged = (value: string): void => {
|
||||
this.setState({
|
||||
emailAddress: value,
|
||||
});
|
||||
};
|
||||
|
||||
onSubmit = () => {
|
||||
const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
|
||||
const QuestionDialog = sdk.getComponent("dialogs.QuestionDialog");
|
||||
|
||||
private onSubmit = (): void => {
|
||||
const emailAddress = this.state.emailAddress;
|
||||
if (!Email.looksValid(emailAddress)) {
|
||||
Modal.createTrackedDialog('Invalid Email Address', '', ErrorDialog, {
|
||||
|
@ -58,8 +70,8 @@ export default class SetEmailDialog extends React.Component {
|
|||
});
|
||||
return;
|
||||
}
|
||||
this._addThreepid = new AddThreepid();
|
||||
this._addThreepid.addEmailAddress(emailAddress).then(() => {
|
||||
this.addThreepid = new AddThreepid();
|
||||
this.addThreepid.addEmailAddress(emailAddress).then(() => {
|
||||
Modal.createTrackedDialog('Verification Pending', '', QuestionDialog, {
|
||||
title: _t("Verification Pending"),
|
||||
description: _t(
|
||||
|
@ -80,11 +92,11 @@ export default class SetEmailDialog extends React.Component {
|
|||
this.setState({ emailBusy: true });
|
||||
};
|
||||
|
||||
onCancelled = () => {
|
||||
private onCancelled = (): void => {
|
||||
this.props.onFinished(false);
|
||||
};
|
||||
|
||||
onEmailDialogFinished = ok => {
|
||||
private onEmailDialogFinished = (ok: boolean): void => {
|
||||
if (ok) {
|
||||
this.verifyEmailAddress();
|
||||
} else {
|
||||
|
@ -92,13 +104,12 @@ export default class SetEmailDialog extends React.Component {
|
|||
}
|
||||
};
|
||||
|
||||
verifyEmailAddress() {
|
||||
this._addThreepid.checkEmailLinkClicked().then(() => {
|
||||
private verifyEmailAddress(): void {
|
||||
this.addThreepid.checkEmailLinkClicked().then(() => {
|
||||
this.props.onFinished(true);
|
||||
}, (err) => {
|
||||
this.setState({ emailBusy: false });
|
||||
if (err.errcode == 'M_THREEPID_AUTH_FAILED') {
|
||||
const QuestionDialog = sdk.getComponent("dialogs.QuestionDialog");
|
||||
const message = _t("Unable to verify email address.") + " " +
|
||||
_t("Please check your email and click on the link it contains. Once this is done, click continue.");
|
||||
Modal.createTrackedDialog('Verification Pending', '3pid Auth Failed', QuestionDialog, {
|
||||
|
@ -108,7 +119,6 @@ export default class SetEmailDialog extends React.Component {
|
|||
onFinished: this.onEmailDialogFinished,
|
||||
});
|
||||
} else {
|
||||
const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
|
||||
console.error("Unable to verify email address: " + err);
|
||||
Modal.createTrackedDialog('Unable to verify email address', '', ErrorDialog, {
|
||||
title: _t("Unable to verify email address."),
|
||||
|
@ -118,15 +128,10 @@ export default class SetEmailDialog extends React.Component {
|
|||
});
|
||||
}
|
||||
|
||||
render() {
|
||||
const BaseDialog = sdk.getComponent('views.dialogs.BaseDialog');
|
||||
const Spinner = sdk.getComponent('elements.Spinner');
|
||||
const EditableText = sdk.getComponent('elements.EditableText');
|
||||
|
||||
public render(): JSX.Element {
|
||||
const emailInput = this.state.emailBusy ? <Spinner /> : <EditableText
|
||||
initialValue={this.state.emailAddress}
|
||||
className="mx_SetEmailDialog_email_input"
|
||||
autoFocus="true"
|
||||
placeholder={_t("Email address")}
|
||||
placeholderClassName="mx_SetEmailDialog_email_input_placeholder"
|
||||
blurToCancel={false}
|
|
@ -17,11 +17,12 @@ limitations under the License.
|
|||
import React from 'react';
|
||||
import { _t } from "../../../languageHandler";
|
||||
import { CommandCategories, Commands } from "../../../SlashCommands";
|
||||
import * as sdk from "../../../index";
|
||||
import { IDialogProps } from "./IDialogProps";
|
||||
import InfoDialog from "./InfoDialog";
|
||||
|
||||
export default ({ onFinished }) => {
|
||||
const InfoDialog = sdk.getComponent('dialogs.InfoDialog');
|
||||
interface IProps extends IDialogProps {}
|
||||
|
||||
const SlashCommandHelpDialog: React.FC<IProps> = ({ onFinished }) => {
|
||||
const categories = {};
|
||||
Commands.forEach(cmd => {
|
||||
if (!cmd.isEnabled()) return;
|
||||
|
@ -62,3 +63,5 @@ export default ({ onFinished }) => {
|
|||
hasCloseButton={true}
|
||||
onFinished={onFinished} />;
|
||||
};
|
||||
|
||||
export default SlashCommandHelpDialog;
|
|
@ -15,40 +15,36 @@ limitations under the License.
|
|||
*/
|
||||
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import * as sdk from '../../../index';
|
||||
import SdkConfig from '../../../SdkConfig';
|
||||
import Modal from '../../../Modal';
|
||||
import { _t } from '../../../languageHandler';
|
||||
import { replaceableComponent } from "../../../utils/replaceableComponent";
|
||||
import BaseDialog from "./BaseDialog";
|
||||
import DialogButtons from "../elements/DialogButtons";
|
||||
import BugReportDialog from "./BugReportDialog";
|
||||
import { IDialogProps } from "./IDialogProps";
|
||||
|
||||
interface IProps extends IDialogProps { }
|
||||
|
||||
@replaceableComponent("views.dialogs.StorageEvictedDialog")
|
||||
export default class StorageEvictedDialog extends React.Component {
|
||||
static propTypes = {
|
||||
onFinished: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
_sendBugReport = ev => {
|
||||
export default class StorageEvictedDialog extends React.Component<IProps> {
|
||||
private sendBugReport = (ev: React.MouseEvent): void => {
|
||||
ev.preventDefault();
|
||||
const BugReportDialog = sdk.getComponent("dialogs.BugReportDialog");
|
||||
Modal.createTrackedDialog('Storage evicted', 'Send Bug Report Dialog', BugReportDialog, {});
|
||||
};
|
||||
|
||||
_onSignOutClick = () => {
|
||||
private onSignOutClick = (): void => {
|
||||
this.props.onFinished(true);
|
||||
};
|
||||
|
||||
render() {
|
||||
const BaseDialog = sdk.getComponent('views.dialogs.BaseDialog');
|
||||
const DialogButtons = sdk.getComponent('views.elements.DialogButtons');
|
||||
|
||||
public render(): JSX.Element {
|
||||
let logRequest;
|
||||
if (SdkConfig.get().bug_report_endpoint_url) {
|
||||
logRequest = _t(
|
||||
"To help us prevent this in future, please <a>send us logs</a>.",
|
||||
{},
|
||||
{
|
||||
a: text => <a href="#" onClick={this._sendBugReport}>{ text }</a>,
|
||||
a: text => <a href="#" onClick={this.sendBugReport}>{ text }</a>,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
@ -73,7 +69,7 @@ export default class StorageEvictedDialog extends React.Component {
|
|||
) } { logRequest }</p>
|
||||
</div>
|
||||
<DialogButtons primaryButton={_t("Sign out")}
|
||||
onPrimaryButtonClick={this._onSignOutClick}
|
||||
onPrimaryButtonClick={this.onSignOutClick}
|
||||
focus={true}
|
||||
hasCancel={false}
|
||||
/>
|
|
@ -15,42 +15,47 @@ limitations under the License.
|
|||
*/
|
||||
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { IntegrationManagers } from "../../../integrations/IntegrationManagers";
|
||||
import { Room } from "matrix-js-sdk/src/models/room";
|
||||
import * as sdk from '../../../index';
|
||||
import { dialogTermsInteractionCallback, TermsNotSignedError } from "../../../Terms";
|
||||
import classNames from 'classnames';
|
||||
import * as ScalarMessaging from "../../../ScalarMessaging";
|
||||
import { replaceableComponent } from "../../../utils/replaceableComponent";
|
||||
import { IntegrationManagerInstance } from "../../../integrations/IntegrationManagerInstance";
|
||||
import ScalarAuthClient from "../../../ScalarAuthClient";
|
||||
import AccessibleButton from "../elements/AccessibleButton";
|
||||
import IntegrationManager from "../settings/IntegrationManager";
|
||||
import { IDialogProps } from "./IDialogProps";
|
||||
|
||||
interface IProps extends IDialogProps {
|
||||
/**
|
||||
* Optional room where the integration manager should be open to
|
||||
*/
|
||||
room?: Room;
|
||||
|
||||
/**
|
||||
* Optional screen to open on the integration manager
|
||||
*/
|
||||
screen?: string;
|
||||
|
||||
/**
|
||||
* Optional integration ID to open in the integration manager
|
||||
*/
|
||||
integrationId?: string;
|
||||
}
|
||||
|
||||
interface IState {
|
||||
managers: IntegrationManagerInstance[];
|
||||
busy: boolean;
|
||||
currentIndex: number;
|
||||
currentConnected: boolean;
|
||||
currentLoading: boolean;
|
||||
currentScalarClient: ScalarAuthClient;
|
||||
}
|
||||
|
||||
@replaceableComponent("views.dialogs.TabbedIntegrationManagerDialog")
|
||||
export default class TabbedIntegrationManagerDialog extends React.Component {
|
||||
static propTypes = {
|
||||
/**
|
||||
* Called with:
|
||||
* * success {bool} True if the user accepted any douments, false if cancelled
|
||||
* * agreedUrls {string[]} List of agreed URLs
|
||||
*/
|
||||
onFinished: PropTypes.func.isRequired,
|
||||
|
||||
/**
|
||||
* Optional room where the integration manager should be open to
|
||||
*/
|
||||
room: PropTypes.instanceOf(Room),
|
||||
|
||||
/**
|
||||
* Optional screen to open on the integration manager
|
||||
*/
|
||||
screen: PropTypes.string,
|
||||
|
||||
/**
|
||||
* Optional integration ID to open in the integration manager
|
||||
*/
|
||||
integrationId: PropTypes.string,
|
||||
};
|
||||
|
||||
constructor(props) {
|
||||
export default class TabbedIntegrationManagerDialog extends React.Component<IProps, IState> {
|
||||
constructor(props: IProps) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
|
@ -63,11 +68,11 @@ export default class TabbedIntegrationManagerDialog extends React.Component {
|
|||
};
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
public componentDidMount(): void {
|
||||
this.openManager(0, true);
|
||||
}
|
||||
|
||||
openManager = async (i, force = false) => {
|
||||
private openManager = async (i: number, force = false): Promise<void> => {
|
||||
if (i === this.state.currentIndex && !force) return;
|
||||
|
||||
const manager = this.state.managers[i];
|
||||
|
@ -120,8 +125,7 @@ export default class TabbedIntegrationManagerDialog extends React.Component {
|
|||
}
|
||||
};
|
||||
|
||||
_renderTabs() {
|
||||
const AccessibleButton = sdk.getComponent("views.elements.AccessibleButton");
|
||||
private renderTabs(): JSX.Element[] {
|
||||
return this.state.managers.map((m, i) => {
|
||||
const classes = classNames({
|
||||
'mx_TabbedIntegrationManagerDialog_tab': true,
|
||||
|
@ -140,8 +144,7 @@ export default class TabbedIntegrationManagerDialog extends React.Component {
|
|||
});
|
||||
}
|
||||
|
||||
_renderTab() {
|
||||
const IntegrationManager = sdk.getComponent("views.settings.IntegrationManager");
|
||||
public renderTab(): JSX.Element {
|
||||
let uiUrl = null;
|
||||
if (this.state.currentScalarClient) {
|
||||
uiUrl = this.state.currentScalarClient.getScalarInterfaceUrlForRoom(
|
||||
|
@ -151,7 +154,6 @@ export default class TabbedIntegrationManagerDialog extends React.Component {
|
|||
);
|
||||
}
|
||||
return <IntegrationManager
|
||||
configured={true}
|
||||
loading={this.state.currentLoading}
|
||||
connected={this.state.currentConnected}
|
||||
url={uiUrl}
|
||||
|
@ -159,14 +161,14 @@ export default class TabbedIntegrationManagerDialog extends React.Component {
|
|||
/>;
|
||||
}
|
||||
|
||||
render() {
|
||||
public render(): JSX.Element {
|
||||
return (
|
||||
<div className='mx_TabbedIntegrationManagerDialog_container'>
|
||||
<div className='mx_TabbedIntegrationManagerDialog_tabs'>
|
||||
{ this._renderTabs() }
|
||||
{ this.renderTabs() }
|
||||
</div>
|
||||
<div className='mx_TabbedIntegrationManagerDialog_currentManager'>
|
||||
{ this._renderTab() }
|
||||
{ this.renderTab() }
|
||||
</div>
|
||||
</div>
|
||||
);
|
|
@ -14,33 +14,39 @@ See the License for the specific language governing permissions and
|
|||
limitations under the License.
|
||||
*/
|
||||
|
||||
import React, { createRef } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import * as sdk from '../../../index';
|
||||
import React, { ChangeEvent, createRef } from 'react';
|
||||
import Field from "../elements/Field";
|
||||
import { _t, _td } from '../../../languageHandler';
|
||||
import { replaceableComponent } from "../../../utils/replaceableComponent";
|
||||
import { IFieldState, IValidationResult } from "../elements/Validation";
|
||||
import BaseDialog from "./BaseDialog";
|
||||
import DialogButtons from "../elements/DialogButtons";
|
||||
import { IDialogProps } from "./IDialogProps";
|
||||
|
||||
interface IProps extends IDialogProps {
|
||||
title?: string;
|
||||
description?: React.ReactNode;
|
||||
value?: string;
|
||||
placeholder?: string;
|
||||
button?: string;
|
||||
busyMessage?: string; // pass _td string
|
||||
focus?: boolean;
|
||||
hasCancel?: boolean;
|
||||
validator?: (fieldState: IFieldState) => IValidationResult; // result of withValidation
|
||||
fixedWidth?: boolean;
|
||||
}
|
||||
|
||||
interface IState {
|
||||
value: string;
|
||||
busy: boolean;
|
||||
valid: boolean;
|
||||
}
|
||||
|
||||
@replaceableComponent("views.dialogs.TextInputDialog")
|
||||
export default class TextInputDialog extends React.Component {
|
||||
static propTypes = {
|
||||
title: PropTypes.string,
|
||||
description: PropTypes.oneOfType([
|
||||
PropTypes.element,
|
||||
PropTypes.string,
|
||||
]),
|
||||
value: PropTypes.string,
|
||||
placeholder: PropTypes.string,
|
||||
button: PropTypes.string,
|
||||
busyMessage: PropTypes.string, // pass _td string
|
||||
focus: PropTypes.bool,
|
||||
onFinished: PropTypes.func.isRequired,
|
||||
hasCancel: PropTypes.bool,
|
||||
validator: PropTypes.func, // result of withValidation
|
||||
fixedWidth: PropTypes.bool,
|
||||
};
|
||||
export default class TextInputDialog extends React.Component<IProps, IState> {
|
||||
private field = createRef<Field>();
|
||||
|
||||
static defaultProps = {
|
||||
public static defaultProps = {
|
||||
title: "",
|
||||
value: "",
|
||||
description: "",
|
||||
|
@ -49,11 +55,9 @@ export default class TextInputDialog extends React.Component {
|
|||
hasCancel: true,
|
||||
};
|
||||
|
||||
constructor(props) {
|
||||
constructor(props: IProps) {
|
||||
super(props);
|
||||
|
||||
this._field = createRef();
|
||||
|
||||
this.state = {
|
||||
value: this.props.value,
|
||||
busy: false,
|
||||
|
@ -61,23 +65,23 @@ export default class TextInputDialog extends React.Component {
|
|||
};
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
public componentDidMount(): void {
|
||||
if (this.props.focus) {
|
||||
// Set the cursor at the end of the text input
|
||||
// this._field.current.value = this.props.value;
|
||||
this._field.current.focus();
|
||||
this.field.current.focus();
|
||||
}
|
||||
}
|
||||
|
||||
onOk = async ev => {
|
||||
private onOk = async (ev: React.FormEvent): Promise<void> => {
|
||||
ev.preventDefault();
|
||||
if (this.props.validator) {
|
||||
this.setState({ busy: true });
|
||||
await this._field.current.validate({ allowEmpty: false });
|
||||
await this.field.current.validate({ allowEmpty: false });
|
||||
|
||||
if (!this._field.current.state.valid) {
|
||||
this._field.current.focus();
|
||||
this._field.current.validate({ allowEmpty: false, focused: true });
|
||||
if (!this.field.current.state.valid) {
|
||||
this.field.current.focus();
|
||||
this.field.current.validate({ allowEmpty: false, focused: true });
|
||||
this.setState({ busy: false });
|
||||
return;
|
||||
}
|
||||
|
@ -85,17 +89,17 @@ export default class TextInputDialog extends React.Component {
|
|||
this.props.onFinished(true, this.state.value);
|
||||
};
|
||||
|
||||
onCancel = () => {
|
||||
private onCancel = (): void => {
|
||||
this.props.onFinished(false);
|
||||
};
|
||||
|
||||
onChange = ev => {
|
||||
private onChange = (ev: ChangeEvent<HTMLInputElement>): void => {
|
||||
this.setState({
|
||||
value: ev.target.value,
|
||||
});
|
||||
};
|
||||
|
||||
onValidate = async fieldState => {
|
||||
private onValidate = async (fieldState: IFieldState): Promise<IValidationResult> => {
|
||||
const result = await this.props.validator(fieldState);
|
||||
this.setState({
|
||||
valid: result.valid,
|
||||
|
@ -103,9 +107,7 @@ export default class TextInputDialog extends React.Component {
|
|||
return result;
|
||||
};
|
||||
|
||||
render() {
|
||||
const BaseDialog = sdk.getComponent('views.dialogs.BaseDialog');
|
||||
const DialogButtons = sdk.getComponent('views.elements.DialogButtons');
|
||||
public render(): JSX.Element {
|
||||
return (
|
||||
<BaseDialog
|
||||
className="mx_TextInputDialog"
|
||||
|
@ -121,13 +123,12 @@ export default class TextInputDialog extends React.Component {
|
|||
<div>
|
||||
<Field
|
||||
className="mx_TextInputDialog_input"
|
||||
ref={this._field}
|
||||
ref={this.field}
|
||||
type="text"
|
||||
label={this.props.placeholder}
|
||||
value={this.state.value}
|
||||
onChange={this.onChange}
|
||||
onValidate={this.props.validator ? this.onValidate : undefined}
|
||||
size="64"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
|
@ -17,11 +17,18 @@ limitations under the License.
|
|||
import filesize from 'filesize';
|
||||
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import * as sdk from '../../../index';
|
||||
import { _t } from '../../../languageHandler';
|
||||
import ContentMessages from '../../../ContentMessages';
|
||||
import { replaceableComponent } from "../../../utils/replaceableComponent";
|
||||
import BaseDialog from "./BaseDialog";
|
||||
import DialogButtons from "../elements/DialogButtons";
|
||||
import { IDialogProps } from "./IDialogProps";
|
||||
|
||||
interface IProps extends IDialogProps {
|
||||
badFiles: File[];
|
||||
totalFiles: number;
|
||||
contentMessages: ContentMessages;
|
||||
}
|
||||
|
||||
/*
|
||||
* Tells the user about files we know cannot be uploaded before we even try uploading
|
||||
|
@ -29,26 +36,16 @@ import { replaceableComponent } from "../../../utils/replaceableComponent";
|
|||
* the size of the file.
|
||||
*/
|
||||
@replaceableComponent("views.dialogs.UploadFailureDialog")
|
||||
export default class UploadFailureDialog extends React.Component {
|
||||
static propTypes = {
|
||||
badFiles: PropTypes.arrayOf(PropTypes.object).isRequired,
|
||||
totalFiles: PropTypes.number.isRequired,
|
||||
contentMessages: PropTypes.instanceOf(ContentMessages).isRequired,
|
||||
onFinished: PropTypes.func.isRequired,
|
||||
}
|
||||
|
||||
_onCancelClick = () => {
|
||||
export default class UploadFailureDialog extends React.Component<IProps> {
|
||||
private onCancelClick = (): void => {
|
||||
this.props.onFinished(false);
|
||||
}
|
||||
};
|
||||
|
||||
_onUploadClick = () => {
|
||||
private onUploadClick = (): void => {
|
||||
this.props.onFinished(true);
|
||||
}
|
||||
|
||||
render() {
|
||||
const BaseDialog = sdk.getComponent('views.dialogs.BaseDialog');
|
||||
const DialogButtons = sdk.getComponent('views.elements.DialogButtons');
|
||||
};
|
||||
|
||||
public render(): JSX.Element {
|
||||
let message;
|
||||
let preview;
|
||||
let buttons;
|
||||
|
@ -65,7 +62,7 @@ export default class UploadFailureDialog extends React.Component {
|
|||
);
|
||||
buttons = <DialogButtons primaryButton={_t('OK')}
|
||||
hasCancel={false}
|
||||
onPrimaryButtonClick={this._onCancelClick}
|
||||
onPrimaryButtonClick={this.onCancelClick}
|
||||
focus={true}
|
||||
/>;
|
||||
} else if (this.props.totalFiles === this.props.badFiles.length) {
|
||||
|
@ -80,7 +77,7 @@ export default class UploadFailureDialog extends React.Component {
|
|||
);
|
||||
buttons = <DialogButtons primaryButton={_t('OK')}
|
||||
hasCancel={false}
|
||||
onPrimaryButtonClick={this._onCancelClick}
|
||||
onPrimaryButtonClick={this.onCancelClick}
|
||||
focus={true}
|
||||
/>;
|
||||
} else {
|
||||
|
@ -96,17 +93,17 @@ export default class UploadFailureDialog extends React.Component {
|
|||
const howManyOthers = this.props.totalFiles - this.props.badFiles.length;
|
||||
buttons = <DialogButtons
|
||||
primaryButton={_t('Upload %(count)s other files', { count: howManyOthers })}
|
||||
onPrimaryButtonClick={this._onUploadClick}
|
||||
onPrimaryButtonClick={this.onUploadClick}
|
||||
hasCancel={true}
|
||||
cancelButton={_t("Cancel All")}
|
||||
onCancel={this._onCancelClick}
|
||||
onCancel={this.onCancelClick}
|
||||
focus={true}
|
||||
/>;
|
||||
}
|
||||
|
||||
return (
|
||||
<BaseDialog className='mx_UploadFailureDialog'
|
||||
onFinished={this._onCancelClick}
|
||||
onFinished={this.onCancelClick}
|
||||
title={_t("Upload Error")}
|
||||
contentId='mx_Dialog_content'
|
||||
>
|
|
@ -47,15 +47,15 @@ export default class WidgetOpenIDPermissionsDialog extends React.PureComponent<I
|
|||
};
|
||||
}
|
||||
|
||||
private onAllow = () => {
|
||||
private onAllow = (): void => {
|
||||
this.onPermissionSelection(true);
|
||||
};
|
||||
|
||||
private onDeny = () => {
|
||||
private onDeny = (): void => {
|
||||
this.onPermissionSelection(false);
|
||||
};
|
||||
|
||||
private onPermissionSelection(allowed: boolean) {
|
||||
private onPermissionSelection(allowed: boolean): void {
|
||||
if (this.state.rememberSelection) {
|
||||
logger.log(`Remembering ${this.props.widget.id} as allowed=${allowed} for OpenID`);
|
||||
|
||||
|
@ -68,11 +68,11 @@ export default class WidgetOpenIDPermissionsDialog extends React.PureComponent<I
|
|||
this.props.onFinished(allowed);
|
||||
}
|
||||
|
||||
private onRememberSelectionChange = (newVal: boolean) => {
|
||||
private onRememberSelectionChange = (newVal: boolean): void => {
|
||||
this.setState({ rememberSelection: newVal });
|
||||
};
|
||||
|
||||
public render() {
|
||||
public render(): JSX.Element {
|
||||
return (
|
||||
<BaseDialog
|
||||
className='mx_WidgetOpenIDPermissionsDialog'
|
||||
|
|
|
@ -16,32 +16,64 @@ limitations under the License.
|
|||
*/
|
||||
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import * as sdk from '../../../../index';
|
||||
import { MatrixClientPeg } from '../../../../MatrixClientPeg';
|
||||
import { MatrixClient } from 'matrix-js-sdk/src/client';
|
||||
import { _t } from '../../../../languageHandler';
|
||||
import { accessSecretStorage } from '../../../../SecurityManager';
|
||||
|
||||
import { IKeyBackupInfo, IKeyBackupRestoreResult } from "matrix-js-sdk/src/crypto/keybackup";
|
||||
import { ISecretStorageKeyInfo } from "matrix-js-sdk/src/crypto/api";
|
||||
import * as sdk from '../../../../index';
|
||||
import { IDialogProps } from "../IDialogProps";
|
||||
import { logger } from "matrix-js-sdk/src/logger";
|
||||
|
||||
const RESTORE_TYPE_PASSPHRASE = 0;
|
||||
const RESTORE_TYPE_RECOVERYKEY = 1;
|
||||
const RESTORE_TYPE_SECRET_STORAGE = 2;
|
||||
enum RestoreType {
|
||||
Passphrase = "passphrase",
|
||||
RecoveryKey = "recovery_key",
|
||||
SecretStorage = "secret_storage"
|
||||
}
|
||||
|
||||
enum ProgressState {
|
||||
PreFetch = "prefetch",
|
||||
Fetch = "fetch",
|
||||
LoadKeys = "load_keys",
|
||||
|
||||
}
|
||||
|
||||
interface IProps extends IDialogProps {
|
||||
// if false, will close the dialog as soon as the restore completes succesfully
|
||||
// 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;
|
||||
}
|
||||
|
||||
interface IState {
|
||||
backupInfo: IKeyBackupInfo;
|
||||
backupKeyStored: Record<string, ISecretStorageKeyInfo>;
|
||||
loading: boolean;
|
||||
loadError: string;
|
||||
restoreError: {
|
||||
errcode: string;
|
||||
};
|
||||
recoveryKey: string;
|
||||
recoverInfo: IKeyBackupRestoreResult;
|
||||
recoveryKeyValid: boolean;
|
||||
forceRecoveryKey: boolean;
|
||||
passPhrase: string;
|
||||
restoreType: RestoreType;
|
||||
progress: {
|
||||
stage: ProgressState;
|
||||
total?: number;
|
||||
successes?: number;
|
||||
failures?: number;
|
||||
};
|
||||
}
|
||||
|
||||
/*
|
||||
* Dialog for restoring e2e keys from a backup and the user's recovery key
|
||||
*/
|
||||
export default class RestoreKeyBackupDialog extends React.PureComponent {
|
||||
static propTypes = {
|
||||
// if false, will close the dialog as soon as the restore completes succesfully
|
||||
// default: true
|
||||
showSummary: PropTypes.bool,
|
||||
// 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: PropTypes.func,
|
||||
};
|
||||
|
||||
export default class RestoreKeyBackupDialog extends React.PureComponent<IProps, IState> {
|
||||
static defaultProps = {
|
||||
showSummary: true,
|
||||
};
|
||||
|
@ -60,58 +92,58 @@ export default class RestoreKeyBackupDialog extends React.PureComponent {
|
|||
forceRecoveryKey: false,
|
||||
passPhrase: '',
|
||||
restoreType: null,
|
||||
progress: { stage: "prefetch" },
|
||||
progress: { stage: ProgressState.PreFetch },
|
||||
};
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this._loadBackupStatus();
|
||||
public componentDidMount(): void {
|
||||
this.loadBackupStatus();
|
||||
}
|
||||
|
||||
_onCancel = () => {
|
||||
private onCancel = (): void => {
|
||||
this.props.onFinished(false);
|
||||
}
|
||||
};
|
||||
|
||||
_onDone = () => {
|
||||
private onDone = (): void => {
|
||||
this.props.onFinished(true);
|
||||
}
|
||||
};
|
||||
|
||||
_onUseRecoveryKeyClick = () => {
|
||||
private onUseRecoveryKeyClick = (): void => {
|
||||
this.setState({
|
||||
forceRecoveryKey: true,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
_progressCallback = (data) => {
|
||||
private progressCallback = (data): void => {
|
||||
this.setState({
|
||||
progress: data,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
_onResetRecoveryClick = () => {
|
||||
private onResetRecoveryClick = (): void => {
|
||||
this.props.onFinished(false);
|
||||
accessSecretStorage(() => {}, /* forceReset = */ true);
|
||||
}
|
||||
accessSecretStorage(async () => {}, /* forceReset = */ true);
|
||||
};
|
||||
|
||||
_onRecoveryKeyChange = (e) => {
|
||||
private onRecoveryKeyChange = (e): void => {
|
||||
this.setState({
|
||||
recoveryKey: e.target.value,
|
||||
recoveryKeyValid: MatrixClientPeg.get().isValidRecoveryKey(e.target.value),
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
_onPassPhraseNext = async () => {
|
||||
private onPassPhraseNext = async (): Promise<void> => {
|
||||
this.setState({
|
||||
loading: true,
|
||||
restoreError: null,
|
||||
restoreType: RESTORE_TYPE_PASSPHRASE,
|
||||
restoreType: RestoreType.Passphrase,
|
||||
});
|
||||
try {
|
||||
// We do still restore the key backup: we must ensure that the key backup key
|
||||
// is the right one and restoring it is currently the only way we can do this.
|
||||
const recoverInfo = await MatrixClientPeg.get().restoreKeyBackupWithPassword(
|
||||
this.state.passPhrase, undefined, undefined, this.state.backupInfo,
|
||||
{ progressCallback: this._progressCallback },
|
||||
{ progressCallback: this.progressCallback },
|
||||
);
|
||||
if (this.props.keyCallback) {
|
||||
const key = await MatrixClientPeg.get().keyBackupKeyFromPassword(
|
||||
|
@ -135,20 +167,20 @@ export default class RestoreKeyBackupDialog extends React.PureComponent {
|
|||
restoreError: e,
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
_onRecoveryKeyNext = async () => {
|
||||
private onRecoveryKeyNext = async (): Promise<void> => {
|
||||
if (!this.state.recoveryKeyValid) return;
|
||||
|
||||
this.setState({
|
||||
loading: true,
|
||||
restoreError: null,
|
||||
restoreType: RESTORE_TYPE_RECOVERYKEY,
|
||||
restoreType: RestoreType.RecoveryKey,
|
||||
});
|
||||
try {
|
||||
const recoverInfo = await MatrixClientPeg.get().restoreKeyBackupWithRecoveryKey(
|
||||
this.state.recoveryKey, undefined, undefined, this.state.backupInfo,
|
||||
{ progressCallback: this._progressCallback },
|
||||
{ progressCallback: this.progressCallback },
|
||||
);
|
||||
if (this.props.keyCallback) {
|
||||
const key = MatrixClientPeg.get().keyBackupKeyFromRecoveryKey(this.state.recoveryKey);
|
||||
|
@ -169,31 +201,30 @@ export default class RestoreKeyBackupDialog extends React.PureComponent {
|
|||
restoreError: e,
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
_onPassPhraseChange = (e) => {
|
||||
private onPassPhraseChange = (e): void => {
|
||||
this.setState({
|
||||
passPhrase: e.target.value,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
async _restoreWithSecretStorage() {
|
||||
private async restoreWithSecretStorage(): Promise<void> {
|
||||
this.setState({
|
||||
loading: true,
|
||||
restoreError: null,
|
||||
restoreType: RESTORE_TYPE_SECRET_STORAGE,
|
||||
restoreType: RestoreType.SecretStorage,
|
||||
});
|
||||
try {
|
||||
// `accessSecretStorage` may prompt for storage access as needed.
|
||||
const recoverInfo = await accessSecretStorage(async () => {
|
||||
return MatrixClientPeg.get().restoreKeyBackupWithSecretStorage(
|
||||
await accessSecretStorage(async () => {
|
||||
await MatrixClientPeg.get().restoreKeyBackupWithSecretStorage(
|
||||
this.state.backupInfo, undefined, undefined,
|
||||
{ progressCallback: this._progressCallback },
|
||||
{ progressCallback: this.progressCallback },
|
||||
);
|
||||
});
|
||||
this.setState({
|
||||
loading: false,
|
||||
recoverInfo,
|
||||
});
|
||||
} catch (e) {
|
||||
logger.log("Error restoring backup", e);
|
||||
|
@ -204,14 +235,14 @@ export default class RestoreKeyBackupDialog extends React.PureComponent {
|
|||
}
|
||||
}
|
||||
|
||||
async _restoreWithCachedKey(backupInfo) {
|
||||
private async restoreWithCachedKey(backupInfo): Promise<boolean> {
|
||||
if (!backupInfo) return false;
|
||||
try {
|
||||
const recoverInfo = await MatrixClientPeg.get().restoreKeyBackupWithCache(
|
||||
undefined, /* targetRoomId */
|
||||
undefined, /* targetSessionId */
|
||||
backupInfo,
|
||||
{ progressCallback: this._progressCallback },
|
||||
{ progressCallback: this.progressCallback },
|
||||
);
|
||||
this.setState({
|
||||
recoverInfo,
|
||||
|
@ -223,7 +254,7 @@ export default class RestoreKeyBackupDialog extends React.PureComponent {
|
|||
}
|
||||
}
|
||||
|
||||
async _loadBackupStatus() {
|
||||
private async loadBackupStatus(): Promise<void> {
|
||||
this.setState({
|
||||
loading: true,
|
||||
loadError: null,
|
||||
|
@ -238,7 +269,7 @@ export default class RestoreKeyBackupDialog extends React.PureComponent {
|
|||
backupKeyStored,
|
||||
});
|
||||
|
||||
const gotCache = await this._restoreWithCachedKey(backupInfo);
|
||||
const gotCache = await this.restoreWithCachedKey(backupInfo);
|
||||
if (gotCache) {
|
||||
logger.log("RestoreKeyBackupDialog: found cached backup key");
|
||||
this.setState({
|
||||
|
@ -249,7 +280,7 @@ export default class RestoreKeyBackupDialog extends React.PureComponent {
|
|||
|
||||
// If the backup key is stored, we can proceed directly to restore.
|
||||
if (backupKeyStored) {
|
||||
return this._restoreWithSecretStorage();
|
||||
return this.restoreWithSecretStorage();
|
||||
}
|
||||
|
||||
this.setState({
|
||||
|
@ -265,7 +296,10 @@ export default class RestoreKeyBackupDialog extends React.PureComponent {
|
|||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
public render(): JSX.Element {
|
||||
// FIXME: Making these into imports will break tests
|
||||
const DialogButtons = sdk.getComponent('views.elements.DialogButtons');
|
||||
const AccessibleButton = sdk.getComponent('elements.AccessibleButton');
|
||||
const BaseDialog = sdk.getComponent('views.dialogs.BaseDialog');
|
||||
const Spinner = sdk.getComponent("elements.Spinner");
|
||||
|
||||
|
@ -281,12 +315,12 @@ export default class RestoreKeyBackupDialog extends React.PureComponent {
|
|||
if (this.state.loading) {
|
||||
title = _t("Restoring keys from backup");
|
||||
let details;
|
||||
if (this.state.progress.stage === "fetch") {
|
||||
if (this.state.progress.stage === ProgressState.Fetch) {
|
||||
details = _t("Fetching keys from server...");
|
||||
} else if (this.state.progress.stage === "load_keys") {
|
||||
} else if (this.state.progress.stage === ProgressState.LoadKeys) {
|
||||
const { total, successes, failures } = this.state.progress;
|
||||
details = _t("%(completed)s of %(total)s keys restored", { total, completed: successes + failures });
|
||||
} else if (this.state.progress.stage === "prefetch") {
|
||||
} else if (this.state.progress.stage === ProgressState.PreFetch) {
|
||||
details = _t("Fetching keys from server...");
|
||||
}
|
||||
content = <div>
|
||||
|
@ -298,7 +332,7 @@ export default class RestoreKeyBackupDialog extends React.PureComponent {
|
|||
content = _t("Unable to load backup status");
|
||||
} else if (this.state.restoreError) {
|
||||
if (this.state.restoreError.errcode === MatrixClient.RESTORE_BACKUP_ERROR_BAD_KEY) {
|
||||
if (this.state.restoreType === RESTORE_TYPE_RECOVERYKEY) {
|
||||
if (this.state.restoreType === RestoreType.RecoveryKey) {
|
||||
title = _t("Security Key mismatch");
|
||||
content = <div>
|
||||
<p>{ _t(
|
||||
|
@ -323,7 +357,6 @@ export default class RestoreKeyBackupDialog extends React.PureComponent {
|
|||
title = _t("Error");
|
||||
content = _t("No backup found!");
|
||||
} else if (this.state.recoverInfo) {
|
||||
const DialogButtons = sdk.getComponent('views.elements.DialogButtons');
|
||||
title = _t("Keys restored");
|
||||
let failedToDecrypt;
|
||||
if (this.state.recoverInfo.total > this.state.recoverInfo.imported) {
|
||||
|
@ -336,14 +369,12 @@ export default class RestoreKeyBackupDialog extends React.PureComponent {
|
|||
<p>{ _t("Successfully restored %(sessionCount)s keys", { sessionCount: this.state.recoverInfo.imported }) }</p>
|
||||
{ failedToDecrypt }
|
||||
<DialogButtons primaryButton={_t('OK')}
|
||||
onPrimaryButtonClick={this._onDone}
|
||||
onPrimaryButtonClick={this.onDone}
|
||||
hasCancel={false}
|
||||
focus={true}
|
||||
/>
|
||||
</div>;
|
||||
} else if (backupHasPassphrase && !this.state.forceRecoveryKey) {
|
||||
const DialogButtons = sdk.getComponent('views.elements.DialogButtons');
|
||||
const AccessibleButton = sdk.getComponent('elements.AccessibleButton');
|
||||
title = _t("Enter Security Phrase");
|
||||
content = <div>
|
||||
<p>{ _t(
|
||||
|
@ -359,16 +390,16 @@ export default class RestoreKeyBackupDialog extends React.PureComponent {
|
|||
<form className="mx_RestoreKeyBackupDialog_primaryContainer">
|
||||
<input type="password"
|
||||
className="mx_RestoreKeyBackupDialog_passPhraseInput"
|
||||
onChange={this._onPassPhraseChange}
|
||||
onChange={this.onPassPhraseChange}
|
||||
value={this.state.passPhrase}
|
||||
autoFocus={true}
|
||||
/>
|
||||
<DialogButtons
|
||||
primaryButton={_t('Next')}
|
||||
onPrimaryButtonClick={this._onPassPhraseNext}
|
||||
onPrimaryButtonClick={this.onPassPhraseNext}
|
||||
primaryIsSubmit={true}
|
||||
hasCancel={true}
|
||||
onCancel={this._onCancel}
|
||||
onCancel={this.onCancel}
|
||||
focus={false}
|
||||
/>
|
||||
</form>
|
||||
|
@ -381,14 +412,14 @@ export default class RestoreKeyBackupDialog extends React.PureComponent {
|
|||
button1: s => <AccessibleButton
|
||||
className="mx_linkButton"
|
||||
element="span"
|
||||
onClick={this._onUseRecoveryKeyClick}
|
||||
onClick={this.onUseRecoveryKeyClick}
|
||||
>
|
||||
{ s }
|
||||
</AccessibleButton>,
|
||||
button2: s => <AccessibleButton
|
||||
className="mx_linkButton"
|
||||
element="span"
|
||||
onClick={this._onResetRecoveryClick}
|
||||
onClick={this.onResetRecoveryClick}
|
||||
>
|
||||
{ s }
|
||||
</AccessibleButton>,
|
||||
|
@ -396,8 +427,6 @@ export default class RestoreKeyBackupDialog extends React.PureComponent {
|
|||
</div>;
|
||||
} else {
|
||||
title = _t("Enter Security Key");
|
||||
const DialogButtons = sdk.getComponent('views.elements.DialogButtons');
|
||||
const AccessibleButton = sdk.getComponent('elements.AccessibleButton');
|
||||
|
||||
let keyStatus;
|
||||
if (this.state.recoveryKey.length === 0) {
|
||||
|
@ -425,15 +454,15 @@ export default class RestoreKeyBackupDialog extends React.PureComponent {
|
|||
|
||||
<div className="mx_RestoreKeyBackupDialog_primaryContainer">
|
||||
<input className="mx_RestoreKeyBackupDialog_recoveryKeyInput"
|
||||
onChange={this._onRecoveryKeyChange}
|
||||
onChange={this.onRecoveryKeyChange}
|
||||
value={this.state.recoveryKey}
|
||||
autoFocus={true}
|
||||
/>
|
||||
{ keyStatus }
|
||||
<DialogButtons primaryButton={_t('Next')}
|
||||
onPrimaryButtonClick={this._onRecoveryKeyNext}
|
||||
onPrimaryButtonClick={this.onRecoveryKeyNext}
|
||||
hasCancel={true}
|
||||
onCancel={this._onCancel}
|
||||
onCancel={this.onCancel}
|
||||
focus={false}
|
||||
primaryDisabled={!this.state.recoveryKeyValid}
|
||||
/>
|
||||
|
@ -445,7 +474,7 @@ export default class RestoreKeyBackupDialog extends React.PureComponent {
|
|||
{
|
||||
button: s => <AccessibleButton className="mx_linkButton"
|
||||
element="span"
|
||||
onClick={this._onResetRecoveryClick}
|
||||
onClick={this.onResetRecoveryClick}
|
||||
>
|
||||
{ s }
|
||||
</AccessibleButton>,
|
|
@ -20,6 +20,7 @@ import BaseDialog from '../BaseDialog';
|
|||
import { _t } from '../../../../languageHandler';
|
||||
import { SetupEncryptionStore, Phase } from '../../../../stores/SetupEncryptionStore';
|
||||
import { replaceableComponent } from "../../../../utils/replaceableComponent";
|
||||
import { IDialogProps } from "../IDialogProps";
|
||||
|
||||
function iconFromPhase(phase: Phase) {
|
||||
if (phase === Phase.Done) {
|
||||
|
@ -29,12 +30,9 @@ function iconFromPhase(phase: Phase) {
|
|||
}
|
||||
}
|
||||
|
||||
interface IProps {
|
||||
onFinished: (success: boolean) => void;
|
||||
}
|
||||
|
||||
interface IProps extends IDialogProps {}
|
||||
interface IState {
|
||||
icon: Phase;
|
||||
icon: string;
|
||||
}
|
||||
|
||||
@replaceableComponent("views.dialogs.security.SetupEncryptionDialog")
|
||||
|
|
Loading…
Reference in a new issue