Apply remainder of ux
This commit is contained in:
parent
db1d3c091e
commit
657457c14b
6 changed files with 139 additions and 50 deletions
|
@ -20,5 +20,7 @@ limitations under the License.
|
||||||
margin-top: 25px;
|
margin-top: 25px;
|
||||||
margin-bottom: 15px;
|
margin-bottom: 15px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
text-align: center;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -42,6 +42,7 @@ export default createReactClass({
|
||||||
button: PropTypes.string,
|
button: PropTypes.string,
|
||||||
focus: PropTypes.bool,
|
focus: PropTypes.bool,
|
||||||
onFinished: PropTypes.func.isRequired,
|
onFinished: PropTypes.func.isRequired,
|
||||||
|
headerImage: PropTypes.string,
|
||||||
},
|
},
|
||||||
|
|
||||||
getDefaultProps: function() {
|
getDefaultProps: function() {
|
||||||
|
@ -56,9 +57,12 @@ export default createReactClass({
|
||||||
render: function() {
|
render: function() {
|
||||||
const BaseDialog = sdk.getComponent('views.dialogs.BaseDialog');
|
const BaseDialog = sdk.getComponent('views.dialogs.BaseDialog');
|
||||||
return (
|
return (
|
||||||
<BaseDialog className="mx_ErrorDialog" onFinished={this.props.onFinished}
|
<BaseDialog
|
||||||
title={this.props.title || _t('Error')}
|
className="mx_ErrorDialog"
|
||||||
contentId='mx_Dialog_content'
|
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'>
|
<div className="mx_Dialog_content" id='mx_Dialog_content'>
|
||||||
{ this.props.description || _t('An error has occurred.') }
|
{ this.props.description || _t('An error has occurred.') }
|
||||||
|
|
|
@ -21,19 +21,47 @@ import VerificationPanel from "./VerificationPanel";
|
||||||
import {MatrixClientPeg} from "../../../MatrixClientPeg";
|
import {MatrixClientPeg} from "../../../MatrixClientPeg";
|
||||||
import {ensureDMExists} from "../../../createRoom";
|
import {ensureDMExists} from "../../../createRoom";
|
||||||
import {useEventEmitter} from "../../../hooks/useEventEmitter";
|
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);
|
const [request, setRequest] = useState(verificationRequest);
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setRequest(verificationRequest);
|
setRequest(verificationRequest);
|
||||||
}, [verificationRequest]);
|
}, [verificationRequest]);
|
||||||
|
|
||||||
const [pending, setPending] = useState(false);
|
const [phase, setPhase] = useState(false);
|
||||||
const changeHandler = useCallback(() => {
|
const changeHandler = useCallback(() => {
|
||||||
setPending(request && request.requested);
|
// handle transitions -> cancelled for mismatches which fire a modal instead of showing a card
|
||||||
}, [request]);
|
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 you’re 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);
|
useEventEmitter(request, "change", changeHandler);
|
||||||
useEffect(changeHandler, [changeHandler]);
|
|
||||||
|
|
||||||
const onStartVerification = useCallback(async () => {
|
const onStartVerification = useCallback(async () => {
|
||||||
const cli = MatrixClientPeg.get();
|
const cli = MatrixClientPeg.get();
|
||||||
|
@ -42,10 +70,18 @@ const EncryptionPanel = ({verificationRequest, member}) => {
|
||||||
setRequest(verificationRequest);
|
setRequest(verificationRequest);
|
||||||
}, [member.userId]);
|
}, [member.userId]);
|
||||||
|
|
||||||
if (!request || pending) {
|
const requested = request && phase === PHASE_REQUESTED;
|
||||||
return <EncryptionInfo onStartVerification={onStartVerification} member={member} pending={pending} />;
|
if (!request || requested) {
|
||||||
|
return <EncryptionInfo onStartVerification={onStartVerification} member={member} pending={requested} />;
|
||||||
} else {
|
} 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 = {
|
EncryptionPanel.propTypes = {
|
||||||
|
|
|
@ -22,6 +22,13 @@ import VerificationQRCode from "../elements/crypto/VerificationQRCode";
|
||||||
import {MatrixClientPeg} from "../../../MatrixClientPeg";
|
import {MatrixClientPeg} from "../../../MatrixClientPeg";
|
||||||
import {_t} from "../../../languageHandler";
|
import {_t} from "../../../languageHandler";
|
||||||
import E2EIcon from "../rooms/E2EIcon";
|
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 {
|
export default class VerificationPanel extends React.PureComponent {
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
|
@ -30,11 +37,22 @@ export default class VerificationPanel extends React.PureComponent {
|
||||||
this._hasVerifier = !!props.request.verifier;
|
this._hasVerifier = !!props.request.verifier;
|
||||||
}
|
}
|
||||||
|
|
||||||
renderQRPhase() {
|
renderQRPhase(pending) {
|
||||||
const {member, request} = this.props;
|
const {member, request} = this.props;
|
||||||
// TODO change the button into a spinner when on click
|
// TODO change the button into a spinner when on click
|
||||||
const AccessibleButton = sdk.getComponent('elements.AccessibleButton');
|
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 cli = MatrixClientPeg.get();
|
||||||
const crossSigningInfo = cli.getStoredCrossSigningForUser(request.otherUserId);
|
const crossSigningInfo = cli.getStoredCrossSigningForUser(request.otherUserId);
|
||||||
if (!crossSigningInfo || !request.requestEvent || !request.requestEvent.getId()) {
|
if (!crossSigningInfo || !request.requestEvent || !request.requestEvent.getId()) {
|
||||||
|
@ -43,9 +61,7 @@ export default class VerificationPanel extends React.PureComponent {
|
||||||
<h3>Verify by emoji</h3>
|
<h3>Verify by emoji</h3>
|
||||||
<p>{_t("Verify by comparing unique emoji.")}</p>
|
<p>{_t("Verify by comparing unique emoji.")}</p>
|
||||||
|
|
||||||
<AccessibleButton kind="primary" className="mx_UserInfo_verify" onClick={this._startSAS}>
|
{ button }
|
||||||
{_t("Verify by emoji")}
|
|
||||||
</AccessibleButton>
|
|
||||||
</div>;
|
</div>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -78,9 +94,7 @@ export default class VerificationPanel extends React.PureComponent {
|
||||||
<h3>Verify by emoji</h3>
|
<h3>Verify by emoji</h3>
|
||||||
<p>{_t("If you can't scan the code above, verify by comparing unique emoji.")}</p>
|
<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}>
|
{ button }
|
||||||
{_t("Verify by emoji")}
|
|
||||||
</AccessibleButton>
|
|
||||||
</div>
|
</div>
|
||||||
</React.Fragment>;
|
</React.Fragment>;
|
||||||
}
|
}
|
||||||
|
@ -97,7 +111,36 @@ export default class VerificationPanel extends React.PureComponent {
|
||||||
})}</p>
|
})}</p>
|
||||||
<E2EIcon isUser={true} status="verified" size={128} />
|
<E2EIcon isUser={true} status="verified" size={128} />
|
||||||
<p>Verify all users in a room to ensure it's secure.</p>
|
<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")}
|
{_t("Got it")}
|
||||||
</AccessibleButton>
|
</AccessibleButton>
|
||||||
</div>
|
</div>
|
||||||
|
@ -105,34 +148,32 @@ export default class VerificationPanel extends React.PureComponent {
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const {member, request} = this.props;
|
const {member} = this.props;
|
||||||
|
|
||||||
const displayName = member.displayName || member.name || member.userId;
|
const displayName = member.displayName || member.name || member.userId;
|
||||||
|
|
||||||
if (request.ready) {
|
switch (this.props.phase) {
|
||||||
return this.renderQRPhase();
|
case PHASE_READY:
|
||||||
} else if (request.started) {
|
return this.renderQRPhase();
|
||||||
if (this.state.sasEvent) {
|
case PHASE_STARTED:
|
||||||
const VerificationShowSas = sdk.getComponent('views.verification.VerificationShowSas');
|
if (this.state.sasEvent) {
|
||||||
// TODO implement "mismatch" vs "cancelled"
|
const VerificationShowSas = sdk.getComponent('views.verification.VerificationShowSas');
|
||||||
return <div className="mx_UserInfo_container">
|
return <div className="mx_UserInfo_container">
|
||||||
<h3>Compare emoji</h3>
|
<h3>Compare emoji</h3>
|
||||||
<VerificationShowSas
|
<VerificationShowSas
|
||||||
displayName={displayName}
|
displayName={displayName}
|
||||||
sas={this.state.sasEvent.sas}
|
sas={this.state.sasEvent.sas}
|
||||||
onCancel={this._onSasMismatchesClick}
|
onCancel={this._onSasMismatchesClick}
|
||||||
onDone={this._onSasMatchesClick}
|
onDone={this._onSasMatchesClick}
|
||||||
/>
|
/>
|
||||||
</div>;
|
</div>;
|
||||||
} else {
|
} else {
|
||||||
return (<p>Setting up SAS verification...</p>);
|
return this.renderQRPhase(true); // keep showing same phase but with a spinner
|
||||||
}
|
}
|
||||||
} else if (request.done) {
|
case PHASE_DONE:
|
||||||
return this.renderVerifiedPhase();
|
return this.renderVerifiedPhase();
|
||||||
} else if (request.cancelled) {
|
case PHASE_CANCELLED:
|
||||||
// TODO check if this matches target
|
return this.renderCancelledPhase();
|
||||||
// TODO should this be a MODAL?
|
|
||||||
return <p>cancelled by {request.cancellingUserId}!</p>;
|
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -143,8 +184,6 @@ export default class VerificationPanel extends React.PureComponent {
|
||||||
await verifier.verify();
|
await verifier.verify();
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error(err);
|
console.error(err);
|
||||||
} finally {
|
|
||||||
this.setState({sasEvent: null});
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -153,7 +192,7 @@ export default class VerificationPanel extends React.PureComponent {
|
||||||
};
|
};
|
||||||
|
|
||||||
_onSasMismatchesClick = () => {
|
_onSasMismatchesClick = () => {
|
||||||
this.state.sasEvent.cancel();
|
this.state.sasEvent.mismatch();
|
||||||
};
|
};
|
||||||
|
|
||||||
_onVerifierShowSas = (sasEvent) => {
|
_onVerifierShowSas = (sasEvent) => {
|
||||||
|
@ -175,7 +214,6 @@ export default class VerificationPanel extends React.PureComponent {
|
||||||
request.verifier.removeListener('show_sas', this._onVerifierShowSas);
|
request.verifier.removeListener('show_sas', this._onVerifierShowSas);
|
||||||
}
|
}
|
||||||
this._hasVerifier = !!request.verifier;
|
this._hasVerifier = !!request.verifier;
|
||||||
this.forceUpdate(); // TODO fix this
|
|
||||||
};
|
};
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
|
|
|
@ -16,7 +16,6 @@ limitations under the License.
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import * as sdk from '../../../index';
|
|
||||||
import { _t, _td } from '../../../languageHandler';
|
import { _t, _td } from '../../../languageHandler';
|
||||||
import {PendingActionSpinner} from "../right_panel/EncryptionInfo";
|
import {PendingActionSpinner} from "../right_panel/EncryptionInfo";
|
||||||
import AccessibleButton from "../elements/AccessibleButton";
|
import AccessibleButton from "../elements/AccessibleButton";
|
||||||
|
|
|
@ -1139,6 +1139,12 @@
|
||||||
"Verify User": "Verify User",
|
"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 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.",
|
"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 you’re verifying is connected to": "The homeserver the user you’re 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",
|
"Members": "Members",
|
||||||
"Files": "Files",
|
"Files": "Files",
|
||||||
"Trusted": "Trusted",
|
"Trusted": "Trusted",
|
||||||
|
@ -1156,11 +1162,15 @@
|
||||||
"This client does not support end-to-end encryption.": "This client does not support end-to-end encryption.",
|
"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.",
|
"Messages in this room are not end-to-end encrypted.": "Messages in this room are not end-to-end encrypted.",
|
||||||
"Security": "Security",
|
"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:",
|
"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.",
|
"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!",
|
"You've successfully verified %(displayName)s!": "You've successfully verified %(displayName)s!",
|
||||||
"Got it": "Got it",
|
"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",
|
"Sunday": "Sunday",
|
||||||
"Monday": "Monday",
|
"Monday": "Monday",
|
||||||
"Tuesday": "Tuesday",
|
"Tuesday": "Tuesday",
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue