From 0b0f10ddf6c0488d48837a8796761b6171875c88 Mon Sep 17 00:00:00 2001 From: David Baker Date: Thu, 21 Jul 2016 16:25:51 +0100 Subject: [PATCH 01/16] Fix tab complete order properly Don't return NaN from your sort functions... --- src/TabComplete.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/TabComplete.js b/src/TabComplete.js index 0ec0b77802..65441c9381 100644 --- a/src/TabComplete.js +++ b/src/TabComplete.js @@ -341,7 +341,12 @@ class TabComplete { } if (a.kind == 'member') { - return this.memberTabOrder[b.member.userId] - this.memberTabOrder[a.member.userId]; + let orderA = this.memberTabOrder[a.member.userId]; + let orderB = this.memberTabOrder[b.member.userId]; + if (orderA === undefined) orderA = -1; + if (orderB === undefined) orderB = -1; + + return orderB - orderA; } // anything else we have no ordering for From ea5e021d8d09a32e6591f53f63589c8452fbbeab Mon Sep 17 00:00:00 2001 From: David Baker Date: Thu, 21 Jul 2016 17:57:55 +0100 Subject: [PATCH 02/16] Refactor MatrixClientPeg Should be functionally identical --- src/MatrixClientPeg.js | 138 +++++++++++++++++++++-------------------- 1 file changed, 71 insertions(+), 67 deletions(-) diff --git a/src/MatrixClientPeg.js b/src/MatrixClientPeg.js index 7c1c5b34d7..bf1659e25c 100644 --- a/src/MatrixClientPeg.js +++ b/src/MatrixClientPeg.js @@ -16,13 +16,10 @@ limitations under the License. 'use strict'; -// A thing that holds your Matrix Client -var Matrix = require("matrix-js-sdk"); -var GuestAccess = require("./GuestAccess"); +import Matrix from 'matrix-js-sdk'; +import GuestAccess from './GuestAccess'; -let matrixClient: MatrixClient = null; - -var localStorage = window.localStorage; +const localStorage = window.localStorage; function deviceId() { // XXX: is Math.random()'s deterministicity a problem here? @@ -35,73 +32,22 @@ function deviceId() { return id; } -function createClientForPeg(hs_url, is_url, user_id, access_token, guestAccess) { - var opts = { - baseUrl: hs_url, - idBaseUrl: is_url, - accessToken: access_token, - userId: user_id, - timelineSupport: true, - }; - - if (localStorage) { - opts.sessionStore = new Matrix.WebStorageSessionStore(localStorage); - opts.deviceId = deviceId(); - } - - matrixClient = Matrix.createClient(opts); - - // we're going to add eventlisteners for each matrix event tile, so the - // potential number of event listeners is quite high. - matrixClient.setMaxListeners(500); - - if (guestAccess) { - console.log("Guest: %s", guestAccess.isGuest()); - matrixClient.setGuest(guestAccess.isGuest()); - var peekedRoomId = guestAccess.getPeekedRoom(); - if (peekedRoomId) { - console.log("Peeking in room %s", peekedRoomId); - matrixClient.peekInRoom(peekedRoomId); - } - } -} - -if (localStorage) { - var hs_url = localStorage.getItem("mx_hs_url"); - var is_url = localStorage.getItem("mx_is_url") || 'https://matrix.org'; - var access_token = localStorage.getItem("mx_access_token"); - var user_id = localStorage.getItem("mx_user_id"); - var guestAccess = new GuestAccess(localStorage); - if (access_token && user_id && hs_url) { - console.log("Restoring session for %s", user_id); - createClientForPeg(hs_url, is_url, user_id, access_token, guestAccess); - } - else { - console.log("Session not found."); - } -} - -class MatrixClient { - +// A thing that holds your Matrix Client +// Also magically works across sessions through the power of localstorage +class MatrixClientPeg { constructor(guestAccess) { + this.matrixClient = null; this.guestAccess = guestAccess; } get(): MatrixClient { - return matrixClient; + return this.matrixClient; } unset() { - matrixClient = null; + this.matrixClient = null; } - // FIXME, XXX: this all seems very convoluted :( - // - // Why do we have this peg wrapper rather than just MatrixClient.get()? - // Why do we name MatrixClient as MatrixClientPeg when we export it? - // - // -matthew - replaceUsingUrls(hs_url, is_url) { this.replaceClient(hs_url, is_url); } @@ -119,7 +65,7 @@ class MatrixClient { } } this.guestAccess.markAsGuest(Boolean(isGuest)); - createClientForPeg(hs_url, is_url, user_id, access_token, this.guestAccess); + this._createClient(hs_url, is_url, user_id, access_token); if (localStorage) { try { localStorage.setItem("mx_hs_url", hs_url); @@ -134,9 +80,67 @@ class MatrixClient { console.warn("No local storage available: can't persist session!"); } } + + getCredentials() { + return [ + this.matrixClient.baseUrl, + this.matrixClient.idBaseUrl, + this.matrixClient.credentials.userId, + this.matrixClient.getAccessToken(), + this.guestAccess.isGuest(), + ]; + } + + tryRestore() { + if (localStorage) { + const hs_url = localStorage.getItem("mx_hs_url"); + const is_url = localStorage.getItem("mx_is_url") || 'https://matrix.org'; + const access_token = localStorage.getItem("mx_access_token"); + const user_id = localStorage.getItem("mx_user_id"); + const guestAccess = new GuestAccess(localStorage); + if (access_token && user_id && hs_url) { + console.log("Restoring session for %s", user_id); + this._createClient(hs_url, is_url, user_id, access_token, guestAccess); + } else { + console.log("Session not found."); + } + } + } + + _createClient(hs_url, is_url, user_id, access_token) { + var opts = { + baseUrl: hs_url, + idBaseUrl: is_url, + accessToken: access_token, + userId: user_id, + timelineSupport: true, + }; + + if (localStorage) { + opts.sessionStore = new Matrix.WebStorageSessionStore(localStorage); + opts.deviceId = deviceId(); + } + + this.matrixClient = Matrix.createClient(opts); + + // we're going to add eventlisteners for each matrix event tile, so the + // potential number of event listeners is quite high. + this.matrixClient.setMaxListeners(500); + + if (this.guestAccess) { + console.log("Guest: %s", this.guestAccess.isGuest()); + this.matrixClient.setGuest(this.guestAccess.isGuest()); + var peekedRoomId = this.guestAccess.getPeekedRoom(); + if (peekedRoomId) { + console.log("Peeking in room %s", peekedRoomId); + this.matrixClient.peekInRoom(peekedRoomId); + } + } + } } -if (!global.mxMatrixClient) { - global.mxMatrixClient = new MatrixClient(new GuestAccess(localStorage)); +if (!global.mxMatrixClientPeg) { + global.mxMatrixClientPeg = new MatrixClientPeg(new GuestAccess(localStorage)); + global.mxMatrixClientPeg.tryRestore(); } -module.exports = global.mxMatrixClient; +module.exports = global.mxMatrixClientPeg; From ad7f8d0a58de9222becf88b08d4b13be5c7f9fc0 Mon Sep 17 00:00:00 2001 From: Richard van der Hoff Date: Fri, 22 Jul 2016 10:12:37 +0100 Subject: [PATCH 03/16] Bump to latest react-gemini-scrollbar I've updated our forks of the gemini-scrollbar project to latest upstream. --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index cd81ad7c56..39709e7e2e 100644 --- a/package.json +++ b/package.json @@ -45,7 +45,7 @@ "react": "^15.0.1", "react-addons-css-transition-group": "^15.1.0", "react-dom": "^15.0.1", - "react-gemini-scrollbar": "matrix-org/react-gemini-scrollbar#c3d942e", + "react-gemini-scrollbar": "matrix-org/react-gemini-scrollbar#dbf0abf", "sanitize-html": "^1.11.1", "velocity-vector": "vector-im/velocity#059e3b2", "whatwg-fetch": "^1.0.0" From e3cdeed32b706b892e34658556fcea1a08516c84 Mon Sep 17 00:00:00 2001 From: Richard van der Hoff Date: Fri, 22 Jul 2016 10:43:50 +0100 Subject: [PATCH 04/16] Bump to react 15.2.1 This should also stop npm complaining about invalid peerDependencies. --- package.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/package.json b/package.json index 39709e7e2e..0d2f6e1a5b 100644 --- a/package.json +++ b/package.json @@ -42,9 +42,9 @@ "matrix-js-sdk": "matrix-org/matrix-js-sdk#develop", "optimist": "^0.6.1", "q": "^1.4.1", - "react": "^15.0.1", - "react-addons-css-transition-group": "^15.1.0", - "react-dom": "^15.0.1", + "react": "^15.2.1", + "react-addons-css-transition-group": "^15.2.1", + "react-dom": "^15.2.1", "react-gemini-scrollbar": "matrix-org/react-gemini-scrollbar#dbf0abf", "sanitize-html": "^1.11.1", "velocity-vector": "vector-im/velocity#059e3b2", From b7e95b3883163b4ee1cbeed246b63d5b9b23069e Mon Sep 17 00:00:00 2001 From: David Baker Date: Fri, 22 Jul 2016 14:00:23 +0100 Subject: [PATCH 05/16] Remove other guestAccess arg --- src/MatrixClientPeg.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/MatrixClientPeg.js b/src/MatrixClientPeg.js index bf1659e25c..77ebbaa2ba 100644 --- a/src/MatrixClientPeg.js +++ b/src/MatrixClientPeg.js @@ -100,7 +100,7 @@ class MatrixClientPeg { const guestAccess = new GuestAccess(localStorage); if (access_token && user_id && hs_url) { console.log("Restoring session for %s", user_id); - this._createClient(hs_url, is_url, user_id, access_token, guestAccess); + this._createClient(hs_url, is_url, user_id, access_token); } else { console.log("Session not found."); } From ddbac8c73a61a3d730e4e6a6414840d3d3e89fd1 Mon Sep 17 00:00:00 2001 From: David Baker Date: Fri, 22 Jul 2016 15:47:47 +0100 Subject: [PATCH 06/16] More refactoring of MatrixClientPeg Including getting rid of GuestAccess as it was basically doing nothing apart from remembering if we were a guest which may as well be done in the same place we save/restore everything else --- src/GuestAccess.js | 51 --------------------------------- src/MatrixClientPeg.js | 64 ++++++++++++++++++++++++------------------ 2 files changed, 37 insertions(+), 78 deletions(-) delete mode 100644 src/GuestAccess.js diff --git a/src/GuestAccess.js b/src/GuestAccess.js deleted file mode 100644 index ef48d23ded..0000000000 --- a/src/GuestAccess.js +++ /dev/null @@ -1,51 +0,0 @@ -/* -Copyright 2015 OpenMarket Ltd - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ -const IS_GUEST_KEY = "matrix-is-guest"; - -class GuestAccess { - - constructor(localStorage) { - this.localStorage = localStorage; - try { - this._isGuest = localStorage.getItem(IS_GUEST_KEY) === "true"; - } - catch (e) {} // don't care - } - - setPeekedRoom(roomId) { - // we purposefully do not persist this to local storage as peeking is - // entirely transient. - this._peekedRoomId = roomId; - } - - getPeekedRoom() { - return this._peekedRoomId; - } - - isGuest() { - return this._isGuest; - } - - markAsGuest(isGuest) { - try { - this.localStorage.setItem(IS_GUEST_KEY, JSON.stringify(isGuest)); - } catch (e) {} // ignore. If they don't do LS, they'll just get a new account. - this._isGuest = isGuest; - this._peekedRoomId = null; - } -} - -module.exports = GuestAccess; diff --git a/src/MatrixClientPeg.js b/src/MatrixClientPeg.js index 77ebbaa2ba..8d155143fc 100644 --- a/src/MatrixClientPeg.js +++ b/src/MatrixClientPeg.js @@ -17,7 +17,6 @@ limitations under the License. 'use strict'; import Matrix from 'matrix-js-sdk'; -import GuestAccess from './GuestAccess'; const localStorage = window.localStorage; @@ -32,12 +31,15 @@ function deviceId() { return id; } -// A thing that holds your Matrix Client -// Also magically works across sessions through the power of localstorage +/** + * Wrapper object for handling the js-sdk Matrix Client object in the react-sdk + * Handles the creation/initialisation of client objects. + * This module provides a singleton instance of this class so the 'current' + * Matrix Client object is available easily. + */ class MatrixClientPeg { - constructor(guestAccess) { + constructor() { this.matrixClient = null; - this.guestAccess = guestAccess; } get(): MatrixClient { @@ -48,15 +50,19 @@ class MatrixClientPeg { this.matrixClient = null; } + /** + * Replace this MatrixClientPeg's client with a client instance that has + * Home Server / Identity Server URLs but no credentials + */ replaceUsingUrls(hs_url, is_url) { - this.replaceClient(hs_url, is_url); + this.replaceUsingAccessToken(hs_url, is_url); } + /** + * Replace this MatrixClientPeg's client with a client instance that has + * Home Server / Identity Server URLs and active credentials + */ replaceUsingAccessToken(hs_url, is_url, user_id, access_token, isGuest) { - this.replaceClient(hs_url, is_url, user_id, access_token, isGuest); - } - - replaceClient(hs_url, is_url, user_id, access_token, isGuest) { if (localStorage) { try { localStorage.clear(); @@ -64,15 +70,20 @@ class MatrixClientPeg { console.warn("Error clearing local storage", e); } } - this.guestAccess.markAsGuest(Boolean(isGuest)); this._createClient(hs_url, is_url, user_id, access_token); + this.matrixClient.setGuest(Boolean(isGuest)); + if (localStorage) { try { localStorage.setItem("mx_hs_url", hs_url); localStorage.setItem("mx_is_url", is_url); - localStorage.setItem("mx_user_id", user_id); - localStorage.setItem("mx_access_token", access_token); - console.log("Session persisted for %s", user_id); + + if (user_id !== undefined && access_token !== undefined) { + localStorage.setItem("mx_user_id", user_id); + localStorage.setItem("mx_access_token", access_token); + localStorage.setItem("mx_is_guest", JSON.stringify(isGuest)); + console.log("Session persisted for %s", user_id); + } } catch (e) { console.warn("Error using local storage: can't persist session!", e); } @@ -87,7 +98,7 @@ class MatrixClientPeg { this.matrixClient.idBaseUrl, this.matrixClient.credentials.userId, this.matrixClient.getAccessToken(), - this.guestAccess.isGuest(), + this.matrixClient.isGuest(), ]; } @@ -97,10 +108,19 @@ class MatrixClientPeg { const is_url = localStorage.getItem("mx_is_url") || 'https://matrix.org'; const access_token = localStorage.getItem("mx_access_token"); const user_id = localStorage.getItem("mx_user_id"); - const guestAccess = new GuestAccess(localStorage); + + let is_guest; + if (localStorage.getItem("mx_is_guest") !== null) { + is_guest = localStorage.getItem("mx_is_guest") === "true"; + } else { + // legacy key name + is_guest = localStorage.getItem("matrix-is-guest") === "true"; + } + if (access_token && user_id && hs_url) { console.log("Restoring session for %s", user_id); this._createClient(hs_url, is_url, user_id, access_token); + this.matrixClient.setGuest(is_guest); } else { console.log("Session not found."); } @@ -126,21 +146,11 @@ class MatrixClientPeg { // we're going to add eventlisteners for each matrix event tile, so the // potential number of event listeners is quite high. this.matrixClient.setMaxListeners(500); - - if (this.guestAccess) { - console.log("Guest: %s", this.guestAccess.isGuest()); - this.matrixClient.setGuest(this.guestAccess.isGuest()); - var peekedRoomId = this.guestAccess.getPeekedRoom(); - if (peekedRoomId) { - console.log("Peeking in room %s", peekedRoomId); - this.matrixClient.peekInRoom(peekedRoomId); - } - } } } if (!global.mxMatrixClientPeg) { - global.mxMatrixClientPeg = new MatrixClientPeg(new GuestAccess(localStorage)); + global.mxMatrixClientPeg = new MatrixClientPeg(); global.mxMatrixClientPeg.tryRestore(); } module.exports = global.mxMatrixClientPeg; From b07e50d418c5d2e75f0725e5699cf415c689181e Mon Sep 17 00:00:00 2001 From: David Baker Date: Fri, 22 Jul 2016 17:30:25 +0100 Subject: [PATCH 07/16] Fix 'start chat' button on MemberInfo this/self fail & related scoping Fixes https://github.com/vector-im/vector-web/issues/1844 --- src/components/views/rooms/MemberInfo.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/components/views/rooms/MemberInfo.js b/src/components/views/rooms/MemberInfo.js index c439f8b40c..07a7b9398d 100644 --- a/src/components/views/rooms/MemberInfo.js +++ b/src/components/views/rooms/MemberInfo.js @@ -406,14 +406,14 @@ module.exports = React.createClass({ this.props.onFinished(); } else { - self.setState({ updating: self.state.updating + 1 }); + this.setState({ updating: this.state.updating + 1 }); createRoom({ createOpts: { invite: [this.props.member.userId], }, - }).finally(function() { - self.props.onFinished(); - self.setState({ updating: self.state.updating - 1 }); + }).finally(() => { + this.props.onFinished(); + this.setState({ updating: this.state.updating - 1 }); }).done(); } }, From 587a86441fad3f7172ae195b9367d2ef80713118 Mon Sep 17 00:00:00 2001 From: David Baker Date: Mon, 25 Jul 2016 16:20:03 +0100 Subject: [PATCH 08/16] This may as wel go in createclient --- src/MatrixClientPeg.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/MatrixClientPeg.js b/src/MatrixClientPeg.js index 8d155143fc..b7feca60ef 100644 --- a/src/MatrixClientPeg.js +++ b/src/MatrixClientPeg.js @@ -71,7 +71,6 @@ class MatrixClientPeg { } } this._createClient(hs_url, is_url, user_id, access_token); - this.matrixClient.setGuest(Boolean(isGuest)); if (localStorage) { try { @@ -146,6 +145,8 @@ class MatrixClientPeg { // we're going to add eventlisteners for each matrix event tile, so the // potential number of event listeners is quite high. this.matrixClient.setMaxListeners(500); + + this.matrixClient.setGuest(Boolean(isGuest)); } } From cbf10bfff6cc1fe06e044c32436ded090f45a2d2 Mon Sep 17 00:00:00 2001 From: David Baker Date: Mon, 25 Jul 2016 16:28:28 +0100 Subject: [PATCH 09/16] PR feedback Reintroduce replaceClient so we're not calling replaceUsingAccessToken without access tokens which is a bit silly. Fix bug from previous commit (pass isGuest through) --- src/MatrixClientPeg.js | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/MatrixClientPeg.js b/src/MatrixClientPeg.js index b7feca60ef..ce4b5ba743 100644 --- a/src/MatrixClientPeg.js +++ b/src/MatrixClientPeg.js @@ -55,7 +55,7 @@ class MatrixClientPeg { * Home Server / Identity Server URLs but no credentials */ replaceUsingUrls(hs_url, is_url) { - this.replaceUsingAccessToken(hs_url, is_url); + this._replaceClient(hs_url, is_url); } /** @@ -63,6 +63,10 @@ class MatrixClientPeg { * Home Server / Identity Server URLs and active credentials */ replaceUsingAccessToken(hs_url, is_url, user_id, access_token, isGuest) { + this._replaceClient(hs_url, is_url, user_id, access_token, isGuest); + } + + _replaceClient(hs_url, is_url, user_id, access_token, isGuest) { if (localStorage) { try { localStorage.clear(); @@ -70,7 +74,7 @@ class MatrixClientPeg { console.warn("Error clearing local storage", e); } } - this._createClient(hs_url, is_url, user_id, access_token); + this._createClient(hs_url, is_url, user_id, access_token, isGuest); if (localStorage) { try { @@ -126,7 +130,7 @@ class MatrixClientPeg { } } - _createClient(hs_url, is_url, user_id, access_token) { + _createClient(hs_url, is_url, user_id, access_token, isGuest) { var opts = { baseUrl: hs_url, idBaseUrl: is_url, From 4ecf5f637225c21e01652d1e8f733d4f2edc2939 Mon Sep 17 00:00:00 2001 From: David Baker Date: Tue, 26 Jul 2016 17:58:19 +0100 Subject: [PATCH 10/16] Fix bug where vector freezes on power level event Make rate_limited_function accept functions with args so we can just ratelimit the event handler & be done with it. Fixes https://github.com/vector-im/vector-web/issues/1877 --- src/components/structures/RoomView.js | 6 ++++-- src/ratelimitedfunc.js | 4 ++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/components/structures/RoomView.js b/src/components/structures/RoomView.js index accf96f349..9aa192d9ca 100644 --- a/src/components/structures/RoomView.js +++ b/src/components/structures/RoomView.js @@ -482,7 +482,9 @@ module.exports = React.createClass({ } }, - onRoomStateMember: function(ev, state, member) { + // rate limited because a power level change will emit an event for every + // member in the room. + onRoomStateMember: new rate_limited_func(function(ev, state, member) { // ignore if we don't have a room yet if (!this.state.room) { return; @@ -511,7 +513,7 @@ module.exports = React.createClass({ member.userId === this.props.ConferenceHandler.getConferenceUserIdForRoom(member.roomId)) { this._updateConfCallNotification(); } - }, + }, 500), _hasUnsentMessages: function(room) { return this._getUnsentMessages(room).length > 0; diff --git a/src/ratelimitedfunc.js b/src/ratelimitedfunc.js index 453669b477..94626d5ad9 100644 --- a/src/ratelimitedfunc.js +++ b/src/ratelimitedfunc.js @@ -23,13 +23,13 @@ module.exports = function(f, minIntervalMs) { var now = Date.now(); if (self.lastCall < now - minIntervalMs) { - f.apply(this); + f.apply(this, arguments); self.lastCall = now; } else if (self.scheduledCall === undefined) { self.scheduledCall = setTimeout( () => { self.scheduledCall = undefined; - f.apply(this); + f.apply(this, arguments); self.lastCall = now; }, (self.lastCall + minIntervalMs) - now From 31399254b62c49200f3d7cb04ca7ebb40cb52fda Mon Sep 17 00:00:00 2001 From: David Baker Date: Tue, 26 Jul 2016 18:15:26 +0100 Subject: [PATCH 11/16] Fix onRoomStateMember debouncing Don't have debounced functions take arsg, because they won't be the same for each invocation. --- src/components/structures/RoomView.js | 18 ++++++++++-------- src/ratelimitedfunc.js | 12 ++++++++++-- 2 files changed, 20 insertions(+), 10 deletions(-) diff --git a/src/components/structures/RoomView.js b/src/components/structures/RoomView.js index 9aa192d9ca..6d35f76529 100644 --- a/src/components/structures/RoomView.js +++ b/src/components/structures/RoomView.js @@ -482,9 +482,7 @@ module.exports = React.createClass({ } }, - // rate limited because a power level change will emit an event for every - // member in the room. - onRoomStateMember: new rate_limited_func(function(ev, state, member) { + onRoomStateMember: function(ev, state, member) { // ignore if we don't have a room yet if (!this.state.room) { return; @@ -495,6 +493,15 @@ module.exports = React.createClass({ return; } + if (this.props.ConferenceHandler && + member.userId === this.props.ConferenceHandler.getConferenceUserIdForRoom(member.roomId)) { + this._updateConfCallNotification(); + } + + this._updateRoomMembers(); + }, + + _updateRoomMembers: new rate_limited_func(function() { // a member state changed in this room, refresh the tab complete list this.tabComplete.loadEntries(this.state.room); this._updateAutoComplete(); @@ -508,11 +515,6 @@ module.exports = React.createClass({ joining: false }); } - - if (this.props.ConferenceHandler && - member.userId === this.props.ConferenceHandler.getConferenceUserIdForRoom(member.roomId)) { - this._updateConfCallNotification(); - } }, 500), _hasUnsentMessages: function(room) { diff --git a/src/ratelimitedfunc.js b/src/ratelimitedfunc.js index 94626d5ad9..ed892f4eac 100644 --- a/src/ratelimitedfunc.js +++ b/src/ratelimitedfunc.js @@ -14,6 +14,14 @@ See the License for the specific language governing permissions and limitations under the License. */ +/** + * 'debounces' a function to only execute every n milliseconds. + * Useful when react-sdk gets many, many events but only wants + * to update the interface once for all of them. + * + * Note that the function must not take arguments, since the args + * could be different for each invocarion of the function. + */ module.exports = function(f, minIntervalMs) { this.lastCall = 0; this.scheduledCall = undefined; @@ -23,13 +31,13 @@ module.exports = function(f, minIntervalMs) { var now = Date.now(); if (self.lastCall < now - minIntervalMs) { - f.apply(this, arguments); + f.apply(this); self.lastCall = now; } else if (self.scheduledCall === undefined) { self.scheduledCall = setTimeout( () => { self.scheduledCall = undefined; - f.apply(this, arguments); + f.apply(this); self.lastCall = now; }, (self.lastCall + minIntervalMs) - now From 09993cd3bcc737c6b33b2d836bebe7342d879092 Mon Sep 17 00:00:00 2001 From: David Baker Date: Tue, 26 Jul 2016 18:19:25 +0100 Subject: [PATCH 12/16] Add comment back --- src/components/structures/RoomView.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/components/structures/RoomView.js b/src/components/structures/RoomView.js index 6d35f76529..9fbdb51f11 100644 --- a/src/components/structures/RoomView.js +++ b/src/components/structures/RoomView.js @@ -501,6 +501,8 @@ module.exports = React.createClass({ this._updateRoomMembers(); }, + // rate limited because a power level change will emit an event for every + // member in the room. _updateRoomMembers: new rate_limited_func(function() { // a member state changed in this room, refresh the tab complete list this.tabComplete.loadEntries(this.state.room); From c8df9148b37136a506333ba968bb7dc227116cb0 Mon Sep 17 00:00:00 2001 From: Richard van der Hoff Date: Wed, 27 Jul 2016 11:35:48 +0100 Subject: [PATCH 13/16] Remove relayoutOnUpdate prop on gemini-scrollbar The latest gemini-scrollbar makes relayoutOnUpdate redundant, so update to it and remove the properties. --- package.json | 2 +- src/components/structures/ScrollPanel.js | 1 - src/components/structures/UserSettings.js | 1 - src/components/views/rooms/MemberList.js | 1 - src/components/views/rooms/RoomList.js | 1 - src/components/views/rooms/SearchableEntityList.js | 1 - 6 files changed, 1 insertion(+), 6 deletions(-) diff --git a/package.json b/package.json index 0d2f6e1a5b..3ea944a066 100644 --- a/package.json +++ b/package.json @@ -45,7 +45,7 @@ "react": "^15.2.1", "react-addons-css-transition-group": "^15.2.1", "react-dom": "^15.2.1", - "react-gemini-scrollbar": "matrix-org/react-gemini-scrollbar#dbf0abf", + "react-gemini-scrollbar": "matrix-org/react-gemini-scrollbar#5e97aef", "sanitize-html": "^1.11.1", "velocity-vector": "vector-im/velocity#059e3b2", "whatwg-fetch": "^1.0.0" diff --git a/src/components/structures/ScrollPanel.js b/src/components/structures/ScrollPanel.js index 63c78d2a3c..9a4c614720 100644 --- a/src/components/structures/ScrollPanel.js +++ b/src/components/structures/ScrollPanel.js @@ -540,7 +540,6 @@ module.exports = React.createClass({ // it's not obvious why we have a separate div and ol anyway. return (
    diff --git a/src/components/structures/UserSettings.js b/src/components/structures/UserSettings.js index 188e140007..75fe1f0825 100644 --- a/src/components/structures/UserSettings.js +++ b/src/components/structures/UserSettings.js @@ -463,7 +463,6 @@ module.exports = React.createClass({

    Profile

    diff --git a/src/components/views/rooms/MemberList.js b/src/components/views/rooms/MemberList.js index 01a952f1d7..80ae90d984 100644 --- a/src/components/views/rooms/MemberList.js +++ b/src/components/views/rooms/MemberList.js @@ -521,7 +521,6 @@ module.exports = React.createClass({
    {inviteMemberListSection} diff --git a/src/components/views/rooms/RoomList.js b/src/components/views/rooms/RoomList.js index aff03182a1..0c8ac7ed8d 100644 --- a/src/components/views/rooms/RoomList.js +++ b/src/components/views/rooms/RoomList.js @@ -325,7 +325,6 @@ module.exports = React.createClass({ return (
    { list } From 1a600b0674da34ff515c785b8c779779ec248301 Mon Sep 17 00:00:00 2001 From: Richard van der Hoff Date: Wed, 27 Jul 2016 11:38:04 +0100 Subject: [PATCH 14/16] Stop the Avatar classes setting properties on s React apparently now checks the properties which are set on DOM elements, and grumbles noisily about unexpected ones. Update BaseAvatar and RoomAvatar so that they don't set unrelated properties on the DOM elements. --- src/components/views/avatars/BaseAvatar.js | 26 ++++++++++++---------- src/components/views/avatars/RoomAvatar.js | 8 ++++--- 2 files changed, 19 insertions(+), 15 deletions(-) diff --git a/src/components/views/avatars/BaseAvatar.js b/src/components/views/avatars/BaseAvatar.js index 66f8e27b88..08d7965455 100644 --- a/src/components/views/avatars/BaseAvatar.js +++ b/src/components/views/avatars/BaseAvatar.js @@ -133,32 +133,34 @@ module.exports = React.createClass({ }, render: function() { - var name = this.props.name; - var imageUrl = this.state.imageUrls[this.state.urlsIndex]; + const {name, idName, title, url, urls, width, height, resizeMethod, + defaultToInitialLetter, + ...otherProps} = this.props; + if (imageUrl === this.state.defaultImageUrl) { - var initialLetter = emojifyText(this._getInitialLetter(this.props.name)); + var initialLetter = emojifyText(this._getInitialLetter(name)); return ( - + + alt="" title={title} onError={this.onError} + width={width} height={height} /> ); } return ( + width={width} height={height} + title={title} alt="" + {...otherProps} /> ); } }); diff --git a/src/components/views/avatars/RoomAvatar.js b/src/components/views/avatars/RoomAvatar.js index 129c68ff1b..dcb25eff61 100644 --- a/src/components/views/avatars/RoomAvatar.js +++ b/src/components/views/avatars/RoomAvatar.js @@ -126,11 +126,13 @@ module.exports = React.createClass({ render: function() { var BaseAvatar = sdk.getComponent("avatars.BaseAvatar"); - var roomName = this.props.room ? this.props.room.name : this.props.oobData.name; + var {room, oobData, ...otherProps} = this.props; + + var roomName = room ? room.name : oobData.name; return ( - ); } From 33088421216f2361fe0a922cee9aaeb902a0a3d6 Mon Sep 17 00:00:00 2001 From: David Baker Date: Wed, 27 Jul 2016 13:28:23 +0100 Subject: [PATCH 15/16] Fix up reskindex.js path Since npm does not put our *own* 'binaries' on the path, we need to use the full path to it --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 0d2f6e1a5b..60fc45db77 100644 --- a/package.json +++ b/package.json @@ -13,7 +13,7 @@ "reskindex": "./reskindex.js" }, "scripts": { - "reskindex": "reskindex -h header", + "reskindex": "./reskindex.js -h header", "build": "babel src -d lib --source-maps --stage 1", "start": "babel src -w -d lib --source-maps --stage 1", "lint": "eslint src/", From 39ae8c6e46a3563dd69235081d3d9e4e334ed164 Mon Sep 17 00:00:00 2001 From: Richard van der Hoff Date: Wed, 27 Jul 2016 15:41:24 +0100 Subject: [PATCH 16/16] formatting PR feedback --- src/components/views/avatars/BaseAvatar.js | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/components/views/avatars/BaseAvatar.js b/src/components/views/avatars/BaseAvatar.js index 08d7965455..f0a36c6608 100644 --- a/src/components/views/avatars/BaseAvatar.js +++ b/src/components/views/avatars/BaseAvatar.js @@ -135,9 +135,11 @@ module.exports = React.createClass({ render: function() { var imageUrl = this.state.imageUrls[this.state.urlsIndex]; - const {name, idName, title, url, urls, width, height, resizeMethod, - defaultToInitialLetter, - ...otherProps} = this.props; + const { + name, idName, title, url, urls, width, height, resizeMethod, + defaultToInitialLetter, + ...otherProps + } = this.props; if (imageUrl === this.state.defaultImageUrl) { var initialLetter = emojifyText(this._getInitialLetter(name));