diff --git a/res/css/_components.scss b/res/css/_components.scss index c47222da59..4081e49630 100644 --- a/res/css/_components.scss +++ b/res/css/_components.scss @@ -67,6 +67,7 @@ @import "./views/dialogs/_RestoreKeyBackupDialog.scss"; @import "./views/dialogs/_RoomSettingsDialog.scss"; @import "./views/dialogs/_RoomUpgradeDialog.scss"; +@import "./views/dialogs/_RoomUpgradeWarningDialog.scss"; @import "./views/dialogs/_SetEmailDialog.scss"; @import "./views/dialogs/_SetMxIdDialog.scss"; @import "./views/dialogs/_SetPasswordDialog.scss"; diff --git a/res/css/views/dialogs/_RoomUpgradeWarningDialog.scss b/res/css/views/dialogs/_RoomUpgradeWarningDialog.scss new file mode 100644 index 0000000000..a40c591f6c --- /dev/null +++ b/res/css/views/dialogs/_RoomUpgradeWarningDialog.scss @@ -0,0 +1,29 @@ +/* +Copyright 2019 The Matrix.org Foundation C.I.C. + +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_RoomUpgradeWarningDialog .mx_SettingsFlag { + .mx_ToggleSwitch { + display: inline-block; + vertical-align: middle; + margin-right: 8px; + } + + .mx_SettingsFlag_label { + display: inline-block; + vertical-align: middle; + } +} + diff --git a/src/RoomInvite.js b/src/RoomInvite.js index b2b8689174..48baad5d9f 100644 --- a/src/RoomInvite.js +++ b/src/RoomInvite.js @@ -153,13 +153,8 @@ function _onStartDmFinished(shouldInvite, addrs) { } } -function _onRoomInviteFinished(roomId, shouldInvite, addrs) { - if (!shouldInvite) return; - - const addrTexts = addrs.map((addr) => addr.address); - - // Invite new users to a room - inviteMultipleToRoom(roomId, addrTexts).then((result) => { +export function inviteUsersToRoom(roomId, userIds) { + return inviteMultipleToRoom(roomId, userIds).then((result) => { const room = MatrixClientPeg.get().getRoom(roomId); return _showAnyInviteErrors(result.states, room, result.inviter); }).catch((err) => { @@ -172,6 +167,15 @@ function _onRoomInviteFinished(roomId, shouldInvite, addrs) { }); } +function _onRoomInviteFinished(roomId, shouldInvite, addrs) { + if (!shouldInvite) return; + + const addrTexts = addrs.map((addr) => addr.address); + + // Invite new users to a room + inviteUsersToRoom(roomId, addrTexts); +} + // TODO: Immutable DMs replaces this function _isDmChat(addrTexts) { if (addrTexts.length === 1 && getAddressType(addrTexts[0]) === 'mx-user-id') { diff --git a/src/SlashCommands.js b/src/SlashCommands.js index 31e7ca4f39..dc16618a21 100644 --- a/src/SlashCommands.js +++ b/src/SlashCommands.js @@ -32,6 +32,7 @@ import { getAddressType } from './UserAddress'; import { abbreviateUrl } from './utils/UrlUtils'; import { getDefaultIdentityServerUrl, useDefaultIdentityServer } from './utils/IdentityServerUtils'; import {isPermalinkHost, parsePermalink} from "./utils/permalinks/Permalinks"; +import {inviteUsersToRoom} from "./RoomInvite"; const singleMxcUpload = async () => { return new Promise((resolve) => { @@ -154,70 +155,54 @@ export const CommandMap = { return reject(_t("You do not have the required permissions to use this command.")); } + const RoomUpgradeWarningDialog = sdk.getComponent("dialogs.RoomUpgradeWarningDialog"); + const {finished} = Modal.createTrackedDialog('Slash Commands', 'upgrade room confirmation', - QuestionDialog, { - title: _t('Room upgrade confirmation'), - description: ( -
-

{_t("Upgrading a room can be destructive and isn't always necessary.")}

-

- {_t( - "Room upgrades are usually recommended when a room version is considered " + - "unstable. Unstable room versions might have bugs, missing features, or " + - "security vulnerabilities.", - {}, { - "i": (sub) => {sub}, - }, - )} -

-

- {_t( - "Room upgrades usually only affect server-side processing of the " + - "room. If you're having problems with your Riot client, please file an issue " + - "with .", - {}, { - "i": (sub) => {sub}, - "issueLink": () => { - return - https://github.com/vector-im/riot-web/issues/new/choose - ; - }, - }, - )} -

-

- {_t( - "Warning: Upgrading a room will not automatically migrate room " + - "members to the new version of the room. We'll post a link to the new room " + - "in the old version of the room - room members will have to click this link to " + - "join the new room.", - {}, { - "b": (sub) => {sub}, - "i": (sub) => {sub}, - }, - )} -

-

- {_t( - "Please confirm that you'd like to go forward with upgrading this room " + - "from to .", - {}, - { - oldVersion: () => {room ? room.getVersion() : "1"}, - newVersion: () => {args}, - }, - )} -

-
- ), - button: _t("Upgrade"), - }); + RoomUpgradeWarningDialog, {roomId: roomId, targetVersion: args}); - return success(finished.then(([confirm]) => { - if (!confirm) return; + return success(finished.then(async ([resp]) => { + if (!resp.continue) return; - return cli.upgradeRoom(roomId, args); + let checkForUpgradeFn; + try { + const upgradePromise = cli.upgradeRoom(roomId, args); + + // We have to wait for the js-sdk to give us the room back so + // we can more effectively abuse the MultiInviter behaviour + // which heavily relies on the Room object being available. + if (resp.invite) { + checkForUpgradeFn = async (newRoom) => { + // The upgradePromise should be done by the time we await it here. + const {replacement_room: newRoomId} = await upgradePromise; + if (newRoom.roomId !== newRoomId) return; + + const joinedMembers = room.getJoinedMembers() + .map(m => m.userId).filter(m => m !== cli.getUserId()); + + if (joinedMembers.length > 0) { + // Errors are handled internally to this function + await inviteUsersToRoom(newRoomId, joinedMembers); + } + + cli.removeListener('Room', checkForUpgradeFn); + }; + cli.on('Room', checkForUpgradeFn); + } + + // We have to await after so that the checkForUpgradesFn has a proper reference + // to the new room's ID. + await upgradePromise; + } catch (e) { + console.error(e); + + if (checkForUpgradeFn) cli.removeListener('Room', checkForUpgradeFn); + + const ErrorDialog = sdk.getComponent('dialogs.ErrorDialog'); + Modal.createTrackedDialog('Slash Commands', 'room upgrade error', ErrorDialog, { + title: _t('Error upgrading room'), + description: _t('Double check that your server supports the room version chosen and try again.'), + }); + } })); } return reject(this.getUsage()); diff --git a/src/components/views/dialogs/RoomUpgradeWarningDialog.js b/src/components/views/dialogs/RoomUpgradeWarningDialog.js new file mode 100644 index 0000000000..3a243a2d93 --- /dev/null +++ b/src/components/views/dialogs/RoomUpgradeWarningDialog.js @@ -0,0 +1,136 @@ +/* +Copyright 2019 The Matrix.org Foundation C.I.C. + +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 {_t} from "../../../languageHandler"; +import sdk from "../../../index"; +import LabelledToggleSwitch from "../elements/LabelledToggleSwitch"; +import MatrixClientPeg from "../../../MatrixClientPeg"; + +export default class RoomUpgradeWarningDialog extends React.Component { + static propTypes = { + onFinished: PropTypes.func.isRequired, + roomId: PropTypes.string.isRequired, + targetVersion: PropTypes.string.isRequired, + }; + + constructor(props) { + super(props); + + const room = MatrixClientPeg.get().getRoom(this.props.roomId); + const joinRules = room ? room.currentState.getStateEvents("m.room.join_rules", "") : null; + const isPrivate = joinRules ? joinRules.getContent()['join_rule'] !== 'public' : true; + this.state = { + currentVersion: room ? room.getVersion() : "1", + isPrivate, + inviteUsersToNewRoom: true, + }; + } + + _onContinue = () => { + this.props.onFinished({continue: true, invite: this.state.isPrivate && this.state.inviteUsersToNewRoom}); + }; + + _onCancel = () => { + this.props.onFinished({continue: false, invite: false}); + }; + + _onInviteUsersToggle = (newVal) => { + this.setState({inviteUsersToNewRoom: newVal}); + }; + + render() { + const BaseDialog = sdk.getComponent('views.dialogs.BaseDialog'); + const DialogButtons = sdk.getComponent('views.elements.DialogButtons'); + + let inviteToggle = null; + if (this.state.isPrivate) { + inviteToggle = ( + + ); + } + + return ( + +
+

{_t("Upgrading a room can be destructive and isn't always necessary.")}

+

+ {_t( + "Room upgrades are usually recommended when a room version is considered " + + "unstable. Unstable room versions might have bugs, missing features, or " + + "security vulnerabilities.", + {}, { + "i": (sub) => {sub}, + }, + )} +

+

+ {_t( + "Room upgrades usually only affect server-side processing of the " + + "room. If you're having problems with your Riot client, please file an issue " + + "with .", + {}, { + "i": (sub) => {sub}, + "issueLink": () => { + return + https://github.com/vector-im/riot-web/issues/new/choose + ; + }, + }, + )} +

+

+ {_t( + "Warning: Upgrading a room will not automatically migrate room " + + "members to the new version of the room. We'll post a link to the new room " + + "in the old version of the room - room members will have to click this link to " + + "join the new room.", + {}, { + "b": (sub) => {sub}, + "i": (sub) => {sub}, + }, + )} +

+

+ {_t( + "Please confirm that you'd like to go forward with upgrading this room " + + "from to .", + {}, + { + oldVersion: () => {this.state.currentVersion}, + newVersion: () => {this.props.targetVersion}, + }, + )} +

+ {inviteToggle} +
+ +
+ ); + } +} diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index 9136f432dd..7b8da63377 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -154,13 +154,8 @@ "To use it, just wait for autocomplete results to load and tab through them.": "To use it, just wait for autocomplete results to load and tab through them.", "Upgrades a room to a new version": "Upgrades a room to a new version", "You do not have the required permissions to use this command.": "You do not have the required permissions to use this command.", - "Room upgrade confirmation": "Room upgrade confirmation", - "Upgrading a room can be destructive and isn't always necessary.": "Upgrading a room can be destructive and isn't always necessary.", - "Room upgrades are usually recommended when a room version is considered unstable. Unstable room versions might have bugs, missing features, or security vulnerabilities.": "Room upgrades are usually recommended when a room version is considered unstable. Unstable room versions might have bugs, missing features, or security vulnerabilities.", - "Room upgrades usually only affect server-side processing of the room. If you're having problems with your Riot client, please file an issue with .": "Room upgrades usually only affect server-side processing of the room. If you're having problems with your Riot client, please file an issue with .", - "Warning: Upgrading a room will not automatically migrate room members to the new version of the room. We'll post a link to the new room in the old version of the room - room members will have to click this link to join the new room.": "Warning: Upgrading a room will not automatically migrate room members to the new version of the room. We'll post a link to the new room in the old version of the room - room members will have to click this link to join the new room.", - "Please confirm that you'd like to go forward with upgrading this room from to .": "Please confirm that you'd like to go forward with upgrading this room from to .", - "Upgrade": "Upgrade", + "Error upgrading room": "Error upgrading room", + "Double check that your server supports the room version chosen and try again.": "Double check that your server supports the room version chosen and try again.", "Changes your display nickname": "Changes your display nickname", "Changes your display nickname in the current room only": "Changes your display nickname in the current room only", "Changes the avatar of the current room": "Changes the avatar of the current room", @@ -715,6 +710,7 @@ "Camera": "Camera", "Voice & Video": "Voice & Video", "This room is not accessible by remote Matrix servers": "This room is not accessible by remote Matrix servers", + "Warning: Upgrading a room will not automatically migrate room members to the new version of the room. We'll post a link to the new room in the old version of the room - room members will have to click this link to join the new room.": "Warning: Upgrading a room will not automatically migrate room members to the new version of the room. We'll post a link to the new room in the old version of the room - room members will have to click this link to join the new room.", "Upgrade this room to the recommended room version": "Upgrade this room to the recommended room version", "this room": "this room", "View older messages in %(roomName)s.": "View older messages in %(roomName)s.", @@ -1430,6 +1426,12 @@ "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", + "Invite joined members to the new room automatically": "Invite joined members to the new room automatically", + "Room upgrade confirmation": "Room upgrade confirmation", + "Upgrading a room can be destructive and isn't always necessary.": "Upgrading a room can be destructive and isn't always necessary.", + "Room upgrades are usually recommended when a room version is considered unstable. Unstable room versions might have bugs, missing features, or security vulnerabilities.": "Room upgrades are usually recommended when a room version is considered unstable. Unstable room versions might have bugs, missing features, or security vulnerabilities.", + "Room upgrades usually only affect server-side processing of the room. If you're having problems with your Riot client, please file an issue with .": "Room upgrades usually only affect server-side processing of the room. If you're having problems with your Riot client, please file an issue with .", + "Please confirm that you'd like to go forward with upgrading this room from to .": "Please confirm that you'd like to go forward with upgrading this room from to .", "Sign out and remove encryption keys?": "Sign out and remove encryption keys?", "Clear Storage and Sign Out": "Clear Storage and Sign Out", "Send Logs": "Send Logs", diff --git a/src/utils/MultiInviter.js b/src/utils/MultiInviter.js index 8b952a2b5b..887d829d76 100644 --- a/src/utils/MultiInviter.js +++ b/src/utils/MultiInviter.js @@ -154,6 +154,8 @@ export default class MultiInviter { return; } + console.error(err); + let errorText; let fatal = false; if (err.errcode === 'M_FORBIDDEN') {