Apply remainder of ux

pull/21833/head
Michael Telatynski 2020-01-28 11:13:09 +00:00
parent db1d3c091e
commit 657457c14b
6 changed files with 139 additions and 50 deletions

View File

@ -20,5 +20,7 @@ limitations under the License.
margin-top: 25px;
margin-bottom: 15px;
}
text-align: center;
}
}

View File

@ -42,6 +42,7 @@ export default createReactClass({
button: PropTypes.string,
focus: PropTypes.bool,
onFinished: PropTypes.func.isRequired,
headerImage: PropTypes.string,
},
getDefaultProps: function() {
@ -56,9 +57,12 @@ export default createReactClass({
render: function() {
const BaseDialog = sdk.getComponent('views.dialogs.BaseDialog');
return (
<BaseDialog className="mx_ErrorDialog" onFinished={this.props.onFinished}
title={this.props.title || _t('Error')}
contentId='mx_Dialog_content'
<BaseDialog
className="mx_ErrorDialog"
onFinished={this.props.onFinished}
title={this.props.title || _t('Error')}
headerImage={this.props.headerImage}
contentId='mx_Dialog_content'
>
<div className="mx_Dialog_content" id='mx_Dialog_content'>
{ this.props.description || _t('An error has occurred.') }

View File

@ -21,19 +21,47 @@ import VerificationPanel from "./VerificationPanel";
import {MatrixClientPeg} from "../../../MatrixClientPeg";
import {ensureDMExists} from "../../../createRoom";
import {useEventEmitter} from "../../../hooks/useEventEmitter";
import Modal from "../../../Modal";
import {PHASE_REQUESTED} from "matrix-js-sdk/src/crypto/verification/request/VerificationRequest";
import * as sdk from "../../../index";
import {_t} from "../../../languageHandler";
const EncryptionPanel = ({verificationRequest, member}) => {
// cancellation codes which constitute a key mismatch
const MISMATCHES = ["m.key_mismatch", "m.user_error", "m.mismatched_sas"];
const EncryptionPanel = ({verificationRequest, member, onClose}) => {
const [request, setRequest] = useState(verificationRequest);
useEffect(() => {
setRequest(verificationRequest);
}, [verificationRequest]);
const [pending, setPending] = useState(false);
const [phase, setPhase] = useState(false);
const changeHandler = useCallback(() => {
setPending(request && request.requested);
}, [request]);
// handle transitions -> cancelled for mismatches which fire a modal instead of showing a card
if (request && request.cancelled && MISMATCHES.includes(request.cancellationCode)) {
const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
Modal.createTrackedDialog("Verification failed", "insecure", ErrorDialog, {
headerImage: require("../../../../res/img/e2e/warning.svg"),
title: _t("Your messages are not secure"),
description: <div>
{_t("One of the following may be compromised:")}
<ul>
<li>{_t("Your homeserver")}</li>
<li>{_t("The homeserver the user youre verifying is connected to")}</li>
<li>{_t("Yours, or the other users internet connection")}</li>
<li>{_t("Yours, or the other users device")}</li>
</ul>
</div>,
onFinished: onClose,
});
return; // don't update phase here as we will be transitioning away from this view shortly
}
if (request) {
setPhase(request.phase);
}
}, [onClose, request]);
useEventEmitter(request, "change", changeHandler);
useEffect(changeHandler, [changeHandler]);
const onStartVerification = useCallback(async () => {
const cli = MatrixClientPeg.get();
@ -42,10 +70,18 @@ const EncryptionPanel = ({verificationRequest, member}) => {
setRequest(verificationRequest);
}, [member.userId]);
if (!request || pending) {
return <EncryptionInfo onStartVerification={onStartVerification} member={member} pending={pending} />;
const requested = request && phase === PHASE_REQUESTED;
if (!request || requested) {
return <EncryptionInfo onStartVerification={onStartVerification} member={member} pending={requested} />;
} else {
return <VerificationPanel member={member} request={request} key={request.channel.transactionId} />;
return (
<VerificationPanel
onClose={onClose}
member={member}
request={request}
key={request.channel.transactionId}
phase={phase} />
);
}
};
EncryptionPanel.propTypes = {

View File

@ -22,6 +22,13 @@ import VerificationQRCode from "../elements/crypto/VerificationQRCode";
import {MatrixClientPeg} from "../../../MatrixClientPeg";
import {_t} from "../../../languageHandler";
import E2EIcon from "../rooms/E2EIcon";
import {
PHASE_READY,
PHASE_DONE,
PHASE_STARTED,
PHASE_CANCELLED,
} from "matrix-js-sdk/src/crypto/verification/request/VerificationRequest";
import Spinner from "../elements/Spinner";
export default class VerificationPanel extends React.PureComponent {
constructor(props) {
@ -30,11 +37,22 @@ export default class VerificationPanel extends React.PureComponent {
this._hasVerifier = !!props.request.verifier;
}
renderQRPhase() {
renderQRPhase(pending) {
const {member, request} = this.props;
// TODO change the button into a spinner when on click
const AccessibleButton = sdk.getComponent('elements.AccessibleButton');
let button;
if (pending) {
button = <Spinner />;
} else {
button = (
<AccessibleButton kind="primary" className="mx_UserInfo_verify" onClick={this._startSAS}>
{_t("Verify by emoji")}
</AccessibleButton>
);
}
const cli = MatrixClientPeg.get();
const crossSigningInfo = cli.getStoredCrossSigningForUser(request.otherUserId);
if (!crossSigningInfo || !request.requestEvent || !request.requestEvent.getId()) {
@ -43,9 +61,7 @@ export default class VerificationPanel extends React.PureComponent {
<h3>Verify by emoji</h3>
<p>{_t("Verify by comparing unique emoji.")}</p>
<AccessibleButton kind="primary" className="mx_UserInfo_verify" onClick={this._startSAS}>
{_t("Verify by emoji")}
</AccessibleButton>
{ button }
</div>;
}
@ -78,9 +94,7 @@ export default class VerificationPanel extends React.PureComponent {
<h3>Verify by emoji</h3>
<p>{_t("If you can't scan the code above, verify by comparing unique emoji.")}</p>
<AccessibleButton kind="primary" className="mx_UserInfo_verify" onClick={this._startSAS}>
{_t("Verify by emoji")}
</AccessibleButton>
{ button }
</div>
</React.Fragment>;
}
@ -97,7 +111,36 @@ export default class VerificationPanel extends React.PureComponent {
})}</p>
<E2EIcon isUser={true} status="verified" size={128} />
<p>Verify all users in a room to ensure it's secure.</p>
<AccessibleButton kind="primary" className="mx_UserInfo_verify" onClick={this._startSAS}>
<AccessibleButton kind="primary" className="mx_UserInfo_verify" onClick={this.props.onClose}>
{_t("Got it")}
</AccessibleButton>
</div>
);
}
renderCancelledPhase() {
const {member, request} = this.props;
const AccessibleButton = sdk.getComponent('elements.AccessibleButton');
let text;
if (request.cancellationCode === "m.timeout") {
text = _t("Verification timed out. Start verification again from their profile.");
} else if (request.cancellingUserId === request.otherUserId) {
text = _t("%(displayName)s cancelled verification. Start verification again from their profile.", {
displayName: member.displayName || member.name || member.userId,
});
} else {
text = _t("You cancelled verification. Start verification again from their profile.");
}
return (
<div className="mx_UserInfo_container">
<h3>Verification cancelled</h3>
<p>{ text }</p>
<AccessibleButton kind="primary" className="mx_UserInfo_verify" onClick={this.props.onClose}>
{_t("Got it")}
</AccessibleButton>
</div>
@ -105,34 +148,32 @@ export default class VerificationPanel extends React.PureComponent {
}
render() {
const {member, request} = this.props;
const {member} = this.props;
const displayName = member.displayName || member.name || member.userId;
if (request.ready) {
return this.renderQRPhase();
} else if (request.started) {
if (this.state.sasEvent) {
const VerificationShowSas = sdk.getComponent('views.verification.VerificationShowSas');
// TODO implement "mismatch" vs "cancelled"
return <div className="mx_UserInfo_container">
<h3>Compare emoji</h3>
<VerificationShowSas
displayName={displayName}
sas={this.state.sasEvent.sas}
onCancel={this._onSasMismatchesClick}
onDone={this._onSasMatchesClick}
/>
</div>;
} else {
return (<p>Setting up SAS verification...</p>);
}
} else if (request.done) {
return this.renderVerifiedPhase();
} else if (request.cancelled) {
// TODO check if this matches target
// TODO should this be a MODAL?
return <p>cancelled by {request.cancellingUserId}!</p>;
switch (this.props.phase) {
case PHASE_READY:
return this.renderQRPhase();
case PHASE_STARTED:
if (this.state.sasEvent) {
const VerificationShowSas = sdk.getComponent('views.verification.VerificationShowSas');
return <div className="mx_UserInfo_container">
<h3>Compare emoji</h3>
<VerificationShowSas
displayName={displayName}
sas={this.state.sasEvent.sas}
onCancel={this._onSasMismatchesClick}
onDone={this._onSasMatchesClick}
/>
</div>;
} else {
return this.renderQRPhase(true); // keep showing same phase but with a spinner
}
case PHASE_DONE:
return this.renderVerifiedPhase();
case PHASE_CANCELLED:
return this.renderCancelledPhase();
}
return null;
}
@ -143,8 +184,6 @@ export default class VerificationPanel extends React.PureComponent {
await verifier.verify();
} catch (err) {
console.error(err);
} finally {
this.setState({sasEvent: null});
}
};
@ -153,7 +192,7 @@ export default class VerificationPanel extends React.PureComponent {
};
_onSasMismatchesClick = () => {
this.state.sasEvent.cancel();
this.state.sasEvent.mismatch();
};
_onVerifierShowSas = (sasEvent) => {
@ -175,7 +214,6 @@ export default class VerificationPanel extends React.PureComponent {
request.verifier.removeListener('show_sas', this._onVerifierShowSas);
}
this._hasVerifier = !!request.verifier;
this.forceUpdate(); // TODO fix this
};
componentDidMount() {

View File

@ -16,7 +16,6 @@ limitations under the License.
import React from 'react';
import PropTypes from 'prop-types';
import * as sdk from '../../../index';
import { _t, _td } from '../../../languageHandler';
import {PendingActionSpinner} from "../right_panel/EncryptionInfo";
import AccessibleButton from "../elements/AccessibleButton";

View File

@ -1139,6 +1139,12 @@
"Verify User": "Verify User",
"For extra security, verify this user by checking a one-time code on both of your devices.": "For extra security, verify this user by checking a one-time code on both of your devices.",
"For maximum security, do this in person.": "For maximum security, do this in person.",
"Your messages are not secure": "Your messages are not secure",
"One of the following may be compromised:": "One of the following may be compromised:",
"Your homeserver": "Your homeserver",
"The homeserver the user youre verifying is connected to": "The homeserver the user youre verifying is connected to",
"Yours, or the other users internet connection": "Yours, or the other users internet connection",
"Yours, or the other users device": "Yours, or the other users device",
"Members": "Members",
"Files": "Files",
"Trusted": "Trusted",
@ -1156,11 +1162,15 @@
"This client does not support end-to-end encryption.": "This client does not support end-to-end encryption.",
"Messages in this room are not end-to-end encrypted.": "Messages in this room are not end-to-end encrypted.",
"Security": "Security",
"Verify by emoji": "Verify by emoji",
"Verify by comparing unique emoji.": "Verify by comparing unique emoji.",
"Ask %(displayName)s to scan your code:": "Ask %(displayName)s to scan your code:",
"If you can't scan the code above, verify by comparing unique emoji.": "If you can't scan the code above, verify by comparing unique emoji.",
"Verify by emoji": "Verify by emoji",
"You've successfully verified %(displayName)s!": "You've successfully verified %(displayName)s!",
"Got it": "Got it",
"Verification timed out. Start verification again from their profile.": "Verification timed out. Start verification again from their profile.",
"%(displayName)s cancelled verification. Start verification again from their profile.": "%(displayName)s cancelled verification. Start verification again from their profile.",
"You cancelled verification. Start verification again from their profile.": "You cancelled verification. Start verification again from their profile.",
"Sunday": "Sunday",
"Monday": "Monday",
"Tuesday": "Tuesday",