From 9f19896df09a0cdc7d870e8f227e31c89d203890 Mon Sep 17 00:00:00 2001 From: David Baker Date: Fri, 17 Aug 2018 14:54:43 +0100 Subject: [PATCH] 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" }