diff --git a/res/css/_components.scss b/res/css/_components.scss index 1e2d7ae156..70c2f17e9a 100644 --- a/res/css/_components.scss +++ b/res/css/_components.scss @@ -60,6 +60,7 @@ @import "./views/elements/_DirectorySearchBox.scss"; @import "./views/elements/_Dropdown.scss"; @import "./views/elements/_EditableItemList.scss"; +@import "./views/elements/_HexVerify.scss"; @import "./views/elements/_ImageView.scss"; @import "./views/elements/_InlineSpinner.scss"; @import "./views/elements/_MemberEventListSummary.scss"; diff --git a/res/css/views/elements/_HexVerify.scss b/res/css/views/elements/_HexVerify.scss new file mode 100644 index 0000000000..3f3ee4b7ea --- /dev/null +++ b/res/css/views/elements/_HexVerify.scss @@ -0,0 +1,34 @@ +/* +Copyright 2019 New Vector Ltd. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +.mx_HexVerify { + text-align: center; +} + +.mx_HexVerify_pair { + display: inline-block; + font-weight: bold; + padding-left: 3px; + padding-right: 3px; +} + +.mx_HexVerify_pair_verified { + color: $accent-color; +} + +.mx_HexVerify_pair:hover{ + color: $accent-color; +} diff --git a/src/components/views/dialogs/DeviceVerifyDialog.js b/src/components/views/dialogs/DeviceVerifyDialog.js index 169dc26c52..eabae56e25 100644 --- a/src/components/views/dialogs/DeviceVerifyDialog.js +++ b/src/components/views/dialogs/DeviceVerifyDialog.js @@ -50,6 +50,7 @@ export default class DeviceVerifyDialog extends React.Component { this.state = { phase: PHASE_START, mode: SettingsStore.isFeatureEnabled("feature_sas") ? MODE_SAS : MODE_LEGACY, + sasVerified: false, }; } @@ -102,6 +103,10 @@ export default class DeviceVerifyDialog extends React.Component { }); } + _onVerifyStateChanged = (newVal) => { + this.setState({sasVerified: newVal}); + } + _onSasMatchesClick = () => { this._showSasEvent.confirm(); this.setState({ @@ -127,7 +132,6 @@ export default class DeviceVerifyDialog extends React.Component { body = this._renderSasVerificationPhaseStart(); break; case PHASE_WAIT_FOR_PARTNER_TO_ACCEPT: - //body = this._renderSasVerificationPhaseWaitForPartnerToAccept(); body = renderSasWaitAccept(this.props.userId); break; case PHASE_SHOW_SAS: @@ -180,6 +184,7 @@ export default class DeviceVerifyDialog extends React.Component { _renderSasVerificationPhaseShowSas() { const DialogButtons = sdk.getComponent('views.elements.DialogButtons'); + const HexVerify = sdk.getComponent('views.elements.HexVerify'); return

{_t( "Verify this user by confirming the following number appears on their screen" @@ -188,9 +193,15 @@ export default class DeviceVerifyDialog extends React.Component { "For maximum security, we reccommend you do this in person or use another " + "trusted means of communication" )}

-
{this._showSasEvent.sas}
+ +

{_t( + "To continue, click on each pair to confirm it's correct.", + )}

diff --git a/src/components/views/dialogs/IncomingSasDialog.js b/src/components/views/dialogs/IncomingSasDialog.js index 732ce2a461..947c757f80 100644 --- a/src/components/views/dialogs/IncomingSasDialog.js +++ b/src/components/views/dialogs/IncomingSasDialog.js @@ -38,6 +38,7 @@ export default class IncomingSasDialog extends React.Component { this._showSasEvent = null; this.state = { phase: PHASE_START, + sasVerified: false, }; this.props.verifier.on('show_sas', this._onVerifierShowSas); this.props.verifier.on('cancel', this._onVerifierCancel); @@ -81,6 +82,10 @@ export default class IncomingSasDialog extends React.Component { }); } + _onVerifiedStateChange = (newVal) => { + this.setState({sasVerified: newVal}); + } + _onSasMatchesClick = () => { this._showSasEvent.confirm(); this.setState({ @@ -121,6 +126,7 @@ export default class IncomingSasDialog extends React.Component { _renderPhaseShowSas() { const DialogButtons = sdk.getComponent('views.elements.DialogButtons'); + const HexVerify = sdk.getComponent('views.elements.HexVerify'); return

{_t( "Verify this user by confirming the following number appears on their screen" @@ -129,9 +135,15 @@ export default class IncomingSasDialog extends React.Component { "For maximum security, we reccommend you do this in person or use another " + "trusted means of communication" )}

-
{this._showSasEvent.sas}
+ +

{_t( + "To continue, click on each pair to confirm it's correct.", + )}

@@ -184,7 +196,6 @@ export default class IncomingSasDialog extends React.Component { } render() { - console.log("rendering pahse "+this.state.phase); let body; switch (this.state.phase) { case PHASE_START: diff --git a/src/components/views/elements/HexVerify.js b/src/components/views/elements/HexVerify.js new file mode 100644 index 0000000000..667857a792 --- /dev/null +++ b/src/components/views/elements/HexVerify.js @@ -0,0 +1,104 @@ +/* +Copyright 2019 New Vector Ltd. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +import React from "react"; +import PropTypes from "prop-types"; +import { _t } from '../../../languageHandler'; +import classnames from 'classnames'; + +import sdk from '../../../index'; + +class HexVerifyPair extends React.Component { + static propTypes = { + text: PropTypes.string.isRequired, + index: PropTypes.number, + verified: PropTypes.bool, + onChange: PropTypes.func.isRequired, + } + + _onClick = () => { + this.setState({verified: !this.props.verified}); + this.props.onChange(this.props.index, !this.props.verified); + } + + render() { + const classNames = { + mx_HexVerify_pair: true, + mx_HexVerify_pair_verified: this.props.verified, + }; + const AccessibleButton = sdk.getComponent('views.elements.AccessibleButton'); + return + {this.props.text} + + } +} + +/** + * Helps a user verify a hexadecimal code matches one displayed + * elsewhere (eg. on a different device) + */ +export default class HexVerify extends React.Component { + static propTypes = { + text: PropTypes.string.isRequired, + onVerifiedStateChange: PropTypes.func, + } + + static defaultProps = { + onVerifiedStateChange: function() {}, + } + + constructor(props) { + super(props); + this.state = { + pairsVerified: [], + }; + for (let i = 0; i < props.text.length; i += 2) { + this.state.pairsVerified.push(false); + } + } + + _onPairChange = (index, newVal) => { + const oldVerified = this.state.pairsVerified.reduce((acc, val) => { + return acc && val; + }, true); + const newPairsVerified = this.state.pairsVerified.slice(0); + newPairsVerified[index] = newVal; + const newVerified = newPairsVerified.reduce((acc, val) => { + return acc && val; + }, true); + this.setState({pairsVerified: newPairsVerified}); + if (oldVerified !== newVerified) { + this.props.onVerifiedStateChange(newVerified); + } + } + + render() { + const pairs = []; + + for (let i = 0; i < this.props.text.length / 2; ++i) { + pairs.push(); + } + return
+ {pairs} +
; + } +} diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index c26330dda4..7f61a13a21 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -947,7 +947,7 @@ "Send Verification Request": "Send Verification Request", "Verify this user by confirming the following number appears on their screen": "Verify this user by confirming the following number appears on their screen", "For maximum security, we reccommend you do this in person or use another trusted means of communication": "For maximum security, we reccommend you do this in person or use another trusted means of communication", - "This Matches": "This Matches", + "To continue, click on each pair to confirm it's correct.": "To continue, click on each pair to confirm it's correct.", "Verification complete!": "Verification complete!", "Done": "Done", "%(userId)s cancelled the verification.": "%(userId)s cancelled the verification.",