From 8641dab756a57fc9b4f918df22201a0c765514f9 Mon Sep 17 00:00:00 2001 From: Richard van der Hoff Date: Tue, 9 Feb 2016 11:08:26 +0000 Subject: [PATCH 01/33] Don't stop scrolling at the read-up-to mark. We want to keep things scrolling up after the read-up-to mark hits the middle of the screen. Do this by giving the ScrollPanel a stickyBottom (provided we're not in old history) instead of updating the scroll position when RRs arrive. Also, when we switch back to a room, if there was no special scroll state, jump to the end of the timeline instead of the RR mark. --- src/components/structures/RoomView.js | 37 ++++++++++----------------- 1 file changed, 13 insertions(+), 24 deletions(-) diff --git a/src/components/structures/RoomView.js b/src/components/structures/RoomView.js index 78c206a28a..76aa967341 100644 --- a/src/components/structures/RoomView.js +++ b/src/components/structures/RoomView.js @@ -66,7 +66,8 @@ module.exports = React.createClass({ roomId: React.PropTypes.string.isRequired, - // id of an event to jump to. If not given, will use the read-up-to-marker. + // id of an event to jump to. If not given, will go to the end of the + // live timeline. eventId: React.PropTypes.string, // where to position the event given by eventId, in pixels from the @@ -196,11 +197,6 @@ module.exports = React.createClass({ _initTimeline: function(props) { var initialEvent = props.eventId; - if (!initialEvent) { - // go to the 'read-up-to' mark if no explicit event given - initialEvent = this.state.readMarkerEventId; - } - var pixelOffset = props.eventPixelOffset; return this._loadTimeline(initialEvent, pixelOffset); }, @@ -499,20 +495,6 @@ module.exports = React.createClass({ readMarkerEventId: readMarkerEventId, readMarkerGhostEventId: readMarkerGhostEventId, }); - - - // if the scrollpanel is following the timeline, attempt to scroll - // it to bring the read message up to the middle of the panel. This - // will have no immediate effect (since we are already at the - // bottom), but will ensure that if there is no further user - // activity, but room activity continues, the read message will - // scroll up to the middle of the window, but no further. - // - // we do this here as well as in sendReadReceipt to deal with - // people using two clients at once. - if (this.refs.messagePanel && this.state.atEndOfLiveTimeline) { - this.refs.messagePanel.scrollToToken(readMarkerEventId); - } } }, @@ -1809,15 +1791,22 @@ module.exports = React.createClass({ ); } else { - // it's important that stickyBottom = false on this, otherwise if somebody hits the - // bottom of the loaded events when viewing historical messages, we get stuck in a - // loop of paginating our way through the entire history of the room. + // give the messagepanel a stickybottom if we're at the end of the + // live timeline, so that the arrival of new events triggers a + // scroll. + // + // Make sure that stickyBottom is *false* if we can paginate + // forwards, otherwise if somebody hits the bottom of the loaded + // events when viewing historical messages, we get stuck in a loop + // of paginating our way through the entire history of the room. + var stickyBottom = !this._timelineWindow.canPaginate(EventTimeline.FORWARDS); + messagePanel = ( + stickyBottom={ stickyBottom }>
  • {this.getEventTiles()}
    From baa6826409aebae93063af0dde95c96733f9d94f Mon Sep 17 00:00:00 2001 From: Matthew Hodgson Date: Tue, 9 Feb 2016 15:07:39 +0000 Subject: [PATCH 02/33] better commenting --- src/HtmlUtils.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/HtmlUtils.js b/src/HtmlUtils.js index 603f595951..87f5bd2cca 100644 --- a/src/HtmlUtils.js +++ b/src/HtmlUtils.js @@ -22,8 +22,9 @@ var highlight = require('highlight.js'); var sanitizeHtmlParams = { allowedTags: [ - 'font', // custom to matrix. deliberately no h1/h2 to stop people shouting. + 'font', // custom to matrix for IRC-style font coloring 'del', // for markdown + // deliberately no h1/h2 to stop people shouting. 'h3', 'h4', 'h5', 'h6', 'blockquote', 'p', 'a', 'ul', 'ol', 'nl', 'li', 'b', 'i', 'strong', 'em', 'strike', 'code', 'hr', 'br', 'div', 'table', 'thead', 'caption', 'tbody', 'tr', 'th', 'td', 'pre' @@ -65,7 +66,7 @@ class Highlighter { // but we're attempting to apply the highlights here to the HTML body. This is // never going to end well - we really should be hooking into the sanitzer HTML // parser to only attempt to highlight text nodes to avoid corrupting tags. - // If and when this happens, we'll probably have to split his method in two between + // If and when this happens, we'll probably have to split this method in two between // HTML and plain-text highlighting. var safeHighlight = this.html ? sanitizeHtml(highlights[0], sanitizeHtmlParams) : highlights[0]; From 3fd7dff26492e8c746479813e3c4e7f24210f324 Mon Sep 17 00:00:00 2001 From: Matthew Hodgson Date: Tue, 9 Feb 2016 15:07:57 +0000 Subject: [PATCH 03/33] unbreak safari --- src/Tinter.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Tinter.js b/src/Tinter.js index f08bdd7297..b258930425 100644 --- a/src/Tinter.js +++ b/src/Tinter.js @@ -76,6 +76,7 @@ var cached = false; function calcCssFixups() { for (var i = 0; i < document.styleSheets.length; i++) { var ss = document.styleSheets[i]; + if (!ss) continue; // well done safari >:( // Chromium apparently sometimes returns null here; unsure why. // see $14534907369972FRXBx:matrix.org in HQ // ...ah, it's because there's a third party extension like From 576b59be665a02210284a6557dc3979709352c69 Mon Sep 17 00:00:00 2001 From: Richard van der Hoff Date: Tue, 9 Feb 2016 15:45:41 +0000 Subject: [PATCH 04/33] Kill off the remains of auto_peek Remove some dead code. --- src/components/structures/MatrixChat.js | 4 ---- src/components/structures/RoomView.js | 15 --------------- 2 files changed, 19 deletions(-) diff --git a/src/components/structures/MatrixChat.js b/src/components/structures/MatrixChat.js index e2f5c24b37..5869c8ef33 100644 --- a/src/components/structures/MatrixChat.js +++ b/src/components/structures/MatrixChat.js @@ -316,9 +316,6 @@ module.exports = React.createClass({ }); break; case 'view_room': - // by default we autoPeek rooms, unless we were called explicitly with - // autoPeek=false by something like RoomDirectory who has already peeked - this.setState({ autoPeek : payload.auto_peek === false ? false : true }); this._viewRoom(payload.room_id, payload.show_settings, payload.event_id); break; case 'view_prev_room': @@ -880,7 +877,6 @@ module.exports = React.createClass({ eventId={this.state.initialEventId} highlightedEventId={this.state.highlightedEventId} eventPixelOffset={this.state.initialEventPixelOffset} - autoPeek={this.state.autoPeek} key={this.state.currentRoom} ConferenceHandler={this.props.ConferenceHandler} /> ); diff --git a/src/components/structures/RoomView.js b/src/components/structures/RoomView.js index 820aaf407a..a72ee07e24 100644 --- a/src/components/structures/RoomView.js +++ b/src/components/structures/RoomView.js @@ -60,8 +60,6 @@ module.exports = React.createClass({ displayName: 'RoomView', propTypes: { ConferenceHandler: React.PropTypes.any, - roomId: React.PropTypes.string, - autoPeek: React.PropTypes.bool, // Now unused, left here temporarily to avoid merge conflicts with @richvdh's branch. roomId: React.PropTypes.string.isRequired, @@ -76,14 +74,6 @@ module.exports = React.createClass({ // ID of an event to highlight. If undefined, no event will be highlighted. // Typically this will either be the same as 'eventId', or undefined. highlightedEventId: React.PropTypes.string, - - autoPeek: React.PropTypes.bool, // should we try to peek the room on mount, or has whoever invoked us already initiated a peek? - }, - - getDefaultProps: function() { - return { - autoPeek: true, - } }, /* properties in RoomView objects include: @@ -155,11 +145,6 @@ module.exports = React.createClass({ // We can /peek though. If it fails then we present the join UI. If it // succeeds then great, show the preview (but we still may be able to /join!). if (!this.state.room) { - if (!this.props.autoPeek) { - console.log("No room loaded, and autopeek disabled"); - return; - } - console.log("Attempting to peek into room %s", this.props.roomId); roomProm = MatrixClientPeg.get().peekInRoom(this.props.roomId).then((room) => { From f48f28eefe96b38cb09d0dbb4a8677c566d5e9ce Mon Sep 17 00:00:00 2001 From: Richard van der Hoff Date: Tue, 9 Feb 2016 15:50:09 +0000 Subject: [PATCH 05/33] Kill another scrollToToken Missed a place where we were scrolling the messagepanel manually --- src/components/structures/RoomView.js | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/src/components/structures/RoomView.js b/src/components/structures/RoomView.js index f7d68edf6f..63b9a14502 100644 --- a/src/components/structures/RoomView.js +++ b/src/components/structures/RoomView.js @@ -1141,19 +1141,6 @@ module.exports = React.createClass({ // it failed, so allow retries next time the user is active this.last_rr_sent_event_id = undefined; }); - - // if the scrollpanel is following the timeline, attempt to scroll - // it to bring the read message up to the middle of the panel. This - // will have no immediate effect (since we are already at the - // bottom), but will ensure that if there is no further user - // activity, but room activity continues, the read message will - // scroll up to the middle of the window, but no further. - // - // we do this here as well as in onRoomReceipt to cater for guest users - // (which do not send out read receipts). - if (this.state.atEndOfLiveTimeline) { - this.refs.messagePanel.scrollToToken(lastReadEvent.getId()); - } } }, From b4a71246869e32c3c1a31a9775228832546096d8 Mon Sep 17 00:00:00 2001 From: David Baker Date: Tue, 9 Feb 2016 16:48:12 +0000 Subject: [PATCH 06/33] Add a null check here since room can be null and this was throwing --- src/components/views/rooms/InviteMemberList.js | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/components/views/rooms/InviteMemberList.js b/src/components/views/rooms/InviteMemberList.js index 8bad76b7ba..73ee4f7c27 100644 --- a/src/components/views/rooms/InviteMemberList.js +++ b/src/components/views/rooms/InviteMemberList.js @@ -42,9 +42,11 @@ module.exports = React.createClass({ // TODO: Keep this list bleeding-edge up-to-date. Practically speaking, // it will do for now not being updated as random new users join different // rooms as this list will be reloaded every room swap. - this._userList = MatrixClientPeg.get().getUsers().filter((u) => { - return !this._room.hasMembershipState(u.userId, "join"); - }); + if (this._room) { + this._userList = MatrixClientPeg.get().getUsers().filter((u) => { + return !this._room.hasMembershipState(u.userId, "join"); + }); + } }, onInvite: function(ev) { From af0597e0c89d6d946abef76f6236c4a4a75299ac Mon Sep 17 00:00:00 2001 From: David Baker Date: Tue, 9 Feb 2016 16:55:03 +0000 Subject: [PATCH 07/33] Joining a room can take a while. Show a spinner otherwise we'll end up in multiple join city. --- src/components/structures/RoomView.js | 16 ++++++++++++---- src/components/views/rooms/RoomPreviewBar.js | 9 +++++++++ 2 files changed, 21 insertions(+), 4 deletions(-) diff --git a/src/components/structures/RoomView.js b/src/components/structures/RoomView.js index a72ee07e24..cd5e1a84a6 100644 --- a/src/components/structures/RoomView.js +++ b/src/components/structures/RoomView.js @@ -1464,7 +1464,9 @@ module.exports = React.createClass({
    + canJoin={ true } canPreview={ false } + spinner={this.state.joining} + />
    {joinErrorText}
    @@ -1507,7 +1509,9 @@ module.exports = React.createClass({ + canJoin={ true } canPreview={ false } + spinner={this.state.joining} + />
    {joinErrorText}
    {rejectErrorText}
    @@ -1573,13 +1577,17 @@ module.exports = React.createClass({ else if (this.state.guestsCanJoin && MatrixClientPeg.get().isGuest() && (!myMember || myMember.membership !== "join")) { aux = ( - + ); } else if (this.state.canPeek && (!myMember || myMember.membership !== "join")) { aux = ( - + ); } diff --git a/src/components/views/rooms/RoomPreviewBar.js b/src/components/views/rooms/RoomPreviewBar.js index 52e6639f13..eb57a0d913 100644 --- a/src/components/views/rooms/RoomPreviewBar.js +++ b/src/components/views/rooms/RoomPreviewBar.js @@ -17,6 +17,7 @@ limitations under the License. 'use strict'; var React = require('react'); +var sdk = require('../../../index'); module.exports = React.createClass({ displayName: 'RoomPreviewBar', @@ -27,6 +28,7 @@ module.exports = React.createClass({ inviterName: React.PropTypes.string, canJoin: React.PropTypes.bool, canPreview: React.PropTypes.bool, + spinner: React.PropTypes.bool, }, getDefaultProps: function() { @@ -40,6 +42,13 @@ module.exports = React.createClass({ render: function() { var joinBlock, previewBlock; + if (this.props.spinner) { + var Spinner = sdk.getComponent("elements.Spinner"); + return (
    + +
    ); + } + if (this.props.inviterName) { joinBlock = (
    From 038f6130794ea671db645d1be09a9a825781b1f7 Mon Sep 17 00:00:00 2001 From: David Baker Date: Wed, 10 Feb 2016 11:16:36 +0000 Subject: [PATCH 08/33] Don;t try to fetch push rules if we're a guest: it throws. --- src/components/views/rooms/RoomSettings.js | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/components/views/rooms/RoomSettings.js b/src/components/views/rooms/RoomSettings.js index 10af37ebf0..c811341610 100644 --- a/src/components/views/rooms/RoomSettings.js +++ b/src/components/views/rooms/RoomSettings.js @@ -37,10 +37,12 @@ module.exports = React.createClass({ }); var areNotifsMuted = false; - var roomPushRule = MatrixClientPeg.get().getRoomPushRule("global", this.props.room.roomId); - if (roomPushRule) { - if (0 <= roomPushRule.actions.indexOf("dont_notify")) { - areNotifsMuted = true; + if (!MatrixClientPeg.get().isGuest()) { + var roomPushRule = MatrixClientPeg.get().getRoomPushRule("global", this.props.room.roomId); + if (roomPushRule) { + if (0 <= roomPushRule.actions.indexOf("dont_notify")) { + areNotifsMuted = true; + } } } From 1e945cd13b78427f9ad0ae2a03e69c4f1dd88d40 Mon Sep 17 00:00:00 2001 From: David Baker Date: Wed, 10 Feb 2016 11:48:35 +0000 Subject: [PATCH 09/33] Don't show the Notifications widget for guests since they can't use them (and it throws an error if you try to mount it). --- src/components/structures/UserSettings.js | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/src/components/structures/UserSettings.js b/src/components/structures/UserSettings.js index 8f7e138bc0..2b35c861b4 100644 --- a/src/components/structures/UserSettings.js +++ b/src/components/structures/UserSettings.js @@ -315,6 +315,16 @@ module.exports = React.createClass({ onFinished={this.onPasswordChanged} /> ); } + var notification_area; + if (!MatrixClientPeg.get().isGuest()) { + notification_area = (
    +

    Notifications

    + +
    + +
    +
    ); + } return (
    @@ -364,11 +374,7 @@ module.exports = React.createClass({ {accountJsx}
    -

    Notifications

    - -
    - -
    + {notification_area}

    Advanced

    From d055dbe522e271d598ba98dabdfd069ea0f0a016 Mon Sep 17 00:00:00 2001 From: Matthew Hodgson Date: Wed, 10 Feb 2016 20:25:32 +0000 Subject: [PATCH 10/33] use sanitize-html's textFilter callback to only apply highlights to textNodes when highlighting HTML. fixes https://github.com/vector-im/vector-web/issues/294 --- src/HtmlUtils.js | 62 ++++++++++++++++++++++++++---------------------- 1 file changed, 33 insertions(+), 29 deletions(-) diff --git a/src/HtmlUtils.js b/src/HtmlUtils.js index 87f5bd2cca..b90cab5d72 100644 --- a/src/HtmlUtils.js +++ b/src/HtmlUtils.js @@ -17,6 +17,7 @@ limitations under the License. 'use strict'; var React = require('react'); +var ReactDOMServer = require('react-dom/server') var sanitizeHtml = require('sanitize-html'); var highlight = require('highlight.js'); @@ -57,24 +58,17 @@ class Highlighter { this._key = 0; } - applyHighlights(safeSnippet, highlights) { + applyHighlights(safeSnippet, safeHighlights) { var lastOffset = 0; var offset; var nodes = []; - // XXX: when highlighting HTML, synapse performs the search on the plaintext body, - // but we're attempting to apply the highlights here to the HTML body. This is - // never going to end well - we really should be hooking into the sanitzer HTML - // parser to only attempt to highlight text nodes to avoid corrupting tags. - // If and when this happens, we'll probably have to split this method in two between - // HTML and plain-text highlighting. - - var safeHighlight = this.html ? sanitizeHtml(highlights[0], sanitizeHtmlParams) : highlights[0]; + var safeHighlight = safeHighlights[0]; while ((offset = safeSnippet.toLowerCase().indexOf(safeHighlight.toLowerCase(), lastOffset)) >= 0) { // handle preamble if (offset > lastOffset) { var subSnippet = safeSnippet.substring(lastOffset, offset); - nodes = nodes.concat(this._applySubHighlights(subSnippet, highlights)); + nodes = nodes.concat(this._applySubHighlights(subSnippet, safeHighlights)); } // do highlight @@ -86,15 +80,15 @@ class Highlighter { // handle postamble if (lastOffset != safeSnippet.length) { var subSnippet = safeSnippet.substring(lastOffset, undefined); - nodes = nodes.concat(this._applySubHighlights(subSnippet, highlights)); + nodes = nodes.concat(this._applySubHighlights(subSnippet, safeHighlights)); } return nodes; } - _applySubHighlights(safeSnippet, highlights) { - if (highlights[1]) { + _applySubHighlights(safeSnippet, safeHighlights) { + if (safeHighlights[1]) { // recurse into this range to check for the next set of highlight matches - return this.applyHighlights(safeSnippet, highlights.slice(1)); + return this.applyHighlights(safeSnippet, safeHighlights.slice(1)); } else { // no more highlights to be found, just return the unhighlighted string @@ -132,7 +126,7 @@ module.exports = { * * content: 'content' of the MatrixEvent * - * highlights: optional list of words to highlight + * highlights: optional list of words to highlight, ordered by longest word first * * opts.onHighlightClick: optional callback function to be called when a * highlighted word is clicked @@ -142,28 +136,38 @@ module.exports = { var isHtml = (content.format === "org.matrix.custom.html"); - var safeBody; + var safeBody, body; if (isHtml) { + // XXX: We sanitize the HTML whilst also highlighting its text nodes, to avoid accidentally trying + // to highlight HTML tags themselves. However, this does mean that we don't highlight textnodes which + // are interrupted by HTML tags (not that we did before) - e.g. foobar won't get highlighted + // by an attempt to search for 'foobar'. Then again, the search query probably wouldn't work either + if (highlights && highlights.length > 0) { + var highlighter = new Highlighter(isHtml, "mx_EventTile_searchHighlight", opts.onHighlightClick); + // XXX: hacky bodge to temporarily apply a textFilter to the sanitizeHtmlParams structure. + sanitizeHtmlParams.textFilter = function(safeText) { + var html = highlighter.applyHighlights(safeText, highlights).map(function(span) { + // XXX: rather clunky conversion from the react nodes returned by applyHighlights + // (which need to be nodes for the non-html highlighting case), to convert them + // back into raw HTML given that's what sanitize-html works in terms of. + return ReactDOMServer.renderToString(span); + }).join(''); + return html; + }; + } safeBody = sanitizeHtml(content.formatted_body, sanitizeHtmlParams); + delete sanitizeHtmlParams.textFilter; + return ; } else { safeBody = content.body; - } - - var body; - if (highlights && highlights.length > 0) { - var highlighter = new Highlighter(isHtml, "mx_EventTile_searchHighlight", opts.onHighlightClick); - body = highlighter.applyHighlights(safeBody, highlights); - } - else { - if (isHtml) { - body = ; + if (highlights && highlights.length > 0) { + var highlighter = new Highlighter(isHtml, "mx_EventTile_searchHighlight", opts.onHighlightClick); + return highlighter.applyHighlights(safeBody, highlights); } else { - body = safeBody; + return safeBody; } } - - return body; }, highlightDom: function(element) { From 92435c0865a0af7e31da1a7a4395ba355b42bdad Mon Sep 17 00:00:00 2001 From: Matthew Hodgson Date: Wed, 10 Feb 2016 23:45:07 +0000 Subject: [PATCH 11/33] ooops, don't forget to actually sanitize the highlights after all that --- src/HtmlUtils.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/HtmlUtils.js b/src/HtmlUtils.js index b90cab5d72..f13295f24a 100644 --- a/src/HtmlUtils.js +++ b/src/HtmlUtils.js @@ -144,15 +144,17 @@ module.exports = { // by an attempt to search for 'foobar'. Then again, the search query probably wouldn't work either if (highlights && highlights.length > 0) { var highlighter = new Highlighter(isHtml, "mx_EventTile_searchHighlight", opts.onHighlightClick); + var safeHighlights = highlights.map(function(highlight) { + return sanitizeHtml(highlight, sanitizeHtmlParams); + }); // XXX: hacky bodge to temporarily apply a textFilter to the sanitizeHtmlParams structure. sanitizeHtmlParams.textFilter = function(safeText) { - var html = highlighter.applyHighlights(safeText, highlights).map(function(span) { + return highlighter.applyHighlights(safeText, safeHighlights).map(function(span) { // XXX: rather clunky conversion from the react nodes returned by applyHighlights // (which need to be nodes for the non-html highlighting case), to convert them // back into raw HTML given that's what sanitize-html works in terms of. return ReactDOMServer.renderToString(span); }).join(''); - return html; }; } safeBody = sanitizeHtml(content.formatted_body, sanitizeHtmlParams); From 1c30640a92d91371afe00e3fe89f690317db3f14 Mon Sep 17 00:00:00 2001 From: Matthew Hodgson Date: Thu, 11 Feb 2016 14:03:48 +0000 Subject: [PATCH 12/33] remove unused 'body' var; use a `finally` to clean up the temporary textfilter --- src/HtmlUtils.js | 38 +++++++++++++++++++++----------------- 1 file changed, 21 insertions(+), 17 deletions(-) diff --git a/src/HtmlUtils.js b/src/HtmlUtils.js index f13295f24a..0b7f17b2b2 100644 --- a/src/HtmlUtils.js +++ b/src/HtmlUtils.js @@ -136,29 +136,33 @@ module.exports = { var isHtml = (content.format === "org.matrix.custom.html"); - var safeBody, body; + var safeBody; if (isHtml) { // XXX: We sanitize the HTML whilst also highlighting its text nodes, to avoid accidentally trying // to highlight HTML tags themselves. However, this does mean that we don't highlight textnodes which // are interrupted by HTML tags (not that we did before) - e.g. foobar won't get highlighted // by an attempt to search for 'foobar'. Then again, the search query probably wouldn't work either - if (highlights && highlights.length > 0) { - var highlighter = new Highlighter(isHtml, "mx_EventTile_searchHighlight", opts.onHighlightClick); - var safeHighlights = highlights.map(function(highlight) { - return sanitizeHtml(highlight, sanitizeHtmlParams); - }); - // XXX: hacky bodge to temporarily apply a textFilter to the sanitizeHtmlParams structure. - sanitizeHtmlParams.textFilter = function(safeText) { - return highlighter.applyHighlights(safeText, safeHighlights).map(function(span) { - // XXX: rather clunky conversion from the react nodes returned by applyHighlights - // (which need to be nodes for the non-html highlighting case), to convert them - // back into raw HTML given that's what sanitize-html works in terms of. - return ReactDOMServer.renderToString(span); - }).join(''); - }; + try { + if (highlights && highlights.length > 0) { + var highlighter = new Highlighter(isHtml, "mx_EventTile_searchHighlight", opts.onHighlightClick); + var safeHighlights = highlights.map(function(highlight) { + return sanitizeHtml(highlight, sanitizeHtmlParams); + }); + // XXX: hacky bodge to temporarily apply a textFilter to the sanitizeHtmlParams structure. + sanitizeHtmlParams.textFilter = function(safeText) { + return highlighter.applyHighlights(safeText, safeHighlights).map(function(span) { + // XXX: rather clunky conversion from the react nodes returned by applyHighlights + // (which need to be nodes for the non-html highlighting case), to convert them + // back into raw HTML given that's what sanitize-html works in terms of. + return ReactDOMServer.renderToString(span); + }).join(''); + }; + } + safeBody = sanitizeHtml(content.formatted_body, sanitizeHtmlParams); + } + finally { + delete sanitizeHtmlParams.textFilter; } - safeBody = sanitizeHtml(content.formatted_body, sanitizeHtmlParams); - delete sanitizeHtmlParams.textFilter; return ; } else { safeBody = content.body; From 3e8cb47abed8638f48ff31d70437b0dfa14a1f6a Mon Sep 17 00:00:00 2001 From: Richard van der Hoff Date: Fri, 12 Feb 2016 10:19:01 +0000 Subject: [PATCH 13/33] Consider mouse-clicking as user activity It's a bit non-sensical that moving the mouse constitutes user activity, but clicking it does not. Fix it. --- src/UserActivity.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/UserActivity.js b/src/UserActivity.js index 669b007934..384dc23059 100644 --- a/src/UserActivity.js +++ b/src/UserActivity.js @@ -30,6 +30,7 @@ class UserActivity { * Start listening to user activity */ start() { + document.onmousedown = this._onUserActivity.bind(this); document.onmousemove = this._onUserActivity.bind(this); document.onkeypress = this._onUserActivity.bind(this); // can't use document.scroll here because that's only the document @@ -46,6 +47,7 @@ class UserActivity { * Stop tracking user activity */ stop() { + document.onmousedown = undefined; document.onmousemove = undefined; document.onkeypress = undefined; window.removeEventListener('wheel', this._onUserActivity.bind(this), true); From 5e5fdb9b9697d039198f63c59254ef9c0b44ebc1 Mon Sep 17 00:00:00 2001 From: Matthew Hodgson Date: Sun, 14 Feb 2016 13:38:12 +0200 Subject: [PATCH 14/33] fix vector-im/vector-web#921 --- src/components/views/elements/TruncatedList.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/views/elements/TruncatedList.js b/src/components/views/elements/TruncatedList.js index 275686c6be..3e174848d3 100644 --- a/src/components/views/elements/TruncatedList.js +++ b/src/components/views/elements/TruncatedList.js @@ -51,7 +51,7 @@ module.exports = React.createClass({ if (this.props.truncateAt >= 0) { var overflowCount = childCount - this.props.truncateAt; - if (overflowCount > 0) { + if (overflowCount > 1) { overflowJsx = this.props.createOverflowElement( overflowCount, childCount ); From 5e407b44b7317d3fe53a03d09287dae8ec1e3dbc Mon Sep 17 00:00:00 2001 From: Matthew Hodgson Date: Sun, 14 Feb 2016 13:45:52 +0200 Subject: [PATCH 15/33] add markdown to tab complete list --- src/SlashCommands.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/SlashCommands.js b/src/SlashCommands.js index d4e7df3a16..fcdd33474e 100644 --- a/src/SlashCommands.js +++ b/src/SlashCommands.js @@ -352,11 +352,12 @@ module.exports = { }, getCommandList: function() { - // Return all the commands plus /me which isn't handled like normal commands + // Return all the commands plus /me and /markdown which aren't handled like normal commands var cmds = Object.keys(commands).sort().map(function(cmdKey) { return commands[cmdKey]; }) cmds.push(new Command("me", "", function(){})); + cmds.push(new Command("markdown", "", function(){})); return cmds; } From 9d3b0f607507190efea0c3d8d1cf504c7b317016 Mon Sep 17 00:00:00 2001 From: Matthew Hodgson Date: Mon, 15 Feb 2016 00:37:59 +0200 Subject: [PATCH 16/33] fix https://github.com/vector-im/vector-web/issues/913 --- src/components/views/rooms/MemberList.js | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/src/components/views/rooms/MemberList.js b/src/components/views/rooms/MemberList.js index 3cddee8632..4e8f38b035 100644 --- a/src/components/views/rooms/MemberList.js +++ b/src/components/views/rooms/MemberList.js @@ -35,21 +35,23 @@ var invite_defer = q.defer(); module.exports = React.createClass({ displayName: 'MemberList', + getInitialState: function() { - if (!this.props.roomId) return { members: [] }; - var cli = MatrixClientPeg.get(); - var room = cli.getRoom(this.props.roomId); - if (!room) return { members: [] }; - - this.memberDict = this.getMemberDict(); - - var members = this.roomMembers(INITIAL_LOAD_NUM_MEMBERS); - return { - members: members, + var state = { + members: [], // ideally we'd size this to the page height, but // in practice I find that a little constraining truncateAt: INITIAL_LOAD_NUM_MEMBERS, }; + if (!this.props.roomId) return state; + var cli = MatrixClientPeg.get(); + var room = cli.getRoom(this.props.roomId); + if (!room) return state; + + this.memberDict = this.getMemberDict(); + + state.members = this.roomMembers(INITIAL_LOAD_NUM_MEMBERS); + return state; }, componentWillMount: function() { From 0a37f1c4787b2e3a3643dcaeecf946b144d79d34 Mon Sep 17 00:00:00 2001 From: Matthew Hodgson Date: Mon, 15 Feb 2016 09:52:57 +0200 Subject: [PATCH 17/33] improve the fix for https://github.com/vector-im/vector-web/issues/917 --- .../views/rooms/SearchableEntityList.js | 51 ++++++++++--------- 1 file changed, 27 insertions(+), 24 deletions(-) diff --git a/src/components/views/rooms/SearchableEntityList.js b/src/components/views/rooms/SearchableEntityList.js index c91e058e4c..fc0446ab41 100644 --- a/src/components/views/rooms/SearchableEntityList.js +++ b/src/components/views/rooms/SearchableEntityList.js @@ -140,34 +140,37 @@ var SearchableEntityList = React.createClass({ } var list; - if (this.props.truncateAt) { // caller wants list truncated - var TruncatedList = sdk.getComponent("elements.TruncatedList"); - list = ( - - {this.state.results.map((entity) => { - return entity.getJsx(); - })} - - ); - } - else { - list = ( -
    - {this.state.results.map((entity) => { - return entity.getJsx(); - })} -
    - ); + if (this.state.results.length) { + if (this.props.truncateAt) { // caller wants list truncated + var TruncatedList = sdk.getComponent("elements.TruncatedList"); + list = ( + + {this.state.results.map((entity) => { + return entity.getJsx(); + })} + + ); + } + else { + list = ( +
    + {this.state.results.map((entity) => { + return entity.getJsx(); + })} +
    + ); + } + list = + { list } + ; } return (
    - {inputBox} - - { list } - + { inputBox } + { list } { this.state.query.length ?

    : '' }
    ); From 0c7573629a4688e0fc58875275e850e557bec31d Mon Sep 17 00:00:00 2001 From: Matthew Hodgson Date: Mon, 15 Feb 2016 11:13:37 +0200 Subject: [PATCH 18/33] spell out that you can invite email addresses --- src/components/views/rooms/InviteMemberList.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/views/rooms/InviteMemberList.js b/src/components/views/rooms/InviteMemberList.js index 73ee4f7c27..480066771b 100644 --- a/src/components/views/rooms/InviteMemberList.js +++ b/src/components/views/rooms/InviteMemberList.js @@ -89,7 +89,7 @@ module.exports = React.createClass({ } return ( - Date: Mon, 15 Feb 2016 19:37:03 +0200 Subject: [PATCH 19/33] sanitize setting displayname prompt --- .../views/dialogs/SetDisplayNameDialog.js | 20 +++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/src/components/views/dialogs/SetDisplayNameDialog.js b/src/components/views/dialogs/SetDisplayNameDialog.js index d1287e2570..624bb50a46 100644 --- a/src/components/views/dialogs/SetDisplayNameDialog.js +++ b/src/components/views/dialogs/SetDisplayNameDialog.js @@ -26,9 +26,20 @@ module.exports = React.createClass({ }, getInitialState: function() { - return { - value: this.props.currentDisplayName || "Guest "+MatrixClientPeg.get().getUserIdLocalpart(), + if (this.props.currentDisplayName) { + return { value: this.props.currentDisplayName }; } + + if (MatrixClientPeg.get().isGuest()) { + return { value : "Guest " + MatrixClientPeg.get().getUserIdLocalpart() }; + } + else { + return { value : MatrixClientPeg.get().getUserIdLocalpart() }; + } + }, + + componentDidMount: function() { + this.refs.input_value.select(); }, getValue: function() { @@ -54,11 +65,12 @@ module.exports = React.createClass({ Set a Display Name
    - Your display name is how you'll appear to others when you speak in rooms. What would you like it to be? + Your display name is how you'll appear to others when you speak in rooms.
    + What would you like it to be?
    - From 30e9c7608945aefeebf1ca42b69411978dd4295c Mon Sep 17 00:00:00 2001 From: Matthew Hodgson Date: Mon, 15 Feb 2016 20:44:13 +0200 Subject: [PATCH 20/33] login as guest button on the login page --- src/components/structures/MatrixChat.js | 6 ++++-- src/components/structures/login/Login.js | 11 ++++++++++- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/src/components/structures/MatrixChat.js b/src/components/structures/MatrixChat.js index 5869c8ef33..0a9231247c 100644 --- a/src/components/structures/MatrixChat.js +++ b/src/components/structures/MatrixChat.js @@ -175,7 +175,7 @@ module.exports = React.createClass({ guest: true }); }, function(err) { - console.error(err.data); + console.error("Failed to register as guest: " + err + " " + err.data); self._setAutoRegisterAsGuest(false); }); }, @@ -970,7 +970,9 @@ module.exports = React.createClass({ onRegisterClick={this.onRegisterClick} homeserverUrl={this.props.config.default_hs_url} identityServerUrl={this.props.config.default_is_url} - onForgotPasswordClick={this.onForgotPasswordClick} /> + onForgotPasswordClick={this.onForgotPasswordClick} + onLoginAsGuestClick={this.props.enableGuest && this.props.config && this.props.config.default_hs_url ? this._registerAsGuest: undefined} + /> ); } } diff --git a/src/components/structures/login/Login.js b/src/components/structures/login/Login.js index b853b8fd95..356439b0cc 100644 --- a/src/components/structures/login/Login.js +++ b/src/components/structures/login/Login.js @@ -35,7 +35,8 @@ module.exports = React.createClass({displayName: 'Login', // login shouldn't know or care how registration is done. onRegisterClick: React.PropTypes.func.isRequired, // login shouldn't care how password recovery is done. - onForgotPasswordClick: React.PropTypes.func + onForgotPasswordClick: React.PropTypes.func, + onLoginAsGuestClick: React.PropTypes.func, }, getDefaultProps: function() { @@ -167,6 +168,13 @@ module.exports = React.createClass({displayName: 'Login', var LoginFooter = sdk.getComponent("login.LoginFooter"); var loader = this.state.busy ?
    : null; + var loginAsGuestJsx; + if (this.props.onLoginAsGuestClick) { + loginAsGuestJsx = + + Login as guest + + } return (
    @@ -188,6 +196,7 @@ module.exports = React.createClass({displayName: 'Login', Create a new account + { loginAsGuestJsx }
    From b1a6575b1dc172e209ab21cdbf5c1b19d03c63a6 Mon Sep 17 00:00:00 2001 From: Matthew Hodgson Date: Mon, 15 Feb 2016 20:59:44 +0200 Subject: [PATCH 21/33] remove ugly join & reject error msgs in favour of modal dialogs --- src/components/structures/RoomView.js | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/components/structures/RoomView.js b/src/components/structures/RoomView.js index d68ae35dc8..842f59700b 100644 --- a/src/components/structures/RoomView.js +++ b/src/components/structures/RoomView.js @@ -1229,11 +1229,19 @@ module.exports = React.createClass({ self.setState({ rejecting: false }); - }, function(err) { - console.error("Failed to reject invite: %s", err); + }, function(error) { + console.error("Failed to reject invite: %s", error); + + var msg = error.message ? error.message : JSON.stringify(error); + var ErrorDialog = sdk.getComponent("dialogs.ErrorDialog"); + Modal.createDialog(ErrorDialog, { + title: "Failed to reject invite", + description: msg + }); + self.setState({ rejecting: false, - rejectError: err + rejectError: error }); }); }, @@ -1427,7 +1435,6 @@ module.exports = React.createClass({ ); } else { - var joinErrorText = this.state.joinError ? "Failed to join room!" : ""; return (
    @@ -1436,7 +1443,6 @@ module.exports = React.createClass({ canJoin={ true } canPreview={ false } spinner={this.state.joining} /> -
    {joinErrorText}
    @@ -1462,10 +1468,6 @@ module.exports = React.createClass({ } else { var inviteEvent = myMember.events.member; var inviterName = inviteEvent.sender ? inviteEvent.sender.name : inviteEvent.getSender(); - // XXX: Leaving this intentionally basic for now because invites are about to change totally - // FIXME: This comment is now outdated - what do we need to fix? ^ - var joinErrorText = this.state.joinError ? "Failed to join room!" : ""; - var rejectErrorText = this.state.rejectError ? "Failed to reject invite!" : ""; // We deliberately don't try to peek into invites, even if we have permission to peek // as they could be a spam vector. @@ -1481,8 +1483,6 @@ module.exports = React.createClass({ canJoin={ true } canPreview={ false } spinner={this.state.joining} /> -
    {joinErrorText}
    -
    {rejectErrorText}
    From 014acbab1f6bf3553b80ad140924399bcf7b696d Mon Sep 17 00:00:00 2001 From: Matthew Hodgson Date: Mon, 15 Feb 2016 21:16:04 +0200 Subject: [PATCH 22/33] restore drag & drop file upload, broken by @richvdh's new timeline stuff --- src/components/structures/RoomView.js | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/src/components/structures/RoomView.js b/src/components/structures/RoomView.js index 842f59700b..25c289ba96 100644 --- a/src/components/structures/RoomView.js +++ b/src/components/structures/RoomView.js @@ -552,14 +552,6 @@ module.exports = React.createClass({ window.addEventListener('resize', this.onResize); this.onResize(); - if (this.refs.roomView) { - var roomView = ReactDOM.findDOMNode(this.refs.roomView); - roomView.addEventListener('drop', this.onDrop); - roomView.addEventListener('dragover', this.onDragOver); - roomView.addEventListener('dragleave', this.onDragLeaveOrEnd); - roomView.addEventListener('dragend', this.onDragLeaveOrEnd); - } - this._updateTabCompleteList(); // XXX: EVIL HACK to autofocus inviting on empty rooms. @@ -597,6 +589,16 @@ module.exports = React.createClass({ // separate component to avoid this ridiculous dance. if (!this.refs.messagePanel) return; + if (this.refs.roomView) { + var roomView = ReactDOM.findDOMNode(this.refs.roomView); + if (!roomView.ondrop) { + roomView.addEventListener('drop', this.onDrop); + roomView.addEventListener('dragover', this.onDragOver); + roomView.addEventListener('dragleave', this.onDragLeaveOrEnd); + roomView.addEventListener('dragend', this.onDragLeaveOrEnd); + } + } + if (!this.refs.messagePanel.initialised) { this._initialiseMessagePanel(); } From af5a866596e71359a810d5d9bea85500f504ca31 Mon Sep 17 00:00:00 2001 From: Matthew Hodgson Date: Mon, 15 Feb 2016 21:29:56 +0200 Subject: [PATCH 23/33] clear upload bar correctly after upload completes by fixing a race and moving the upload_finished dispatch after clearing up the inprogress uploads data structure. I have zero idea how this ever worked... :/ --- src/ContentMessages.js | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/ContentMessages.js b/src/ContentMessages.js index 82c295756b..bbd714fa57 100644 --- a/src/ContentMessages.js +++ b/src/ContentMessages.js @@ -92,6 +92,7 @@ class ContentMessages { this.inprogress.push(upload); dis.dispatch({action: 'upload_started'}); + var error; var self = this; return def.promise.then(function() { upload.promise = matrixClient.uploadContent(file); @@ -103,11 +104,10 @@ class ContentMessages { dis.dispatch({action: 'upload_progress', upload: upload}); } }).then(function(url) { - dis.dispatch({action: 'upload_finished', upload: upload}); content.url = url; return matrixClient.sendMessage(roomId, content); }, function(err) { - dis.dispatch({action: 'upload_failed', upload: upload}); + error = err; if (!upload.canceled) { var desc = "The file '"+upload.fileName+"' failed to upload."; if (err.http_status == 413) { @@ -128,6 +128,12 @@ class ContentMessages { break; } } + if (error) { + dis.dispatch({action: 'upload_failed', upload: upload}); + } + else { + dis.dispatch({action: 'upload_finished', upload: upload}); + } }); } From 0d153df417a7c43c63a7696a11b4f6951f751841 Mon Sep 17 00:00:00 2001 From: Matthew Hodgson Date: Mon, 15 Feb 2016 21:58:37 +0200 Subject: [PATCH 24/33] improve registration fail error msg slightly --- src/components/structures/login/Registration.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/structures/login/Registration.js b/src/components/structures/login/Registration.js index 5666318368..64df73962c 100644 --- a/src/components/structures/login/Registration.js +++ b/src/components/structures/login/Registration.js @@ -126,7 +126,7 @@ module.exports = React.createClass({ if (!response || !response.user_id || !response.access_token) { console.error("Final response is missing keys."); self.setState({ - errorText: "There was a problem processing the response." + errorText: "Registration failed on server" }); return; } From dfbc88d421f5c6f7d83432eade282bd74b58bb9f Mon Sep 17 00:00:00 2001 From: Matthew Hodgson Date: Mon, 15 Feb 2016 22:01:05 +0200 Subject: [PATCH 25/33] fix keyboard shortcuts on logout prompt --- src/components/views/dialogs/LogoutPrompt.js | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/components/views/dialogs/LogoutPrompt.js b/src/components/views/dialogs/LogoutPrompt.js index 824924e999..06d3d4dec1 100644 --- a/src/components/views/dialogs/LogoutPrompt.js +++ b/src/components/views/dialogs/LogoutPrompt.js @@ -31,14 +31,22 @@ module.exports = React.createClass({ } }, + onKeyDown: function(e) { + if (e.keyCode === 27) { // escape + e.stopPropagation(); + e.preventDefault(); + this.cancelPrompt(); + } + }, + render: function() { return (
    Sign out?
    -
    - +
    +
    From 576de32ce4b1ff07356298035d96d4a1f9650975 Mon Sep 17 00:00:00 2001 From: Matthew Hodgson Date: Mon, 15 Feb 2016 22:01:22 +0200 Subject: [PATCH 26/33] show vaguely accurate default avatar --- src/components/views/settings/ChangeAvatar.js | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/src/components/views/settings/ChangeAvatar.js b/src/components/views/settings/ChangeAvatar.js index 89303856b2..9b03aba1a3 100644 --- a/src/components/views/settings/ChangeAvatar.js +++ b/src/components/views/settings/ChangeAvatar.js @@ -110,19 +110,17 @@ module.exports = React.createClass({ }, render: function() { - var RoomAvatar = sdk.getComponent('avatars.RoomAvatar'); var avatarImg; // Having just set an avatar we just display that since it will take a little // time to propagate through to the RoomAvatar. if (this.props.room && !this.avatarSet) { + var RoomAvatar = sdk.getComponent('avatars.RoomAvatar'); avatarImg = ; } else { - var style = { - width: this.props.width, - height: this.props.height, - objectFit: 'cover', - }; - avatarImg = ; + var BaseAvatar = sdk.getComponent("avatars.BaseAvatar"); + // XXX: FIXME: once we track in the JS what our own displayname is(!) then use it here rather than ? + avatarImg = } var uploadSection; From 6b48b626e61e9aa0ec4669aae224014ef93b111a Mon Sep 17 00:00:00 2001 From: Matthew Hodgson Date: Mon, 15 Feb 2016 20:39:02 +0000 Subject: [PATCH 27/33] fix spinner of doom --- src/components/structures/login/Registration.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/components/structures/login/Registration.js b/src/components/structures/login/Registration.js index 64df73962c..9ec379a814 100644 --- a/src/components/structures/login/Registration.js +++ b/src/components/structures/login/Registration.js @@ -115,6 +115,9 @@ module.exports = React.createClass({ onProcessingRegistration: function(promise) { var self = this; promise.done(function(response) { + self.setState({ + busy: false + }); if (!response || !response.access_token) { console.warn( "FIXME: Register fulfilled without a final response, " + @@ -136,9 +139,6 @@ module.exports = React.createClass({ identityServerUrl: self.registerLogic.getIdentityServerUrl(), accessToken: response.access_token }); - self.setState({ - busy: false - }); }, function(err) { if (err.message) { self.setState({ From 61018f4f38bce689db01bf9a2528ea09523f8654 Mon Sep 17 00:00:00 2001 From: Matthew Hodgson Date: Mon, 15 Feb 2016 20:42:44 +0000 Subject: [PATCH 28/33] whitespace --- src/components/views/dialogs/LogoutPrompt.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/views/dialogs/LogoutPrompt.js b/src/components/views/dialogs/LogoutPrompt.js index 06d3d4dec1..67fedfe840 100644 --- a/src/components/views/dialogs/LogoutPrompt.js +++ b/src/components/views/dialogs/LogoutPrompt.js @@ -45,7 +45,7 @@ module.exports = React.createClass({
    Sign out?
    -
    +
    From ca56b7ec2d39fdec96a582a2ce95278a6f495221 Mon Sep 17 00:00:00 2001 From: Matthew Hodgson Date: Mon, 15 Feb 2016 20:43:43 +0000 Subject: [PATCH 29/33] match partial names in memberlist --- src/components/views/rooms/MemberList.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/views/rooms/MemberList.js b/src/components/views/rooms/MemberList.js index 4e8f38b035..eba09ca9da 100644 --- a/src/components/views/rooms/MemberList.js +++ b/src/components/views/rooms/MemberList.js @@ -327,7 +327,7 @@ module.exports = React.createClass({ var memberList = self.state.members.filter(function(userId) { var m = self.memberDict[userId]; - if (query && m.name.toLowerCase().indexOf(query) !== 0) { + if (query && m.name.toLowerCase().indexOf(query) === -1) { return false; } return m.membership == membership; From 4b8b2ade8b8a322fdb70ca26bb6a8326c1ec8df4 Mon Sep 17 00:00:00 2001 From: Matthew Hodgson Date: Mon, 15 Feb 2016 21:50:39 +0000 Subject: [PATCH 30/33] fix login-on-guest-bar-NPE crash https://github.com/vector-im/vector-web/issues/930 --- src/components/structures/RoomStatusBar.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/components/structures/RoomStatusBar.js b/src/components/structures/RoomStatusBar.js index 9ff3925b10..a4ac219b95 100644 --- a/src/components/structures/RoomStatusBar.js +++ b/src/components/structures/RoomStatusBar.js @@ -64,7 +64,10 @@ module.exports = React.createClass({ }, componentWillUnmount: function() { - MatrixClientPeg.get().removeListener("sync", this.onSyncStateChange); + // we may have entirely lost our client as we're logging out before clicking login on the guest bar... + if (MatrixClientPeg.get()) { + MatrixClientPeg.get().removeListener("sync", this.onSyncStateChange); + } }, onSyncStateChange: function(state, prevState) { From 687eae7f438826e53479d9f50fb22376a1298acb Mon Sep 17 00:00:00 2001 From: Matthew Hodgson Date: Mon, 15 Feb 2016 22:07:08 +0000 Subject: [PATCH 31/33] stop floods of notifs when doing a logout+login --- src/Notifier.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/Notifier.js b/src/Notifier.js index e52fd252fe..b64a001a5f 100644 --- a/src/Notifier.js +++ b/src/Notifier.js @@ -182,6 +182,9 @@ var Notifier = { if (state === "PREPARED" || state === "SYNCING") { this.isPrepared = true; } + else if (state === "STOPPED" || state === "ERROR") { + this.isPrepared = false; + } }, onRoomTimeline: function(ev, room, toStartOfTimeline) { From 34d0fc890a54f7e85aa29247a136820bb8a85287 Mon Sep 17 00:00:00 2001 From: Matthew Hodgson Date: Tue, 16 Feb 2016 17:39:32 +0000 Subject: [PATCH 32/33] disable scroll-to-token entirely temporarily - https://github.com/vector-im/vector-web/issues/946 --- src/components/structures/ScrollPanel.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/components/structures/ScrollPanel.js b/src/components/structures/ScrollPanel.js index 514937f877..fd8befea01 100644 --- a/src/components/structures/ScrollPanel.js +++ b/src/components/structures/ScrollPanel.js @@ -418,7 +418,9 @@ module.exports = React.createClass({ var scrollState = this.scrollState; var scrollNode = this._getScrollNode(); - if (scrollState.stuckAtBottom) { + // XXX: DISABLE SCROLL TO TOKEN ENTIRELY TEMPORARILY AS IT'S SCREWING + // UP MY DEMO - see https://github.com/vector-im/vector-web/issues/946 + if (true || scrollState.stuckAtBottom) { scrollNode.scrollTop = scrollNode.scrollHeight; debuglog("Scrolled to bottom; offset now", scrollNode.scrollTop); } else if (scrollState.trackedScrollToken) { From 38a2a61b38b804bdb59758bf256b53c533001dc7 Mon Sep 17 00:00:00 2001 From: Matthew Hodgson Date: Tue, 16 Feb 2016 19:39:22 +0000 Subject: [PATCH 33/33] back out hacky previous commit as #946 only happens when gemini is disabled --- src/components/structures/ScrollPanel.js | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/components/structures/ScrollPanel.js b/src/components/structures/ScrollPanel.js index fd8befea01..514937f877 100644 --- a/src/components/structures/ScrollPanel.js +++ b/src/components/structures/ScrollPanel.js @@ -418,9 +418,7 @@ module.exports = React.createClass({ var scrollState = this.scrollState; var scrollNode = this._getScrollNode(); - // XXX: DISABLE SCROLL TO TOKEN ENTIRELY TEMPORARILY AS IT'S SCREWING - // UP MY DEMO - see https://github.com/vector-im/vector-web/issues/946 - if (true || scrollState.stuckAtBottom) { + if (scrollState.stuckAtBottom) { scrollNode.scrollTop = scrollNode.scrollHeight; debuglog("Scrolled to bottom; offset now", scrollNode.scrollTop); } else if (scrollState.trackedScrollToken) {