From a11e1cc2f9e37e1261eade4d9c3c141dc7ae43fb Mon Sep 17 00:00:00 2001 From: Luke Barnard Date: Wed, 27 Jun 2018 17:39:20 +0100 Subject: [PATCH 01/45] [WIP] Send lazy_load_memberers filter when syncing --- src/MatrixClientPeg.js | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/MatrixClientPeg.js b/src/MatrixClientPeg.js index 9d86a62de4..01a411663c 100644 --- a/src/MatrixClientPeg.js +++ b/src/MatrixClientPeg.js @@ -34,6 +34,14 @@ interface MatrixClientCreds { guest: boolean, } +const FILTER_CONTENT = { + room: { + state: { + lazy_load_members: true, + }, + }, +}; + /** * Wrapper object for handling the js-sdk Matrix Client object in the react-sdk * Handles the creation/initialisation of client objects. @@ -99,6 +107,8 @@ class MatrixClientPeg { // the react sdk doesn't work without this, so don't allow opts.pendingEventOrdering = "detached"; + opts.filter = await this.matrixClient.createFilter(FILTER_CONTENT); + try { const promise = this.matrixClient.store.startup(); console.log(`MatrixClientPeg: waiting for MatrixClient store to initialise`); From 8c985bd049a639e46db9698ed3a8ca96235ffb99 Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Tue, 10 Jul 2018 17:20:52 +0200 Subject: [PATCH 02/45] ask client to lazy load room members when viewing a room --- src/stores/RoomViewStore.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/stores/RoomViewStore.js b/src/stores/RoomViewStore.js index fed0d7b4a1..05ad16912c 100644 --- a/src/stores/RoomViewStore.js +++ b/src/stores/RoomViewStore.js @@ -133,6 +133,7 @@ class RoomViewStore extends Store { _viewRoom(payload) { if (payload.room_id) { + MatrixClientPeg.get().loadRoomMembersIfNeeded(payload.room_id); const newState = { roomId: payload.room_id, roomAlias: payload.room_alias, From de3e143a8fd68bf1b9d6e1f01fd2808958375fae Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Thu, 12 Jul 2018 19:38:08 +0200 Subject: [PATCH 03/45] only lazy load the members when viewing a joined room --- src/components/structures/RoomView.js | 3 +++ src/stores/RoomViewStore.js | 1 - 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/components/structures/RoomView.js b/src/components/structures/RoomView.js index 0325b3d9a6..2c322b269d 100644 --- a/src/components/structures/RoomView.js +++ b/src/components/structures/RoomView.js @@ -312,6 +312,9 @@ module.exports = React.createClass({ // Stop peeking because we have joined this room previously MatrixClientPeg.get().stopPeeking(); this.setState({isPeeking: false}); + + //viewing a previously joined room, try to lazy load members + MatrixClientPeg.get().loadRoomMembersIfNeeded(room.roomId); } } }, diff --git a/src/stores/RoomViewStore.js b/src/stores/RoomViewStore.js index 05ad16912c..fed0d7b4a1 100644 --- a/src/stores/RoomViewStore.js +++ b/src/stores/RoomViewStore.js @@ -133,7 +133,6 @@ class RoomViewStore extends Store { _viewRoom(payload) { if (payload.room_id) { - MatrixClientPeg.get().loadRoomMembersIfNeeded(payload.room_id); const newState = { roomId: payload.room_id, roomAlias: payload.room_alias, From 19387805ac3a95a8f0eaa84406c50767be0f0492 Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Thu, 12 Jul 2018 19:39:52 +0200 Subject: [PATCH 04/45] Don't assume RoomMember has a state event in direct chat detection Instead call the method on the member that takes lazy loading into account --- src/components/structures/RoomView.js | 23 +++++++---------------- 1 file changed, 7 insertions(+), 16 deletions(-) diff --git a/src/components/structures/RoomView.js b/src/components/structures/RoomView.js index 2c322b269d..3a9b3ae748 100644 --- a/src/components/structures/RoomView.js +++ b/src/components/structures/RoomView.js @@ -749,21 +749,15 @@ module.exports = React.createClass({ }, _updateDMState() { - const me = this.state.room.getMember(MatrixClientPeg.get().credentials.userId); + const me = this.state.room.getMember(MatrixClientPeg.get().getUserId()); if (!me || me.membership !== "join") { return; } + const roomId = this.state.room.roomId; + const dmInviter = me.getDirectChatInviter(); - // The user may have accepted an invite with is_direct set - if (me.events.member.getPrevContent().membership === "invite" && - me.events.member.getPrevContent().is_direct - ) { - // This is a DM with the sender of the invite event (which we assume - // preceded the join event) - Rooms.setDMRoom( - this.state.room.roomId, - me.events.member.getUnsigned().prev_sender, - ); + if (dmInviter) { + Rooms.setDMRoom(roomId, dmInviter); return; } @@ -777,11 +771,8 @@ module.exports = React.createClass({ // The user may have sent an invite with is_direct sent const other = invitedMembers[0]; - if (other && - other.membership === "invite" && - other.events.member.getContent().is_direct - ) { - Rooms.setDMRoom(this.state.room.roomId, other.userId); + if (other && !!other.getDirectChatInviter()) { + Rooms.setDMRoom(roomId, other.userId); return; } }, From 6ff92ede8f26b403cc05f5e18b0c0f445bfdff13 Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Fri, 13 Jul 2018 12:51:32 +0200 Subject: [PATCH 05/45] this code actually never fires, so remove it and that's ok because createRoom will already mark a DM as such on the inviter's side. So here we just handle the invitees side. --- src/components/structures/RoomView.js | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/src/components/structures/RoomView.js b/src/components/structures/RoomView.js index 3a9b3ae748..a545eaab54 100644 --- a/src/components/structures/RoomView.js +++ b/src/components/structures/RoomView.js @@ -758,22 +758,6 @@ module.exports = React.createClass({ if (dmInviter) { Rooms.setDMRoom(roomId, dmInviter); - return; - } - - const invitedMembers = this.state.room.getMembersWithMembership("invite"); - const joinedMembers = this.state.room.getMembersWithMembership("join"); - - // There must be one invited member and one joined member - if (invitedMembers.length !== 1 || joinedMembers.length !== 1) { - return; - } - - // The user may have sent an invite with is_direct sent - const other = invitedMembers[0]; - if (other && !!other.getDirectChatInviter()) { - Rooms.setDMRoom(roomId, other.userId); - return; } }, From 54904c9282a14a31d9f01388b5341721f0f7a100 Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Fri, 13 Jul 2018 14:21:15 +0200 Subject: [PATCH 06/45] use member helper method instead of digging inside member --- src/utils/DMRoomMap.js | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/src/utils/DMRoomMap.js b/src/utils/DMRoomMap.js index d6242719ba..186dc31c3a 100644 --- a/src/utils/DMRoomMap.js +++ b/src/utils/DMRoomMap.js @@ -97,14 +97,8 @@ export default class DMRoomMap { // no entry? if the room is an invite, look for the is_direct hint. const room = this.matrixClient.getRoom(roomId); if (room) { - const me = room.getMember(this.matrixClient.credentials.userId); - if (me.membership == 'invite') { - // The 'direct' hihnt is there, so declare that this is a DM room for - // whoever invited us. - if (me.events.member.getContent().is_direct) { - return me.events.member.getSender(); - } - } + const me = room.getMember(this.matrixClient.getUserId()); + return me.getDirectChatInviter(); } } return this.roomToUser[roomId]; From e237a02fa5005e5ce23c0bef799e735eb7182abc Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Fri, 13 Jul 2018 14:23:24 +0200 Subject: [PATCH 07/45] use more consistent naming --- src/components/structures/RoomView.js | 2 +- src/utils/DMRoomMap.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/structures/RoomView.js b/src/components/structures/RoomView.js index a545eaab54..8d4a15642f 100644 --- a/src/components/structures/RoomView.js +++ b/src/components/structures/RoomView.js @@ -754,7 +754,7 @@ module.exports = React.createClass({ return; } const roomId = this.state.room.roomId; - const dmInviter = me.getDirectChatInviter(); + const dmInviter = me.getDMInviter(); if (dmInviter) { Rooms.setDMRoom(roomId, dmInviter); diff --git a/src/utils/DMRoomMap.js b/src/utils/DMRoomMap.js index 186dc31c3a..d47282f370 100644 --- a/src/utils/DMRoomMap.js +++ b/src/utils/DMRoomMap.js @@ -98,7 +98,7 @@ export default class DMRoomMap { const room = this.matrixClient.getRoom(roomId); if (room) { const me = room.getMember(this.matrixClient.getUserId()); - return me.getDirectChatInviter(); + return me.getDMInviter(); } } return this.roomToUser[roomId]; From 8529bc55e3a7a7aa5176ad261740a3e6bedb8c17 Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Fri, 13 Jul 2018 14:25:12 +0200 Subject: [PATCH 08/45] use helper method for knowing whether a user was kicked mainly for readability, but also to discourage grabbing into the member because of lazy loading changes --- src/Rooms.js | 3 +-- src/stores/RoomListStore.js | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/src/Rooms.js b/src/Rooms.js index ffa39141ff..607bd60b98 100644 --- a/src/Rooms.js +++ b/src/Rooms.js @@ -81,8 +81,7 @@ export function isConfCallRoom(room, me, conferenceHandler) { } export function looksLikeDirectMessageRoom(room, me) { - if (me.membership == "join" || me.membership === "ban" || - (me.membership === "leave" && me.events.member.getSender() !== me.events.member.getStateKey())) { + if (me.membership == "join" || me.membership === "ban" || me.isKicked()) { // Used to split rooms via tags const tagNames = Object.keys(room.tags); // Used for 1:1 direct chats diff --git a/src/stores/RoomListStore.js b/src/stores/RoomListStore.js index b6d0949dd3..8dbfca8875 100644 --- a/src/stores/RoomListStore.js +++ b/src/stores/RoomListStore.js @@ -178,8 +178,7 @@ class RoomListStore extends Store { if (me.membership == "invite") { lists["im.vector.fake.invite"].push(room); - } else if (me.membership == "join" || me.membership === "ban" || - (me.membership === "leave" && me.events.member.getSender() !== me.events.member.getStateKey())) { + } else if (me.membership == "join" || me.membership === "ban" || me.isKicked()) { // Used to split rooms via tags let tagNames = Object.keys(room.tags); From af77f0206af4f71895f9aea0f6ba3054f229adec Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Fri, 13 Jul 2018 15:46:46 +0200 Subject: [PATCH 09/45] don't assume a member has events associated --- src/Rooms.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Rooms.js b/src/Rooms.js index 607bd60b98..aa025d7dc5 100644 --- a/src/Rooms.js +++ b/src/Rooms.js @@ -166,7 +166,7 @@ export function guessDMRoomTarget(room, me) { for (const user of room.getJoinedMembers()) { if (user.userId == me.userId) continue; - if (oldestTs === undefined || user.events.member.getTs() < oldestTs) { + if (oldestTs === undefined || (user.events.member && user.events.member.getTs() < oldestTs)) { oldestUser = user; oldestTs = user.events.member.getTs(); } @@ -177,7 +177,7 @@ export function guessDMRoomTarget(room, me) { for (const user of room.currentState.getMembers()) { if (user.userId == me.userId) continue; - if (oldestTs === undefined || user.events.member.getTs() < oldestTs) { + if (oldestTs === undefined || (user.events.member && user.events.member.getTs() < oldestTs)) { oldestUser = user; oldestTs = user.events.member.getTs(); } From 8c9ceca14f5057cf09a20291255bae679b75c16a Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Fri, 13 Jul 2018 15:57:12 +0200 Subject: [PATCH 10/45] take into account lazy loading when enlarging avatar --- src/components/views/rooms/MemberInfo.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/views/rooms/MemberInfo.js b/src/components/views/rooms/MemberInfo.js index 67189ac90f..8b465cef9d 100644 --- a/src/components/views/rooms/MemberInfo.js +++ b/src/components/views/rooms/MemberInfo.js @@ -598,7 +598,7 @@ module.exports = withMatrixClient(React.createClass({ onMemberAvatarClick: function() { const member = this.props.member; - const avatarUrl = member.user ? member.user.avatarUrl : member.events.member.getContent().avatar_url; + const avatarUrl = member.getMxcAvatarUrl(); if (!avatarUrl) return; const httpUrl = this.props.matrixClient.mxcUrlToHttp(avatarUrl); From 866b4bb067576c30927344ef4e98df6dd22ccad3 Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Fri, 13 Jul 2018 16:01:29 +0200 Subject: [PATCH 11/45] use isKicked method in preview bar --- src/components/views/rooms/RoomPreviewBar.js | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/components/views/rooms/RoomPreviewBar.js b/src/components/views/rooms/RoomPreviewBar.js index 536093807a..64c9b376df 100644 --- a/src/components/views/rooms/RoomPreviewBar.js +++ b/src/components/views/rooms/RoomPreviewBar.js @@ -101,11 +101,7 @@ module.exports = React.createClass({ const myMember = this.props.room ? this.props.room.currentState.members[ MatrixClientPeg.get().credentials.userId ] : null; - const kicked = ( - myMember && - myMember.membership == 'leave' && - myMember.events.member.getSender() != MatrixClientPeg.get().credentials.userId - ); + const kicked = myMember.isKicked(); const banned = myMember && myMember.membership == 'ban'; if (this.props.inviterName) { From af07d734325d6664af6a7e393a18b035591c1fc6 Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Mon, 16 Jul 2018 15:07:53 +0200 Subject: [PATCH 12/45] hide lazy loading behind feature flag --- src/MatrixClientPeg.js | 4 +++- src/components/structures/RoomView.js | 6 +++++- src/settings/Settings.js | 5 +++++ 3 files changed, 13 insertions(+), 2 deletions(-) diff --git a/src/MatrixClientPeg.js b/src/MatrixClientPeg.js index 01a411663c..82d18d307d 100644 --- a/src/MatrixClientPeg.js +++ b/src/MatrixClientPeg.js @@ -107,7 +107,9 @@ class MatrixClientPeg { // the react sdk doesn't work without this, so don't allow opts.pendingEventOrdering = "detached"; - opts.filter = await this.matrixClient.createFilter(FILTER_CONTENT); + if (SettingsStore.isFeatureEnabled('feature_lazyloading')) { + opts.filter = await this.matrixClient.createFilter(FILTER_CONTENT); + } try { const promise = this.matrixClient.store.startup(); diff --git a/src/components/structures/RoomView.js b/src/components/structures/RoomView.js index 8d4a15642f..2eaf0180a9 100644 --- a/src/components/structures/RoomView.js +++ b/src/components/structures/RoomView.js @@ -314,7 +314,11 @@ module.exports = React.createClass({ this.setState({isPeeking: false}); //viewing a previously joined room, try to lazy load members - MatrixClientPeg.get().loadRoomMembersIfNeeded(room.roomId); + + // lazy load members if enabled + if (SettingsStore.isFeatureEnabled('feature_lazyloading')) { + MatrixClientPeg.get().loadRoomMembersIfNeeded(room.roomId); + } } } }, diff --git a/src/settings/Settings.js b/src/settings/Settings.js index e3f5855f0d..d76c1fd8e8 100644 --- a/src/settings/Settings.js +++ b/src/settings/Settings.js @@ -83,6 +83,11 @@ export const SETTINGS = { supportedLevels: LEVELS_FEATURE, default: false, }, + "feature_lazyloading": { + isFeature: true, + displayName: _td("Increase performance by loading room members on first view"), + supportedLevels: LEVELS_FEATURE, + }, "MessageComposerInput.dontSuggestEmoji": { supportedLevels: LEVELS_ACCOUNT_SETTINGS, displayName: _td('Disable Emoji suggestions while typing'), From 201332d96cde1ef154abca0c40d99cf7c70cc845 Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Mon, 16 Jul 2018 15:39:19 +0200 Subject: [PATCH 13/45] move comment after rebase --- src/components/structures/RoomView.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/structures/RoomView.js b/src/components/structures/RoomView.js index 2eaf0180a9..9eb02999fd 100644 --- a/src/components/structures/RoomView.js +++ b/src/components/structures/RoomView.js @@ -309,12 +309,12 @@ module.exports = React.createClass({ } }); } else if (room) { + //viewing a previously joined room, try to lazy load members + // Stop peeking because we have joined this room previously MatrixClientPeg.get().stopPeeking(); this.setState({isPeeking: false}); - //viewing a previously joined room, try to lazy load members - // lazy load members if enabled if (SettingsStore.isFeatureEnabled('feature_lazyloading')) { MatrixClientPeg.get().loadRoomMembersIfNeeded(room.roomId); From 34cb89e86a47b831a3d6bfea8add6b3b56746a50 Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Wed, 18 Jul 2018 12:14:26 +0200 Subject: [PATCH 14/45] fix tests --- test/components/views/rooms/RoomList-test.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/test/components/views/rooms/RoomList-test.js b/test/components/views/rooms/RoomList-test.js index f40a89777b..c0a0b8eb67 100644 --- a/test/components/views/rooms/RoomList-test.js +++ b/test/components/views/rooms/RoomList-test.js @@ -14,7 +14,7 @@ import dis from '../../../../src/dispatcher'; import DMRoomMap from '../../../../src/utils/DMRoomMap.js'; import GroupStore from '../../../../src/stores/GroupStore.js'; -import { Room, RoomMember } from 'matrix-js-sdk'; +import { MatrixClient, Room, RoomMember } from 'matrix-js-sdk'; function generateRoomId() { return '!' + Math.random().toString().slice(2, 10) + ':domain'; @@ -48,6 +48,8 @@ describe('RoomList', () => { sandbox = TestUtils.stubClient(sandbox); client = MatrixClientPeg.get(); client.credentials = {userId: myUserId}; + //revert this to prototype method as the test-utils monkey-patches this to return a hardcoded value + client.getUserId = MatrixClient.prototype.getUserId; clock = lolex.install(); From 33f556066636cebf2ac55264e0496a06cd308bca Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Thu, 19 Jul 2018 15:04:39 +0200 Subject: [PATCH 15/45] move error message to caller of lazy loading --- src/components/structures/RoomView.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/components/structures/RoomView.js b/src/components/structures/RoomView.js index 9eb02999fd..c742975377 100644 --- a/src/components/structures/RoomView.js +++ b/src/components/structures/RoomView.js @@ -317,7 +317,12 @@ module.exports = React.createClass({ // lazy load members if enabled if (SettingsStore.isFeatureEnabled('feature_lazyloading')) { - MatrixClientPeg.get().loadRoomMembersIfNeeded(room.roomId); + MatrixClientPeg.get().loadRoomMembersIfNeeded(room.roomId).catch((err) => { + const errorMessage = `Fetching room members for ${this.roomId} failed.` + + " Room members will appear incomplete."; + console.error(errorMessage); + console.error(err); + }); } } } From f79e2eae40b75a87702e6dfcc7bcdb9176b6b4ef Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Wed, 25 Jul 2018 12:39:46 +0200 Subject: [PATCH 16/45] might not have loaded members here yet --- src/components/views/rooms/RoomPreviewBar.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/components/views/rooms/RoomPreviewBar.js b/src/components/views/rooms/RoomPreviewBar.js index 64c9b376df..5ec19d185e 100644 --- a/src/components/views/rooms/RoomPreviewBar.js +++ b/src/components/views/rooms/RoomPreviewBar.js @@ -98,11 +98,11 @@ module.exports = React.createClass({ ); } - const myMember = this.props.room ? this.props.room.currentState.members[ - MatrixClientPeg.get().credentials.userId - ] : null; - const kicked = myMember.isKicked(); - const banned = myMember && myMember.membership == 'ban'; + const myMember = this.props.room ? + this.props.room.getMember(MatrixClientPeg.get().getUserId()) : + null; + const kicked = myMember && myMember.isKicked(); + const banned = myMember && myMember && myMember.membership == 'ban'; if (this.props.inviterName) { let emailMatchBlock; From 7ea913ccecbae5ccf887e16358bb3faa93bd7f57 Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Wed, 25 Jul 2018 14:14:36 +0200 Subject: [PATCH 17/45] fall back to synced membership when own membership is not yet available (due to lazy loading) --- src/stores/RoomListStore.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/stores/RoomListStore.js b/src/stores/RoomListStore.js index 8dbfca8875..b95eafa2ab 100644 --- a/src/stores/RoomListStore.js +++ b/src/stores/RoomListStore.js @@ -174,11 +174,11 @@ class RoomListStore extends Store { this._matrixClient.getRooms().forEach((room, index) => { const me = room.getMember(this._matrixClient.credentials.userId); - if (!me) return; + const membership = me ? me.membership : room.getSyncedMembership(); - if (me.membership == "invite") { + if (membership == "invite") { lists["im.vector.fake.invite"].push(room); - } else if (me.membership == "join" || me.membership === "ban" || me.isKicked()) { + } else if (membership == "join" || membership === "ban" || me.isKicked()) { // Used to split rooms via tags let tagNames = Object.keys(room.tags); @@ -205,10 +205,10 @@ class RoomListStore extends Store { } else { lists["im.vector.fake.recent"].push(room); } - } else if (me.membership === "leave") { + } else if (membership === "leave") { lists["im.vector.fake.archived"].push(room); } else { - console.error("unrecognised membership: " + me.membership + " - this should never happen"); + console.error("unrecognised membership: " + membership + " - this should never happen"); } }); From d87d34030aff688b6cf811e540f6bea2e3ff64da Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Wed, 25 Jul 2018 14:54:10 +0200 Subject: [PATCH 18/45] fix dm detection and conf call code with lazy loading --- src/Rooms.js | 46 ++++++++++++++------------ src/components/views/rooms/RoomList.js | 4 +-- src/stores/RoomListStore.js | 3 +- src/utils/DMRoomMap.js | 3 +- 4 files changed, 29 insertions(+), 27 deletions(-) diff --git a/src/Rooms.js b/src/Rooms.js index aa025d7dc5..2fda46450f 100644 --- a/src/Rooms.js +++ b/src/Rooms.js @@ -31,26 +31,26 @@ export function getDisplayAliasForRoom(room) { * If the room contains only two members including the logged-in user, * return the other one. Otherwise, return null. */ -export function getOnlyOtherMember(room, me) { - const joinedMembers = room.getJoinedMembers(); +export function getOnlyOtherMember(room, myUserId) { - if (joinedMembers.length === 2) { - return joinedMembers.filter(function(m) { - return m.userId !== me.userId; + if (room.currentState.getJoinedMemberCount() === 2) { + return room.getJoinedMembers().filter(function(m) { + return m.userId !== myUserId; })[0]; } return null; } -function _isConfCallRoom(room, me, conferenceHandler) { +function _isConfCallRoom(room, myUserId, conferenceHandler) { if (!conferenceHandler) return false; - if (me.membership != "join") { + const myMembership = room.getMyMembership(myUserId); + if (myMembership != "join") { return false; } - const otherMember = getOnlyOtherMember(room, me); + const otherMember = getOnlyOtherMember(room, myUserId); if (otherMember === null) { return false; } @@ -68,28 +68,30 @@ const isConfCallRoomCache = { // $roomId: bool }; -export function isConfCallRoom(room, me, conferenceHandler) { +export function isConfCallRoom(room, myUserId, conferenceHandler) { if (isConfCallRoomCache[room.roomId] !== undefined) { return isConfCallRoomCache[room.roomId]; } - const result = _isConfCallRoom(room, me, conferenceHandler); + const result = _isConfCallRoom(room, myUserId, conferenceHandler); isConfCallRoomCache[room.roomId] = result; return result; } -export function looksLikeDirectMessageRoom(room, me) { - if (me.membership == "join" || me.membership === "ban" || me.isKicked()) { +export function looksLikeDirectMessageRoom(room, myUserId) { + const myMembership = room.getMyMembership(myUserId); + const me = room.getMember(myUserId); + + if (myMembership == "join" || myMembership === "ban" || (me && me.isKicked())) { // Used to split rooms via tags const tagNames = Object.keys(room.tags); // Used for 1:1 direct chats - const members = room.currentState.getMembers(); - // Show 1:1 chats in seperate "Direct Messages" section as long as they haven't // been moved to a different tag section - if (members.length === 2 && !tagNames.length) { + // TODO: Use SUMMARYAPI to take invited users into account + if (room.currentState.getJoinedMemberCount() === 2 && !tagNames.length) { return true; } } @@ -99,10 +101,10 @@ export function looksLikeDirectMessageRoom(room, me) { export function guessAndSetDMRoom(room, isDirect) { let newTarget; if (isDirect) { - const guessedTarget = guessDMRoomTarget( - room, room.getMember(MatrixClientPeg.get().credentials.userId), + const guessedUserId = guessDMRoomTargetId( + room, MatrixClientPeg.get().getUserId() ); - newTarget = guessedTarget.userId; + newTarget = guessedUserId; } else { newTarget = null; } @@ -158,13 +160,13 @@ export function setDMRoom(roomId, userId) { * Given a room, estimate which of its members is likely to * be the target if the room were a DM room and return that user. */ -export function guessDMRoomTarget(room, me) { +function guessDMRoomTargetId(room, myUserId) { let oldestTs; let oldestUser; // Pick the joined user who's been here longest (and isn't us), for (const user of room.getJoinedMembers()) { - if (user.userId == me.userId) continue; + if (user.userId == myUserId) continue; if (oldestTs === undefined || (user.events.member && user.events.member.getTs() < oldestTs)) { oldestUser = user; @@ -175,7 +177,7 @@ export function guessDMRoomTarget(room, me) { // if there are no joined members other than us, use the oldest member for (const user of room.currentState.getMembers()) { - if (user.userId == me.userId) continue; + if (user.userId == myUserId) continue; if (oldestTs === undefined || (user.events.member && user.events.member.getTs() < oldestTs)) { oldestUser = user; @@ -183,6 +185,6 @@ export function guessDMRoomTarget(room, me) { } } - if (oldestUser === undefined) return me; + if (oldestUser === undefined) return myUserId; return oldestUser; } diff --git a/src/components/views/rooms/RoomList.js b/src/components/views/rooms/RoomList.js index 8533e3f61a..9d48ed32c9 100644 --- a/src/components/views/rooms/RoomList.js +++ b/src/components/views/rooms/RoomList.js @@ -342,8 +342,8 @@ module.exports = React.createClass({ if (!taggedRoom) { return; } - const me = taggedRoom.getMember(MatrixClientPeg.get().credentials.userId); - if (HIDE_CONFERENCE_CHANS && Rooms.isConfCallRoom(taggedRoom, me, this.props.ConferenceHandler)) { + const myUserId = MatrixClientPeg.get().getUserId(); + if (HIDE_CONFERENCE_CHANS && Rooms.isConfCallRoom(taggedRoom, myUserId, this.props.ConferenceHandler)) { return; } diff --git a/src/stores/RoomListStore.js b/src/stores/RoomListStore.js index b95eafa2ab..38ad7e0b3d 100644 --- a/src/stores/RoomListStore.js +++ b/src/stores/RoomListStore.js @@ -173,8 +173,7 @@ class RoomListStore extends Store { if (!this._matrixClient) return; this._matrixClient.getRooms().forEach((room, index) => { - const me = room.getMember(this._matrixClient.credentials.userId); - const membership = me ? me.membership : room.getSyncedMembership(); + const membership = room.getMyMembership(this._matrixClient.getUserId()); if (membership == "invite") { lists["im.vector.fake.invite"].push(room); diff --git a/src/utils/DMRoomMap.js b/src/utils/DMRoomMap.js index d47282f370..acb1573cbf 100644 --- a/src/utils/DMRoomMap.js +++ b/src/utils/DMRoomMap.js @@ -96,9 +96,10 @@ export default class DMRoomMap { if (this.roomToUser[roomId] === undefined) { // no entry? if the room is an invite, look for the is_direct hint. const room = this.matrixClient.getRoom(roomId); + // TODO Use SUMMARYAPI to fix DM detection? if (room) { const me = room.getMember(this.matrixClient.getUserId()); - return me.getDMInviter(); + return me && me.getDMInviter(); } } return this.roomToUser[roomId]; From cfd20c7e85c75671bf408c8eea6e3114bd78696d Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Wed, 25 Jul 2018 16:08:44 +0200 Subject: [PATCH 19/45] fix error/tests --- src/stores/RoomListStore.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/stores/RoomListStore.js b/src/stores/RoomListStore.js index 38ad7e0b3d..e415389423 100644 --- a/src/stores/RoomListStore.js +++ b/src/stores/RoomListStore.js @@ -173,11 +173,13 @@ class RoomListStore extends Store { if (!this._matrixClient) return; this._matrixClient.getRooms().forEach((room, index) => { - const membership = room.getMyMembership(this._matrixClient.getUserId()); + const myUserId = this._matrixClient.getUserId(); + const membership = room.getMyMembership(myUserId); + const me = room.getMember(myUserId); if (membership == "invite") { lists["im.vector.fake.invite"].push(room); - } else if (membership == "join" || membership === "ban" || me.isKicked()) { + } else if (membership == "join" || membership === "ban" || (me && me.isKicked())) { // Used to split rooms via tags let tagNames = Object.keys(room.tags); From b9bbb7ee16c300bd2bb3741365a8929de9bfa1fb Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Thu, 2 Aug 2018 11:40:25 +0200 Subject: [PATCH 20/45] pass in userId with room in test --- src/VectorConferenceHandler.js | 2 +- test/components/views/rooms/RoomList-test.js | 15 ++++++++------- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/src/VectorConferenceHandler.js b/src/VectorConferenceHandler.js index 19081726b2..af6659d343 100644 --- a/src/VectorConferenceHandler.js +++ b/src/VectorConferenceHandler.js @@ -84,7 +84,7 @@ ConferenceCall.prototype._getConferenceUserRoom = function() { preset: "private_chat", invite: [this.confUserId] }).then(function(res) { - return new Room(res.room_id); + return new Room(res.room_id, client.getUserId()); }); }; diff --git a/test/components/views/rooms/RoomList-test.js b/test/components/views/rooms/RoomList-test.js index c0a0b8eb67..8a596d4151 100644 --- a/test/components/views/rooms/RoomList-test.js +++ b/test/components/views/rooms/RoomList-test.js @@ -20,15 +20,16 @@ function generateRoomId() { return '!' + Math.random().toString().slice(2, 10) + ':domain'; } -function createRoom(opts) { - const room = new Room(generateRoomId()); - if (opts) { - Object.assign(room, opts); - } - return room; -} describe('RoomList', () => { + function createRoom(opts) { + const room = new Room(generateRoomId(), client.getUserId()); + if (opts) { + Object.assign(room, opts); + } + return room; + } + let parentDiv = null; let sandbox = null; let client = null; From 908de56c6da45f4a398c025c639935157b90dd28 Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Thu, 2 Aug 2018 11:42:05 +0200 Subject: [PATCH 21/45] replace getMember(myId).membership with getMyMembership This works with rooms which haven't had their members loaded yet. --- src/RoomInvite.js | 3 +-- src/components/structures/RoomView.js | 3 ++- .../views/dialogs/ChatCreateOrReuseDialog.js | 6 +++--- src/components/views/rooms/MemberInfo.js | 11 ++++++----- src/components/views/rooms/RoomTile.js | 6 ++---- src/utils/WidgetUtils.js | 3 +-- 6 files changed, 15 insertions(+), 17 deletions(-) diff --git a/src/RoomInvite.js b/src/RoomInvite.js index 3a9088e65f..a96d1b2f6b 100644 --- a/src/RoomInvite.js +++ b/src/RoomInvite.js @@ -194,8 +194,7 @@ function _getDirectMessageRooms(addr) { const rooms = dmRooms.filter((dmRoom) => { const room = MatrixClientPeg.get().getRoom(dmRoom); if (room) { - const me = room.getMember(MatrixClientPeg.get().credentials.userId); - return me && me.membership == 'join'; + return room.getMyMembership() === 'join'; } }); return rooms; diff --git a/src/components/structures/RoomView.js b/src/components/structures/RoomView.js index c742975377..5135936a28 100644 --- a/src/components/structures/RoomView.js +++ b/src/components/structures/RoomView.js @@ -759,7 +759,8 @@ module.exports = React.createClass({ _updateDMState() { const me = this.state.room.getMember(MatrixClientPeg.get().getUserId()); - if (!me || me.membership !== "join") { + const room = this.state.room; + if (room.getMyMembership() != "join") { return; } const roomId = this.state.room.roomId; diff --git a/src/components/views/dialogs/ChatCreateOrReuseDialog.js b/src/components/views/dialogs/ChatCreateOrReuseDialog.js index b7cf0f5a6e..550abe5299 100644 --- a/src/components/views/dialogs/ChatCreateOrReuseDialog.js +++ b/src/components/views/dialogs/ChatCreateOrReuseDialog.js @@ -54,8 +54,8 @@ export default class ChatCreateOrReuseDialog extends React.Component { for (const roomId of dmRooms) { const room = client.getRoom(roomId); if (room) { - const me = room.getMember(client.credentials.userId); - const highlight = room.getUnreadNotificationCount('highlight') > 0 || me.membership === "invite"; + const isInvite = room.getMyMembership() === "invite"; + const highlight = room.getUnreadNotificationCount('highlight') > 0 || isInvite; tiles.push( , ); diff --git a/src/components/views/rooms/MemberInfo.js b/src/components/views/rooms/MemberInfo.js index 8b465cef9d..794e1e5cfd 100644 --- a/src/components/views/rooms/MemberInfo.js +++ b/src/components/views/rooms/MemberInfo.js @@ -774,15 +774,16 @@ module.exports = withMatrixClient(React.createClass({ for (const roomId of dmRooms) { const room = this.props.matrixClient.getRoom(roomId); if (room) { - const me = room.getMember(this.props.matrixClient.credentials.userId); - + const myMembership = room.getMyMembership(); + if (myMembership !== 'join') continue; + + const isInvite = myMembership === "invite"; // not a DM room if we have are not joined - if (!me.membership || me.membership !== 'join') continue; // not a DM room if they are not joined const them = this.props.member; if (!them.membership || them.membership !== 'join') continue; - const highlight = room.getUnreadNotificationCount('highlight') > 0 || me.membership === 'invite'; + const highlight = room.getUnreadNotificationCount('highlight') > 0 || isInvite; tiles.push( , ); diff --git a/src/components/views/rooms/RoomTile.js b/src/components/views/rooms/RoomTile.js index ee7f8a76c7..d73f9b5ccf 100644 --- a/src/components/views/rooms/RoomTile.js +++ b/src/components/views/rooms/RoomTile.js @@ -243,9 +243,7 @@ module.exports = React.createClass({ }, render: function() { - const myUserId = MatrixClientPeg.get().credentials.userId; - const me = this.props.room.currentState.members[myUserId]; - + const isInvite = this.props.room.getMyMembership() === "invite"; const notificationCount = this.state.notificationCount; // var highlightCount = this.props.room.getUnreadNotificationCount("highlight"); @@ -259,7 +257,7 @@ module.exports = React.createClass({ 'mx_RoomTile_unread': this.props.unread, 'mx_RoomTile_unreadNotify': notifBadges, 'mx_RoomTile_highlight': mentionBadges, - 'mx_RoomTile_invited': (me && me.membership === 'invite'), + 'mx_RoomTile_invited': isInvite, 'mx_RoomTile_menuDisplayed': this.state.menuDisplayed, 'mx_RoomTile_noBadges': !badges, 'mx_RoomTile_transparent': this.props.transparent, diff --git a/src/utils/WidgetUtils.js b/src/utils/WidgetUtils.js index 008bed1005..b5a2ae31fb 100644 --- a/src/utils/WidgetUtils.js +++ b/src/utils/WidgetUtils.js @@ -77,8 +77,7 @@ export default class WidgetUtils { return false; } - const member = room.getMember(me); - if (!member || member.membership !== "join") { + if (room.getMyMembership() !== "join") { console.warn(`User ${me} is not in room ${roomId}`); return false; } From b151956f7b15f9ab7e252155f88b9f11d90518be Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Thu, 2 Aug 2018 11:42:19 +0200 Subject: [PATCH 22/45] Use room method to help with DM detection with fallback to summary heroes/counts. --- src/components/structures/RoomView.js | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/components/structures/RoomView.js b/src/components/structures/RoomView.js index 5135936a28..08bed976cc 100644 --- a/src/components/structures/RoomView.js +++ b/src/components/structures/RoomView.js @@ -758,16 +758,13 @@ module.exports = React.createClass({ }, _updateDMState() { - const me = this.state.room.getMember(MatrixClientPeg.get().getUserId()); const room = this.state.room; if (room.getMyMembership() != "join") { return; } - const roomId = this.state.room.roomId; - const dmInviter = me.getDMInviter(); - + const dmInviter = room.getDMInviter(); if (dmInviter) { - Rooms.setDMRoom(roomId, dmInviter); + Rooms.setDMRoom(room.roomId, dmInviter); } }, From 802efc76944192354cd58a9ff0c8191bd4c82db3 Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Thu, 2 Aug 2018 11:47:24 +0200 Subject: [PATCH 23/45] given the != join check, isInvite will never be true Also, put comments in right order --- src/components/views/rooms/MemberInfo.js | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/components/views/rooms/MemberInfo.js b/src/components/views/rooms/MemberInfo.js index 794e1e5cfd..c635f09e2c 100644 --- a/src/components/views/rooms/MemberInfo.js +++ b/src/components/views/rooms/MemberInfo.js @@ -775,15 +775,14 @@ module.exports = withMatrixClient(React.createClass({ const room = this.props.matrixClient.getRoom(roomId); if (room) { const myMembership = room.getMyMembership(); + // not a DM room if we have are not joined if (myMembership !== 'join') continue; - const isInvite = myMembership === "invite"; - // not a DM room if we have are not joined - // not a DM room if they are not joined const them = this.props.member; + // not a DM room if they are not joined if (!them.membership || them.membership !== 'join') continue; - const highlight = room.getUnreadNotificationCount('highlight') > 0 || isInvite; + const highlight = room.getUnreadNotificationCount('highlight') > 0; tiles.push( , ); From 4b96e01410bebeb883bf393004468bd81d7868c9 Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Thu, 2 Aug 2018 09:49:54 +0200 Subject: [PATCH 24/45] enable newly introduced option --- src/MatrixClientPeg.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/MatrixClientPeg.js b/src/MatrixClientPeg.js index 82d18d307d..e8ef689294 100644 --- a/src/MatrixClientPeg.js +++ b/src/MatrixClientPeg.js @@ -108,6 +108,7 @@ class MatrixClientPeg { opts.pendingEventOrdering = "detached"; if (SettingsStore.isFeatureEnabled('feature_lazyloading')) { + opts.lazyLoadMembers = true; opts.filter = await this.matrixClient.createFilter(FILTER_CONTENT); } From e1843601becdeaa89eef2a264c1b027c648bd65a Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Thu, 2 Aug 2018 17:09:18 +0200 Subject: [PATCH 25/45] support directional content in pill member fake --- src/components/views/elements/Pill.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/components/views/elements/Pill.js b/src/components/views/elements/Pill.js index f3b6d4e32a..38847a3584 100644 --- a/src/components/views/elements/Pill.js +++ b/src/components/views/elements/Pill.js @@ -187,6 +187,9 @@ const Pill = React.createClass({ getContent: () => { return {avatar_url: resp.avatar_url}; }, + getDirectionalContent: function() { + return this.getContent(); + } }; this.setState({member}); }).catch((err) => { From 42fc83d2a84ddd3ce7919b37627cda1b9ea6117f Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Thu, 2 Aug 2018 19:38:41 +0200 Subject: [PATCH 26/45] Move LL filter creation inside MatrixClient As we need an option to turn lazy loading on (we can't just accept a filter, as /messages has an incompatible filter), better only pass the option and create the filter inside startClient --- src/MatrixClientPeg.js | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/src/MatrixClientPeg.js b/src/MatrixClientPeg.js index e8ef689294..f5872812de 100644 --- a/src/MatrixClientPeg.js +++ b/src/MatrixClientPeg.js @@ -34,14 +34,6 @@ interface MatrixClientCreds { guest: boolean, } -const FILTER_CONTENT = { - room: { - state: { - lazy_load_members: true, - }, - }, -}; - /** * Wrapper object for handling the js-sdk Matrix Client object in the react-sdk * Handles the creation/initialisation of client objects. @@ -109,7 +101,6 @@ class MatrixClientPeg { if (SettingsStore.isFeatureEnabled('feature_lazyloading')) { opts.lazyLoadMembers = true; - opts.filter = await this.matrixClient.createFilter(FILTER_CONTENT); } try { @@ -128,7 +119,7 @@ class MatrixClientPeg { MatrixActionCreators.start(this.matrixClient); console.log(`MatrixClientPeg: really starting MatrixClient`); - this.get().startClient(opts); + await this.get().startClient(opts); console.log(`MatrixClientPeg: MatrixClient started`); } From f1643f77ac44ba24e9b6654c4d45fb2a14f984ce Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Thu, 2 Aug 2018 19:57:20 +0200 Subject: [PATCH 27/45] fix lint --- src/components/views/elements/Pill.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/views/elements/Pill.js b/src/components/views/elements/Pill.js index 38847a3584..e06ed5a22f 100644 --- a/src/components/views/elements/Pill.js +++ b/src/components/views/elements/Pill.js @@ -189,7 +189,7 @@ const Pill = React.createClass({ }, getDirectionalContent: function() { return this.getContent(); - } + }, }; this.setState({member}); }).catch((err) => { From 1009bc43aec17e0bc4f2e2f2c6fe24cca82c3576 Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Tue, 7 Aug 2018 14:47:06 +0200 Subject: [PATCH 28/45] call method on room now to LL members --- src/components/structures/RoomView.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/structures/RoomView.js b/src/components/structures/RoomView.js index 08bed976cc..855090873f 100644 --- a/src/components/structures/RoomView.js +++ b/src/components/structures/RoomView.js @@ -317,7 +317,7 @@ module.exports = React.createClass({ // lazy load members if enabled if (SettingsStore.isFeatureEnabled('feature_lazyloading')) { - MatrixClientPeg.get().loadRoomMembersIfNeeded(room.roomId).catch((err) => { + room.loadMembersIfNeeded().catch((err) => { const errorMessage = `Fetching room members for ${this.roomId} failed.` + " Room members will appear incomplete."; console.error(errorMessage); From 1f8e3e5d43f45c57b69d6b51065f61b0f998ade0 Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Tue, 7 Aug 2018 14:48:07 +0200 Subject: [PATCH 29/45] await room.getEncryptionTargetMembers in cryptodevices --- src/cryptodevices.js | 29 ++++++++++++++--------------- 1 file changed, 14 insertions(+), 15 deletions(-) diff --git a/src/cryptodevices.js b/src/cryptodevices.js index 92237ce7f5..246fae3d73 100644 --- a/src/cryptodevices.js +++ b/src/cryptodevices.js @@ -43,27 +43,26 @@ export function markAllDevicesKnown(matrixClient, devices) { * @return {Promise} A promise which resolves to a map userId->deviceId->{@link * module:crypto~DeviceInfo|DeviceInfo}. */ -export function getUnknownDevicesForRoom(matrixClient, room) { - const roomMembers = room.getEncryptionTargetMembers().map((m) => { +export async function getUnknownDevicesForRoom(matrixClient, room) { + const roomMembers = await room.getEncryptionTargetMembers().map((m) => { return m.userId; }); - return matrixClient.downloadKeys(roomMembers, false).then((devices) => { - const unknownDevices = {}; - // This is all devices in this room, so find the unknown ones. - Object.keys(devices).forEach((userId) => { - Object.keys(devices[userId]).map((deviceId) => { - const device = devices[userId][deviceId]; + const devices = await matrixClient.downloadKeys(roomMembers, false); + const unknownDevices = {}; + // This is all devices in this room, so find the unknown ones. + Object.keys(devices).forEach((userId) => { + Object.keys(devices[userId]).map((deviceId) => { + const device = devices[userId][deviceId]; - if (device.isUnverified() && !device.isKnown()) { - if (unknownDevices[userId] === undefined) { - unknownDevices[userId] = {}; - } - unknownDevices[userId][deviceId] = device; + if (device.isUnverified() && !device.isKnown()) { + if (unknownDevices[userId] === undefined) { + unknownDevices[userId] = {}; } - }); + unknownDevices[userId][deviceId] = device; + } }); - return unknownDevices; }); + return unknownDevices; } function focusComposer() { From 90fc15ff3e0fdaf2e06d9889e51253d46bc7110e Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Tue, 7 Aug 2018 15:33:37 +0200 Subject: [PATCH 30/45] adjust room constructor --- src/VectorConferenceHandler.js | 2 +- test/components/views/rooms/RoomList-test.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/VectorConferenceHandler.js b/src/VectorConferenceHandler.js index af6659d343..9ba46b2ab6 100644 --- a/src/VectorConferenceHandler.js +++ b/src/VectorConferenceHandler.js @@ -84,7 +84,7 @@ ConferenceCall.prototype._getConferenceUserRoom = function() { preset: "private_chat", invite: [this.confUserId] }).then(function(res) { - return new Room(res.room_id, client.getUserId()); + return new Room(res.room_id, null, client.getUserId()); }); }; diff --git a/test/components/views/rooms/RoomList-test.js b/test/components/views/rooms/RoomList-test.js index 8a596d4151..1e9f80f161 100644 --- a/test/components/views/rooms/RoomList-test.js +++ b/test/components/views/rooms/RoomList-test.js @@ -23,7 +23,7 @@ function generateRoomId() { describe('RoomList', () => { function createRoom(opts) { - const room = new Room(generateRoomId(), client.getUserId()); + const room = new Room(generateRoomId(), null, client.getUserId()); if (opts) { Object.assign(room, opts); } From 308a6b419e666c19af9dd3a67ccaee7b11c3dbc2 Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Thu, 9 Aug 2018 16:56:02 +0200 Subject: [PATCH 31/45] allow a setting controller to validate and revert a change (asynchronously) --- src/components/structures/UserSettings.js | 6 +++--- src/settings/SettingsStore.js | 20 ++++++++++++++----- src/settings/controllers/SettingController.js | 4 ++++ 3 files changed, 22 insertions(+), 8 deletions(-) diff --git a/src/components/structures/UserSettings.js b/src/components/structures/UserSettings.js index d02d8b23e5..701247bd8b 100644 --- a/src/components/structures/UserSettings.js +++ b/src/components/structures/UserSettings.js @@ -844,8 +844,8 @@ module.exports = React.createClass({ SettingsStore.getLabsFeatures().forEach((featureId) => { // TODO: this ought to be a separate component so that we don't need // to rebind the onChange each time we render - const onChange = (e) => { - SettingsStore.setFeatureEnabled(featureId, e.target.checked); + const onChange = async (e) => { + await SettingsStore.setFeatureEnabled(featureId, e.target.checked); this.forceUpdate(); }; @@ -855,7 +855,7 @@ module.exports = React.createClass({ type="checkbox" id={featureId} name={featureId} - defaultChecked={SettingsStore.isFeatureEnabled(featureId)} + checked={SettingsStore.isFeatureEnabled(featureId)} onChange={onChange} /> diff --git a/src/settings/SettingsStore.js b/src/settings/SettingsStore.js index a1b88fb0c2..1da882577f 100644 --- a/src/settings/SettingsStore.js +++ b/src/settings/SettingsStore.js @@ -260,7 +260,7 @@ export default class SettingsStore { * @param {*} value The new value of the setting, may be null. * @return {Promise} Resolves when the setting has been changed. */ - static setValue(settingName, roomId, level, value) { + static async setValue(settingName, roomId, level, value) { // Verify that the setting is actually a setting if (!SETTINGS[settingName]) { throw new Error("Setting '" + settingName + "' does not appear to be a setting."); @@ -275,11 +275,21 @@ export default class SettingsStore { throw new Error("User cannot set " + settingName + " at " + level + " in " + roomId); } - return handler.setValue(settingName, roomId, value).then(() => { - const controller = SETTINGS[settingName].controller; - if (!controller) return; + const controller = SETTINGS[settingName].controller; + if (controller) { + const changeAllowed = await controller.canChangeTo(level, roomId, value); + if (!changeAllowed) { + return false; + } + } + + await handler.setValue(settingName, roomId, value); + + if (controller) { controller.onChange(level, roomId, value); - }); + } + + return true; } /** diff --git a/src/settings/controllers/SettingController.js b/src/settings/controllers/SettingController.js index 0ebe0042e6..6b09c4fc53 100644 --- a/src/settings/controllers/SettingController.js +++ b/src/settings/controllers/SettingController.js @@ -39,6 +39,10 @@ export default class SettingController { return null; // no override } + canChangeTo(level, roomId, newValue) { + return Promise.resolve(true); + } + /** * Called when the setting value has been changed. * @param {string} level The level at which the setting has been modified. From 028f09e5d15a223f87d564318dee98ee9b43db4f Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Thu, 9 Aug 2018 18:39:55 +0200 Subject: [PATCH 32/45] lazy loading settings controller to show dialog and export e2e keys --- .../controllers/LazyLoadingController.js | 66 +++++++++++++++++++ 1 file changed, 66 insertions(+) create mode 100644 src/settings/controllers/LazyLoadingController.js diff --git a/src/settings/controllers/LazyLoadingController.js b/src/settings/controllers/LazyLoadingController.js new file mode 100644 index 0000000000..506a1c7f2b --- /dev/null +++ b/src/settings/controllers/LazyLoadingController.js @@ -0,0 +1,66 @@ +/* +Copyright 2018 New Vector + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +import SettingsStore from "../SettingsStore"; +import SettingController from "./SettingController"; +import Modal from "../../Modal"; +import sdk from "../../index"; +import MatrixClientPeg from "../../MatrixClientPeg"; +import dis from "../../dispatcher"; +import { _t } from "../../languageHandler"; + +export default class LazyLoadingController extends SettingController { + onChange(level, roomId, newValue) { + dis.dispatch({action: 'flush_storage_reload'}); + } + + canChangeTo(level, roomId, newValue) { + return new Promise((resolve) => this._showReloadDialog(resolve)); + } + + _showReloadDialog(onFinished) { + const QuestionDialog = sdk.getComponent("dialogs.QuestionDialog"); + Modal.createDialog(QuestionDialog, { + title: _t("Turn on/off lazy load members"), + description: +
+ { _t("To enable or disable the lazy loading of members, " + + "the current synced state needs to be cleared out. " + + "This also includes your end-to-end encryption keys, " + + "so to keep being able to decrypt all your existing encrypted messages, " + + "you'll need to export your E2E room keys and import them again afterwards.") } +
, + button: _t("Clear sync state and reload"), + extraButtons: [ + , + ], + onFinished, + }); + } + + _onExportE2eKeysClicked() { + Modal.createTrackedDialogAsync('Export E2E Keys', '', (cb) => { + require.ensure(['../../async-components/views/dialogs/ExportE2eKeysDialog'], () => { + cb(require('../../async-components/views/dialogs/ExportE2eKeysDialog')); + }, "e2e-export"); + }, { + matrixClient: MatrixClientPeg.get(), + }); + } +} From a02e1aa2b240c2f0d25018207e76e7106661649d Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Thu, 9 Aug 2018 18:41:18 +0200 Subject: [PATCH 33/45] add action/method to flush storage and reload the session --- src/Lifecycle.js | 29 +++++++++++++++++++++++++ src/components/structures/MatrixChat.js | 8 +++++++ 2 files changed, 37 insertions(+) diff --git a/src/Lifecycle.js b/src/Lifecycle.js index f32f105889..4d62e6bb46 100644 --- a/src/Lifecycle.js +++ b/src/Lifecycle.js @@ -499,3 +499,32 @@ export function stopMatrixClient() { MatrixClientPeg.unset(); } } +/** + * Clears indexeddb storage and reloads the session + * localStorage is preserved. + */ +export async function flushStorageAndReload() { + // gather the credentials from localStorage as + // MatrixClient.credentials only contains the userId + // (gets cleared somewhere probably) + // this just does the reverse of _persistCredentialsToLocalStorage + const credentials = { + homeserverUrl: localStorage.getItem("mx_hs_url"), + identityServerUrl: localStorage.getItem("mx_is_url"), + userId: localStorage.getItem("mx_user_id"), + accessToken: localStorage.getItem("mx_access_token"), + guest: JSON.parse(localStorage.getItem("mx_is_guest")), + deviceId: localStorage.getItem("mx_device_id"), + }; + // stop the client so it's not running anymore + stopMatrixClient(); + // create a temporary client to clear out the persistent stores. + const cli = createMatrixClient({ + // we'll never make any requests, so can pass a bogus HS URL + baseUrl: "", + }); + // clear indexeddb + await cli.clearStores(); + // start the session again without clearing storage + _doSetLoggedIn(credentials, false); +} \ No newline at end of file diff --git a/src/components/structures/MatrixChat.js b/src/components/structures/MatrixChat.js index e0bbf50d5a..dc2c1cb90b 100644 --- a/src/components/structures/MatrixChat.js +++ b/src/components/structures/MatrixChat.js @@ -479,6 +479,14 @@ export default React.createClass({ case 'logout': Lifecycle.logout(); break; + case 'flush_storage_reload': + // flushStorageAndReload will dispatch actions + // which we can't do in a dispatch handler + // so yield first + setTimeout(() => { + Lifecycle.flushStorageAndReload(); + }, 0); + break; case 'start_registration': this._startRegistration(payload.params || {}); break; From 3731431e59387423c0c019fd638d8d280c6f2018 Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Thu, 9 Aug 2018 18:41:41 +0200 Subject: [PATCH 34/45] use LL controller in setting --- src/settings/Settings.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/settings/Settings.js b/src/settings/Settings.js index d76c1fd8e8..76e002adac 100644 --- a/src/settings/Settings.js +++ b/src/settings/Settings.js @@ -21,7 +21,7 @@ import { NotificationBodyEnabledController, NotificationsEnabledController, } from "./controllers/NotificationControllers"; - +import LazyLoadingController from "./controllers/LazyLoadingController"; // These are just a bunch of helper arrays to avoid copy/pasting a bunch of times const LEVELS_ROOM_SETTINGS = ['device', 'room-device', 'room-account', 'account', 'config']; @@ -87,6 +87,8 @@ export const SETTINGS = { isFeature: true, displayName: _td("Increase performance by loading room members on first view"), supportedLevels: LEVELS_FEATURE, + controller: new LazyLoadingController(), + default: false, }, "MessageComposerInput.dontSuggestEmoji": { supportedLevels: LEVELS_ACCOUNT_SETTINGS, From 28292c338873c4eb91aacd3b68ff80c7a23e28d5 Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Mon, 13 Aug 2018 11:14:30 +0200 Subject: [PATCH 35/45] implement PR feedback, move LL dialog to UserSettings --- src/components/structures/UserSettings.js | 35 +++++++++++++++++- src/settings/SettingsStore.js | 11 +----- .../controllers/LazyLoadingController.js | 37 ------------------- src/settings/controllers/SettingController.js | 5 --- 4 files changed, 35 insertions(+), 53 deletions(-) diff --git a/src/components/structures/UserSettings.js b/src/components/structures/UserSettings.js index 701247bd8b..a5ba4ff0fa 100644 --- a/src/components/structures/UserSettings.js +++ b/src/components/structures/UserSettings.js @@ -845,7 +845,15 @@ module.exports = React.createClass({ // TODO: this ought to be a separate component so that we don't need // to rebind the onChange each time we render const onChange = async (e) => { - await SettingsStore.setFeatureEnabled(featureId, e.target.checked); + const checked = e.target.checked; + if (featureId === "feature_lazyloading") { + const confirmed = await this._onLazyLoadChanging(); + if (!confirmed) { + e.preventDefault(); + return; + } + } + await SettingsStore.setFeatureEnabled(featureId, checked); this.forceUpdate(); }; @@ -878,6 +886,31 @@ module.exports = React.createClass({ ); }, + _onLazyLoadChanging: function() { + return new Promise((resolve) => { + const QuestionDialog = sdk.getComponent("dialogs.QuestionDialog"); + Modal.createDialog(QuestionDialog, { + title: _t("Turn on/off lazy load members"), + description: +
+ { _t("To enable or disable the lazy loading of members, " + + "the current synced state needs to be cleared out. " + + "This also includes your end-to-end encryption keys, " + + "so to keep being able to decrypt all your existing encrypted messages, " + + "you'll need to export your E2E room keys and import them again afterwards.") } +
, + button: _t("Clear sync state and reload"), + extraButtons: [ + , + ], + onFinished: resolve, + }); + }); + }, + _renderDeactivateAccount: function() { return

{ _t("Deactivate Account") }

diff --git a/src/settings/SettingsStore.js b/src/settings/SettingsStore.js index 1da882577f..76dd89a7f7 100644 --- a/src/settings/SettingsStore.js +++ b/src/settings/SettingsStore.js @@ -275,21 +275,12 @@ export default class SettingsStore { throw new Error("User cannot set " + settingName + " at " + level + " in " + roomId); } - const controller = SETTINGS[settingName].controller; - if (controller) { - const changeAllowed = await controller.canChangeTo(level, roomId, value); - if (!changeAllowed) { - return false; - } - } - await handler.setValue(settingName, roomId, value); + const controller = SETTINGS[settingName].controller; if (controller) { controller.onChange(level, roomId, value); } - - return true; } /** diff --git a/src/settings/controllers/LazyLoadingController.js b/src/settings/controllers/LazyLoadingController.js index 506a1c7f2b..5d592ad6f8 100644 --- a/src/settings/controllers/LazyLoadingController.js +++ b/src/settings/controllers/LazyLoadingController.js @@ -26,41 +26,4 @@ export default class LazyLoadingController extends SettingController { onChange(level, roomId, newValue) { dis.dispatch({action: 'flush_storage_reload'}); } - - canChangeTo(level, roomId, newValue) { - return new Promise((resolve) => this._showReloadDialog(resolve)); - } - - _showReloadDialog(onFinished) { - const QuestionDialog = sdk.getComponent("dialogs.QuestionDialog"); - Modal.createDialog(QuestionDialog, { - title: _t("Turn on/off lazy load members"), - description: -
- { _t("To enable or disable the lazy loading of members, " + - "the current synced state needs to be cleared out. " + - "This also includes your end-to-end encryption keys, " + - "so to keep being able to decrypt all your existing encrypted messages, " + - "you'll need to export your E2E room keys and import them again afterwards.") } -
, - button: _t("Clear sync state and reload"), - extraButtons: [ - , - ], - onFinished, - }); - } - - _onExportE2eKeysClicked() { - Modal.createTrackedDialogAsync('Export E2E Keys', '', (cb) => { - require.ensure(['../../async-components/views/dialogs/ExportE2eKeysDialog'], () => { - cb(require('../../async-components/views/dialogs/ExportE2eKeysDialog')); - }, "e2e-export"); - }, { - matrixClient: MatrixClientPeg.get(), - }); - } } diff --git a/src/settings/controllers/SettingController.js b/src/settings/controllers/SettingController.js index 6b09c4fc53..9dcb6259b5 100644 --- a/src/settings/controllers/SettingController.js +++ b/src/settings/controllers/SettingController.js @@ -38,11 +38,6 @@ export default class SettingController { getValueOverride(level, roomId, calculatedValue, calculatedAtLevel) { return null; // no override } - - canChangeTo(level, roomId, newValue) { - return Promise.resolve(true); - } - /** * Called when the setting value has been changed. * @param {string} level The level at which the setting has been modified. From 09ad138306ae89b032ff32313c6874b652de252e Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Mon, 13 Aug 2018 11:16:29 +0200 Subject: [PATCH 36/45] remove unused imports --- src/settings/controllers/LazyLoadingController.js | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/settings/controllers/LazyLoadingController.js b/src/settings/controllers/LazyLoadingController.js index 5d592ad6f8..334111ef7c 100644 --- a/src/settings/controllers/LazyLoadingController.js +++ b/src/settings/controllers/LazyLoadingController.js @@ -14,13 +14,8 @@ See the License for the specific language governing permissions and limitations under the License. */ -import SettingsStore from "../SettingsStore"; import SettingController from "./SettingController"; -import Modal from "../../Modal"; -import sdk from "../../index"; -import MatrixClientPeg from "../../MatrixClientPeg"; import dis from "../../dispatcher"; -import { _t } from "../../languageHandler"; export default class LazyLoadingController extends SettingController { onChange(level, roomId, newValue) { From 2f9aaeca465063364d182d1568c1ce2de80625bc Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Mon, 13 Aug 2018 11:17:04 +0200 Subject: [PATCH 37/45] undo whitespace change --- src/settings/controllers/SettingController.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/settings/controllers/SettingController.js b/src/settings/controllers/SettingController.js index 9dcb6259b5..0ebe0042e6 100644 --- a/src/settings/controllers/SettingController.js +++ b/src/settings/controllers/SettingController.js @@ -38,6 +38,7 @@ export default class SettingController { getValueOverride(level, roomId, calculatedValue, calculatedAtLevel) { return null; // no override } + /** * Called when the setting value has been changed. * @param {string} level The level at which the setting has been modified. From 94c424d3bb495f271a7ba00a4538b09dfb3e37bc Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Mon, 13 Aug 2018 11:24:32 +0200 Subject: [PATCH 38/45] translations --- src/i18n/strings/en_EN.json | 6 +++++- src/settings/Settings.js | 2 +- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index 9acde2b80c..f1f056f58e 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -1216,5 +1216,9 @@ "Import": "Import", "Failed to set direct chat tag": "Failed to set direct chat tag", "Failed to remove tag %(tagName)s from room": "Failed to remove tag %(tagName)s from room", - "Failed to add tag %(tagName)s to room": "Failed to add tag %(tagName)s to room" + "Failed to add tag %(tagName)s to room": "Failed to add tag %(tagName)s to room", + "Increase performance by only loading room members on first view": "Increase performance by only loading room members on first view", + "Turn on/off lazy load members": "Turn on/off lazy load members", + "To enable or disable the lazy loading of members, the current synced state needs to be cleared out. This also includes your end-to-end encryption keys, so to keep being able to decrypt all your existing encrypted messages, you'll need to export your E2E room keys and import them again afterwards.": "To enable or disable the lazy loading of members, the current synced state needs to be cleared out. This also includes your end-to-end encryption keys, so to keep being able to decrypt all your existing encrypted messages, you'll need to export your E2E room keys and import them again afterwards.", + "Clear sync state and reload": "Clear sync state and reload" } diff --git a/src/settings/Settings.js b/src/settings/Settings.js index 76e002adac..0594c63eb9 100644 --- a/src/settings/Settings.js +++ b/src/settings/Settings.js @@ -85,7 +85,7 @@ export const SETTINGS = { }, "feature_lazyloading": { isFeature: true, - displayName: _td("Increase performance by loading room members on first view"), + displayName: _td("Increase performance by only loading room members on first view"), supportedLevels: LEVELS_FEATURE, controller: new LazyLoadingController(), default: false, From 84b74247e625cc7bbab36db7caca38a18db403b4 Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Mon, 13 Aug 2018 16:19:44 +0200 Subject: [PATCH 39/45] check if server supports lazy loading before enabling --- src/components/structures/UserSettings.js | 28 +++++++++++++++++++---- src/i18n/strings/en_EN.json | 4 +++- 2 files changed, 27 insertions(+), 5 deletions(-) diff --git a/src/components/structures/UserSettings.js b/src/components/structures/UserSettings.js index a5ba4ff0fa..157540f183 100644 --- a/src/components/structures/UserSettings.js +++ b/src/components/structures/UserSettings.js @@ -847,7 +847,7 @@ module.exports = React.createClass({ const onChange = async (e) => { const checked = e.target.checked; if (featureId === "feature_lazyloading") { - const confirmed = await this._onLazyLoadChanging(); + const confirmed = await this._onLazyLoadChanging(checked); if (!confirmed) { e.preventDefault(); return; @@ -886,9 +886,28 @@ module.exports = React.createClass({ ); }, - _onLazyLoadChanging: function() { - return new Promise((resolve) => { - const QuestionDialog = sdk.getComponent("dialogs.QuestionDialog"); + _onLazyLoadChanging: async function(enabling) { + const QuestionDialog = sdk.getComponent("dialogs.QuestionDialog"); + // don't prevent turning LL off when not supported + if (enabling) { + const supported = await MatrixClientPeg.get().doesServerSupportLazyLoading(); + if (!supported) { + await new Promise((resolve) => { + Modal.createDialog(QuestionDialog, { + title: _t("Lazy loading members not supported"), + description: +
+ { _t("Lazy loading is not supported by your " + + "current homeserver.") } +
, + button: _t("OK"), + onFinished: resolve, + }); + }); + return false; + } + } + const confirmed = await new Promise((resolve) => { Modal.createDialog(QuestionDialog, { title: _t("Turn on/off lazy load members"), description: @@ -909,6 +928,7 @@ module.exports = React.createClass({ onFinished: resolve, }); }); + return confirmed; }, _renderDeactivateAccount: function() { diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index f1f056f58e..eb05ff36e7 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -1220,5 +1220,7 @@ "Increase performance by only loading room members on first view": "Increase performance by only loading room members on first view", "Turn on/off lazy load members": "Turn on/off lazy load members", "To enable or disable the lazy loading of members, the current synced state needs to be cleared out. This also includes your end-to-end encryption keys, so to keep being able to decrypt all your existing encrypted messages, you'll need to export your E2E room keys and import them again afterwards.": "To enable or disable the lazy loading of members, the current synced state needs to be cleared out. This also includes your end-to-end encryption keys, so to keep being able to decrypt all your existing encrypted messages, you'll need to export your E2E room keys and import them again afterwards.", - "Clear sync state and reload": "Clear sync state and reload" + "Clear sync state and reload": "Clear sync state and reload", + "Lazy loading members not supported": "Lazy load members not supported", + "Lazy loading is not supported by your current homeserver.": "Lazy loading is not supported by your current homeserver." } From 3e9d1342e83aab8cb7c0b8b1d8558f8aa9192004 Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Mon, 13 Aug 2018 16:51:37 +0200 Subject: [PATCH 40/45] fix lint --- src/Lifecycle.js | 2 +- src/settings/SettingsStore.js | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/Lifecycle.js b/src/Lifecycle.js index 4d62e6bb46..4ae651d3bb 100644 --- a/src/Lifecycle.js +++ b/src/Lifecycle.js @@ -527,4 +527,4 @@ export async function flushStorageAndReload() { await cli.clearStores(); // start the session again without clearing storage _doSetLoggedIn(credentials, false); -} \ No newline at end of file +} diff --git a/src/settings/SettingsStore.js b/src/settings/SettingsStore.js index 76dd89a7f7..f17d47401e 100644 --- a/src/settings/SettingsStore.js +++ b/src/settings/SettingsStore.js @@ -248,7 +248,7 @@ export default class SettingsStore { if (actualValue !== undefined && actualValue !== null) return actualValue; return calculatedValue; } - + /* eslint-disable valid-jsdoc */ /** * Sets the value for a setting. The room ID is optional if the setting is not being * set for a particular room, otherwise it should be supplied. The value may be null @@ -260,6 +260,7 @@ export default class SettingsStore { * @param {*} value The new value of the setting, may be null. * @return {Promise} Resolves when the setting has been changed. */ + /* eslint-enable valid-jsdoc */ static async setValue(settingName, roomId, level, value) { // Verify that the setting is actually a setting if (!SETTINGS[settingName]) { From 08f322753dddf4cf408c24cdddde31250786359d Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Mon, 13 Aug 2018 16:53:16 +0200 Subject: [PATCH 41/45] explain exception --- src/settings/SettingsStore.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/settings/SettingsStore.js b/src/settings/SettingsStore.js index f17d47401e..cb6d83e884 100644 --- a/src/settings/SettingsStore.js +++ b/src/settings/SettingsStore.js @@ -248,7 +248,7 @@ export default class SettingsStore { if (actualValue !== undefined && actualValue !== null) return actualValue; return calculatedValue; } - /* eslint-disable valid-jsdoc */ + /* eslint-disable valid-jsdoc */ //https://github.com/eslint/eslint/issues/7307 /** * Sets the value for a setting. The room ID is optional if the setting is not being * set for a particular room, otherwise it should be supplied. The value may be null From 7dc92fe91729dea9782533747df4532cf79948f2 Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Mon, 13 Aug 2018 17:24:27 +0200 Subject: [PATCH 42/45] Avoid clearing e2e encryption keys, also avoid warning --- src/components/structures/UserSettings.js | 25 ++----------------- src/i18n/strings/en_EN.json | 3 --- .../controllers/LazyLoadingController.js | 11 +++++--- 3 files changed, 10 insertions(+), 29 deletions(-) diff --git a/src/components/structures/UserSettings.js b/src/components/structures/UserSettings.js index 157540f183..f4dc92aca4 100644 --- a/src/components/structures/UserSettings.js +++ b/src/components/structures/UserSettings.js @@ -887,12 +887,12 @@ module.exports = React.createClass({ }, _onLazyLoadChanging: async function(enabling) { - const QuestionDialog = sdk.getComponent("dialogs.QuestionDialog"); // don't prevent turning LL off when not supported if (enabling) { const supported = await MatrixClientPeg.get().doesServerSupportLazyLoading(); if (!supported) { await new Promise((resolve) => { + const QuestionDialog = sdk.getComponent("dialogs.QuestionDialog"); Modal.createDialog(QuestionDialog, { title: _t("Lazy loading members not supported"), description: @@ -907,28 +907,7 @@ module.exports = React.createClass({ return false; } } - const confirmed = await new Promise((resolve) => { - Modal.createDialog(QuestionDialog, { - title: _t("Turn on/off lazy load members"), - description: -
- { _t("To enable or disable the lazy loading of members, " + - "the current synced state needs to be cleared out. " + - "This also includes your end-to-end encryption keys, " + - "so to keep being able to decrypt all your existing encrypted messages, " + - "you'll need to export your E2E room keys and import them again afterwards.") } -
, - button: _t("Clear sync state and reload"), - extraButtons: [ - , - ], - onFinished: resolve, - }); - }); - return confirmed; + return true; }, _renderDeactivateAccount: function() { diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index eb05ff36e7..2926f4c87c 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -1218,9 +1218,6 @@ "Failed to remove tag %(tagName)s from room": "Failed to remove tag %(tagName)s from room", "Failed to add tag %(tagName)s to room": "Failed to add tag %(tagName)s to room", "Increase performance by only loading room members on first view": "Increase performance by only loading room members on first view", - "Turn on/off lazy load members": "Turn on/off lazy load members", - "To enable or disable the lazy loading of members, the current synced state needs to be cleared out. This also includes your end-to-end encryption keys, so to keep being able to decrypt all your existing encrypted messages, you'll need to export your E2E room keys and import them again afterwards.": "To enable or disable the lazy loading of members, the current synced state needs to be cleared out. This also includes your end-to-end encryption keys, so to keep being able to decrypt all your existing encrypted messages, you'll need to export your E2E room keys and import them again afterwards.", - "Clear sync state and reload": "Clear sync state and reload", "Lazy loading members not supported": "Lazy load members not supported", "Lazy loading is not supported by your current homeserver.": "Lazy loading is not supported by your current homeserver." } diff --git a/src/settings/controllers/LazyLoadingController.js b/src/settings/controllers/LazyLoadingController.js index 334111ef7c..90f095c9ca 100644 --- a/src/settings/controllers/LazyLoadingController.js +++ b/src/settings/controllers/LazyLoadingController.js @@ -15,10 +15,15 @@ limitations under the License. */ import SettingController from "./SettingController"; -import dis from "../../dispatcher"; +import MatrixClientPeg from "../../MatrixClientPeg"; +import PlatformPeg from "../../PlatformPeg"; export default class LazyLoadingController extends SettingController { - onChange(level, roomId, newValue) { - dis.dispatch({action: 'flush_storage_reload'}); + async onChange(level, roomId, newValue) { + if (!PlatformPeg.get()) return; + + MatrixClientPeg.get().stopClient(); + await MatrixClientPeg.get().store.deleteAllData(); + PlatformPeg.get().reload(); } } From a86ca952b3cac2370e4fd7cc5f370d93a34a8318 Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Tue, 14 Aug 2018 09:38:43 +0200 Subject: [PATCH 43/45] forgot to remove this --- src/Lifecycle.js | 29 ------------------------- src/components/structures/MatrixChat.js | 8 ------- 2 files changed, 37 deletions(-) diff --git a/src/Lifecycle.js b/src/Lifecycle.js index 4ae651d3bb..f32f105889 100644 --- a/src/Lifecycle.js +++ b/src/Lifecycle.js @@ -499,32 +499,3 @@ export function stopMatrixClient() { MatrixClientPeg.unset(); } } -/** - * Clears indexeddb storage and reloads the session - * localStorage is preserved. - */ -export async function flushStorageAndReload() { - // gather the credentials from localStorage as - // MatrixClient.credentials only contains the userId - // (gets cleared somewhere probably) - // this just does the reverse of _persistCredentialsToLocalStorage - const credentials = { - homeserverUrl: localStorage.getItem("mx_hs_url"), - identityServerUrl: localStorage.getItem("mx_is_url"), - userId: localStorage.getItem("mx_user_id"), - accessToken: localStorage.getItem("mx_access_token"), - guest: JSON.parse(localStorage.getItem("mx_is_guest")), - deviceId: localStorage.getItem("mx_device_id"), - }; - // stop the client so it's not running anymore - stopMatrixClient(); - // create a temporary client to clear out the persistent stores. - const cli = createMatrixClient({ - // we'll never make any requests, so can pass a bogus HS URL - baseUrl: "", - }); - // clear indexeddb - await cli.clearStores(); - // start the session again without clearing storage - _doSetLoggedIn(credentials, false); -} diff --git a/src/components/structures/MatrixChat.js b/src/components/structures/MatrixChat.js index dc2c1cb90b..e0bbf50d5a 100644 --- a/src/components/structures/MatrixChat.js +++ b/src/components/structures/MatrixChat.js @@ -479,14 +479,6 @@ export default React.createClass({ case 'logout': Lifecycle.logout(); break; - case 'flush_storage_reload': - // flushStorageAndReload will dispatch actions - // which we can't do in a dispatch handler - // so yield first - setTimeout(() => { - Lifecycle.flushStorageAndReload(); - }, 0); - break; case 'start_registration': this._startRegistration(payload.params || {}); break; From c8b6ccba2595f604b517223e2c8202b276d2974c Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Tue, 14 Aug 2018 11:43:03 +0200 Subject: [PATCH 44/45] fallback to room summary for dm detection --- src/Rooms.js | 5 +++-- src/utils/DMRoomMap.js | 4 +--- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/src/Rooms.js b/src/Rooms.js index 2fda46450f..024a4f1d8c 100644 --- a/src/Rooms.js +++ b/src/Rooms.js @@ -90,8 +90,9 @@ export function looksLikeDirectMessageRoom(room, myUserId) { // Used for 1:1 direct chats // Show 1:1 chats in seperate "Direct Messages" section as long as they haven't // been moved to a different tag section - // TODO: Use SUMMARYAPI to take invited users into account - if (room.currentState.getJoinedMemberCount() === 2 && !tagNames.length) { + const totalMemberCount = room.currentState.getJoinedMemberCount() + + room.currentState.getInvitedMemberCount(); + if (totalMemberCount === 2 && !tagNames.length) { return true; } } diff --git a/src/utils/DMRoomMap.js b/src/utils/DMRoomMap.js index acb1573cbf..d95eda490d 100644 --- a/src/utils/DMRoomMap.js +++ b/src/utils/DMRoomMap.js @@ -96,10 +96,8 @@ export default class DMRoomMap { if (this.roomToUser[roomId] === undefined) { // no entry? if the room is an invite, look for the is_direct hint. const room = this.matrixClient.getRoom(roomId); - // TODO Use SUMMARYAPI to fix DM detection? if (room) { - const me = room.getMember(this.matrixClient.getUserId()); - return me && me.getDMInviter(); + return room.getDMInviter(); } } return this.roomToUser[roomId]; From 440ebd187da349414520e89202e5c52903d44d82 Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Tue, 14 Aug 2018 11:47:05 +0200 Subject: [PATCH 45/45] getMyMembership() doesn't take id anymore --- src/Rooms.js | 4 ++-- src/stores/RoomListStore.js | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Rooms.js b/src/Rooms.js index 024a4f1d8c..96e7c140bb 100644 --- a/src/Rooms.js +++ b/src/Rooms.js @@ -45,7 +45,7 @@ export function getOnlyOtherMember(room, myUserId) { function _isConfCallRoom(room, myUserId, conferenceHandler) { if (!conferenceHandler) return false; - const myMembership = room.getMyMembership(myUserId); + const myMembership = room.getMyMembership(); if (myMembership != "join") { return false; } @@ -81,7 +81,7 @@ export function isConfCallRoom(room, myUserId, conferenceHandler) { } export function looksLikeDirectMessageRoom(room, myUserId) { - const myMembership = room.getMyMembership(myUserId); + const myMembership = room.getMyMembership(); const me = room.getMember(myUserId); if (myMembership == "join" || myMembership === "ban" || (me && me.isKicked())) { diff --git a/src/stores/RoomListStore.js b/src/stores/RoomListStore.js index e415389423..7eb7b79509 100644 --- a/src/stores/RoomListStore.js +++ b/src/stores/RoomListStore.js @@ -174,7 +174,7 @@ class RoomListStore extends Store { this._matrixClient.getRooms().forEach((room, index) => { const myUserId = this._matrixClient.getUserId(); - const membership = room.getMyMembership(myUserId); + const membership = room.getMyMembership(); const me = room.getMember(myUserId); if (membership == "invite") {