From ef88e02931f8de61960e362a636e47bc7fb102fc Mon Sep 17 00:00:00 2001 From: Sijmen Schoon Date: Sun, 8 Jan 2017 02:20:59 +0100 Subject: [PATCH 001/202] Add support for pasting into the text box Only supports the new rich-text-supporting text editor --- src/ContentMessages.js | 4 ++-- src/components/views/rooms/MessageComposer.js | 10 ++++++---- src/components/views/rooms/MessageComposerInput.js | 8 ++++++++ 3 files changed, 16 insertions(+), 6 deletions(-) diff --git a/src/ContentMessages.js b/src/ContentMessages.js index c169ce64b5..765c7ed976 100644 --- a/src/ContentMessages.js +++ b/src/ContentMessages.js @@ -276,7 +276,7 @@ class ContentMessages { sendContentToRoom(file, roomId, matrixClient) { const content = { - body: file.name, + body: file.name || 'Attachment', info: { size: file.size, } @@ -316,7 +316,7 @@ class ContentMessages { } const upload = { - fileName: file.name, + fileName: file.name || 'Attachment', roomId: roomId, total: 0, loaded: 0, diff --git a/src/components/views/rooms/MessageComposer.js b/src/components/views/rooms/MessageComposer.js index ee9c49d52a..6810e75f53 100644 --- a/src/components/views/rooms/MessageComposer.js +++ b/src/components/views/rooms/MessageComposer.js @@ -91,8 +91,9 @@ export default class MessageComposer extends React.Component { this.refs.uploadInput.click(); } - onUploadFileSelected(ev) { - let files = ev.target.files; + onUploadFileSelected(files, isPasted) { + if (!isPasted) + files = files.target.files; let QuestionDialog = sdk.getComponent("dialogs.QuestionDialog"); let TintableSvg = sdk.getComponent("elements.TintableSvg"); @@ -100,7 +101,7 @@ export default class MessageComposer extends React.Component { let fileList = []; for (let i=0; i - {files[i].name} + {files[i].name || 'Attachment'} ); } @@ -171,7 +172,7 @@ export default class MessageComposer extends React.Component { } onUpArrow() { - return this.refs.autocomplete.onUpArrow(); + return this.refs.autocomplete.onUpArrow(); } onDownArrow() { @@ -293,6 +294,7 @@ export default class MessageComposer extends React.Component { tryComplete={this._tryComplete} onUpArrow={this.onUpArrow} onDownArrow={this.onDownArrow} + onUploadFileSelected={this.onUploadFileSelected} tabComplete={this.props.tabComplete} // used for old messagecomposerinput/tabcomplete onContentChanged={this.onInputContentChanged} onInputStateChanged={this.onInputStateChanged} />, diff --git a/src/components/views/rooms/MessageComposerInput.js b/src/components/views/rooms/MessageComposerInput.js index 37d937d6f5..f0658ab543 100644 --- a/src/components/views/rooms/MessageComposerInput.js +++ b/src/components/views/rooms/MessageComposerInput.js @@ -83,6 +83,7 @@ export default class MessageComposerInput extends React.Component { this.onAction = this.onAction.bind(this); this.handleReturn = this.handleReturn.bind(this); this.handleKeyCommand = this.handleKeyCommand.bind(this); + this.handlePastedFiles = this.handlePastedFiles.bind(this); this.onEditorContentChanged = this.onEditorContentChanged.bind(this); this.setEditorState = this.setEditorState.bind(this); this.onUpArrow = this.onUpArrow.bind(this); @@ -473,6 +474,10 @@ export default class MessageComposerInput extends React.Component { return false; } + handlePastedFiles(files) { + this.props.onUploadFileSelected(files, true); + } + handleReturn(ev) { if (ev.shiftKey) { this.onEditorContentChanged(RichUtils.insertSoftNewline(this.state.editorState)); @@ -728,6 +733,7 @@ export default class MessageComposerInput extends React.Component { keyBindingFn={MessageComposerInput.getKeyBinding} handleKeyCommand={this.handleKeyCommand} handleReturn={this.handleReturn} + handlePastedFiles={this.handlePastedFiles} stripPastedStyles={!this.state.isRichtextEnabled} onTab={this.onTab} onUpArrow={this.onUpArrow} @@ -757,6 +763,8 @@ MessageComposerInput.propTypes = { onDownArrow: React.PropTypes.func, + onUploadFileSelected: React.PropTypes.func, + // attempts to confirm currently selected completion, returns whether actually confirmed tryComplete: React.PropTypes.func, From 8e3f2eb858b16d81fbe4f69896cf4b831ce3873f Mon Sep 17 00:00:00 2001 From: Luke Barnard Date: Wed, 11 Jan 2017 16:35:37 +0000 Subject: [PATCH 002/202] Allow [bf]g colors for style attrib Instead of dropping the style attribute on `` tags entirely, sanitise aggressively and only keep `background-color` and `color` keys, and also sanitise the values to prevent `url(XXXXXX)` and `expression(XXXXXX)` type XSS attacks. --- src/HtmlUtils.js | 32 +++++++++++++++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) diff --git a/src/HtmlUtils.js b/src/HtmlUtils.js index fc1630b6fb..ae594de960 100644 --- a/src/HtmlUtils.js +++ b/src/HtmlUtils.js @@ -28,6 +28,11 @@ emojione.imagePathSVG = 'emojione/svg/'; emojione.imageType = 'svg'; const EMOJI_REGEX = new RegExp(emojione.unicodeRegexp+"+", "gi"); +const COLOR_REGEX = /^[#a-z0-9]+$/; +const ALLOWED_CSS = { + "background-color": COLOR_REGEX, + "color": COLOR_REGEX, +}; /* modified from https://github.com/Ranks/emojione/blob/master/lib/js/emojione.js * because we want to include emoji shortnames in title text @@ -91,7 +96,7 @@ var sanitizeHtmlParams = { ], allowedAttributes: { // custom ones first: - font: [ 'color' ], // custom to matrix + font: [ 'color' , 'style' ], // custom to matrix a: [ 'href', 'name', 'target', 'rel' ], // remote target: custom to matrix // We don't currently allow img itself by default, but this // would make sense if we did @@ -136,6 +141,31 @@ var sanitizeHtmlParams = { attribs.rel = 'noopener'; // https://mathiasbynens.github.io/rel-noopener/ return { tagName: tagName, attribs : attribs }; }, + '*': function(tagName, attribs) { + // Only allow certain CSS attributes to avoid XSS attacks + // Sanitizing values to avoid `url(...)` and `expression(...)` attacks + if (!attribs.style) { + return { tagName: tagName, attribs : attribs }; + } + + const pairs = attribs.style.split(';'); + let sanitisedStyle = ""; + for (let i = 0; i < pairs.length; i++) { + const pair = pairs[i].split(':'); + if (!Object.keys(ALLOWED_CSS).includes(pair[0]) || !ALLOWED_CSS[pair[0]].test(pair[1])) { + continue; + } + sanitisedStyle += pair[0] + ":" + pair[1] + ";"; + } + + if (sanitisedStyle) { + attribs.style = sanitisedStyle; + } else { + delete attribs.style; + } + + return { tagName: tagName, attribs : attribs }; + }, }, }; From 32185befc009a32534e111fd549a50b873ad3ff4 Mon Sep 17 00:00:00 2001 From: Luke Barnard Date: Wed, 11 Jan 2017 16:41:05 +0000 Subject: [PATCH 003/202] Only transform --- src/HtmlUtils.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/HtmlUtils.js b/src/HtmlUtils.js index ae594de960..a8fb763a8d 100644 --- a/src/HtmlUtils.js +++ b/src/HtmlUtils.js @@ -141,7 +141,7 @@ var sanitizeHtmlParams = { attribs.rel = 'noopener'; // https://mathiasbynens.github.io/rel-noopener/ return { tagName: tagName, attribs : attribs }; }, - '*': function(tagName, attribs) { + 'font': function(tagName, attribs) { // Only allow certain CSS attributes to avoid XSS attacks // Sanitizing values to avoid `url(...)` and `expression(...)` attacks if (!attribs.style) { From 8d3876c7d04dea503e4c08f97e475069527eba5b Mon Sep 17 00:00:00 2001 From: Luke Barnard Date: Thu, 9 Feb 2017 15:14:16 +0000 Subject: [PATCH 004/202] MELS: either expanded or summary, not both Fixes vector-im/riot-web#3097 --- .../views/elements/MemberEventListSummary.js | 37 +++++++++---------- 1 file changed, 17 insertions(+), 20 deletions(-) diff --git a/src/components/views/elements/MemberEventListSummary.js b/src/components/views/elements/MemberEventListSummary.js index 1a73b5a50e..1eb7b928d1 100644 --- a/src/components/views/elements/MemberEventListSummary.js +++ b/src/components/views/elements/MemberEventListSummary.js @@ -108,7 +108,7 @@ module.exports = React.createClass({ } return ( - + {summaries.join(", ")} ); @@ -264,7 +264,7 @@ module.exports = React.createClass({ ); }); return ( - + {avatars} ); @@ -397,31 +397,28 @@ module.exports = React.createClass({ (seq1, seq2) => aggregate.indices[seq1] > aggregate.indices[seq2] ); - const avatars = this._renderAvatars(avatarMembers); - const summary = this._renderSummary(aggregate.names, orderedTransitionSequences); - const toggleButton = ( - - {expanded ? 'collapse' : 'expand'} - - ); - - const summaryContainer = ( -
-
- - {avatars} - - - {summary} -   - {toggleButton} + let summaryContainer = null; + if (!expanded) { + summaryContainer = ( +
+
+ {this._renderAvatars(avatarMembers)} + {this._renderSummary(aggregate.names, orderedTransitionSequences)} +
+ ); + } + const toggleButton = ( +
+ {expanded ? 'collapse' : 'expand'}
); return (
+ {toggleButton} {summaryContainer} + {expanded ?
 
: null} {expandedEvents}
); From 4f4e9a6c3af648658229dc249c6af6a73ff5eafc Mon Sep 17 00:00:00 2001 From: David Baker Date: Fri, 17 Feb 2017 18:06:00 +0000 Subject: [PATCH 005/202] Fix block quotes all being on a single line Fixes https://github.com/vector-im/riot-web/issues/3154 --- src/Markdown.js | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/Markdown.js b/src/Markdown.js index d6dc979a5a..4a46ce4f24 100644 --- a/src/Markdown.js +++ b/src/Markdown.js @@ -92,7 +92,16 @@ export default class Markdown { } toHTML() { - const renderer = new commonmark.HtmlRenderer({safe: false}); + const renderer = new commonmark.HtmlRenderer({ + safe: false, + + // Set soft breaks to hard HTML breaks: commonmark + // puts softbreaks in for multiple lines in a blockquote, + // so if these are just newline characters then the + // block quote ends up all on one line + // (https://github.com/vector-im/riot-web/issues/3154) + softbreak: '
', + }); const real_paragraph = renderer.paragraph; renderer.paragraph = function(node, entering) { From 8990e770b742b05ac3026bc9f8be36edd9a5b515 Mon Sep 17 00:00:00 2001 From: Matthew Hodgson Date: Mon, 20 Feb 2017 01:43:55 +0200 Subject: [PATCH 006/202] fix colouring in voip dark theme --- src/components/structures/RoomStatusBar.js | 3 ++- src/components/structures/RoomView.js | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/components/structures/RoomStatusBar.js b/src/components/structures/RoomStatusBar.js index ca50e1071a..95631e304d 100644 --- a/src/components/structures/RoomStatusBar.js +++ b/src/components/structures/RoomStatusBar.js @@ -194,8 +194,9 @@ module.exports = React.createClass({ } if (this.props.hasActiveCall) { + var TintableSvg = sdk.getComponent("elements.TintableSvg"); return ( - + ); } diff --git a/src/components/structures/RoomView.js b/src/components/structures/RoomView.js index 5bf192dfc6..acdea38c69 100644 --- a/src/components/structures/RoomView.js +++ b/src/components/structures/RoomView.js @@ -1639,14 +1639,14 @@ module.exports = React.createClass({ videoMuteButton =
- {call.isLocalVideoMuted()
; } voiceMuteButton =
- {call.isMicrophoneMuted()
; From 6fead66f8920d1b9c76431bd9f46e8dc3099f655 Mon Sep 17 00:00:00 2001 From: Luke Barnard Date: Mon, 20 Feb 2017 10:59:11 +0000 Subject: [PATCH 007/202] MELS: check scroll on load + use mels-1,-2,... key To fix https://github.com/vector-im/riot-web/issues/2916, force the checking of scroll position by calling _onWidgetLoad (might need renaming...) when a MELS is expanded/contracted. Also use an keying scheme for MELS that doesn't depend on the events contained, but rather a simple incrementing index based on the order of the MELS as it appears amongst all MELS. --- src/components/structures/MessagePanel.js | 13 ++++++++----- .../views/elements/MemberEventListSummary.js | 3 +++ 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/src/components/structures/MessagePanel.js b/src/components/structures/MessagePanel.js index dcebe38fa4..6b61759344 100644 --- a/src/components/structures/MessagePanel.js +++ b/src/components/structures/MessagePanel.js @@ -237,6 +237,7 @@ module.exports = React.createClass({ this.eventNodes = {}; var i; + let melsIndex = 0; // first figure out which is the last event in the list which we're // actually going to show; this allows us to behave slightly @@ -302,10 +303,10 @@ module.exports = React.createClass({ // instead will allow new props to be provided. In turn, the shouldComponentUpdate // method on MELS can be used to prevent unnecessary renderings. // - // Whilst back-paginating with a MELS at the top of the panel, prevEvent will be null, - // so use the key "membereventlistsummary-initial". Otherwise, use the ID of the first - // membership event, which will not change during forward pagination. - const key = "membereventlistsummary-" + (prevEvent ? mxEv.getId() : "initial"); + // melsIndex is deliberately unrelated to the contained events so that pagination + // will not cause it to be recreated. + const key = "membereventlistsummary-" + melsIndex; + melsIndex++; if (this._wantsDateSeparator(prevEvent, mxEv.getDate())) { let dateSeparator =
  • ; @@ -349,7 +350,9 @@ module.exports = React.createClass({ + data-scroll-token={eventId} + onToggle={this._onWidgetLoad} // Update scroll state + > {eventTiles} ); diff --git a/src/components/views/elements/MemberEventListSummary.js b/src/components/views/elements/MemberEventListSummary.js index 510d861730..a3c31ae65b 100644 --- a/src/components/views/elements/MemberEventListSummary.js +++ b/src/components/views/elements/MemberEventListSummary.js @@ -30,6 +30,8 @@ module.exports = React.createClass({ avatarsMaxLength: React.PropTypes.number, // The minimum number of events needed to trigger summarisation threshold: React.PropTypes.number, + // Called when the MELS expansion is toggled + onToggle: React.PropTypes.func, }, getInitialState: function() { @@ -63,6 +65,7 @@ module.exports = React.createClass({ this.setState({ expanded: !this.state.expanded, }); + this.props.onToggle(); }, /** From 9eef3c53a340c9af3a1582283a4f35daeb8a3034 Mon Sep 17 00:00:00 2001 From: Luke Barnard Date: Mon, 20 Feb 2017 16:53:26 +0000 Subject: [PATCH 008/202] Allow setting the default HS from the query parameter Fixes https://github.com/vector-im/riot-web/issues/3207 --- src/components/structures/MatrixChat.js | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/components/structures/MatrixChat.js b/src/components/structures/MatrixChat.js index 3265249105..e885614ffe 100644 --- a/src/components/structures/MatrixChat.js +++ b/src/components/structures/MatrixChat.js @@ -226,6 +226,15 @@ module.exports = React.createClass({ if (this._teamToken) { console.info(`Team token set to ${this._teamToken}`); } + + // Set a default HS with query param `hs_url` + const paramHs = this.props.startingFragmentQueryParams.hs_url; + if (paramHs) { + console.log('Setting register_hs_url ', paramHs); + this.setState({ + register_hs_url: paramHs, + }); + } }, componentDidMount: function() { From 6af0b9618a6cb82a84a86a331321942a337bc736 Mon Sep 17 00:00:00 2001 From: Matthew Hodgson Date: Tue, 21 Feb 2017 00:19:49 +0000 Subject: [PATCH 009/202] first cut of improving UX for deleting devices. This adds a 5 minute auth cache to speed up the process of deleting old devices. It has the following nastinesses (mainly due to being written on a flight whilst juggling kids): * the auth cache is done as context attached to MatrixChat. one could argue that it should be per-client instead, but we don't yet have multiple clients. * the auth cache is only maintained currently in DevicesPanelEntry (i.e. set & invalidated). One could argue that it might be better maintained in InteractiveAuth.js or a dedicated cache object abstraction, but given the only use I can think of is when managing devices, perhaps this is good enough for now. --- src/components/structures/MatrixChat.js | 5 ++++ .../views/settings/DevicesPanelEntry.js | 24 +++++++++++++++---- 2 files changed, 24 insertions(+), 5 deletions(-) diff --git a/src/components/structures/MatrixChat.js b/src/components/structures/MatrixChat.js index 3265249105..8cc966607c 100644 --- a/src/components/structures/MatrixChat.js +++ b/src/components/structures/MatrixChat.js @@ -68,6 +68,7 @@ module.exports = React.createClass({ childContextTypes: { appConfig: React.PropTypes.object, + authCache: React.PropTypes.object, }, AuxPanel: { @@ -77,6 +78,10 @@ module.exports = React.createClass({ getChildContext: function() { return { appConfig: this.props.config, + authCache: { + auth: {}, + lastUpdate: 0, + }, }; }, diff --git a/src/components/views/settings/DevicesPanelEntry.js b/src/components/views/settings/DevicesPanelEntry.js index 4fa7d961ac..e889f88222 100644 --- a/src/components/views/settings/DevicesPanelEntry.js +++ b/src/components/views/settings/DevicesPanelEntry.js @@ -19,6 +19,11 @@ import React from 'react'; import sdk from '../../../index'; import MatrixClientPeg from '../../../MatrixClientPeg'; import Modal from '../../../Modal'; +import DateUtils from '../../../DateUtils'; +import utils from 'matrix-js-sdk/lib/utils'; + + +const AUTH_CACHE_AGE = 5 * 60 * 1000; // 5 minutes export default class DevicesPanelEntry extends React.Component { constructor(props, context) { @@ -30,7 +35,6 @@ export default class DevicesPanelEntry extends React.Component { }; this._unmounted = false; - this._onDeleteClick = this._onDeleteClick.bind(this); this._onDisplayNameChanged = this._onDisplayNameChanged.bind(this); this._makeDeleteRequest = this._makeDeleteRequest.bind(this); @@ -53,8 +57,12 @@ export default class DevicesPanelEntry extends React.Component { _onDeleteClick() { this.setState({deleting: true}); - // try without interactive auth to start off - this._makeDeleteRequest(null).catch((error) => { + if (this.context.authCache.lastUpdate < Date.now() - AUTH_CACHE_AGE) { + this.context.authCache.auth = null; + } + + // try with auth cache (which is null, so no interactive auth, to start off) + this._makeDeleteRequest(this.context.authCache.auth).catch((error) => { if (this._unmounted) { return; } if (error.httpStatus !== 401 || !error.data || !error.data.flows) { // doesn't look like an interactive-auth failure @@ -83,6 +91,9 @@ export default class DevicesPanelEntry extends React.Component { } _makeDeleteRequest(auth) { + this.context.authCache.auth = auth; + this.context.authCache.lastUpdate = Date.now(); + const device = this.props.device; return MatrixClientPeg.get().deleteDevice(device.device_id, auth).then( () => { @@ -110,8 +121,7 @@ export default class DevicesPanelEntry extends React.Component { let lastSeen = ""; if (device.last_seen_ts) { - // todo: format the timestamp as "5 minutes ago" or whatever. - const lastSeenDate = new Date(device.last_seen_ts); + const lastSeenDate = DateUtils.formatDate(new Date(device.last_seen_ts)); lastSeen = device.last_seen_ip + " @ " + lastSeenDate.toLocaleString(); } @@ -160,6 +170,10 @@ DevicesPanelEntry.propTypes = { onDeleted: React.PropTypes.func, }; +DevicesPanelEntry.contextTypes = { + authCache: React.PropTypes.object, +}; + DevicesPanelEntry.defaultProps = { onDeleted: function() {}, }; From aabf9255d2c84ff19609a7aa15be1b0e203d0895 Mon Sep 17 00:00:00 2001 From: Matthew Hodgson Date: Tue, 21 Feb 2017 01:03:22 +0000 Subject: [PATCH 010/202] anchor the authcache on LoggedInView to prevent it persisting over logouts --- src/components/structures/LoggedInView.js | 5 +++++ src/components/structures/MatrixChat.js | 5 ----- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/components/structures/LoggedInView.js b/src/components/structures/LoggedInView.js index aa9470f126..c2243820cd 100644 --- a/src/components/structures/LoggedInView.js +++ b/src/components/structures/LoggedInView.js @@ -49,11 +49,16 @@ export default React.createClass({ childContextTypes: { matrixClient: React.PropTypes.instanceOf(Matrix.MatrixClient), + authCache: React.PropTypes.object, }, getChildContext: function() { return { matrixClient: this._matrixClient, + authCache: { + auth: {}, + lastUpdate: 0, + }, }; }, diff --git a/src/components/structures/MatrixChat.js b/src/components/structures/MatrixChat.js index 8cc966607c..3265249105 100644 --- a/src/components/structures/MatrixChat.js +++ b/src/components/structures/MatrixChat.js @@ -68,7 +68,6 @@ module.exports = React.createClass({ childContextTypes: { appConfig: React.PropTypes.object, - authCache: React.PropTypes.object, }, AuxPanel: { @@ -78,10 +77,6 @@ module.exports = React.createClass({ getChildContext: function() { return { appConfig: this.props.config, - authCache: { - auth: {}, - lastUpdate: 0, - }, }; }, From 629f8caad7015bbfc18f0448fab04c1adbac8501 Mon Sep 17 00:00:00 2001 From: Matthew Hodgson Date: Tue, 21 Feb 2017 09:50:10 +0000 Subject: [PATCH 011/202] oops, remove unneeded import --- src/components/views/settings/DevicesPanelEntry.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/components/views/settings/DevicesPanelEntry.js b/src/components/views/settings/DevicesPanelEntry.js index e889f88222..60501e326f 100644 --- a/src/components/views/settings/DevicesPanelEntry.js +++ b/src/components/views/settings/DevicesPanelEntry.js @@ -20,8 +20,6 @@ import sdk from '../../../index'; import MatrixClientPeg from '../../../MatrixClientPeg'; import Modal from '../../../Modal'; import DateUtils from '../../../DateUtils'; -import utils from 'matrix-js-sdk/lib/utils'; - const AUTH_CACHE_AGE = 5 * 60 * 1000; // 5 minutes From 05d242cb5cd6a4686aff84442ecbcf95252377ec Mon Sep 17 00:00:00 2001 From: Matthew Hodgson Date: Tue, 21 Feb 2017 14:49:30 +0000 Subject: [PATCH 012/202] allow @local:domain style mxids --- src/Invite.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/Invite.js b/src/Invite.js index d1f03fe211..0e8aca2cb5 100644 --- a/src/Invite.js +++ b/src/Invite.js @@ -19,8 +19,7 @@ import MultiInviter from './utils/MultiInviter'; const emailRegex = /^\S+@\S+\.\S+$/; -// We allow localhost for mxids to avoid confusion -const mxidRegex = /^@\S+:(?:\S+\.\S+|localhost)$/ +const mxidRegex = /^@\S+:\S+$/ export function getAddressType(inputText) { const isEmailAddress = emailRegex.test(inputText); From 581c8c138ea24bb483fdb4cdf5fe93cf37771e11 Mon Sep 17 00:00:00 2001 From: Luke Barnard Date: Tue, 21 Feb 2017 15:01:18 +0000 Subject: [PATCH 013/202] Do not push a dummy element with a scroll token for invisible events If an event does not `wantTile`, do not add a dummy element with a scroll token, as this can be unperformant with 1000s of events. --- src/components/structures/MessagePanel.js | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/components/structures/MessagePanel.js b/src/components/structures/MessagePanel.js index dcebe38fa4..821cbc5744 100644 --- a/src/components/structures/MessagePanel.js +++ b/src/components/structures/MessagePanel.js @@ -362,10 +362,6 @@ module.exports = React.createClass({ // replacing all of the DOM elements every time we paginate. ret.push(...this._getTilesForEvent(prevEvent, mxEv, last)); prevEvent = mxEv; - } else if (!mxEv.status) { - // if we aren't showing the event, put in a dummy scroll token anyway, so - // that we can scroll to the right place. - ret.push(
  • ); } var isVisibleReadMarker = false; From fd146a732b3c3611929c202767c8187ddea10825 Mon Sep 17 00:00:00 2001 From: Luke Barnard Date: Tue, 21 Feb 2017 15:33:44 +0000 Subject: [PATCH 014/202] Clarify non-e2e vs. e2e /w composers placeholder MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit For E2E rooms, display "Send an encrypted message…" otherwise display "Send a plaintext message…" as the placeholder for the input box in [old] message composer. --- src/components/views/rooms/MessageComposer.js | 8 ++++++-- src/components/views/rooms/MessageComposerInput.js | 2 +- src/components/views/rooms/MessageComposerInputOld.js | 5 ++++- 3 files changed, 11 insertions(+), 4 deletions(-) diff --git a/src/components/views/rooms/MessageComposer.js b/src/components/views/rooms/MessageComposer.js index 113224666d..719187740b 100644 --- a/src/components/views/rooms/MessageComposer.js +++ b/src/components/views/rooms/MessageComposer.js @@ -223,8 +223,8 @@ export default class MessageComposer extends React.Component { ); let e2eImg, e2eTitle, e2eClass; - - if (MatrixClientPeg.get().isRoomEncrypted(this.props.room.roomId)) { + const roomIsEncrypted = MatrixClientPeg.get().isRoomEncrypted(this.props.room.roomId); + if (roomIsEncrypted) { // FIXME: show a /!\ if there are untrusted devices in the room... e2eImg = 'img/e2e-verified.svg'; e2eTitle = 'Encrypted room'; @@ -286,12 +286,16 @@ export default class MessageComposer extends React.Component { key="controls_formatting" /> ); + const placeholderText = roomIsEncrypted ? + "Send an encrypted message…" : "Send a plaintext message…"; + controls.push( this.messageComposerInput = c} key="controls_input" onResize={this.props.onResize} room={this.props.room} + placeholder={placeholderText} tryComplete={this._tryComplete} onUpArrow={this.onUpArrow} onDownArrow={this.onDownArrow} diff --git a/src/components/views/rooms/MessageComposerInput.js b/src/components/views/rooms/MessageComposerInput.js index 9aab174511..61dd1e1b1c 100644 --- a/src/components/views/rooms/MessageComposerInput.js +++ b/src/components/views/rooms/MessageComposerInput.js @@ -721,7 +721,7 @@ export default class MessageComposerInput extends React.Component { title={`Markdown is ${this.state.isRichtextEnabled ? 'disabled' : 'enabled'}`} src={`img/button-md-${!this.state.isRichtextEnabled}.png`} /> -