From 437d53d1ccff764978613da396165d27257436f6 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Fri, 9 Jul 2021 08:43:41 +0100 Subject: [PATCH] Update space children (best effort) when upgrading a room --- .../views/dialogs/RoomUpgradeDialog.js | 4 +- src/stores/SpaceStore.tsx | 4 + src/utils/RoomUpgrade.ts | 89 +++++++++++-------- 3 files changed, 60 insertions(+), 37 deletions(-) diff --git a/src/components/views/dialogs/RoomUpgradeDialog.js b/src/components/views/dialogs/RoomUpgradeDialog.js index 90092df7a5..acbb99099f 100644 --- a/src/components/views/dialogs/RoomUpgradeDialog.js +++ b/src/components/views/dialogs/RoomUpgradeDialog.js @@ -17,10 +17,10 @@ limitations under the License. import React from 'react'; import PropTypes from 'prop-types'; import * as sdk from '../../../index'; -import { MatrixClientPeg } from '../../../MatrixClientPeg'; import Modal from '../../../Modal'; import { _t } from '../../../languageHandler'; import { replaceableComponent } from "../../../utils/replaceableComponent"; +import { upgradeRoom } from "../../../utils/RoomUpgrade"; @replaceableComponent("views.dialogs.RoomUpgradeDialog") export default class RoomUpgradeDialog extends React.Component { @@ -45,7 +45,7 @@ export default class RoomUpgradeDialog extends React.Component { _onUpgradeClick = () => { this.setState({ busy: true }); - MatrixClientPeg.get().upgradeRoom(this.props.room.roomId, this._targetVersion).then(() => { + upgradeRoom(this.props.room, this._targetVersion, false, false).then(() => { this.props.onFinished(true); }).catch((err) => { const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog"); diff --git a/src/stores/SpaceStore.tsx b/src/stores/SpaceStore.tsx index 9a2dc027c2..91bc0a027c 100644 --- a/src/stores/SpaceStore.tsx +++ b/src/stores/SpaceStore.tsx @@ -335,6 +335,10 @@ export class SpaceStoreClass extends AsyncStoreWithClient { return sortBy(parents, r => r.roomId)?.[0] || null; } + public getKnownParents(roomId: string): Set { + return this.parentMap.get(roomId) || new Set(); + } + public getSpaceFilteredRoomIds = (space: Room | null): Set => { if (!space && SettingsStore.getValue("feature_spaces.all_rooms")) { return new Set(this.matrixClient.getVisibleRooms().map(r => r.roomId)); diff --git a/src/utils/RoomUpgrade.ts b/src/utils/RoomUpgrade.ts index 7330b23863..e632ec6345 100644 --- a/src/utils/RoomUpgrade.ts +++ b/src/utils/RoomUpgrade.ts @@ -15,60 +15,79 @@ limitations under the License. */ import { Room } from "matrix-js-sdk/src/models/room"; +import { EventType } from "matrix-js-sdk/src/@types/event"; import { inviteUsersToRoom } from "../RoomInvite"; import Modal from "../Modal"; import { _t } from "../languageHandler"; import ErrorDialog from "../components/views/dialogs/ErrorDialog"; +import SpaceStore from "../stores/SpaceStore"; export async function upgradeRoom( room: Room, targetVersion: string, inviteUsers = false, - // eslint-disable-next-line camelcase -): Promise<{ replacement_room: string }> { + handleError = true, + updateSpaces = true, +): Promise { const cli = room.client; - let checkForUpgradeFn: (room: Room) => Promise; + let newRoomId: string; try { - const upgradePromise = cli.upgradeRoom(room.roomId, targetVersion); - - // 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 (inviteUsers) { - checkForUpgradeFn = async (newRoom: Room) => { - // The upgradePromise should be done by the time we await it here. - const { replacement_room: newRoomId } = await upgradePromise; - if (newRoom.roomId !== newRoomId) return; - - const toInvite = [ - ...room.getMembersWithMembership("join"), - ...room.getMembersWithMembership("invite"), - ].map(m => m.userId).filter(m => m !== cli.getUserId()); - - if (toInvite.length > 0) { - // Errors are handled internally to this function - await inviteUsersToRoom(newRoomId, toInvite); - } - - 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. - return upgradePromise; + ({ replacement_room: newRoomId } = await cli.upgradeRoom(room.roomId, targetVersion)); } catch (e) { + if (!handleError) throw e; console.error(e); - if (checkForUpgradeFn) cli.removeListener('Room', checkForUpgradeFn); - - Modal.createTrackedDialog('Slash Commands', 'room upgrade error', ErrorDialog, { + Modal.createTrackedDialog("Room Upgrade Error", "", ErrorDialog, { title: _t('Error upgrading room'), description: _t('Double check that your server supports the room version chosen and try again.'), }); throw e; } + + // 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 (inviteUsers) { + const checkForUpgradeFn = async (newRoom: Room): Promise => { + // The upgradePromise should be done by the time we await it here. + if (newRoom.roomId !== newRoomId) return; + + const toInvite = [ + ...room.getMembersWithMembership("join"), + ...room.getMembersWithMembership("invite"), + ].map(m => m.userId).filter(m => m !== cli.getUserId()); + + if (toInvite.length > 0) { + // Errors are handled internally to this function + await inviteUsersToRoom(newRoomId, toInvite); + } + + cli.removeListener('Room', checkForUpgradeFn); + }; + cli.on('Room', checkForUpgradeFn); + } + + if (updateSpaces) { + const parents = SpaceStore.instance.getKnownParents(room.roomId); + try { + for (const parentId of parents) { + const parent = cli.getRoom(parentId); + if (!parent?.currentState.maySendStateEvent(EventType.SpaceChild, cli.getUserId())) continue; + + const currentEv = parent.currentState.getStateEvents(EventType.SpaceChild, room.roomId); + await cli.sendStateEvent(parentId, EventType.SpaceChild, { + ...(currentEv?.getContent() || {}), // copy existing attributes like suggested + via: [cli.getDomain()], + }, newRoomId); + await cli.sendStateEvent(parentId, EventType.SpaceChild, {}, room.roomId); + } + } catch (e) { + // These errors are not critical to the room upgrade itself + console.warn("Failed to update parent spaces during room upgrade", e); + } + } + + return newRoomId; }