diff --git a/src/RichText.js b/src/RichText.js index 35ad926b91..5fe920fe50 100644 --- a/src/RichText.js +++ b/src/RichText.js @@ -273,11 +273,15 @@ export function attachImmutableEntitiesToEmoji(editorState: EditorState): Editor }); if (!newContentState.equals(contentState)) { - return EditorState.push( + const oldSelection = editorState.getSelection(); + editorState = EditorState.push( editorState, newContentState, 'convert-to-immutable-emojis', ); + // this is somewhat of a hack, we're undoing selection changes caused above + // it would be better not to make those changes in the first place + editorState = EditorState.forceSelection(editorState, oldSelection); } return editorState; diff --git a/src/component-index.js b/src/component-index.js index 08477c676e..bb2d887e40 100644 --- a/src/component-index.js +++ b/src/component-index.js @@ -58,6 +58,7 @@ module.exports.components['views.dialogs.SetDisplayNameDialog'] = require('./com module.exports.components['views.dialogs.TextInputDialog'] = require('./components/views/dialogs/TextInputDialog'); module.exports.components['views.elements.AddressSelector'] = require('./components/views/elements/AddressSelector'); module.exports.components['views.elements.AddressTile'] = require('./components/views/elements/AddressTile'); +module.exports.components['views.elements.DeviceVerifyButtons'] = require('./components/views/elements/DeviceVerifyButtons'); module.exports.components['views.elements.EditableText'] = require('./components/views/elements/EditableText'); module.exports.components['views.elements.EditableTextContainer'] = require('./components/views/elements/EditableTextContainer'); module.exports.components['views.elements.EmojiText'] = require('./components/views/elements/EmojiText'); diff --git a/src/components/views/dialogs/EncryptedEventDialog.js b/src/components/views/dialogs/EncryptedEventDialog.js index be8656064c..c86b1d20f8 100644 --- a/src/components/views/dialogs/EncryptedEventDialog.js +++ b/src/components/views/dialogs/EncryptedEventDialog.js @@ -152,12 +152,12 @@ module.exports = React.createClass({ }, render: function() { - var MemberDeviceInfo = sdk.getComponent('rooms.MemberDeviceInfo'); + var DeviceVerifyButtons = sdk.getComponent('elements.DeviceVerifyButtons'); var buttons = null; if (this.state.device) { buttons = ( - ); diff --git a/src/components/views/elements/DeviceVerifyButtons.js b/src/components/views/elements/DeviceVerifyButtons.js new file mode 100644 index 0000000000..90af1635c9 --- /dev/null +++ b/src/components/views/elements/DeviceVerifyButtons.js @@ -0,0 +1,131 @@ +/* +Copyright 2016 OpenMarket 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 MatrixClientPeg from '../../../MatrixClientPeg'; +import sdk from '../../../index'; +import Modal from '../../../Modal'; + +export default React.createClass({ + displayName: 'DeviceVerifyButtons', + + propTypes: { + userId: React.PropTypes.string.isRequired, + device: React.PropTypes.object.isRequired, + }, + + onVerifyClick: function() { + var QuestionDialog = sdk.getComponent("dialogs.QuestionDialog"); + Modal.createDialog(QuestionDialog, { + title: "Verify device", + description: ( +
+

+ To verify that this device can be trusted, please contact its + owner using some other means (e.g. in person or a phone call) + and ask them whether the key they see in their User Settings + for this device matches the key below: +

+
+
    +
  • { this.props.device.getDisplayName() }
  • +
  • { this.props.device.deviceId}
  • +
  • { this.props.device.getFingerprint() }
  • +
+
+

+ If it matches, press the verify button below. + If it doesnt, then someone else is intercepting this device + and you probably want to press the block button instead. +

+

+ In future this verification process will be more sophisticated. +

+
+ ), + button: "I verify that the keys match", + onFinished: confirm=>{ + if (confirm) { + MatrixClientPeg.get().setDeviceVerified( + this.props.userId, this.props.device.deviceId, true + ); + } + }, + }); + }, + + onUnverifyClick: function() { + MatrixClientPeg.get().setDeviceVerified( + this.props.userId, this.props.device.deviceId, false + ); + }, + + onBlockClick: function() { + MatrixClientPeg.get().setDeviceBlocked( + this.props.userId, this.props.device.deviceId, true + ); + }, + + onUnblockClick: function() { + MatrixClientPeg.get().setDeviceBlocked( + this.props.userId, this.props.device.deviceId, false + ); + }, + + render: function() { + var blockButton = null, verifyButton = null; + + if (this.props.device.isBlocked()) { + blockButton = ( + + ); + } else { + blockButton = ( + + ); + } + + if (this.props.device.isVerified()) { + verifyButton = ( + + ); + } else { + verifyButton = ( + + ); + } + + // mx_MemberDeviceInfo because the vector's CSS on EncryptedEventDialog is awful + return ( +
+ { verifyButton } + { blockButton } +
+ ); + }, +}); diff --git a/src/components/views/rooms/Autocomplete.js b/src/components/views/rooms/Autocomplete.js index 8a32883404..9be91e068a 100644 --- a/src/components/views/rooms/Autocomplete.js +++ b/src/components/views/rooms/Autocomplete.js @@ -17,7 +17,8 @@ export default class Autocomplete extends React.Component { super(props); this.completionPromise = null; - this.onConfirm = this.onConfirm.bind(this); + this.hide = this.hide.bind(this); + this.onCompletionClicked = this.onCompletionClicked.bind(this); this.state = { // list of completionResults, each containing completions @@ -137,6 +138,10 @@ export default class Autocomplete extends React.Component { e.preventDefault(); // selectionOffset = 0, so we don't end up completing when autocomplete is hidden + this.hide(); + } + + hide() { this.setState({hide: true, selectionOffset: 0}); } @@ -152,16 +157,13 @@ export default class Autocomplete extends React.Component { return done.promise; } - /** called from MessageComposerInput - * @returns {boolean} whether confirmation was handled - */ - onConfirm(): boolean { + onCompletionClicked(): boolean { if (this.countCompletions() === 0 || this.state.selectionOffset === COMPOSER_SELECTED) { return false; } - let selectedCompletion = this.state.completionList[this.state.selectionOffset - 1]; - this.props.onConfirm(selectedCompletion.range, selectedCompletion.completion); + this.props.onConfirm(this.state.completionList[this.state.selectionOffset - 1]); + this.hide(); return true; } @@ -199,7 +201,7 @@ export default class Autocomplete extends React.Component { let onMouseOver = () => this.setSelection(componentPosition); let onClick = () => { this.setSelection(componentPosition); - this.onConfirm(); + this.onCompletionClicked(); }; return React.cloneElement(completion.component, { diff --git a/src/components/views/rooms/MemberDeviceInfo.js b/src/components/views/rooms/MemberDeviceInfo.js index fe308bd1c5..51bf7d3637 100644 --- a/src/components/views/rooms/MemberDeviceInfo.js +++ b/src/components/views/rooms/MemberDeviceInfo.js @@ -15,152 +15,48 @@ limitations under the License. */ import React from 'react'; -import MatrixClientPeg from '../../../MatrixClientPeg'; import sdk from '../../../index'; -import Modal from '../../../Modal'; export default class MemberDeviceInfo extends React.Component { - constructor(props) { - super(props); - this.onVerifyClick = this.onVerifyClick.bind(this); - this.onUnverifyClick = this.onUnverifyClick.bind(this); - this.onBlockClick = this.onBlockClick.bind(this); - this.onUnblockClick = this.onUnblockClick.bind(this); - } - - onVerifyClick() { - var QuestionDialog = sdk.getComponent("dialogs.QuestionDialog"); - Modal.createDialog(QuestionDialog, { - title: "Verify device", - description: ( -
-

- To verify that this device can be trusted, please contact its - owner using some other means (e.g. in person or a phone call) - and ask them whether the key they see in their User Settings - for this device matches the key below: -

-
-
    -
  • { this.props.device.getDisplayName() }
  • -
  • { this.props.device.deviceId}
  • -
  • { this.props.device.getFingerprint() }
  • -
-
-

- If it matches, press the verify button below. - If it doesn't, then someone else is intercepting this device - and you probably want to press the block button instead. -

-

- In future this verification process will be more sophisticated. -

-
- ), - button: "I verify that the keys match", - onFinished: confirm=>{ - if (confirm) { - MatrixClientPeg.get().setDeviceVerified( - this.props.userId, this.props.device.deviceId, true - ); - } - }, - }); - } - - onUnverifyClick() { - MatrixClientPeg.get().setDeviceVerified( - this.props.userId, this.props.device.deviceId, false - ); - } - - onBlockClick() { - MatrixClientPeg.get().setDeviceBlocked( - this.props.userId, this.props.device.deviceId, true - ); - } - - onUnblockClick() { - MatrixClientPeg.get().setDeviceBlocked( - this.props.userId, this.props.device.deviceId, false - ); - } - render() { - if (!this.props.device) { - return
; - } + var indicator = null; + var DeviceVerifyButtons = sdk.getComponent('elements.DeviceVerifyButtons'); - var indicator = null, blockButton = null, verifyButton = null; if (this.props.device.isBlocked()) { - blockButton = ( - - ); - } else { - blockButton = ( - - ); - } - - if (this.props.device.isVerified()) { - verifyButton = ( - - ); - } else { - verifyButton = ( - - ); - } - - if (!this.props.hideInfo) { - if (this.props.device.isBlocked()) { - indicator = ( + indicator = (
- Blocked + Blocked
- ); - } else if (this.props.device.isVerified()) { - indicator = ( + ); + } else if (this.props.device.isVerified()) { + indicator = (
- Verified + Verified
- ); - } else { - indicator = ( + ); + } else { + indicator = (
- Unverified + Unverified
- ); - } - - var deviceName = this.props.device.ambiguous ? - (this.props.device.getDisplayName() ? this.props.device.getDisplayName() : "") + " (" + this.props.device.deviceId + ")" : - this.props.device.getDisplayName(); - - var info = ( -
-
{deviceName}{indicator}
-
); } + var deviceName = this.props.device.ambiguous ? + (this.props.device.getDisplayName() ? this.props.device.getDisplayName() : "") + " (" + this.props.device.deviceId + ")" : + this.props.device.getDisplayName(); + + // add the deviceId as a titletext to help with debugging return ( -
- { info } - { verifyButton } - { blockButton } +
+
+
+ {deviceName} + {indicator} +
+
+
); } @@ -170,5 +66,4 @@ MemberDeviceInfo.displayName = 'MemberDeviceInfo'; MemberDeviceInfo.propTypes = { userId: React.PropTypes.string.isRequired, device: React.PropTypes.object.isRequired, - hideInfo: React.PropTypes.bool, }; diff --git a/src/components/views/rooms/MessageComposer.js b/src/components/views/rooms/MessageComposer.js index 395141aa15..4aeebe1411 100644 --- a/src/components/views/rooms/MessageComposer.js +++ b/src/components/views/rooms/MessageComposer.js @@ -178,7 +178,7 @@ export default class MessageComposer extends React.Component { _tryComplete(): boolean { if (this.refs.autocomplete) { - return this.refs.autocomplete.onConfirm(); + return this.refs.autocomplete.onCompletionClicked(); } return false; } diff --git a/src/components/views/rooms/MessageComposerInput.js b/src/components/views/rooms/MessageComposerInput.js index 2733bddc14..dac952a0c3 100644 --- a/src/components/views/rooms/MessageComposerInput.js +++ b/src/components/views/rooms/MessageComposerInput.js @@ -567,6 +567,8 @@ export default class MessageComposerInput extends React.Component { editorState: this.createEditorState(), }); + this.autocomplete.hide(); + return true; }