Support for room upgrades

* Show when a room upgrade is needed
 * Dialog box to perform the upgrade
This commit is contained in:
David Baker 2018-08-17 14:54:43 +01:00
parent c410a737d9
commit 9f19896df0
10 changed files with 273 additions and 5 deletions

View file

@ -39,6 +39,7 @@
@import "./views/dialogs/_EncryptedEventDialog.scss"; @import "./views/dialogs/_EncryptedEventDialog.scss";
@import "./views/dialogs/_GroupAddressPicker.scss"; @import "./views/dialogs/_GroupAddressPicker.scss";
@import "./views/dialogs/_QuestionDialog.scss"; @import "./views/dialogs/_QuestionDialog.scss";
@import "./views/dialogs/_RoomUpgradeDialog.scss";
@import "./views/dialogs/_SetEmailDialog.scss"; @import "./views/dialogs/_SetEmailDialog.scss";
@import "./views/dialogs/_SetMxIdDialog.scss"; @import "./views/dialogs/_SetMxIdDialog.scss";
@import "./views/dialogs/_SetPasswordDialog.scss"; @import "./views/dialogs/_SetPasswordDialog.scss";
@ -99,6 +100,7 @@
@import "./views/rooms/_RoomSettings.scss"; @import "./views/rooms/_RoomSettings.scss";
@import "./views/rooms/_RoomTile.scss"; @import "./views/rooms/_RoomTile.scss";
@import "./views/rooms/_RoomTooltip.scss"; @import "./views/rooms/_RoomTooltip.scss";
@import "./views/rooms/_RoomUpgradeWarningBar.scss";
@import "./views/rooms/_SearchBar.scss"; @import "./views/rooms/_SearchBar.scss";
@import "./views/rooms/_SearchableEntityList.scss"; @import "./views/rooms/_SearchableEntityList.scss";
@import "./views/rooms/_Stickers.scss"; @import "./views/rooms/_Stickers.scss";

View file

@ -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;
}

View file

@ -20,6 +20,7 @@ limitations under the License.
margin-bottom: 20px; margin-bottom: 20px;
} }
.mx_RoomSettings_upgradeButton,
.mx_RoomSettings_leaveButton, .mx_RoomSettings_leaveButton,
.mx_RoomSettings_unbanButton { .mx_RoomSettings_unbanButton {
@mixin mx_DialogButton; @mixin mx_DialogButton;
@ -27,11 +28,16 @@ limitations under the License.
margin-right: 8px; margin-right: 8px;
} }
.mx_RoomSettings_upgradeButton,
.mx_RoomSettings_leaveButton:hover, .mx_RoomSettings_leaveButton:hover,
.mx_RoomSettings_unbanButton:hover { .mx_RoomSettings_unbanButton:hover {
@mixin mx_DialogButton_hover; @mixin mx_DialogButton_hover;
} }
.mx_RoomSettings_upgradeButton.danger {
@mixin mx_DialogButton_danger;
}
.mx_RoomSettings_integrationsButton_error { .mx_RoomSettings_integrationsButton_error {
position: relative; position: relative;
cursor: not-allowed; cursor: not-allowed;

View file

@ -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%;
}

View file

@ -171,6 +171,10 @@ $progressbar-color: #000;
outline: none; outline: none;
} }
@define-mixin mx_DialogButton_danger {
background-color: $warning-color;
}
@define-mixin mx_DialogButton_hover { @define-mixin mx_DialogButton_hover {
} }

View file

