From d9d8f2055ff34b2c5e2ca7167c9728ecd3e0bb19 Mon Sep 17 00:00:00 2001 From: Luke Barnard Date: Mon, 7 Aug 2017 16:23:37 +0100 Subject: [PATCH 01/13] Allow default for ctrl+shift+b, ctrl+shift+u in RTE fixes vector-im/riot-web#4750 --- src/components/views/rooms/MessageComposerInput.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/components/views/rooms/MessageComposerInput.js b/src/components/views/rooms/MessageComposerInput.js index c16348300f..68df8fce57 100644 --- a/src/components/views/rooms/MessageComposerInput.js +++ b/src/components/views/rooms/MessageComposerInput.js @@ -104,7 +104,11 @@ export default class MessageComposerInput extends React.Component { } // Allow opening of dev tools. getDefaultKeyBinding would be 'italic' for KEY_I - if (e.keyCode === KeyCode.KEY_I && e.shiftKey && e.ctrlKey) { + // Likewise protect bold and underline (in case some browsers use these as + // shortcuts for things). + if ([KeyCode.KEY_B, KeyCode.KEY_I, KeyCode.KEY_U].includes(e.keyCode) && + e.shiftKey && e.ctrlKey + ) { // When null is returned, draft-js will NOT preventDefault, allowing dev tools // to be toggled when the editor is focussed return null; From 641fda01622754f31a7b49629a617440317c86c1 Mon Sep 17 00:00:00 2001 From: Luke Barnard Date: Mon, 7 Aug 2017 16:29:22 +0100 Subject: [PATCH 02/13] Adjust comment --- src/components/views/rooms/MessageComposerInput.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/components/views/rooms/MessageComposerInput.js b/src/components/views/rooms/MessageComposerInput.js index 68df8fce57..f2c6c3a054 100644 --- a/src/components/views/rooms/MessageComposerInput.js +++ b/src/components/views/rooms/MessageComposerInput.js @@ -109,8 +109,7 @@ export default class MessageComposerInput extends React.Component { if ([KeyCode.KEY_B, KeyCode.KEY_I, KeyCode.KEY_U].includes(e.keyCode) && e.shiftKey && e.ctrlKey ) { - // When null is returned, draft-js will NOT preventDefault, allowing dev tools - // to be toggled when the editor is focussed + // When null is returned, draft-js will NOT preventDefault return null; } From 7018deee44da83472be19145cc8f8612ae5d8d32 Mon Sep 17 00:00:00 2001 From: Luke Barnard Date: Mon, 7 Aug 2017 17:16:42 +0100 Subject: [PATCH 03/13] Fix ctrl+a, backspace toggling block format Now it will delete the selected range (and not toggle the block format). Fixes vector-im/riot-web#4753 --- src/components/views/rooms/MessageComposerInput.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/components/views/rooms/MessageComposerInput.js b/src/components/views/rooms/MessageComposerInput.js index f2c6c3a054..d619de96d3 100644 --- a/src/components/views/rooms/MessageComposerInput.js +++ b/src/components/views/rooms/MessageComposerInput.js @@ -519,7 +519,8 @@ export default class MessageComposerInput extends React.Component { newState = RichUtils.toggleInlineStyle(this.state.editorState, 'STRIKETHROUGH'); } else if (shouldToggleBlockFormat) { const currentStartOffset = this.state.editorState.getSelection().getStartOffset(); - if (currentStartOffset === 0) { + const currentEndOffset = this.state.editorState.getSelection().getEndOffset(); + if (currentStartOffset === 0 && currentEndOffset === 0) { // Toggle current block type (setting it to 'unstyled') newState = RichUtils.toggleBlockType(this.state.editorState, currentBlockType); } From 1743c047bd66366779b80defdb3f2f7534ae7233 Mon Sep 17 00:00:00 2001 From: Luke Barnard Date: Tue, 8 Aug 2017 10:28:11 +0100 Subject: [PATCH 04/13] Use the rawDisplayName for the user provider completion to make sure that the length of text in the decoration (See ) is equal to the length of text in the completion (underlying text range that the Entity covers). --- src/autocomplete/UserProvider.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/autocomplete/UserProvider.js b/src/autocomplete/UserProvider.js index 9c93cf537f..499ddb51ce 100644 --- a/src/autocomplete/UserProvider.js +++ b/src/autocomplete/UserProvider.js @@ -54,7 +54,9 @@ export default class UserProvider extends AutocompleteProvider { completions = this.matcher.match(command[0]).map((user) => { const displayName = (user.name || user.userId || '').replace(' (IRC)', ''); // FIXME when groups are done return { - completion: displayName, + // Length of completion should equal length of text in decorator. draft-js + // relies on the length of the entity === length of the text in the decoration. + completion: user.rawDisplayName, suffix: range.start === 0 ? ': ' : ' ', href: 'https://matrix.to/#/' + user.userId, component: ( From b08d32371d07efc572610bdbdc4dabfc6bc5aa6e Mon Sep 17 00:00:00 2001 From: Luke Barnard Date: Tue, 8 Aug 2017 11:13:29 +0100 Subject: [PATCH 05/13] Add optional setting for hiding avatars in s As part of https://github.com/vector-im/riot-web/issues/4640#issuecomment-316659445 --- src/components/structures/UserSettings.js | 4 ++++ src/components/views/elements/Pill.js | 10 ++++++++-- src/components/views/messages/TextualBody.js | 8 +++++++- src/components/views/rooms/MessageComposerInput.js | 8 +++++++- src/i18n/strings/en_EN.json | 3 ++- 5 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/components/structures/UserSettings.js b/src/components/structures/UserSettings.js index 1e0fcff445..72a287d584 100644 --- a/src/components/structures/UserSettings.js +++ b/src/components/structures/UserSettings.js @@ -101,6 +101,10 @@ const SETTINGS_LABELS = [ id: 'MessageComposerInput.autoReplaceEmoji', label: 'Automatically replace plain text Emoji', }, + { + id :'Pill.shouldHidePillAvatar', + label: 'Hide avatars in user and room mentions', + } /* { id: 'useFixedWidthFont', diff --git a/src/components/views/elements/Pill.js b/src/components/views/elements/Pill.js index 8d19eb5999..b5fa163608 100644 --- a/src/components/views/elements/Pill.js +++ b/src/components/views/elements/Pill.js @@ -47,6 +47,8 @@ const Pill = React.createClass({ inMessage: PropTypes.bool, // The room in which this pill is being rendered room: PropTypes.instanceOf(Room), + // Whether to include an avatar in the pill + shouldShowPillAvatar: PropTypes.bool, }, getInitialState() { @@ -155,7 +157,9 @@ const Pill = React.createClass({ if (member) { userId = member.userId; linkText = member.rawDisplayName.replace(' (IRC)', ''); // FIXME when groups are done - avatar = ; + if (this.props.shouldShowPillAvatar) { + avatar = ; + } pillClass = 'mx_UserPill'; } } @@ -164,7 +168,9 @@ const Pill = React.createClass({ const room = this.state.room; if (room) { linkText = (room ? getDisplayAliasForRoom(room) : null) || resource; - avatar = ; + if (this.props.shouldShowPillAvatar) { + avatar = ; + } pillClass = 'mx_RoomPill'; } } diff --git a/src/components/views/messages/TextualBody.js b/src/components/views/messages/TextualBody.js index 6d4d01a196..27dba76146 100644 --- a/src/components/views/messages/TextualBody.js +++ b/src/components/views/messages/TextualBody.js @@ -170,6 +170,7 @@ module.exports = React.createClass({ }, pillifyLinks: function(nodes) { + const shouldShowPillAvatar = !UserSettingsStore.getSyncedSetting("Pill.shouldHidePillAvatar", false); for (let i = 0; i < nodes.length; i++) { const node = nodes[i]; if (node.tagName === "A" && node.getAttribute("href")) { @@ -181,7 +182,12 @@ module.exports = React.createClass({ const pillContainer = document.createElement('span'); const room = MatrixClientPeg.get().getRoom(this.props.mxEvent.getRoomId()); - const pill = ; + const pill = ; ReactDOM.render(pill, pillContainer); node.parentNode.replaceChild(pillContainer, node); diff --git a/src/components/views/rooms/MessageComposerInput.js b/src/components/views/rooms/MessageComposerInput.js index d619de96d3..81704d5aba 100644 --- a/src/components/views/rooms/MessageComposerInput.js +++ b/src/components/views/rooms/MessageComposerInput.js @@ -188,13 +188,19 @@ export default class MessageComposerInput extends React.Component { createEditorState(richText: boolean, contentState: ?ContentState): EditorState { const decorators = richText ? RichText.getScopedRTDecorators(this.props) : RichText.getScopedMDDecorators(this.props); + const shouldShowPillAvatar = !UserSettingsStore.getSyncedSetting("Pill.shouldHidePillAvatar", false); decorators.push({ strategy: this.findLinkEntities.bind(this), component: (entityProps) => { const Pill = sdk.getComponent('elements.Pill'); const {url} = entityProps.contentState.getEntity(entityProps.entityKey).getData(); if (Pill.isPillUrl(url)) { - return ; + return ; } return ( diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index 0402c242aa..0d5b7d9d96 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -966,5 +966,6 @@ "Edit Group": "Edit Group", "Automatically replace plain text Emoji": "Automatically replace plain text Emoji", "Failed to upload image": "Failed to upload image", - "Failed to update group": "Failed to update group" + "Failed to update group": "Failed to update group", + "Hide avatars in user and room mentions": "Hide avatars in user and room mentions" } From 91a1cc443142e31fa53c4684e1a38a22e4615609 Mon Sep 17 00:00:00 2001 From: Luke Barnard Date: Tue, 8 Aug 2017 13:36:43 +0100 Subject: [PATCH 06/13] Mandate ctrl/meta ONLY for a subset of key bindings Because by default dratf-js doesn't check that other modifiers are _not_ pressed. --- .../views/rooms/MessageComposerInput.js | 42 +++++++++++++------ 1 file changed, 29 insertions(+), 13 deletions(-) diff --git a/src/components/views/rooms/MessageComposerInput.js b/src/components/views/rooms/MessageComposerInput.js index d619de96d3..1de2214574 100644 --- a/src/components/views/rooms/MessageComposerInput.js +++ b/src/components/views/rooms/MessageComposerInput.js @@ -97,23 +97,39 @@ export default class MessageComposerInput extends React.Component { onInputStateChanged: React.PropTypes.func, }; - static getKeyBinding(e: SyntheticKeyboardEvent): string { - // C-m => Toggles between rich text and markdown modes - if (e.keyCode === KeyCode.KEY_M && KeyBindingUtil.isCtrlKeyCommand(e)) { - return 'toggle-mode'; + static getKeyBinding(ev: SyntheticKeyboardEvent): string { + const isMac = navigator.platform.toUpperCase().indexOf('MAC') >= 0; + let ctrlCmdOnly; + if (isMac) { + ctrlCmdOnly = ev.metaKey && !ev.altKey && !ev.ctrlKey && !ev.shiftKey; + } else { + ctrlCmdOnly = ev.ctrlKey && !ev.altKey && !ev.metaKey && !ev.shiftKey; } - // Allow opening of dev tools. getDefaultKeyBinding would be 'italic' for KEY_I - // Likewise protect bold and underline (in case some browsers use these as - // shortcuts for things). - if ([KeyCode.KEY_B, KeyCode.KEY_I, KeyCode.KEY_U].includes(e.keyCode) && - e.shiftKey && e.ctrlKey - ) { - // When null is returned, draft-js will NOT preventDefault - return null; + // Restrict a subset of key bindings to ONLY having ctrl/meta* pressed and + // importantly NOT having alt, shift, meta/ctrl* pressed. draft-js does not + // handle this in `getDefaultKeyBinding` so we do it ourselves here. + // + // * if macOS, read second option + const ctrlCmdCommand = { + // C-m => Toggles between rich text and markdown modes + [KeyCode.KEY_M]: 'toggle-mode', + [KeyCode.KEY_B]: 'bold', + [KeyCode.KEY_I]: 'italic', + [KeyCode.KEY_U]: 'underline', + [KeyCode.KEY_J]: 'code', + [KeyCode.KEY_O]: 'split-block', + }[ev.keyCode]; + + if (ctrlCmdCommand) { + if (!ctrlCmdOnly) { + return null; + } + return ctrlCmdCommand; } - return getDefaultKeyBinding(e); + // Handle keys such as return, left and right arrows etc. + return getDefaultKeyBinding(ev); } static getBlockStyle(block: ContentBlock): ?string { From bef67262905bee335e90f60627bda0b1ebf9ecf1 Mon Sep 17 00:00:00 2001 From: Luke Barnard Date: Tue, 8 Aug 2017 13:42:51 +0100 Subject: [PATCH 07/13] Lint --- src/components/structures/UserSettings.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/structures/UserSettings.js b/src/components/structures/UserSettings.js index 72a287d584..916e50d86b 100644 --- a/src/components/structures/UserSettings.js +++ b/src/components/structures/UserSettings.js @@ -102,9 +102,9 @@ const SETTINGS_LABELS = [ label: 'Automatically replace plain text Emoji', }, { - id :'Pill.shouldHidePillAvatar', + id: 'Pill.shouldHidePillAvatar', label: 'Hide avatars in user and room mentions', - } + }, /* { id: 'useFixedWidthFont', From 503fa6a7b3f6c201c805eca3da16f7535364312b Mon Sep 17 00:00:00 2001 From: Luke Barnard Date: Tue, 8 Aug 2017 14:59:56 +0100 Subject: [PATCH 08/13] Always use message `body` when quoting (not formatted_body) This is because draft-js has regressed with a bug that causes some entities to not exist within a given ContentState - see vector-im/riot-web#4756 --- src/components/views/rooms/MessageComposerInput.js | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/components/views/rooms/MessageComposerInput.js b/src/components/views/rooms/MessageComposerInput.js index 283a0e9330..4e6b57b7f4 100644 --- a/src/components/views/rooms/MessageComposerInput.js +++ b/src/components/views/rooms/MessageComposerInput.js @@ -279,10 +279,13 @@ export default class MessageComposerInput extends React.Component { } break; case 'quote': { - let {body, formatted_body} = payload.event.getContent(); - formatted_body = formatted_body || escape(body); - if (formatted_body) { - let content = RichText.htmlToContentState(`
${formatted_body}
`); + let {body} = payload.event.getContent(); + /// XXX: Not doing rich-text quoting from formatted-body because draft-js + /// has regressed such that when links are quoted, errors are thrown. See + /// https://github.com/vector-im/riot-web/issues/4756. + body = escape(body); + if (body) { + let content = RichText.htmlToContentState(`
${body}
`); if (!this.state.isRichtextEnabled) { content = ContentState.createFromText(RichText.stateToMarkdown(content)); } From a72f38799f82cabe1bc9d0fc1b9d610311490707 Mon Sep 17 00:00:00 2001 From: Luke Barnard Date: Tue, 8 Aug 2017 15:58:15 +0100 Subject: [PATCH 09/13] Disable autocompletions for users and rooms when entering a command This only affects commands that take a room alias or user ID as an argument. (Leaving commands such as `/me` unaffected) --- src/autocomplete/RoomProvider.js | 6 ++++++ src/autocomplete/UserProvider.js | 6 ++++++ 2 files changed, 12 insertions(+) diff --git a/src/autocomplete/RoomProvider.js b/src/autocomplete/RoomProvider.js index 3749e7e693..1770089eb2 100644 --- a/src/autocomplete/RoomProvider.js +++ b/src/autocomplete/RoomProvider.js @@ -49,6 +49,12 @@ export default class RoomProvider extends AutocompleteProvider { async getCompletions(query: string, selection: {start: number, end: number}, force = false) { const RoomAvatar = sdk.getComponent('views.avatars.RoomAvatar'); + // Disable autocompletions when composing commands because of various issues + // (see https://github.com/vector-im/riot-web/issues/4762) + if (/^(\/join|\/leave)/.test(query)) { + return []; + } + const client = MatrixClientPeg.get(); let completions = []; const {command, range} = this.getCurrentCommand(query, selection, force); diff --git a/src/autocomplete/UserProvider.js b/src/autocomplete/UserProvider.js index 499ddb51ce..5db0369150 100644 --- a/src/autocomplete/UserProvider.js +++ b/src/autocomplete/UserProvider.js @@ -48,6 +48,12 @@ export default class UserProvider extends AutocompleteProvider { async getCompletions(query: string, selection: {start: number, end: number}, force = false) { const MemberAvatar = sdk.getComponent('views.avatars.MemberAvatar'); + // Disable autocompletions when composing commands because of various issues + // (see https://github.com/vector-im/riot-web/issues/4762) + if (/^(\/ban|\/unban|\/op|\/deop|\/invite|\/kick|\/verify)/.test(query)) { + return []; + } + let completions = []; let {command, range} = this.getCurrentCommand(query, selection, force); if (command) { From cb8a66b5a119d46e93265b0f73031e10d7559fa0 Mon Sep 17 00:00:00 2001 From: Luke Barnard Date: Tue, 8 Aug 2017 17:25:11 +0100 Subject: [PATCH 10/13] When `hide`ing autocomplete, also remove completion state --- src/components/views/rooms/Autocomplete.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/views/rooms/Autocomplete.js b/src/components/views/rooms/Autocomplete.js index 1ea2eada7c..cdd57801a5 100644 --- a/src/components/views/rooms/Autocomplete.js +++ b/src/components/views/rooms/Autocomplete.js @@ -172,7 +172,7 @@ export default class Autocomplete extends React.Component { } hide() { - this.setState({hide: true, selectionOffset: 0}); + this.setState({hide: true, selectionOffset: 0, completions: [], completionList: []}); } forceComplete() { From da85cb9f454227d9d165c1e6660463cafaa59457 Mon Sep 17 00:00:00 2001 From: Richard van der Hoff Date: Tue, 8 Aug 2017 12:34:40 +0100 Subject: [PATCH 11/13] Show unencrypted messages as unencrypted Previously, we were special-casing outgoing messages such that they were shown as encrypted even when encryption had failed for some reason. There's no need for this: outgoing messages have a working isEncrypted() method which we can use to show whether the event has been encrypted yet. Arguably we could do better than an open padlock for events in the 'encrypting' send state, but I'm not really sure what. --- src/components/views/rooms/EventTile.js | 94 +++++++++++++++++++------ 1 file changed, 71 insertions(+), 23 deletions(-) diff --git a/src/components/views/rooms/EventTile.js b/src/components/views/rooms/EventTile.js index b3831a7d0d..815f0a3c6a 100644 --- a/src/components/views/rooms/EventTile.js +++ b/src/components/views/rooms/EventTile.js @@ -155,7 +155,9 @@ module.exports = withMatrixClient(React.createClass({ }, componentWillReceiveProps: function(nextProps) { - if (nextProps.mxEvent !== this.props.mxEvent) { + // re-check the sender verification as outgoing events progress through + // the send process. + if (nextProps.eventSendStatus !== this.props.eventSendStatus) { this._verifyEvent(nextProps.mxEvent); } }, @@ -386,6 +388,36 @@ module.exports = withMatrixClient(React.createClass({ }); }, + _renderE2EPadlock: function() { + const ev = this.props.mxEvent; + const props = {onClick: this.onCryptoClicked}; + + + if (ev.getContent().msgtype === 'm.bad.encrypted') { + return ; + } else if (ev.isEncrypted()) { + if (this.state.verified) { + return ; + } else { + return ; + } + } else { + // XXX: if the event is being encrypted (ie eventSendStatus === + // encrypting), it might be nice to show something other than the + // open padlock? + + // if the event is not encrypted, but it's an e2e room, show the + // open padlock + const e2eEnabled = this.props.matrixClient.isRoomEncrypted(ev.getRoomId()); + if (e2eEnabled) { + return ; + } + } + + // no padlock needed + return null; + }, + render: function() { var MessageTimestamp = sdk.getComponent('messages.MessageTimestamp'); var SenderProfile = sdk.getComponent('messages.SenderProfile'); @@ -407,7 +439,6 @@ module.exports = withMatrixClient(React.createClass({ throw new Error("Event type not supported"); } - var e2eEnabled = this.props.matrixClient.isRoomEncrypted(this.props.mxEvent.getRoomId()); var isSending = (['sending', 'queued', 'encrypting'].indexOf(this.props.eventSendStatus) !== -1); const isRedacted = (eventType === 'm.room.message') && this.props.isRedacted; @@ -485,26 +516,7 @@ module.exports = withMatrixClient(React.createClass({ const editButton = ( ); - let e2e; - // cosmetic padlocks: - if ((e2eEnabled && this.props.eventSendStatus) || this.props.mxEvent.getType() === 'm.room.encryption') { - e2e = {_t("Encrypted; - } - // real padlocks - else if (this.props.mxEvent.isEncrypted() || (e2eEnabled && this.props.eventSendStatus)) { - if (this.props.mxEvent.getContent().msgtype === 'm.bad.encrypted') { - e2e = {_t("Undecryptable")}; - } - else if (this.state.verified == true || (e2eEnabled && this.props.eventSendStatus)) { - e2e = {_t("Encrypted; - } - else { - e2e = {_t("Encrypted; - } - } - else if (e2eEnabled) { - e2e = {_t("Unencrypted; - } + const timestamp = this.props.mxEvent.getTs() ? : null; @@ -572,7 +584,7 @@ module.exports = withMatrixClient(React.createClass({ { timestamp } - { e2e } + { this._renderE2EPadlock() } + ); +} + +function E2ePadlockVerified(props) { + return ( + + ); +} + +function E2ePadlockUnverified(props) { + return ( + + ); +} + +function E2ePadlockUnencrypted(props) { + return ( + + ); +} + +function E2ePadlock(props) { + return ; +} From 38114711fdde1fa5c019c34f3a9c93d3b4e64108 Mon Sep 17 00:00:00 2001 From: Richard van der Hoff Date: Tue, 8 Aug 2017 22:19:38 +0100 Subject: [PATCH 12/13] Make MatrixChat do fewer render cycles during mount This is mostly with the intent of making the login tests more reliable, but it seems generally worthwhile: * keep screenAfterLogin in the object props rather than `state` so that we can clear it without triggering a rerender * also move our record of the window width to the object props, and call `handleResize` from componentWillMount rather than componentDidMount so that we don't trigger a rerender by updating `state.width` * Remove update of unused `loading` state --- src/components/structures/MatrixChat.js | 48 ++++++++++++------------- 1 file changed, 23 insertions(+), 25 deletions(-) diff --git a/src/components/structures/MatrixChat.js b/src/components/structures/MatrixChat.js index b90cb53435..cb6419c9e8 100644 --- a/src/components/structures/MatrixChat.js +++ b/src/components/structures/MatrixChat.js @@ -131,9 +131,6 @@ module.exports = React.createClass({ // the master view we are showing. view: VIEWS.LOADING, - // a thing to call showScreen with once login completes. - screenAfterLogin: this.props.initialScreenAfterLogin, - // What the LoggedInView would be showing if visible page_type: null, @@ -147,8 +144,6 @@ module.exports = React.createClass({ collapse_lhs: false, collapse_rhs: false, - ready: false, - width: 10000, leftOpacity: 1.0, middleOpacity: 1.0, rightOpacity: 1.0, @@ -274,6 +269,15 @@ module.exports = React.createClass({ register_hs_url: paramHs, }); } + + // a thing to call showScreen with once login completes. this is kept + // outside this.state because updating it should never trigger a + // rerender. + this._screenAfterLogin = this.props.initialScreenAfterLogin; + + this._windowWidth = 10000; + this.handleResize(); + window.addEventListener('resize', this.handleResize); }, componentDidMount: function() { @@ -294,9 +298,6 @@ module.exports = React.createClass({ linkifyMatrix.onGroupClick = this.onGroupClick; } - window.addEventListener('resize', this.handleResize); - this.handleResize(); - const teamServerConfig = this.props.config.teamServerConfig || {}; Lifecycle.initRtsClient(teamServerConfig.teamServerURL); @@ -312,13 +313,12 @@ module.exports = React.createClass({ // if the user has followed a login or register link, don't reanimate // the old creds, but rather go straight to the relevant page - const firstScreen = this.state.screenAfterLogin ? - this.state.screenAfterLogin.screen : null; + const firstScreen = this._screenAfterLogin ? + this._screenAfterLogin.screen : null; if (firstScreen === 'login' || firstScreen === 'register' || firstScreen === 'forgot_password') { - this.setState({loading: false}); this._showScreenAfterLogin(); return; } @@ -367,9 +367,9 @@ module.exports = React.createClass({ } const newState = { viewUserId: null, - }; - Object.assign(newState, state); - this.setState(newState); + }; + Object.assign(newState, state); + this.setState(newState); }, onAction: function(payload) { @@ -992,14 +992,12 @@ module.exports = React.createClass({ _showScreenAfterLogin: function() { // If screenAfterLogin is set, use that, then null it so that a second login will // result in view_home_page, _user_settings or _room_directory - if (this.state.screenAfterLogin && this.state.screenAfterLogin.screen) { + if (this._screenAfterLogin && this._screenAfterLogin.screen) { this.showScreen( - this.state.screenAfterLogin.screen, - this.state.screenAfterLogin.params, + this._screenAfterLogin.screen, + this._screenAfterLogin.params, ); - // XXX: is this necessary? `showScreen` should do it for us. - this.notifyNewScreen(this.state.screenAfterLogin.screen); - this.setState({screenAfterLogin: null}); + this._screenAfterLogin = null; } else if (localStorage && localStorage.getItem('mx_last_room_id')) { // Before defaulting to directory, show the last viewed room dis.dispatch({ @@ -1276,20 +1274,20 @@ module.exports = React.createClass({ const hideRhsThreshold = 820; const showRhsThreshold = 820; - if (this.state.width > hideLhsThreshold && window.innerWidth <= hideLhsThreshold) { + if (this._windowWidth > hideLhsThreshold && window.innerWidth <= hideLhsThreshold) { dis.dispatch({ action: 'hide_left_panel' }); } - if (this.state.width <= showLhsThreshold && window.innerWidth > showLhsThreshold) { + if (this._windowWidth <= showLhsThreshold && window.innerWidth > showLhsThreshold) { dis.dispatch({ action: 'show_left_panel' }); } - if (this.state.width > hideRhsThreshold && window.innerWidth <= hideRhsThreshold) { + if (this._windowWidth > hideRhsThreshold && window.innerWidth <= hideRhsThreshold) { dis.dispatch({ action: 'hide_right_panel' }); } - if (this.state.width <= showRhsThreshold && window.innerWidth > showRhsThreshold) { + if (this._windowWidth <= showRhsThreshold && window.innerWidth > showRhsThreshold) { dis.dispatch({ action: 'show_right_panel' }); } - this.setState({width: window.innerWidth}); + this._windowWidth = window.innerWidth; }, onRoomCreated: function(roomId) { From 610b2a3a428df410d2549508b2a3f883526c4e3b Mon Sep 17 00:00:00 2001 From: Luke Barnard Date: Wed, 9 Aug 2017 10:40:06 +0100 Subject: [PATCH 13/13] For mentions, always use rawDisplayName and remove (IRC) --- src/autocomplete/UserProvider.js | 2 +- src/components/views/rooms/MessageComposerInput.js | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/autocomplete/UserProvider.js b/src/autocomplete/UserProvider.js index 5db0369150..69b80dade4 100644 --- a/src/autocomplete/UserProvider.js +++ b/src/autocomplete/UserProvider.js @@ -62,7 +62,7 @@ export default class UserProvider extends AutocompleteProvider { return { // Length of completion should equal length of text in decorator. draft-js // relies on the length of the entity === length of the text in the decoration. - completion: user.rawDisplayName, + completion: user.rawDisplayName.replace(' (IRC)', ''), suffix: range.start === 0 ? ': ' : ' ', href: 'https://matrix.to/#/' + user.userId, component: ( diff --git a/src/components/views/rooms/MessageComposerInput.js b/src/components/views/rooms/MessageComposerInput.js index 4e6b57b7f4..b2c1436365 100644 --- a/src/components/views/rooms/MessageComposerInput.js +++ b/src/components/views/rooms/MessageComposerInput.js @@ -269,7 +269,8 @@ export default class MessageComposerInput extends React.Component { // paths for inserting a user pill is not fun const selection = this.state.editorState.getSelection(); const member = this.props.room.getMember(payload.user_id); - const completion = member ? member.name.replace(' (IRC)', '') : payload.user_id; + const completion = member ? + member.rawDisplayName.replace(' (IRC)', '') : payload.user_id; this.setDisplayedCompletion({ completion, selection,