Update space children (best effort) when upgrading a room

This commit is contained in:
Michael Telatynski 2021-07-09 08:43:41 +01:00
parent f412fb44a7
commit 437d53d1cc
3 changed files with 60 additions and 37 deletions

View file

@ -17,10 +17,10 @@ limitations under the License.
import React from 'react'; import React from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import * as sdk from '../../../index'; import * as sdk from '../../../index';
import { MatrixClientPeg } from '../../../MatrixClientPeg';
import Modal from '../../../Modal'; import Modal from '../../../Modal';
import { _t } from '../../../languageHandler'; import { _t } from '../../../languageHandler';
import { replaceableComponent } from "../../../utils/replaceableComponent"; import { replaceableComponent } from "../../../utils/replaceableComponent";
import { upgradeRoom } from "../../../utils/RoomUpgrade";
@replaceableComponent("views.dialogs.RoomUpgradeDialog") @replaceableComponent("views.dialogs.RoomUpgradeDialog")
export default class RoomUpgradeDialog extends React.Component { export default class RoomUpgradeDialog extends React.Component {
@ -45,7 +45,7 @@ export default class RoomUpgradeDialog extends React.Component {
_onUpgradeClick = () => { _onUpgradeClick = () => {
this.setState({ busy: true }); 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); this.props.onFinished(true);
}).catch((err) => { }).catch((err) => {
const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog"); const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");

View file

@ -335,6 +335,10 @@ export class SpaceStoreClass extends AsyncStoreWithClient<IState> {
return sortBy(parents, r => r.roomId)?.[0] || null; return sortBy(parents, r => r.roomId)?.[0] || null;
} }
public getKnownParents(roomId: string): Set<string> {
return this.parentMap.get(roomId) || new Set();
}
public getSpaceFilteredRoomIds = (space: Room | null): Set<string> => { public getSpaceFilteredRoomIds = (space: Room | null): Set<string> => {
if (!space && SettingsStore.getValue("feature_spaces.all_rooms")) { if (!space && SettingsStore.getValue("feature_spaces.all_rooms")) {
return new Set(this.matrixClient.getVisibleRooms().map(r => r.roomId)); return new Set(this.matrixClient.getVisibleRooms().map(r => r.roomId));

View file

@ -15,60 +15,79 @@ limitations under the License.
*/ */
import { Room } from "matrix-js-sdk/src/models/room"; import { Room } from "matrix-js-sdk/src/models/room";
import { EventType } from "matrix-js-sdk/src/@types/event";
import { inviteUsersToRoom } from "../RoomInvite"; import { inviteUsersToRoom } from "../RoomInvite";
import Modal from "../Modal"; import Modal from "../Modal";
import { _t } from "../languageHandler"; import { _t } from "../languageHandler";
import ErrorDialog from "../components/views/dialogs/ErrorDialog"; import ErrorDialog from "../components/views/dialogs/ErrorDialog";
import SpaceStore from "../stores/SpaceStore";
export async function upgradeRoom( export async function upgradeRoom(
room: Room, room: Room,
targetVersion: string, targetVersion: string,
inviteUsers = false, inviteUsers = false,
// eslint-disable-next-line camelcase handleError = true,
): Promise<{ replacement_room: string }> { updateSpaces = true,
): Promise<string> {
const cli = room.client; const cli = room.client;
let checkForUpgradeFn: (room: Room) => Promise<void>; let newRoomId: string;
try { try {
const upgradePromise = cli.upgradeRoom(room.roomId, targetVersion); ({ replacement_room: newRoomId } = await 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;
} catch (e) { } catch (e) {
if (!handleError) throw e;
console.error(e); console.error(e);
if (checkForUpgradeFn) cli.removeListener('Room', checkForUpgradeFn); Modal.createTrackedDialog("Room Upgrade Error", "", ErrorDialog, {
Modal.createTrackedDialog('Slash Commands', 'room upgrade error', ErrorDialog, {
title: _t('Error upgrading room'), title: _t('Error upgrading room'),
description: _t('Double check that your server supports the room version chosen and try again.'), description: _t('Double check that your server supports the room version chosen and try again.'),
}); });
throw e; 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<void> => {
// 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;
} }