@ -1461,6 +1461,7 @@ module.exports = React.createClass({
const RoomPreviewBar = sdk.getComponent("rooms.RoomPreviewBar"); const RoomPreviewBar = sdk.getComponent("rooms.RoomPreviewBar");
const Loader = sdk.getComponent("elements.Spinner"); const Loader = sdk.getComponent("elements.Spinner");
const TimelinePanel = sdk.getComponent("structures.TimelinePanel"); const TimelinePanel = sdk.getComponent("structures.TimelinePanel");
const RoomUpgradeWarningBar = sdk.getComponent("rooms.RoomUpgradeWarningBar");
if (!this.state.room) { if (!this.state.room) {
if (this.state.roomLoading || this.state.peekLoading) { 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 aux = null;
let hideCancel = false; let hideCancel = false;
if (this.state.editingRoomSettings) { if (this.state.editingRoomSettings) {
@ -1597,6 +1603,9 @@ module.exports = React.createClass({
} else if (this.state.searching) { } else if (this.state.searching) {
hideCancel = true; // has own cancel hideCancel = true; // has own cancel
aux = <SearchBar ref="search_bar" searchInProgress={this.state.searchInProgress} onCancelClick={this.onCancelSearchClick} onSearch={this.onSearch} />; aux = <SearchBar ref="search_bar" searchInProgress={this.state.searchInProgress} onCancelClick={this.onCancelSearchClick} onSearch={this.onSearch} />;
} else if (showRoomUpgradeBar) {
aux = <RoomUpgradeWarningBar room={this.state.room} />;
hideCancel = true;
} else if (this.state.showingPinned) { } else if (this.state.showingPinned) {
hideCancel = true; // has own cancel hideCancel = true; // has own cancel
aux = <PinnedEventsPanel room={this.state.room} onCancelClick={this.onPinnedClick} />; aux = <PinnedEventsPanel room={this.state.room} onCancelClick={this.onPinnedClick} />;

View file

@ -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 = <Spinner />;
} else {
buttons = <DialogButtons
primaryButton={_t(
'Upgrade this room to version %(version)s',
{version: this._targetVersion},
)}
primaryButtonClass="danger"
hasCancel={true}
onPrimaryButtonClick={this._onUpgradeClick}
focus={this.props.focus}
onCancel={this._onCancelClick}
/>;
}
return (
<BaseDialog className="mx_RoomUpgradeDialog"
onFinished={this.onCancelled}
title={_t("Upgrade Room Version")}
contentId='mx_Dialog_content'
onFinished={this.props.onFinished}
hasCancel={true}
>
<p>
{_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:",
)}
</p>
<ol>
<li>{_t("Create a new room with the same name, description and avatar")}</li>
<li>{_t("Update any local room aliases to point to the new room")}</li>
<li>{_t("Stop users from speaking in the old version of the room, and post a message advising users to move to the new room")}</li>
<li>{_t("Put a link back to the old room at the start of the new room so people can see old messages")}</li>
</ol>
{buttons}
</BaseDialog>
);
},
});

View file

@ -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() { _onRoomMemberMembership: function() {
// Update, since our banned user list may have changed // Update, since our banned user list may have changed
this.forceUpdate(); this.forceUpdate();
@ -930,6 +935,13 @@ module.exports = React.createClass({
); );
}); });
let roomUpgradeButton = null;
if (this.props.room.shouldUpgradeToVersion() && this.props.room.userMayUpgradeRoom(myUserId)) {
roomUpgradeButton = <AccessibleButton className="mx_RoomSettings_upgradeButton danger" onClick={this._onRoomUpgradeClick}>
{ _t("Upgrade room to version %(ver)s", {ver: this.props.room.shouldUpgradeToVersion()}) }
</AccessibleButton>;
}
return ( return (
<div className="mx_RoomSettings"> <div className="mx_RoomSettings">
@ -1041,7 +1053,8 @@ module.exports = React.createClass({
<h3>{ _t('Advanced') }</h3> <h3>{ _t('Advanced') }</h3>
<div className="mx_RoomSettings_settings"> <div className="mx_RoomSettings_settings">
{ _t('Internal room ID: ') } <code>{ this.props.room.roomId }</code><br /> { _t('Internal room ID: ') } <code>{ this.props.room.roomId }</code><br />
{ _t('Room version number: ') } <code>{ this.props.room.getVersion() }</code> { _t('Room version number: ') } <code>{ this.props.room.getVersion() }</code><br />
{ roomUpgradeButton }
</div> </div>
</div> </div>
); );

View file

@ -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 (
<div className="mx_RoomUpgradeWarningBar">
<div className="mx_RoomUpgradeWarningBar_header">
{_t("There is a known vulnerability affecting this room.")}
</div>
<div className="mx_RoomUpgradeWarningBar_body">
{_t("This room version is vulnerable to malicious modification of room state.")}
</div>
<p className="mx_RoomUpgradeWarningBar_upgradelink">
<AccessibleButton onClick={this.onUpgradeClick}>
{_t("Click here to upgrade to the latest room version and ensure room integrity is protected.")}
</AccessibleButton>
</p>
<div className="mx_RoomUpgradeWarningBar_small">
{_t("Only room administrators will see this warning")}
</div>
</div>
);
},
});

View file

@ -205,6 +205,7 @@
"Authentication check failed: incorrect password?": "Authentication check failed: incorrect password?", "Authentication check failed: incorrect password?": "Authentication check failed: incorrect password?",
"Failed to join room": "Failed to join room", "Failed to join room": "Failed to join room",
"Message Pinning": "Message Pinning", "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", "Disable Emoji suggestions while typing": "Disable Emoji suggestions while typing",
"Use compact timeline layout": "Use compact timeline layout", "Use compact timeline layout": "Use compact timeline layout",
"Hide removed messages": "Hide removed messages", "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.", "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", "Click here to fix": "Click here to fix",
"To send events of type <eventType/>, you must be a": "To send events of type <eventType/>, you must be a", "To send events of type <eventType/>, you must be a": "To send events of type <eventType/>, 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?", "Who can access this room?": "Who can access this room?",
"Only people who have been invited": "Only people who have been invited", "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", "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", "Ignore request": "Ignore request",
"Loading device info...": "Loading device info...", "Loading device info...": "Loading device info...",
"Encryption key request": "Encryption key request", "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", "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:", "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", "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", "Labs": "Labs",
"These are experimental features that may break in unexpected ways": "These are experimental features that may break in unexpected ways", "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", "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", "Deactivate my account": "Deactivate my account",
"Clear Cache": "Clear Cache", "Clear Cache": "Clear Cache",
"Clear Cache and Reload": "Clear Cache and Reload", "Clear Cache and Reload": "Clear Cache and Reload",
@ -1231,8 +1238,5 @@
"Import": "Import", "Import": "Import",
"Failed to set direct chat tag": "Failed to set direct chat tag", "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 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",
"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."
} }