From 9f19896df09a0cdc7d870e8f227e31c89d203890 Mon Sep 17 00:00:00 2001 From: David Baker Date: Fri, 17 Aug 2018 14:54:43 +0100 Subject: [PATCH 01/26] Support for room upgrades * Show when a room upgrade is needed * Dialog box to perform the upgrade --- res/css/_components.scss | 2 + res/css/views/dialogs/_RoomUpgradeDialog.scss | 19 ++++ res/css/views/rooms/_RoomSettings.scss | 6 + .../views/rooms/_RoomUpgradeWarningBar.scss | 48 ++++++++ res/themes/light/css/_base.scss | 4 + src/components/structures/RoomView.js | 9 ++ .../views/dialogs/RoomUpgradeDialog.js | 106 ++++++++++++++++++ src/components/views/rooms/RoomSettings.js | 15 ++- .../views/rooms/RoomUpgradeWarningBar.js | 57 ++++++++++ src/i18n/strings/en_EN.json | 12 +- 10 files changed, 273 insertions(+), 5 deletions(-) create mode 100644 res/css/views/dialogs/_RoomUpgradeDialog.scss create mode 100644 res/css/views/rooms/_RoomUpgradeWarningBar.scss create mode 100644 src/components/views/dialogs/RoomUpgradeDialog.js create mode 100644 src/components/views/rooms/RoomUpgradeWarningBar.js diff --git a/res/css/_components.scss b/res/css/_components.scss index 173939e143..62cdb4533e 100644 --- a/res/css/_components.scss +++ b/res/css/_components.scss @@ -39,6 +39,7 @@ @import "./views/dialogs/_EncryptedEventDialog.scss"; @import "./views/dialogs/_GroupAddressPicker.scss"; @import "./views/dialogs/_QuestionDialog.scss"; +@import "./views/dialogs/_RoomUpgradeDialog.scss"; @import "./views/dialogs/_SetEmailDialog.scss"; @import "./views/dialogs/_SetMxIdDialog.scss"; @import "./views/dialogs/_SetPasswordDialog.scss"; @@ -99,6 +100,7 @@ @import "./views/rooms/_RoomSettings.scss"; @import "./views/rooms/_RoomTile.scss"; @import "./views/rooms/_RoomTooltip.scss"; +@import "./views/rooms/_RoomUpgradeWarningBar.scss"; @import "./views/rooms/_SearchBar.scss"; @import "./views/rooms/_SearchableEntityList.scss"; @import "./views/rooms/_Stickers.scss"; diff --git a/res/css/views/dialogs/_RoomUpgradeDialog.scss b/res/css/views/dialogs/_RoomUpgradeDialog.scss new file mode 100644 index 0000000000..2e3ac5fdea --- /dev/null +++ b/res/css/views/dialogs/_RoomUpgradeDialog.scss @@ -0,0 +1,19 @@ +/* +Copyright 2018 New Vector 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. +*/ + +.mx_RoomUpgradeDialog { + padding-right: 70px; +} diff --git a/res/css/views/rooms/_RoomSettings.scss b/res/css/views/rooms/_RoomSettings.scss index 4013af4c7c..f04042ea77 100644 --- a/res/css/views/rooms/_RoomSettings.scss +++ b/res/css/views/rooms/_RoomSettings.scss @@ -20,6 +20,7 @@ limitations under the License. margin-bottom: 20px; } +.mx_RoomSettings_upgradeButton, .mx_RoomSettings_leaveButton, .mx_RoomSettings_unbanButton { @mixin mx_DialogButton; @@ -27,11 +28,16 @@ limitations under the License. margin-right: 8px; } +.mx_RoomSettings_upgradeButton, .mx_RoomSettings_leaveButton:hover, .mx_RoomSettings_unbanButton:hover { @mixin mx_DialogButton_hover; } +.mx_RoomSettings_upgradeButton.danger { + @mixin mx_DialogButton_danger; +} + .mx_RoomSettings_integrationsButton_error { position: relative; cursor: not-allowed; diff --git a/res/css/views/rooms/_RoomUpgradeWarningBar.scss b/res/css/views/rooms/_RoomUpgradeWarningBar.scss new file mode 100644 index 0000000000..82785b82d2 --- /dev/null +++ b/res/css/views/rooms/_RoomUpgradeWarningBar.scss @@ -0,0 +1,48 @@ +/* +Copyright 2018 New Vector 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. +*/ + +.mx_RoomUpgradeWarningBar { + text-align: center; + height: 176px; + background-color: $event-selected-color; + align-items: center; + flex-direction: column; + justify-content: center; + display: flex; + background-color: $preview-bar-bg-color; + -webkit-align-items: center; + padding-left: 20px; + padding-right: 20px; +} + +.mx_RoomUpgradeWarningBar_header { + color: $warning-color; + font-weight: bold; +} + +.mx_RoomUpgradeWarningBar_body { + color: $warning-color; +} + +.mx_RoomUpgradeWarningBar_upgradelink { + color: $warning-color; + text-decoration: underline; +} + +.mx_RoomUpgradeWarningBar_small { + color: $greyed-fg-color; + font-size: 70%; +} diff --git a/res/themes/light/css/_base.scss b/res/themes/light/css/_base.scss index 7d004bd831..c7fd38259c 100644 --- a/res/themes/light/css/_base.scss +++ b/res/themes/light/css/_base.scss @@ -171,6 +171,10 @@ $progressbar-color: #000; outline: none; } +@define-mixin mx_DialogButton_danger { + background-color: $warning-color; +} + @define-mixin mx_DialogButton_hover { } diff --git a/src/components/structures/RoomView.js b/src/components/structures/RoomView.js index 855090873f..5243cd15fa 100644 --- a/src/components/structures/RoomView.js +++ b/src/components/structures/RoomView.js @@ -1461,6 +1461,7 @@ module.exports = React.createClass({ const RoomPreviewBar = sdk.getComponent("rooms.RoomPreviewBar"); const Loader = sdk.getComponent("elements.Spinner"); const TimelinePanel = sdk.getComponent("structures.TimelinePanel"); + const RoomUpgradeWarningBar = sdk.getComponent("rooms.RoomUpgradeWarningBar"); if (!this.state.room) { if (this.state.roomLoading || this.state.peekLoading) { @@ -1586,6 +1587,11 @@ module.exports = React.createClass({ />; } + const showRoomUpgradeBar = ( + this.state.room.shouldUpgradeToVersion() && + this.state.room.userMayUpgradeRoom(MatrixClientPeg.get().credentials.userId) + ); + let aux = null; let hideCancel = false; if (this.state.editingRoomSettings) { @@ -1597,6 +1603,9 @@ module.exports = React.createClass({ } else if (this.state.searching) { hideCancel = true; // has own cancel aux = ; + } else if (showRoomUpgradeBar) { + aux = ; + hideCancel = true; } else if (this.state.showingPinned) { hideCancel = true; // has own cancel aux = ; diff --git a/src/components/views/dialogs/RoomUpgradeDialog.js b/src/components/views/dialogs/RoomUpgradeDialog.js new file mode 100644 index 0000000000..936ff745d1 --- /dev/null +++ b/src/components/views/dialogs/RoomUpgradeDialog.js @@ -0,0 +1,106 @@ +/* +Copyright 2018 New Vector 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. +*/ + +import React from 'react'; +import PropTypes from 'prop-types'; +import sdk from '../../../index'; +import MatrixClientPeg from '../../../MatrixClientPeg'; +import Modal from '../../../Modal'; +import { _t } from '../../../languageHandler'; + +export default React.createClass({ + displayName: 'RoomUpgradeDialog', + + propTypes: { + room: PropTypes.object.isRequired, + onFinished: PropTypes.func.isRequired, + }, + + componentWillMount: function() { + this._targetVersion = this.props.room.shouldUpgradeToVersion(); + }, + + getInitialState: function() { + return { + busy: false, + }; + }, + + _onCancelClick: function() { + this.props.onFinished(false); + }, + + _onUpgradeClick: function() { + this.setState({busy: true}); + MatrixClientPeg.get().upgradeRoom(this.props.room.roomId, this._targetVersion).catch((err) => { + const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog"); + Modal.createTrackedDialog('Failed to upgrade room', '', ErrorDialog, { + title: _t("Failed to upgrade room"), + description: ((err && err.message) ? err.message : _t("The room upgrade could not be completed")), + }); + }).finally(() => { + this.setState({busy: false}); + }); + }, + + render: function() { + const BaseDialog = sdk.getComponent('views.dialogs.BaseDialog'); + const DialogButtons = sdk.getComponent('views.elements.DialogButtons'); + const Spinner = sdk.getComponent('views.elements.Spinner'); + + let buttons; + if (this.state.busy) { + buttons = ; + } else { + buttons = ; + } + + return ( + +

+ {_t( + "Upgrading this room requires closing down the current " + + "instance of the room and creating a new room it its place. " + + "To give room members the best possible experience, we will:", + )} +

+
    +
  1. {_t("Create a new room with the same name, description and avatar")}
  2. +
  3. {_t("Update any local room aliases to point to the new room")}
  4. +
  5. {_t("Stop users from speaking in the old version of the room, and post a message advising users to move to the new room")}
  6. +
  7. {_t("Put a link back to the old room at the start of the new room so people can see old messages")}
  8. +
+ {buttons} +
+ ); + }, +}); diff --git a/src/components/views/rooms/RoomSettings.js b/src/components/views/rooms/RoomSettings.js index 7a78d205b9..aeb55be075 100644 --- a/src/components/views/rooms/RoomSettings.js +++ b/src/components/views/rooms/RoomSettings.js @@ -572,6 +572,11 @@ module.exports = React.createClass({ }); }, + _onRoomUpgradeClick: function() { + const RoomUpgradeDialog = sdk.getComponent('dialogs.RoomUpgradeDialog'); + Modal.createTrackedDialog('Upgrade Room Version', '', RoomUpgradeDialog, {room: this.props.room}); + }, + _onRoomMemberMembership: function() { // Update, since our banned user list may have changed this.forceUpdate(); @@ -930,6 +935,13 @@ module.exports = React.createClass({ ); }); + let roomUpgradeButton = null; + if (this.props.room.shouldUpgradeToVersion() && this.props.room.userMayUpgradeRoom(myUserId)) { + roomUpgradeButton = + { _t("Upgrade room to version %(ver)s", {ver: this.props.room.shouldUpgradeToVersion()}) } + ; + } + return (
@@ -1041,7 +1053,8 @@ module.exports = React.createClass({

{ _t('Advanced') }

{ _t('Internal room ID: ') } { this.props.room.roomId }
- { _t('Room version number: ') } { this.props.room.getVersion() } + { _t('Room version number: ') } { this.props.room.getVersion() }
+ { roomUpgradeButton }
); diff --git a/src/components/views/rooms/RoomUpgradeWarningBar.js b/src/components/views/rooms/RoomUpgradeWarningBar.js new file mode 100644 index 0000000000..a464d95140 --- /dev/null +++ b/src/components/views/rooms/RoomUpgradeWarningBar.js @@ -0,0 +1,57 @@ +/* +Copyright 2018 New Vector 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. +*/ + +import React from 'react'; +import PropTypes from 'prop-types'; +import sdk from '../../../index'; +import Modal from '../../../Modal'; + +import { _t } from '../../../languageHandler'; + +module.exports = React.createClass({ + displayName: 'RoomUpgardeWarningBar', + + propTypes: { + room: PropTypes.object.isRequired, + }, + + onUpgradeClick: function() { + const RoomUpgradeDialog = sdk.getComponent('dialogs.RoomUpgradeDialog'); + Modal.createTrackedDialog('Upgrade Room Version', '', RoomUpgradeDialog, {room: this.props.room}); + }, + + render: function() { + const AccessibleButton = sdk.getComponent('elements.AccessibleButton'); + return ( +
+
+ {_t("There is a known vulnerability affecting this room.")} +
+
+ {_t("This room version is vulnerable to malicious modification of room state.")} +
+

+ + {_t("Click here to upgrade to the latest room version and ensure room integrity is protected.")} + +

+
+ {_t("Only room administrators will see this warning")} +
+
+ ); + }, +}); diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index fcc9bcc8be..043b90518d 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -205,6 +205,7 @@ "Authentication check failed: incorrect password?": "Authentication check failed: incorrect password?", "Failed to join room": "Failed to join room", "Message Pinning": "Message Pinning", + "Increase performance by only loading room members on first view": "Increase performance by only loading room members on first view", "Disable Emoji suggestions while typing": "Disable Emoji suggestions while typing", "Use compact timeline layout": "Use compact timeline layout", "Hide removed messages": "Hide removed messages", @@ -528,6 +529,7 @@ "Guests cannot join this room even if explicitly invited.": "Guests cannot join this room even if explicitly invited.", "Click here to fix": "Click here to fix", "To send events of type , you must be a": "To send events of type , you must be a", + "Upgrade room to version %(ver)s": "Upgrade room to version %(ver)s", "Who can access this room?": "Who can access this room?", "Only people who have been invited": "Only people who have been invited", "Anyone who knows the room's link, apart from guests": "Anyone who knows the room's link, apart from guests", @@ -865,6 +867,9 @@ "Ignore request": "Ignore request", "Loading device info...": "Loading device info...", "Encryption key request": "Encryption key request", + "Failed to upgrade room": "Failed to upgrade room", + "The room upgrade could not be completed": "The room upgrade could not be completed", + "Upgrade this room to version %(version)s": "Upgrade this room to version %(version)s", "Upgrade Room Version": "Upgrade Room Version", "Upgrading this room requires closing down the current instance of the room and creating a new room it its place. To give room members the best possible experience, we will:": "Upgrading this room requires closing down the current instance of the room and creating a new room it its place. To give room members the best possible experience, we will:", "Create a new room with the same name, description and avatar": "Create a new room with the same name, description and avatar", @@ -1121,6 +1126,8 @@ "Labs": "Labs", "These are experimental features that may break in unexpected ways": "These are experimental features that may break in unexpected ways", "Use with caution": "Use with caution", + "Lazy loading members not supported": "Lazy loading members not supported", + "Lazy loading is not supported by your current homeserver.": "Lazy loading is not supported by your current homeserver.", "Deactivate my account": "Deactivate my account", "Clear Cache": "Clear Cache", "Clear Cache and Reload": "Clear Cache and Reload", @@ -1231,8 +1238,5 @@ "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", - "Increase performance by only loading room members on first view": "Increase performance by only loading room members on first view", - "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." + "Failed to add tag %(tagName)s to room": "Failed to add tag %(tagName)s to room" } From 353b6e9e6d82f2d1e25398059ec0631a1dba76ce Mon Sep 17 00:00:00 2001 From: David Baker Date: Fri, 17 Aug 2018 15:15:53 +0100 Subject: [PATCH 02/26] Add stub --- test/test-utils.js | 1 + 1 file changed, 1 insertion(+) diff --git a/test/test-utils.js b/test/test-utils.js index 975a4df0ee..eab355d8a7 100644 --- a/test/test-utils.js +++ b/test/test-utils.js @@ -256,6 +256,7 @@ export function mkStubRoom(roomId = null) { getAccountData: () => null, hasMembershipState: () => null, getVersion: () => '1', + shouldUpgradeToVersion: () => null, currentState: { getStateEvents: sinon.stub(), mayClientSendStateEvent: sinon.stub().returns(true), From 8d6d1be992206a39dbac00f88c03e965f07f3dc3 Mon Sep 17 00:00:00 2001 From: David Baker Date: Tue, 21 Aug 2018 16:40:04 +0100 Subject: [PATCH 03/26] Typo --- src/components/views/rooms/RoomUpgradeWarningBar.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/views/rooms/RoomUpgradeWarningBar.js b/src/components/views/rooms/RoomUpgradeWarningBar.js index a464d95140..75a5901fc9 100644 --- a/src/components/views/rooms/RoomUpgradeWarningBar.js +++ b/src/components/views/rooms/RoomUpgradeWarningBar.js @@ -22,7 +22,7 @@ import Modal from '../../../Modal'; import { _t } from '../../../languageHandler'; module.exports = React.createClass({ - displayName: 'RoomUpgardeWarningBar', + displayName: 'RoomUpgradeWarningBar', propTypes: { room: PropTypes.object.isRequired, From 941a3cfa53f6add5f3521471ea9ab3fff14dcfd0 Mon Sep 17 00:00:00 2001 From: David Baker Date: Tue, 21 Aug 2018 18:33:25 +0100 Subject: [PATCH 04/26] Support M_INCOMPATIBLE_ROOM_VERSION For https://github.com/vector-im/riot-web/issues/7164 --- src/i18n/strings/en_EN.json | 20 ++++++-------------- src/stores/RoomViewStore.js | 8 +++++++- 2 files changed, 13 insertions(+), 15 deletions(-) diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index fcc9bcc8be..b560c7e71b 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -203,8 +203,11 @@ "Your browser does not support the required cryptography extensions": "Your browser does not support the required cryptography extensions", "Not a valid Riot keyfile": "Not a valid Riot keyfile", "Authentication check failed: incorrect password?": "Authentication check failed: incorrect password?", + "Sorry, your homeserver is too old to participate in this room.": "Sorry, your homeserver is too old to participate in this room.", + "Please contact your homeserver administrator.": "Please contact your homeserver administrator.", "Failed to join room": "Failed to join room", "Message Pinning": "Message Pinning", + "Increase performance by only loading room members on first view": "Increase performance by only loading room members on first view", "Disable Emoji suggestions while typing": "Disable Emoji suggestions while typing", "Use compact timeline layout": "Use compact timeline layout", "Hide removed messages": "Hide removed messages", @@ -543,10 +546,6 @@ "Internal room ID: ": "Internal room ID: ", "Room version number: ": "Room version number: ", "Add a topic": "Add a topic", - "There is a known vulnerability affecting this room.": "There is a known vulnerability affecting this room.", - "This room version is vulnerable to malicious modification of room state.": "This room version is vulnerable to malicious modification of room state.", - "Click here to upgrade to the latest room version and ensure room integrity is protected.": "Click here to upgrade to the latest room version and ensure room integrity is protected.", - "Only room administrators will see this warning": "Only room administrators will see this warning", "Search…": "Search…", "This Room": "This Room", "All Rooms": "All Rooms", @@ -865,12 +864,6 @@ "Ignore request": "Ignore request", "Loading device info...": "Loading device info...", "Encryption key request": "Encryption key request", - "Upgrade Room Version": "Upgrade Room Version", - "Upgrading this room requires closing down the current instance of the room and creating a new room it its place. To give room members the best possible experience, we will:": "Upgrading this room requires closing down the current instance of the room and creating a new room it its place. To give room members the best possible experience, we will:", - "Create a new room with the same name, description and avatar": "Create a new room with the same name, description and avatar", - "Update any local room aliases to point to the new room": "Update any local room aliases to point to the new room", - "Stop users from speaking in the old version of the room, and post a message advising users to move to the new room": "Stop users from speaking in the old version of the room, and post a message advising users to move to the new room", - "Put a link back to the old room at the start of the new room so people can see old messages": "Put a link back to the old room at the start of the new room so people can see old messages", "Sign out": "Sign out", "Log out and remove encryption keys?": "Log out and remove encryption keys?", "Clear Storage and Sign Out": "Clear Storage and Sign Out", @@ -1121,6 +1114,8 @@ "Labs": "Labs", "These are experimental features that may break in unexpected ways": "These are experimental features that may break in unexpected ways", "Use with caution": "Use with caution", + "Lazy loading members not supported": "Lazy loading members not supported", + "Lazy loading is not supported by your current homeserver.": "Lazy loading is not supported by your current homeserver.", "Deactivate my account": "Deactivate my account", "Clear Cache": "Clear Cache", "Clear Cache and Reload": "Clear Cache and Reload", @@ -1231,8 +1226,5 @@ "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", - "Increase performance by only loading room members on first view": "Increase performance by only loading room members on first view", - "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." + "Failed to add tag %(tagName)s to room": "Failed to add tag %(tagName)s to room" } diff --git a/src/stores/RoomViewStore.js b/src/stores/RoomViewStore.js index fed0d7b4a1..f15925f480 100644 --- a/src/stores/RoomViewStore.js +++ b/src/stores/RoomViewStore.js @@ -223,7 +223,13 @@ class RoomViewStore extends Store { action: 'join_room_error', err: err, }); - const msg = err.message ? err.message : JSON.stringify(err); + let msg = err.message ? err.message : JSON.stringify(err); + if (err.errcode === 'M_INCOMPATIBLE_ROOM_VERSION') { + msg =
+ {_t("Sorry, your homeserver is too old to participate in this room.")}
+ {_t("Please contact your homeserver administrator.")} +
; + } const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog"); Modal.createTrackedDialog('Failed to join room', '', ErrorDialog, { title: _t("Failed to join room"), From 01a0260ac8a05f35c7499b18810e677c66390f04 Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Wed, 22 Aug 2018 10:33:13 +0200 Subject: [PATCH 05/26] use getMyMembership instead of member --- src/components/structures/RoomView.js | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/src/components/structures/RoomView.js b/src/components/structures/RoomView.js index 855090873f..92be0954f9 100644 --- a/src/components/structures/RoomView.js +++ b/src/components/structures/RoomView.js @@ -310,7 +310,7 @@ 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}); @@ -1507,9 +1507,8 @@ module.exports = React.createClass({ } } - const myUserId = MatrixClientPeg.get().credentials.userId; - const myMember = this.state.room.getMember(myUserId); - if (myMember && myMember.membership == 'invite') { + const myMembership = this.state.room.getMyMembership(); + if (myMembership == 'invite') { if (this.state.joining || this.state.rejecting) { return (
@@ -1517,6 +1516,8 @@ module.exports = React.createClass({
); } else { + const myUserId = MatrixClientPeg.get().credentials.userId; + const myMember = this.state.room.getMember(myUserId); const inviteEvent = myMember.events.member; var inviterName = inviteEvent.sender ? inviteEvent.sender.name : inviteEvent.getSender(); @@ -1600,7 +1601,7 @@ module.exports = React.createClass({ } else if (this.state.showingPinned) { hideCancel = true; // has own cancel aux = ; - } else if (!myMember || myMember.membership !== "join") { + } else if (!myMembership || myMembership !== "join") { // We do have a room object for this room, but we're not currently in it. // We may have a 3rd party invite to it. var inviterName = undefined; @@ -1642,7 +1643,7 @@ module.exports = React.createClass({ let messageComposer, searchInfo; const canSpeak = ( // joined and not showing search results - myMember && (myMember.membership == 'join') && !this.state.searchResults + myMembership == 'join' && !this.state.searchResults ); if (canSpeak) { messageComposer = @@ -1777,15 +1778,15 @@ module.exports = React.createClass({ oobData={this.props.oobData} editing={this.state.editingRoomSettings} saving={this.state.uploadingRoomSettings} - inRoom={myMember && myMember.membership === 'join'} + inRoom={myMembership === 'join'} collapsedRhs={this.props.collapsedRhs} onSearchClick={this.onSearchClick} onSettingsClick={this.onSettingsClick} onPinnedClick={this.onPinnedClick} onSaveClick={this.onSettingsSaveClick} onCancelClick={(aux && !hideCancel) ? this.onCancelClick : null} - onForgetClick={(myMember && myMember.membership === "leave") ? this.onForgetClick : null} - onLeaveClick={(myMember && myMember.membership === "join") ? this.onLeaveClick : null} + onForgetClick={(myMembership === "leave") ? this.onForgetClick : null} + onLeaveClick={(myMembership === "join") ? this.onLeaveClick : null} /> { auxPanel }
From c38b286e8e6f7aaa3cf2d6f77939e3182736c8c1 Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Wed, 22 Aug 2018 12:28:00 +0200 Subject: [PATCH 06/26] falsy check is obsolete now --- 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 92be0954f9..84b8788297 100644 --- a/src/components/structures/RoomView.js +++ b/src/components/structures/RoomView.js @@ -1601,7 +1601,7 @@ module.exports = React.createClass({ } else if (this.state.showingPinned) { hideCancel = true; // has own cancel aux = ; - } else if (!myMembership || myMembership !== "join") { + } else if (myMembership !== "join") { // We do have a room object for this room, but we're not currently in it. // We may have a 3rd party invite to it. var inviterName = undefined; From b7570002e0e531d80cc63b221cce1881a7a3637a Mon Sep 17 00:00:00 2001 From: David Baker Date: Wed, 22 Aug 2018 12:01:29 +0100 Subject: [PATCH 07/26] Hide replaced rooms By using new getVisibleRooms() in js-sdk --- src/components/views/rooms/RoomList.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/views/rooms/RoomList.js b/src/components/views/rooms/RoomList.js index 732048f712..3e632ba8ce 100644 --- a/src/components/views/rooms/RoomList.js +++ b/src/components/views/rooms/RoomList.js @@ -97,7 +97,7 @@ module.exports = React.createClass({ }; // All rooms that should be kept in the room list when filtering. // By default, show all rooms. - this._visibleRooms = MatrixClientPeg.get().getRooms(); + this._visibleRooms = MatrixClientPeg.get().getVisibleRooms(); // Listen to updates to group data. RoomList cares about members and rooms in order // to filter the room list when group tags are selected. @@ -302,7 +302,7 @@ module.exports = React.createClass({ this._visibleRooms = Array.from(roomSet); } else { // Show all rooms - this._visibleRooms = MatrixClientPeg.get().getRooms(); + this._visibleRooms = MatrixClientPeg.get().getVisibleRooms(); } this._delayedRefreshRoomList(); }, From 860c6355f026aabab384e75402f0de475510d49e Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Wed, 22 Aug 2018 14:06:28 +0200 Subject: [PATCH 08/26] do self membership checks with getMyMembership elsewhere as well --- src/ScalarMessaging.js | 5 ++--- .../views/context_menus/RoomTileContextMenu.js | 10 ++++------ src/components/views/rooms/RoomSettings.js | 8 ++++---- 3 files changed, 10 insertions(+), 13 deletions(-) diff --git a/src/ScalarMessaging.js b/src/ScalarMessaging.js index 3325044b84..7ec928f895 100644 --- a/src/ScalarMessaging.js +++ b/src/ScalarMessaging.js @@ -497,12 +497,11 @@ function canSendEvent(event, roomId) { sendError(event, _t('This room is not recognised.')); return; } - const me = client.credentials.userId; - const member = room.getMember(me); - if (!member || member.membership !== "join") { + if (room.getMyMembership() !== "join") { sendError(event, _t('You are not in this room.')); return; } + const me = client.credentials.userId; let canSend = false; if (isState) { diff --git a/src/components/views/context_menus/RoomTileContextMenu.js b/src/components/views/context_menus/RoomTileContextMenu.js index 77f71fa8fa..ce9895447e 100644 --- a/src/components/views/context_menus/RoomTileContextMenu.js +++ b/src/components/views/context_menus/RoomTileContextMenu.js @@ -346,20 +346,18 @@ module.exports = React.createClass({ }, render: function() { - const myMember = this.props.room.getMember( - MatrixClientPeg.get().credentials.userId, - ); + const myMembership = this.props.room.getMyMembership(); // Can't set notif level or tags on non-join rooms - if (myMember.membership !== 'join') { - return this._renderLeaveMenu(myMember.membership); + if (myMembership !== 'join') { + return this._renderLeaveMenu(myMembership); } return (
{ this._renderNotifMenu() }
- { this._renderLeaveMenu(myMember.membership) } + { this._renderLeaveMenu(myMembership) }
{ this._renderRoomTagMenu() }
diff --git a/src/components/views/rooms/RoomSettings.js b/src/components/views/rooms/RoomSettings.js index 7a78d205b9..3b95fa9905 100644 --- a/src/components/views/rooms/RoomSettings.js +++ b/src/components/views/rooms/RoomSettings.js @@ -794,15 +794,15 @@ module.exports = React.createClass({ } let leaveButton = null; - const myMember = this.props.room.getMember(myUserId); - if (myMember) { - if (myMember.membership === "join") { + const myMemberShip = this.props.room.getMyMembership(); + if (myMemberShip) { + if (myMemberShip === "join") { leaveButton = ( { _t('Leave room') } ); - } else if (myMember.membership === "leave") { + } else if (myMemberShip === "leave") { leaveButton = ( { _t('Forget room') } From a54629276fe225ad0281bf2f805b5d5b2fd6dc94 Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Wed, 22 Aug 2018 14:08:18 +0200 Subject: [PATCH 09/26] use getJoinedMemberCount() instead of getJoinedMembers().length as the latter doesnt take LL into account --- src/ScalarMessaging.js | 2 +- src/VectorConferenceHandler.js | 2 +- src/components/structures/RightPanel.js | 2 +- src/components/structures/RoomView.js | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/ScalarMessaging.js b/src/ScalarMessaging.js index 7ec928f895..fa7b8c5b76 100644 --- a/src/ScalarMessaging.js +++ b/src/ScalarMessaging.js @@ -480,7 +480,7 @@ function getMembershipCount(event, roomId) { sendError(event, _t('This room is not recognised.')); return; } - const count = room.getJoinedMembers().length; + const count = room.getJoinedMemberCount(); sendResponse(event, count); } diff --git a/src/VectorConferenceHandler.js b/src/VectorConferenceHandler.js index 9ba46b2ab6..c53a01d464 100644 --- a/src/VectorConferenceHandler.js +++ b/src/VectorConferenceHandler.js @@ -72,7 +72,7 @@ ConferenceCall.prototype._getConferenceUserRoom = function() { for (var i = 0; i < rooms.length; i++) { var confUser = rooms[i].getMember(this.confUserId); if (confUser && confUser.membership === "join" && - rooms[i].getJoinedMembers().length === 2) { + rooms[i].getJoinedMemberCount() === 2) { confRoom = rooms[i]; break; } diff --git a/src/components/structures/RightPanel.js b/src/components/structures/RightPanel.js index 18523ceb59..bd4ed722cb 100644 --- a/src/components/structures/RightPanel.js +++ b/src/components/structures/RightPanel.js @@ -280,7 +280,7 @@ module.exports = React.createClass({ const room = cli.getRoom(this.props.roomId); let isUserInRoom; if (room) { - const numMembers = room.getJoinedMembers().length; + const numMembers = room.getJoinedMemberCount(); membersTitle = _t('%(count)s Members', { count: numMembers }); membersBadge =
{ formatCount(numMembers) }
; isUserInRoom = room.hasMembershipState(this.context.matrixClient.credentials.userId, 'join'); diff --git a/src/components/structures/RoomView.js b/src/components/structures/RoomView.js index 84b8788297..7b7a7bb813 100644 --- a/src/components/structures/RoomView.js +++ b/src/components/structures/RoomView.js @@ -363,7 +363,7 @@ module.exports = React.createClass({ // XXX: EVIL HACK to autofocus inviting on empty rooms. // We use the setTimeout to avoid racing with focus_composer. if (this.state.room && - this.state.room.getJoinedMembers().length == 1 && + this.state.room.getJoinedMemberCount() == 1 && this.state.room.getLiveTimeline() && this.state.room.getLiveTimeline().getEvents() && this.state.room.getLiveTimeline().getEvents().length <= 6) { From 852d6a3be5a4bb2d9c3c6ee42fd4fafd64e08dc8 Mon Sep 17 00:00:00 2001 From: David Baker Date: Wed, 22 Aug 2018 14:34:02 +0100 Subject: [PATCH 10/26] Fix CPU spin on joining large room checkIfAlone() filters the whole member list, which is fine until we do it once for every membership event, then we have an n^2 problem. Move it into the rate limited function. Fixes https://github.com/vector-im/riot-web/issues/7163 --- 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 855090873f..1ecd7dad2e 100644 --- a/src/components/structures/RoomView.js +++ b/src/components/structures/RoomView.js @@ -701,7 +701,6 @@ module.exports = React.createClass({ } this._updateRoomMembers(); - this._checkIfAlone(this.state.room); }, onRoomMemberMembership: function(ev, member, oldMembership) { @@ -717,6 +716,7 @@ module.exports = React.createClass({ // refresh the conf call notification state this._updateConfCallNotification(); this._updateDMState(); + this._checkIfAlone(this.state.room); }, 500), _checkIfAlone: function(room) { From db7dd8e9e88edec5d128cea502b567c6384a12c2 Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Wed, 22 Aug 2018 16:00:03 +0200 Subject: [PATCH 11/26] fix tests --- test/test-utils.js | 1 + 1 file changed, 1 insertion(+) diff --git a/test/test-utils.js b/test/test-utils.js index 975a4df0ee..25df9686ff 100644 --- a/test/test-utils.js +++ b/test/test-utils.js @@ -256,6 +256,7 @@ export function mkStubRoom(roomId = null) { getAccountData: () => null, hasMembershipState: () => null, getVersion: () => '1', + getMyMembership: () => "join", currentState: { getStateEvents: sinon.stub(), mayClientSendStateEvent: sinon.stub().returns(true), From bf7633250aa5c3308b15f358e610e24dcebfd8c8 Mon Sep 17 00:00:00 2001 From: David Baker Date: Wed, 22 Aug 2018 16:16:17 +0100 Subject: [PATCH 12/26] Revert "Fix showing peek preview while LL members are loading" --- src/ScalarMessaging.js | 7 ++++--- src/VectorConferenceHandler.js | 2 +- src/components/structures/RightPanel.js | 2 +- src/components/structures/RoomView.js | 21 +++++++++---------- .../context_menus/RoomTileContextMenu.js | 10 +++++---- src/components/views/rooms/RoomSettings.js | 8 +++---- test/test-utils.js | 1 - 7 files changed, 26 insertions(+), 25 deletions(-) diff --git a/src/ScalarMessaging.js b/src/ScalarMessaging.js index fa7b8c5b76..3325044b84 100644 --- a/src/ScalarMessaging.js +++ b/src/ScalarMessaging.js @@ -480,7 +480,7 @@ function getMembershipCount(event, roomId) { sendError(event, _t('This room is not recognised.')); return; } - const count = room.getJoinedMemberCount(); + const count = room.getJoinedMembers().length; sendResponse(event, count); } @@ -497,11 +497,12 @@ function canSendEvent(event, roomId) { sendError(event, _t('This room is not recognised.')); return; } - if (room.getMyMembership() !== "join") { + const me = client.credentials.userId; + const member = room.getMember(me); + if (!member || member.membership !== "join") { sendError(event, _t('You are not in this room.')); return; } - const me = client.credentials.userId; let canSend = false; if (isState) { diff --git a/src/VectorConferenceHandler.js b/src/VectorConferenceHandler.js index c53a01d464..9ba46b2ab6 100644 --- a/src/VectorConferenceHandler.js +++ b/src/VectorConferenceHandler.js @@ -72,7 +72,7 @@ ConferenceCall.prototype._getConferenceUserRoom = function() { for (var i = 0; i < rooms.length; i++) { var confUser = rooms[i].getMember(this.confUserId); if (confUser && confUser.membership === "join" && - rooms[i].getJoinedMemberCount() === 2) { + rooms[i].getJoinedMembers().length === 2) { confRoom = rooms[i]; break; } diff --git a/src/components/structures/RightPanel.js b/src/components/structures/RightPanel.js index bd4ed722cb..18523ceb59 100644 --- a/src/components/structures/RightPanel.js +++ b/src/components/structures/RightPanel.js @@ -280,7 +280,7 @@ module.exports = React.createClass({ const room = cli.getRoom(this.props.roomId); let isUserInRoom; if (room) { - const numMembers = room.getJoinedMemberCount(); + const numMembers = room.getJoinedMembers().length; membersTitle = _t('%(count)s Members', { count: numMembers }); membersBadge =
{ formatCount(numMembers) }
; isUserInRoom = room.hasMembershipState(this.context.matrixClient.credentials.userId, 'join'); diff --git a/src/components/structures/RoomView.js b/src/components/structures/RoomView.js index 7b7a7bb813..855090873f 100644 --- a/src/components/structures/RoomView.js +++ b/src/components/structures/RoomView.js @@ -310,7 +310,7 @@ 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}); @@ -363,7 +363,7 @@ module.exports = React.createClass({ // XXX: EVIL HACK to autofocus inviting on empty rooms. // We use the setTimeout to avoid racing with focus_composer. if (this.state.room && - this.state.room.getJoinedMemberCount() == 1 && + this.state.room.getJoinedMembers().length == 1 && this.state.room.getLiveTimeline() && this.state.room.getLiveTimeline().getEvents() && this.state.room.getLiveTimeline().getEvents().length <= 6) { @@ -1507,8 +1507,9 @@ module.exports = React.createClass({ } } - const myMembership = this.state.room.getMyMembership(); - if (myMembership == 'invite') { + const myUserId = MatrixClientPeg.get().credentials.userId; + const myMember = this.state.room.getMember(myUserId); + if (myMember && myMember.membership == 'invite') { if (this.state.joining || this.state.rejecting) { return (
@@ -1516,8 +1517,6 @@ module.exports = React.createClass({
); } else { - const myUserId = MatrixClientPeg.get().credentials.userId; - const myMember = this.state.room.getMember(myUserId); const inviteEvent = myMember.events.member; var inviterName = inviteEvent.sender ? inviteEvent.sender.name : inviteEvent.getSender(); @@ -1601,7 +1600,7 @@ module.exports = React.createClass({ } else if (this.state.showingPinned) { hideCancel = true; // has own cancel aux = ; - } else if (myMembership !== "join") { + } else if (!myMember || myMember.membership !== "join") { // We do have a room object for this room, but we're not currently in it. // We may have a 3rd party invite to it. var inviterName = undefined; @@ -1643,7 +1642,7 @@ module.exports = React.createClass({ let messageComposer, searchInfo; const canSpeak = ( // joined and not showing search results - myMembership == 'join' && !this.state.searchResults + myMember && (myMember.membership == 'join') && !this.state.searchResults ); if (canSpeak) { messageComposer = @@ -1778,15 +1777,15 @@ module.exports = React.createClass({ oobData={this.props.oobData} editing={this.state.editingRoomSettings} saving={this.state.uploadingRoomSettings} - inRoom={myMembership === 'join'} + inRoom={myMember && myMember.membership === 'join'} collapsedRhs={this.props.collapsedRhs} onSearchClick={this.onSearchClick} onSettingsClick={this.onSettingsClick} onPinnedClick={this.onPinnedClick} onSaveClick={this.onSettingsSaveClick} onCancelClick={(aux && !hideCancel) ? this.onCancelClick : null} - onForgetClick={(myMembership === "leave") ? this.onForgetClick : null} - onLeaveClick={(myMembership === "join") ? this.onLeaveClick : null} + onForgetClick={(myMember && myMember.membership === "leave") ? this.onForgetClick : null} + onLeaveClick={(myMember && myMember.membership === "join") ? this.onLeaveClick : null} /> { auxPanel }
diff --git a/src/components/views/context_menus/RoomTileContextMenu.js b/src/components/views/context_menus/RoomTileContextMenu.js index ce9895447e..77f71fa8fa 100644 --- a/src/components/views/context_menus/RoomTileContextMenu.js +++ b/src/components/views/context_menus/RoomTileContextMenu.js @@ -346,18 +346,20 @@ module.exports = React.createClass({ }, render: function() { - const myMembership = this.props.room.getMyMembership(); + const myMember = this.props.room.getMember( + MatrixClientPeg.get().credentials.userId, + ); // Can't set notif level or tags on non-join rooms - if (myMembership !== 'join') { - return this._renderLeaveMenu(myMembership); + if (myMember.membership !== 'join') { + return this._renderLeaveMenu(myMember.membership); } return (
{ this._renderNotifMenu() }
- { this._renderLeaveMenu(myMembership) } + { this._renderLeaveMenu(myMember.membership) }
{ this._renderRoomTagMenu() }
diff --git a/src/components/views/rooms/RoomSettings.js b/src/components/views/rooms/RoomSettings.js index 3b95fa9905..7a78d205b9 100644 --- a/src/components/views/rooms/RoomSettings.js +++ b/src/components/views/rooms/RoomSettings.js @@ -794,15 +794,15 @@ module.exports = React.createClass({ } let leaveButton = null; - const myMemberShip = this.props.room.getMyMembership(); - if (myMemberShip) { - if (myMemberShip === "join") { + const myMember = this.props.room.getMember(myUserId); + if (myMember) { + if (myMember.membership === "join") { leaveButton = ( { _t('Leave room') } ); - } else if (myMemberShip === "leave") { + } else if (myMember.membership === "leave") { leaveButton = ( { _t('Forget room') } diff --git a/test/test-utils.js b/test/test-utils.js index 25df9686ff..975a4df0ee 100644 --- a/test/test-utils.js +++ b/test/test-utils.js @@ -256,7 +256,6 @@ export function mkStubRoom(roomId = null) { getAccountData: () => null, hasMembershipState: () => null, getVersion: () => '1', - getMyMembership: () => "join", currentState: { getStateEvents: sinon.stub(), mayClientSendStateEvent: sinon.stub().returns(true), From f679acea08e4d9ae42edc334176cf9b0ce9f72ee Mon Sep 17 00:00:00 2001 From: David Baker Date: Wed, 22 Aug 2018 16:35:58 +0100 Subject: [PATCH 13/26] Revert "Revert "Fix showing peek preview while LL members are loading"" --- src/ScalarMessaging.js | 7 +++---- src/VectorConferenceHandler.js | 2 +- src/components/structures/RightPanel.js | 2 +- src/components/structures/RoomView.js | 21 ++++++++++--------- .../context_menus/RoomTileContextMenu.js | 10 ++++----- src/components/views/rooms/RoomSettings.js | 8 +++---- test/test-utils.js | 1 + 7 files changed, 25 insertions(+), 26 deletions(-) diff --git a/src/ScalarMessaging.js b/src/ScalarMessaging.js index 3325044b84..fa7b8c5b76 100644 --- a/src/ScalarMessaging.js +++ b/src/ScalarMessaging.js @@ -480,7 +480,7 @@ function getMembershipCount(event, roomId) { sendError(event, _t('This room is not recognised.')); return; } - const count = room.getJoinedMembers().length; + const count = room.getJoinedMemberCount(); sendResponse(event, count); } @@ -497,12 +497,11 @@ function canSendEvent(event, roomId) { sendError(event, _t('This room is not recognised.')); return; } - const me = client.credentials.userId; - const member = room.getMember(me); - if (!member || member.membership !== "join") { + if (room.getMyMembership() !== "join") { sendError(event, _t('You are not in this room.')); return; } + const me = client.credentials.userId; let canSend = false; if (isState) { diff --git a/src/VectorConferenceHandler.js b/src/VectorConferenceHandler.js index 9ba46b2ab6..c53a01d464 100644 --- a/src/VectorConferenceHandler.js +++ b/src/VectorConferenceHandler.js @@ -72,7 +72,7 @@ ConferenceCall.prototype._getConferenceUserRoom = function() { for (var i = 0; i < rooms.length; i++) { var confUser = rooms[i].getMember(this.confUserId); if (confUser && confUser.membership === "join" && - rooms[i].getJoinedMembers().length === 2) { + rooms[i].getJoinedMemberCount() === 2) { confRoom = rooms[i]; break; } diff --git a/src/components/structures/RightPanel.js b/src/components/structures/RightPanel.js index 18523ceb59..bd4ed722cb 100644 --- a/src/components/structures/RightPanel.js +++ b/src/components/structures/RightPanel.js @@ -280,7 +280,7 @@ module.exports = React.createClass({ const room = cli.getRoom(this.props.roomId); let isUserInRoom; if (room) { - const numMembers = room.getJoinedMembers().length; + const numMembers = room.getJoinedMemberCount(); membersTitle = _t('%(count)s Members', { count: numMembers }); membersBadge =
{ formatCount(numMembers) }
; isUserInRoom = room.hasMembershipState(this.context.matrixClient.credentials.userId, 'join'); diff --git a/src/components/structures/RoomView.js b/src/components/structures/RoomView.js index 855090873f..7b7a7bb813 100644 --- a/src/components/structures/RoomView.js +++ b/src/components/structures/RoomView.js @@ -310,7 +310,7 @@ 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}); @@ -363,7 +363,7 @@ module.exports = React.createClass({ // XXX: EVIL HACK to autofocus inviting on empty rooms. // We use the setTimeout to avoid racing with focus_composer. if (this.state.room && - this.state.room.getJoinedMembers().length == 1 && + this.state.room.getJoinedMemberCount() == 1 && this.state.room.getLiveTimeline() && this.state.room.getLiveTimeline().getEvents() && this.state.room.getLiveTimeline().getEvents().length <= 6) { @@ -1507,9 +1507,8 @@ module.exports = React.createClass({ } } - const myUserId = MatrixClientPeg.get().credentials.userId; - const myMember = this.state.room.getMember(myUserId); - if (myMember && myMember.membership == 'invite') { + const myMembership = this.state.room.getMyMembership(); + if (myMembership == 'invite') { if (this.state.joining || this.state.rejecting) { return (
@@ -1517,6 +1516,8 @@ module.exports = React.createClass({
); } else { + const myUserId = MatrixClientPeg.get().credentials.userId; + const myMember = this.state.room.getMember(myUserId); const inviteEvent = myMember.events.member; var inviterName = inviteEvent.sender ? inviteEvent.sender.name : inviteEvent.getSender(); @@ -1600,7 +1601,7 @@ module.exports = React.createClass({ } else if (this.state.showingPinned) { hideCancel = true; // has own cancel aux = ; - } else if (!myMember || myMember.membership !== "join") { + } else if (myMembership !== "join") { // We do have a room object for this room, but we're not currently in it. // We may have a 3rd party invite to it. var inviterName = undefined; @@ -1642,7 +1643,7 @@ module.exports = React.createClass({ let messageComposer, searchInfo; const canSpeak = ( // joined and not showing search results - myMember && (myMember.membership == 'join') && !this.state.searchResults + myMembership == 'join' && !this.state.searchResults ); if (canSpeak) { messageComposer = @@ -1777,15 +1778,15 @@ module.exports = React.createClass({ oobData={this.props.oobData} editing={this.state.editingRoomSettings} saving={this.state.uploadingRoomSettings} - inRoom={myMember && myMember.membership === 'join'} + inRoom={myMembership === 'join'} collapsedRhs={this.props.collapsedRhs} onSearchClick={this.onSearchClick} onSettingsClick={this.onSettingsClick} onPinnedClick={this.onPinnedClick} onSaveClick={this.onSettingsSaveClick} onCancelClick={(aux && !hideCancel) ? this.onCancelClick : null} - onForgetClick={(myMember && myMember.membership === "leave") ? this.onForgetClick : null} - onLeaveClick={(myMember && myMember.membership === "join") ? this.onLeaveClick : null} + onForgetClick={(myMembership === "leave") ? this.onForgetClick : null} + onLeaveClick={(myMembership === "join") ? this.onLeaveClick : null} /> { auxPanel }
diff --git a/src/components/views/context_menus/RoomTileContextMenu.js b/src/components/views/context_menus/RoomTileContextMenu.js index 77f71fa8fa..ce9895447e 100644 --- a/src/components/views/context_menus/RoomTileContextMenu.js +++ b/src/components/views/context_menus/RoomTileContextMenu.js @@ -346,20 +346,18 @@ module.exports = React.createClass({ }, render: function() { - const myMember = this.props.room.getMember( - MatrixClientPeg.get().credentials.userId, - ); + const myMembership = this.props.room.getMyMembership(); // Can't set notif level or tags on non-join rooms - if (myMember.membership !== 'join') { - return this._renderLeaveMenu(myMember.membership); + if (myMembership !== 'join') { + return this._renderLeaveMenu(myMembership); } return (
{ this._renderNotifMenu() }
- { this._renderLeaveMenu(myMember.membership) } + { this._renderLeaveMenu(myMembership) }
{ this._renderRoomTagMenu() }
diff --git a/src/components/views/rooms/RoomSettings.js b/src/components/views/rooms/RoomSettings.js index 7a78d205b9..3b95fa9905 100644 --- a/src/components/views/rooms/RoomSettings.js +++ b/src/components/views/rooms/RoomSettings.js @@ -794,15 +794,15 @@ module.exports = React.createClass({ } let leaveButton = null; - const myMember = this.props.room.getMember(myUserId); - if (myMember) { - if (myMember.membership === "join") { + const myMemberShip = this.props.room.getMyMembership(); + if (myMemberShip) { + if (myMemberShip === "join") { leaveButton = ( { _t('Leave room') } ); - } else if (myMember.membership === "leave") { + } else if (myMemberShip === "leave") { leaveButton = ( { _t('Forget room') } diff --git a/test/test-utils.js b/test/test-utils.js index 975a4df0ee..25df9686ff 100644 --- a/test/test-utils.js +++ b/test/test-utils.js @@ -256,6 +256,7 @@ export function mkStubRoom(roomId = null) { getAccountData: () => null, hasMembershipState: () => null, getVersion: () => '1', + getMyMembership: () => "join", currentState: { getStateEvents: sinon.stub(), mayClientSendStateEvent: sinon.stub().returns(true), From a5f98b6a29675f1acc09f655e63230a6d366f499 Mon Sep 17 00:00:00 2001 From: David Baker Date: Wed, 22 Aug 2018 17:05:36 +0100 Subject: [PATCH 14/26] Make the is-alone check use efficient methods --- 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 1ecd7dad2e..dd8fba3929 100644 --- a/src/components/structures/RoomView.js +++ b/src/components/structures/RoomView.js @@ -729,8 +729,8 @@ module.exports = React.createClass({ return; } - const joinedMembers = room.currentState.getMembers().filter((m) => m.membership === "join" || m.membership === "invite"); - this.setState({isAlone: joinedMembers.length === 1}); + const joinedOrInvitedMemberCount = room.getJoinedMemberCount() + room.getInvitedMemberCount(); + this.setState({isAlone: joinedOrInvitedMemberCount === 1}); }, _updateConfCallNotification: function() { From 3b0b836637aa42ef22de4fb8b4c294cd24113a1c Mon Sep 17 00:00:00 2001 From: David Baker Date: Wed, 22 Aug 2018 17:54:02 +0100 Subject: [PATCH 15/26] Change format of server usage limit message As per https://github.com/matrix-org/matrix-doc/issues/1452 / https://github.com/matrix-org/synapse/pull/3680/ --- src/components/structures/LoggedInView.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/components/structures/LoggedInView.js b/src/components/structures/LoggedInView.js index 2bc6522232..0c4688a411 100644 --- a/src/components/structures/LoggedInView.js +++ b/src/components/structures/LoggedInView.js @@ -434,7 +434,10 @@ const LoggedInView = React.createClass({ } const usageLimitEvent = this.state.serverNoticeEvents.find((e) => { - return e && e.getType() === 'm.server_notice.usage_limit_reached'; + return ( + e && e.getType() === 'm.room.message' && + e.getContent()['server_notice_type'] === 'm.server_notice.usage_limit_reached' + ); }); let topBar; From 37e60e4fc46e1e4b4446b18d34e678fd901a3516 Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Thu, 23 Aug 2018 00:02:20 +0200 Subject: [PATCH 16/26] wait for members loading to finish to show the avatar in the composer --- src/components/views/rooms/MessageComposer.js | 30 +++++++++++++++---- 1 file changed, 24 insertions(+), 6 deletions(-) diff --git a/src/components/views/rooms/MessageComposer.js b/src/components/views/rooms/MessageComposer.js index a7e02d16ae..eccdfe17af 100644 --- a/src/components/views/rooms/MessageComposer.js +++ b/src/components/views/rooms/MessageComposer.js @@ -71,6 +71,23 @@ export default class MessageComposer extends React.Component { // XXX: fragile as all hell - fixme somehow, perhaps with a dedicated Room.encryption event or something. MatrixClientPeg.get().on("event", this.onEvent); this._roomStoreToken = RoomViewStore.addListener(this._onRoomViewStoreUpdate); + this._waitForOwnMember(); + } + + _waitForOwnMember() { + // if we have the member already, do that + const me = this.props.room.getMember(MatrixClientPeg.get().getUserId()); + if (me) { + this.setState({me}); + return; + } + // Otherwise, wait for member loading to finish and then update the member for the avatar. + // The members should already be loading, and loadMembersIfNeeded + // will return the promise for the existing operation + this.props.room.loadMembersIfNeeded().then(() => { + const me = this.props.room.getMember(MatrixClientPeg.get().getUserId()); + this.setState({me}); + }); } componentWillUnmount() { @@ -208,7 +225,6 @@ export default class MessageComposer extends React.Component { } render() { - const me = this.props.room.getMember(MatrixClientPeg.get().credentials.userId); const uploadInputStyle = {display: 'none'}; const MemberAvatar = sdk.getComponent('avatars.MemberAvatar'); const TintableSvg = sdk.getComponent("elements.TintableSvg"); @@ -216,11 +232,13 @@ export default class MessageComposer extends React.Component { const controls = []; - controls.push( -
- -
, - ); + if (this.state.me) { + controls.push( +
+ +
, + ); + } let e2eImg, e2eTitle, e2eClass; const roomIsEncrypted = MatrixClientPeg.get().isRoomEncrypted(this.props.room.roomId); From 54620591ccc8026de15f41f75740b6e795a04418 Mon Sep 17 00:00:00 2001 From: David Baker Date: Thu, 23 Aug 2018 12:05:09 +0100 Subject: [PATCH 17/26] Don't crash if the value of a room tag is null We were checking that the order was not null, but not the object itself. --- src/stores/RoomListStore.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/stores/RoomListStore.js b/src/stores/RoomListStore.js index 0a0a39a450..6571e1590f 100644 --- a/src/stores/RoomListStore.js +++ b/src/stores/RoomListStore.js @@ -284,8 +284,8 @@ class RoomListStore extends Store { if (optimisticRequest && roomB === optimisticRequest.room) metaB = optimisticRequest.metaData; // Make sure the room tag has an order element, if not set it to be the bottom - const a = metaA.order; - const b = metaB.order; + const a = metaA ? metaA.order : undefined; + const b = metaB ? metaB.order : undefined; // Order undefined room tag orders to the bottom if (a === undefined && b !== undefined) { From cb84feefa928e5d8f5c98724844d3e327d50132e Mon Sep 17 00:00:00 2001 From: David Baker Date: Thu, 23 Aug 2018 12:14:52 +0100 Subject: [PATCH 18/26] Add stub for getVisibleRooms() Fixes the tests --- test/components/views/rooms/RoomList-test.js | 1 + test/test-utils.js | 1 + 2 files changed, 2 insertions(+) diff --git a/test/components/views/rooms/RoomList-test.js b/test/components/views/rooms/RoomList-test.js index 1e9f80f161..fbb2dd4af9 100644 --- a/test/components/views/rooms/RoomList-test.js +++ b/test/components/views/rooms/RoomList-test.js @@ -94,6 +94,7 @@ describe('RoomList', () => { createRoom({tags: {'m.lowpriority': {}}, name: 'Some unimportant room'}), createRoom({tags: {'custom.tag': {}}, name: 'Some room customly tagged'}), ]; + client.getVisibleRooms = client.getRooms; const roomMap = {}; client.getRooms().forEach((r) => { diff --git a/test/test-utils.js b/test/test-utils.js index 25df9686ff..ef864bf9e0 100644 --- a/test/test-utils.js +++ b/test/test-utils.js @@ -74,6 +74,7 @@ export function createTestClient() { getPushActionsForEvent: sinon.stub(), getRoom: sinon.stub().returns(mkStubRoom()), getRooms: sinon.stub().returns([]), + getVisibleRooms: sinon.stub().returns([]), getGroups: sinon.stub().returns([]), loginFlows: sinon.stub(), on: sinon.stub(), From e3afbb157da2ad494b0910ec5660ca601b0bc3fd Mon Sep 17 00:00:00 2001 From: David Baker Date: Thu, 23 Aug 2018 12:05:09 +0100 Subject: [PATCH 19/26] Don't crash if the value of a room tag is null We were checking that the order was not null, but not the object itself. --- src/stores/RoomListStore.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/stores/RoomListStore.js b/src/stores/RoomListStore.js index 5b38792678..4cc4e048df 100644 --- a/src/stores/RoomListStore.js +++ b/src/stores/RoomListStore.js @@ -284,8 +284,8 @@ class RoomListStore extends Store { if (optimisticRequest && roomB === optimisticRequest.room) metaB = optimisticRequest.metaData; // Make sure the room tag has an order element, if not set it to be the bottom - const a = metaA.order; - const b = metaB.order; + const a = metaA ? metaA.order : undefined; + const b = metaB ? metaB.order : undefined; // Order undefined room tag orders to the bottom if (a === undefined && b !== undefined) { From 3192ecca7aa39d2e5d880206a8a9514a54146358 Mon Sep 17 00:00:00 2001 From: David Baker Date: Thu, 23 Aug 2018 15:25:32 +0100 Subject: [PATCH 20/26] Prepare changelog for v0.13.2 --- CHANGELOG.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index a3edbce2dd..f668ca3f97 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +Changes in [0.13.2](https://github.com/matrix-org/matrix-react-sdk/releases/tag/v0.13.2) (2018-08-23) +===================================================================================================== +[Full Changelog](https://github.com/matrix-org/matrix-react-sdk/compare/v0.13.1...v0.13.2) + + * Don't crash if the value of a room tag is null + [\#2135](https://github.com/matrix-org/matrix-react-sdk/pull/2135) + Changes in [0.13.1](https://github.com/matrix-org/matrix-react-sdk/releases/tag/v0.13.1) (2018-08-20) ===================================================================================================== [Full Changelog](https://github.com/matrix-org/matrix-react-sdk/compare/v0.13.1-rc.1...v0.13.1) From 04d1ba26372bddc76af39580ce25d865b12a87af Mon Sep 17 00:00:00 2001 From: David Baker Date: Thu, 23 Aug 2018 15:25:33 +0100 Subject: [PATCH 21/26] v0.13.2 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index f0a6004366..1bdced5caf 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "matrix-react-sdk", - "version": "0.13.1", + "version": "0.13.2", "description": "SDK for matrix.org using React", "author": "matrix.org", "repository": { From 18ed6eb63c3075efeda709505b77615c7d8c0544 Mon Sep 17 00:00:00 2001 From: David Baker Date: Fri, 24 Aug 2018 11:35:21 +0100 Subject: [PATCH 22/26] Rener terms & conditions in settings If configured in config file --- src/components/structures/UserSettings.js | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/src/components/structures/UserSettings.js b/src/components/structures/UserSettings.js index f4dc92aca4..ea700272b6 100644 --- a/src/components/structures/UserSettings.js +++ b/src/components/structures/UserSettings.js @@ -921,6 +921,25 @@ module.exports = React.createClass({
; }, + _renderTermsAndConditionsLinks: function() { + if (SdkConfig.get().terms_and_conditions_links) { + const tncLinks = []; + for (const tncEntry of SdkConfig.get().terms_and_conditions_links) { + tncLinks.push(); + } + return
+

{ _t("Terms and Conditions") }

+
+ {tncLinks} +
+
; + } else { + return null; + } + }, + _renderClearCache: function() { return

{ _t("Clear Cache") }

@@ -1407,6 +1426,8 @@ module.exports = React.createClass({ { this._renderDeactivateAccount() } + { this._renderTermsAndConditionsLinks() } +
); From c03abddd1c7c3208cd62cf56cc47814b4c15c7c8 Mon Sep 17 00:00:00 2001 From: David Baker Date: Fri, 24 Aug 2018 11:38:38 +0100 Subject: [PATCH 23/26] s/terms and conditions/legal/ --- src/components/structures/UserSettings.js | 2 +- src/i18n/strings/en_EN.json | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/components/structures/UserSettings.js b/src/components/structures/UserSettings.js index ea700272b6..53e1ddea71 100644 --- a/src/components/structures/UserSettings.js +++ b/src/components/structures/UserSettings.js @@ -930,7 +930,7 @@ module.exports = React.createClass({
); } return
-

{ _t("Terms and Conditions") }

+

{ _t("Legal") }

{tncLinks}
diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index b560c7e71b..2371ba0ca6 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -1117,6 +1117,7 @@ "Lazy loading members not supported": "Lazy loading members not supported", "Lazy loading is not supported by your current homeserver.": "Lazy loading is not supported by your current homeserver.", "Deactivate my account": "Deactivate my account", + "Legal": "Legal", "Clear Cache": "Clear Cache", "Clear Cache and Reload": "Clear Cache and Reload", "Updates": "Updates", From 36665d3c6993e18566d41d5d008ee90eb3733eda Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Mon, 27 Aug 2018 17:40:31 +0200 Subject: [PATCH 24/26] Fix: dont show 1:1 avatar for room with only 2 members loaded --- src/components/views/avatars/RoomAvatar.js | 62 +++++----------------- 1 file changed, 14 insertions(+), 48 deletions(-) diff --git a/src/components/views/avatars/RoomAvatar.js b/src/components/views/avatars/RoomAvatar.js index 821448207f..c3336ce36a 100644 --- a/src/components/views/avatars/RoomAvatar.js +++ b/src/components/views/avatars/RoomAvatar.js @@ -19,6 +19,7 @@ import {ContentRepo} from "matrix-js-sdk"; import MatrixClientPeg from "../../../MatrixClientPeg"; import Modal from '../../../Modal'; import sdk from "../../../index"; +import DMRoomMap from '../../../utils/DMRoomMap'; module.exports = React.createClass({ displayName: 'RoomAvatar', @@ -109,56 +110,21 @@ module.exports = React.createClass({ getOneToOneAvatar: function(props) { if (!props.room) return null; - const mlist = props.room.currentState.members; - const userIds = []; - const leftUserIds = []; - // for .. in optimisation to return early if there are >2 keys - for (const uid in mlist) { - if (mlist.hasOwnProperty(uid)) { - if (["join", "invite"].includes(mlist[uid].membership)) { - userIds.push(uid); - } else { - leftUserIds.push(uid); - } - } - if (userIds.length > 2) { - return null; - } + const otherUserId = DMRoomMap.shared().getUserIdForRoomId(props.room.roomId); + if (!otherUserId) { + return null; } - - if (userIds.length == 2) { - let theOtherGuy = null; - if (mlist[userIds[0]].userId == MatrixClientPeg.get().credentials.userId) { - theOtherGuy = mlist[userIds[1]]; - } else { - theOtherGuy = mlist[userIds[0]]; - } - return theOtherGuy.getAvatarUrl( - MatrixClientPeg.get().getHomeserverUrl(), - Math.floor(props.width * window.devicePixelRatio), - Math.floor(props.height * window.devicePixelRatio), - props.resizeMethod, - false, - ); - } else if (userIds.length == 1) { - // The other 1-1 user left, leaving just the current user, so show the left user's avatar - if (leftUserIds.length === 1) { - return mlist[leftUserIds[0]].getAvatarUrl( - MatrixClientPeg.get().getHomeserverUrl(), - props.width, props.height, props.resizeMethod, - false, - ); - } - return mlist[userIds[0]].getAvatarUrl( - MatrixClientPeg.get().getHomeserverUrl(), - Math.floor(props.width * window.devicePixelRatio), - Math.floor(props.height * window.devicePixelRatio), - props.resizeMethod, - false, - ); - } else { - return null; + const otherMember = props.room.getMember(otherUserId); + if (!otherMember) { + return null; } + return otherMember.getAvatarUrl( + MatrixClientPeg.get().getHomeserverUrl(), + Math.floor(props.width * window.devicePixelRatio), + Math.floor(props.height * window.devicePixelRatio), + props.resizeMethod, + false, + ); }, onRoomAvatarClick: function() { From 6a077655e9f60f86b8776e6ca2113c043f7f5226 Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Mon, 27 Aug 2018 18:48:21 +0200 Subject: [PATCH 25/26] bring back old behaviour to also show member avatars if not marked as 1:1 room --- src/components/views/avatars/RoomAvatar.js | 43 +++++++++++++++------- 1 file changed, 29 insertions(+), 14 deletions(-) diff --git a/src/components/views/avatars/RoomAvatar.js b/src/components/views/avatars/RoomAvatar.js index c3336ce36a..48c3d2a0d2 100644 --- a/src/components/views/avatars/RoomAvatar.js +++ b/src/components/views/avatars/RoomAvatar.js @@ -108,23 +108,38 @@ module.exports = React.createClass({ }, getOneToOneAvatar: function(props) { - if (!props.room) return null; + const room = props.room; + if (!room) { + return null; + } + let otherMember = null; + const otherUserId = DMRoomMap.shared().getUserIdForRoomId(room.roomId); + if (otherUserId) { + otherMember = room.getMember(otherUserId); + } else { + // if the room is not marked as a 1:1, but only has max 2 members + // then still try to show any avatar (pref. other member) + const totalMemberCount = room.getJoinedMemberCount() + + room.getInvitedMemberCount(); + const members = room.currentState.getMembers(); + if (totalMemberCount == 2) { + const myUserId = MatrixClientPeg.get().getUserId(); + otherMember = members.find(m => m.userId !== myUserId); + } else if(totalMemberCount == 1) { + otherMember = members[0]; + } - const otherUserId = DMRoomMap.shared().getUserIdForRoomId(props.room.roomId); - if (!otherUserId) { - return null; } - const otherMember = props.room.getMember(otherUserId); - if (!otherMember) { - return null; + if (otherMember) { + return otherMember.getAvatarUrl( + MatrixClientPeg.get().getHomeserverUrl(), + Math.floor(props.width * window.devicePixelRatio), + Math.floor(props.height * window.devicePixelRatio), + props.resizeMethod, + false, + ); } - return otherMember.getAvatarUrl( - MatrixClientPeg.get().getHomeserverUrl(), - Math.floor(props.width * window.devicePixelRatio), - Math.floor(props.height * window.devicePixelRatio), - props.resizeMethod, - false, - ); + return null; }, onRoomAvatarClick: function() { From 06160f5fae35672f3608e4a9435e707e8c964f0f Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Mon, 27 Aug 2018 18:52:33 +0200 Subject: [PATCH 26/26] fix lint --- src/components/views/avatars/RoomAvatar.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/components/views/avatars/RoomAvatar.js b/src/components/views/avatars/RoomAvatar.js index 48c3d2a0d2..e0105159fb 100644 --- a/src/components/views/avatars/RoomAvatar.js +++ b/src/components/views/avatars/RoomAvatar.js @@ -125,10 +125,9 @@ module.exports = React.createClass({ if (totalMemberCount == 2) { const myUserId = MatrixClientPeg.get().getUserId(); otherMember = members.find(m => m.userId !== myUserId); - } else if(totalMemberCount == 1) { + } else if (totalMemberCount == 1) { otherMember = members[0]; } - } if (otherMember) { return otherMember.getAvatarUrl(