element-web/src/createRoom.ts

399 lines
15 KiB
TypeScript
Raw Normal View History

/*
Copyright 2015, 2016 OpenMarket Ltd
Copyright 2019, 2020 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.
*/
Prepare for Element Call integration (#9224) * Improve accessibility and testability of Tooltip Adding a role to Tooltip was motivated by React Testing Library's reliance on accessibility-related attributes to locate elements. * Make the ReadyWatchingStore constructor safer The ReadyWatchingStore constructor previously had a chance to immediately call onReady, which was dangerous because it was potentially calling the derived class's onReady at a point when the derived class hadn't even finished construction yet. In normal usage, I guess this never was a problem, but it was causing some of the tests I was writing to crash. This is solved by separating out the onReady call into a start method. * Rename 1:1 call components to 'LegacyCall' to reflect the fact that they're slated for removal, and to not clash with the new Call code. * Refactor VideoChannelStore into Call and CallStore Call is an abstract class that currently only has a Jitsi implementation, but this will make it easy to later add an Element Call implementation. * Remove WidgetReady, ClientReady, and ForceHangupCall hacks These are no longer used by the new Jitsi call implementation, and can be removed. * yarn i18n * Delete call map entries instead of inserting nulls * Allow multiple active calls and consolidate call listeners * Fix a race condition when creating a video room * Un-hardcode the media device fallback labels * Apply misc code review fixes * yarn i18n * Disconnect from calls more politely on logout * Fix some strict mode errors * Fix another updateRoom race condition
2022-08-30 19:13:39 +00:00
import { MatrixClient, ClientEvent } from "matrix-js-sdk/src/client";
import { Room } from "matrix-js-sdk/src/models/room";
import { EventType, RoomCreateTypeField, RoomType } from "matrix-js-sdk/src/@types/event";
import { ICreateRoomOpts } from "matrix-js-sdk/src/@types/requests";
import {
HistoryVisibility,
JoinRule,
Preset,
RestrictedAllowType,
Visibility,
} from "matrix-js-sdk/src/@types/partials";
import { logger } from "matrix-js-sdk/src/logger";
import { MatrixClientPeg } from './MatrixClientPeg';
import Modal from './Modal';
import { _t } from './languageHandler';
import dis from "./dispatcher/dispatcher";
import * as Rooms from "./Rooms";
import { getAddressType } from "./UserAddress";
2022-03-24 21:26:09 +00:00
import { VIRTUAL_ROOM_EVENT_TYPE } from "./call-types";
2021-11-11 13:07:41 +00:00
import SpaceStore from "./stores/spaces/SpaceStore";
import { makeSpaceParentEvent } from "./utils/space";
Prepare for Element Call integration (#9224) * Improve accessibility and testability of Tooltip Adding a role to Tooltip was motivated by React Testing Library's reliance on accessibility-related attributes to locate elements. * Make the ReadyWatchingStore constructor safer The ReadyWatchingStore constructor previously had a chance to immediately call onReady, which was dangerous because it was potentially calling the derived class's onReady at a point when the derived class hadn't even finished construction yet. In normal usage, I guess this never was a problem, but it was causing some of the tests I was writing to crash. This is solved by separating out the onReady call into a start method. * Rename 1:1 call components to 'LegacyCall' to reflect the fact that they're slated for removal, and to not clash with the new Call code. * Refactor VideoChannelStore into Call and CallStore Call is an abstract class that currently only has a Jitsi implementation, but this will make it easy to later add an Element Call implementation. * Remove WidgetReady, ClientReady, and ForceHangupCall hacks These are no longer used by the new Jitsi call implementation, and can be removed. * yarn i18n * Delete call map entries instead of inserting nulls * Allow multiple active calls and consolidate call listeners * Fix a race condition when creating a video room * Un-hardcode the media device fallback labels * Apply misc code review fixes * yarn i18n * Disconnect from calls more politely on logout * Fix some strict mode errors * Fix another updateRoom race condition
2022-08-30 19:13:39 +00:00
import { JitsiCall } from "./models/Call";
2021-06-29 12:11:58 +00:00
import { Action } from "./dispatcher/actions";
import ErrorDialog from "./components/views/dialogs/ErrorDialog";
import Spinner from "./components/views/elements/Spinner";
import { ViewRoomPayload } from "./dispatcher/payloads/ViewRoomPayload";
import { findDMForUser } from "./utils/dm/findDMForUser";
import { privateShouldBeEncrypted } from "./utils/rooms";
import { waitForMember } from "./utils/membership";
import { PreferredRoomVersions } from "./utils/PreferredRoomVersions";
2020-07-20 19:50:12 +00:00
// we define a number of interfaces which take their names from the js-sdk
2020-07-20 19:43:49 +00:00
/* eslint-disable camelcase */
export interface IOpts {
dmUserId?: string;
createOpts?: ICreateRoomOpts;
spinner?: boolean;
guestAccess?: boolean;
encryption?: boolean;
inlineErrors?: boolean;
andView?: boolean;
avatar?: File | string; // will upload if given file, else mxcUrl is needed
roomType?: RoomType | string;
historyVisibility?: HistoryVisibility;
parentSpace?: Room;
// contextually only makes sense if parentSpace is specified, if true then will be added to parentSpace as suggested
suggested?: boolean;
joinRule?: JoinRule;
}
/**
* Create a new room, and switch to it.
*
* @param {object=} opts parameters for creating the room
* @param {string=} opts.dmUserId If specified, make this a DM room for this user and invite them
* @param {object=} opts.createOpts set of options to pass to createRoom call.
* @param {bool=} opts.spinner True to show a modal spinner while the room is created.
* Default: True
* @param {bool=} opts.guestAccess Whether to enable guest access.
* Default: True
2020-01-23 14:05:38 +00:00
* @param {bool=} opts.encryption Whether to enable encryption.
* Default: False
* @param {bool=} opts.inlineErrors True to raise errors off the promise instead of resolving to null.
* Default: False
* @param {bool=} opts.andView True to dispatch an action to view the room once it has been created.
*
* @returns {Promise} which resolves to the room id, or null if the
* action was aborted or failed.
*/
export default async function createRoom(opts: IOpts): Promise<string | null> {
opts = opts || {};
if (opts.spinner === undefined) opts.spinner = true;
if (opts.guestAccess === undefined) opts.guestAccess = true;
2020-01-23 14:05:38 +00:00
if (opts.encryption === undefined) opts.encryption = false;
const client = MatrixClientPeg.get();
if (client.isGuest()) {
2021-06-29 12:11:58 +00:00
dis.dispatch({ action: 'require_registration' });
return null;
}
const defaultPreset = opts.dmUserId ? Preset.TrustedPrivateChat : Preset.PrivateChat;
// set some defaults for the creation
const createOpts: ICreateRoomOpts = opts.createOpts || {};
createOpts.preset = createOpts.preset || defaultPreset;
createOpts.visibility = createOpts.visibility || Visibility.Private;
if (opts.dmUserId && createOpts.invite === undefined) {
switch (getAddressType(opts.dmUserId)) {
case 'mx-user-id':
createOpts.invite = [opts.dmUserId];
break;
case 'email':
createOpts.invite_3pid = [{
id_server: MatrixClientPeg.get().getIdentityServerUrl(true),
medium: 'email',
address: opts.dmUserId,
}];
}
}
if (opts.dmUserId && createOpts.is_direct === undefined) {
createOpts.is_direct = true;
}
if (opts.roomType) {
createOpts.creation_content = {
...createOpts.creation_content,
[RoomCreateTypeField]: opts.roomType,
};
// Video rooms require custom power levels
if (opts.roomType === RoomType.ElementVideo) {
createOpts.power_level_content_override = {
events: {
Prepare for Element Call integration (#9224) * Improve accessibility and testability of Tooltip Adding a role to Tooltip was motivated by React Testing Library's reliance on accessibility-related attributes to locate elements. * Make the ReadyWatchingStore constructor safer The ReadyWatchingStore constructor previously had a chance to immediately call onReady, which was dangerous because it was potentially calling the derived class's onReady at a point when the derived class hadn't even finished construction yet. In normal usage, I guess this never was a problem, but it was causing some of the tests I was writing to crash. This is solved by separating out the onReady call into a start method. * Rename 1:1 call components to 'LegacyCall' to reflect the fact that they're slated for removal, and to not clash with the new Call code. * Refactor VideoChannelStore into Call and CallStore Call is an abstract class that currently only has a Jitsi implementation, but this will make it easy to later add an Element Call implementation. * Remove WidgetReady, ClientReady, and ForceHangupCall hacks These are no longer used by the new Jitsi call implementation, and can be removed. * yarn i18n * Delete call map entries instead of inserting nulls * Allow multiple active calls and consolidate call listeners * Fix a race condition when creating a video room * Un-hardcode the media device fallback labels * Apply misc code review fixes * yarn i18n * Disconnect from calls more politely on logout * Fix some strict mode errors * Fix another updateRoom race condition
2022-08-30 19:13:39 +00:00
// Allow all users to send call membership updates
[JitsiCall.MEMBER_EVENT_TYPE]: 0,
// Make widgets immutable, even to admins
"im.vector.modular.widgets": 200,
// Annoyingly, we have to reiterate all the defaults here
[EventType.RoomName]: 50,
[EventType.RoomAvatar]: 50,
[EventType.RoomPowerLevels]: 100,
[EventType.RoomHistoryVisibility]: 100,
[EventType.RoomCanonicalAlias]: 50,
[EventType.RoomTombstone]: 100,
[EventType.RoomServerAcl]: 100,
[EventType.RoomEncryption]: 100,
},
users: {
// Temporarily give ourselves the power to set up a widget
[client.getUserId()]: 200,
},
};
}
}
// By default, view the room after creating it
if (opts.andView === undefined) {
opts.andView = true;
}
2020-01-23 14:05:38 +00:00
createOpts.initial_state = createOpts.initial_state || [];
// Allow guests by default since the room is private and they'd
// need an invite. This means clicking on a 3pid invite email can
// actually drop you right in to a chat.
if (opts.guestAccess) {
createOpts.initial_state.push({
2020-01-23 14:05:38 +00:00
type: 'm.room.guest_access',
state_key: '',
content: {
guest_access: 'can_join',
},
2020-01-23 14:05:38 +00:00
});
}
if (opts.encryption) {
createOpts.initial_state.push({
type: 'm.room.encryption',
state_key: '',
2020-01-23 14:05:38 +00:00
content: {
algorithm: 'm.megolm.v1.aes-sha2',
},
});
}
if (opts.parentSpace) {
2021-07-06 11:05:30 +00:00
createOpts.initial_state.push(makeSpaceParentEvent(opts.parentSpace, true));
if (!opts.historyVisibility) {
opts.historyVisibility = createOpts.preset === Preset.PublicChat
? HistoryVisibility.WorldReadable
: HistoryVisibility.Invited;
}
if (opts.joinRule === JoinRule.Restricted) {
createOpts.room_version = PreferredRoomVersions.RestrictedRooms;
createOpts.initial_state.push({
type: EventType.RoomJoinRules,
content: {
"join_rule": JoinRule.Restricted,
"allow": [{
"type": RestrictedAllowType.RoomMembership,
"room_id": opts.parentSpace.roomId,
}],
},
});
}
}
// we handle the restricted join rule in the parentSpace handling block above
if (opts.joinRule && opts.joinRule !== JoinRule.Restricted) {
2021-07-06 11:05:30 +00:00
createOpts.initial_state.push({
type: EventType.RoomJoinRules,
content: { join_rule: opts.joinRule },
});
}
if (opts.avatar) {
let url = opts.avatar;
if (opts.avatar instanceof File) {
url = await client.uploadContent(opts.avatar);
}
createOpts.initial_state.push({
type: EventType.RoomAvatar,
content: { url },
});
}
if (opts.historyVisibility) {
createOpts.initial_state.push({
type: EventType.RoomHistoryVisibility,
content: {
"history_visibility": opts.historyVisibility,
},
});
}
let modal;
if (opts.spinner) modal = Modal.createDialog(Spinner, null, 'mx_Dialog_spinner');
Prepare for Element Call integration (#9224) * Improve accessibility and testability of Tooltip Adding a role to Tooltip was motivated by React Testing Library's reliance on accessibility-related attributes to locate elements. * Make the ReadyWatchingStore constructor safer The ReadyWatchingStore constructor previously had a chance to immediately call onReady, which was dangerous because it was potentially calling the derived class's onReady at a point when the derived class hadn't even finished construction yet. In normal usage, I guess this never was a problem, but it was causing some of the tests I was writing to crash. This is solved by separating out the onReady call into a start method. * Rename 1:1 call components to 'LegacyCall' to reflect the fact that they're slated for removal, and to not clash with the new Call code. * Refactor VideoChannelStore into Call and CallStore Call is an abstract class that currently only has a Jitsi implementation, but this will make it easy to later add an Element Call implementation. * Remove WidgetReady, ClientReady, and ForceHangupCall hacks These are no longer used by the new Jitsi call implementation, and can be removed. * yarn i18n * Delete call map entries instead of inserting nulls * Allow multiple active calls and consolidate call listeners * Fix a race condition when creating a video room * Un-hardcode the media device fallback labels * Apply misc code review fixes * yarn i18n * Disconnect from calls more politely on logout * Fix some strict mode errors * Fix another updateRoom race condition
2022-08-30 19:13:39 +00:00
let roomId: string;
let room: Promise<Room>;
return client.createRoom(createOpts).catch(function(err) {
// NB This checks for the Synapse-specific error condition of a room creation
// having been denied because the requesting user wanted to publish the room,
// but the server denies them that permission (via room_list_publication_rules).
// The check below responds by retrying without publishing the room.
if (err.httpStatus === 403 && err.errcode === "M_UNKNOWN" && err.data.error === "Not allowed to publish room") {
logger.warn("Failed to publish room, try again without publishing it");
createOpts.visibility = Visibility.Private;
return client.createRoom(createOpts);
} else {
return Promise.reject(err);
}
}).finally(function() {
if (modal) modal.close();
Prepare for Element Call integration (#9224) * Improve accessibility and testability of Tooltip Adding a role to Tooltip was motivated by React Testing Library's reliance on accessibility-related attributes to locate elements. * Make the ReadyWatchingStore constructor safer The ReadyWatchingStore constructor previously had a chance to immediately call onReady, which was dangerous because it was potentially calling the derived class's onReady at a point when the derived class hadn't even finished construction yet. In normal usage, I guess this never was a problem, but it was causing some of the tests I was writing to crash. This is solved by separating out the onReady call into a start method. * Rename 1:1 call components to 'LegacyCall' to reflect the fact that they're slated for removal, and to not clash with the new Call code. * Refactor VideoChannelStore into Call and CallStore Call is an abstract class that currently only has a Jitsi implementation, but this will make it easy to later add an Element Call implementation. * Remove WidgetReady, ClientReady, and ForceHangupCall hacks These are no longer used by the new Jitsi call implementation, and can be removed. * yarn i18n * Delete call map entries instead of inserting nulls * Allow multiple active calls and consolidate call listeners * Fix a race condition when creating a video room * Un-hardcode the media device fallback labels * Apply misc code review fixes * yarn i18n * Disconnect from calls more politely on logout * Fix some strict mode errors * Fix another updateRoom race condition
2022-08-30 19:13:39 +00:00
}).then(async res => {
roomId = res.room_id;
Prepare for Element Call integration (#9224) * Improve accessibility and testability of Tooltip Adding a role to Tooltip was motivated by React Testing Library's reliance on accessibility-related attributes to locate elements. * Make the ReadyWatchingStore constructor safer The ReadyWatchingStore constructor previously had a chance to immediately call onReady, which was dangerous because it was potentially calling the derived class's onReady at a point when the derived class hadn't even finished construction yet. In normal usage, I guess this never was a problem, but it was causing some of the tests I was writing to crash. This is solved by separating out the onReady call into a start method. * Rename 1:1 call components to 'LegacyCall' to reflect the fact that they're slated for removal, and to not clash with the new Call code. * Refactor VideoChannelStore into Call and CallStore Call is an abstract class that currently only has a Jitsi implementation, but this will make it easy to later add an Element Call implementation. * Remove WidgetReady, ClientReady, and ForceHangupCall hacks These are no longer used by the new Jitsi call implementation, and can be removed. * yarn i18n * Delete call map entries instead of inserting nulls * Allow multiple active calls and consolidate call listeners * Fix a race condition when creating a video room * Un-hardcode the media device fallback labels * Apply misc code review fixes * yarn i18n * Disconnect from calls more politely on logout * Fix some strict mode errors * Fix another updateRoom race condition
2022-08-30 19:13:39 +00:00
room = new Promise(resolve => {
const storedRoom = client.getRoom(roomId);
if (storedRoom) {
resolve(storedRoom);
} else {
// The room hasn't arrived down sync yet
const onRoom = (emittedRoom: Room) => {
if (emittedRoom.roomId === roomId) {
resolve(emittedRoom);
client.off(ClientEvent.Room, onRoom);
}
};
client.on(ClientEvent.Room, onRoom);
}
});
if (opts.dmUserId) await Rooms.setDMRoom(roomId, opts.dmUserId);
}).then(() => {
if (opts.parentSpace) {
return SpaceStore.instance.addRoomToSpace(opts.parentSpace, roomId, [client.getDomain()], opts.suggested);
}
}).then(async () => {
if (opts.roomType === RoomType.ElementVideo) {
Prepare for Element Call integration (#9224) * Improve accessibility and testability of Tooltip Adding a role to Tooltip was motivated by React Testing Library's reliance on accessibility-related attributes to locate elements. * Make the ReadyWatchingStore constructor safer The ReadyWatchingStore constructor previously had a chance to immediately call onReady, which was dangerous because it was potentially calling the derived class's onReady at a point when the derived class hadn't even finished construction yet. In normal usage, I guess this never was a problem, but it was causing some of the tests I was writing to crash. This is solved by separating out the onReady call into a start method. * Rename 1:1 call components to 'LegacyCall' to reflect the fact that they're slated for removal, and to not clash with the new Call code. * Refactor VideoChannelStore into Call and CallStore Call is an abstract class that currently only has a Jitsi implementation, but this will make it easy to later add an Element Call implementation. * Remove WidgetReady, ClientReady, and ForceHangupCall hacks These are no longer used by the new Jitsi call implementation, and can be removed. * yarn i18n * Delete call map entries instead of inserting nulls * Allow multiple active calls and consolidate call listeners * Fix a race condition when creating a video room * Un-hardcode the media device fallback labels * Apply misc code review fixes * yarn i18n * Disconnect from calls more politely on logout * Fix some strict mode errors * Fix another updateRoom race condition
2022-08-30 19:13:39 +00:00
// Set up video rooms with a Jitsi call
await JitsiCall.create(await room);
// Reset our power level back to admin so that the widget becomes immutable
Prepare for Element Call integration (#9224) * Improve accessibility and testability of Tooltip Adding a role to Tooltip was motivated by React Testing Library's reliance on accessibility-related attributes to locate elements. * Make the ReadyWatchingStore constructor safer The ReadyWatchingStore constructor previously had a chance to immediately call onReady, which was dangerous because it was potentially calling the derived class's onReady at a point when the derived class hadn't even finished construction yet. In normal usage, I guess this never was a problem, but it was causing some of the tests I was writing to crash. This is solved by separating out the onReady call into a start method. * Rename 1:1 call components to 'LegacyCall' to reflect the fact that they're slated for removal, and to not clash with the new Call code. * Refactor VideoChannelStore into Call and CallStore Call is an abstract class that currently only has a Jitsi implementation, but this will make it easy to later add an Element Call implementation. * Remove WidgetReady, ClientReady, and ForceHangupCall hacks These are no longer used by the new Jitsi call implementation, and can be removed. * yarn i18n * Delete call map entries instead of inserting nulls * Allow multiple active calls and consolidate call listeners * Fix a race condition when creating a video room * Un-hardcode the media device fallback labels * Apply misc code review fixes * yarn i18n * Disconnect from calls more politely on logout * Fix some strict mode errors * Fix another updateRoom race condition
2022-08-30 19:13:39 +00:00
const plEvent = (await room)?.currentState.getStateEvents(EventType.RoomPowerLevels, "");
await client.setPowerLevel(roomId, client.getUserId()!, 100, plEvent);
Voice rooms prototype (#8084) * Add voice room labs flag Signed-off-by: Robin Townsend <robin@robin.town> * Add more widget actions for interacting with Jitsi Signed-off-by: Robin Townsend <robin@robin.town> * Factor out a more generic Jitsi creation utility Signed-off-by: Robin Townsend <robin@robin.town> * Add utilities for managing voice channels Signed-off-by: Robin Townsend <robin@robin.town> * Enable creation of voice rooms Signed-off-by: Robin Townsend <robin@robin.town> * Force a maximized view of voice channel widgets Signed-off-by: Robin Townsend <robin@robin.town> * Add voice channel store Signed-off-by: Robin Townsend <robin@robin.town> * Factor out a more generic FacePile Signed-off-by: Robin Townsend <robin@robin.town> * Implement room tile changes for voice rooms Signed-off-by: Robin Townsend <robin@robin.town> * Add interactive radio component to the left panel Signed-off-by: Robin Townsend <robin@robin.town> * Test voice rooms Signed-off-by: Robin Townsend <robin@robin.town> * Update name of call room type Signed-off-by: Robin Townsend <robin@robin.town> * Clarify that voice rooms are under development Signed-off-by: Robin Townsend <robin@robin.town> * Use readonly Signed-off-by: Robin Townsend <robin@robin.town> * Move acks to the end of handlers Signed-off-by: Robin Townsend <robin@robin.town> * Add comment about avatar URLs coming from Jitsi Signed-off-by: Robin Townsend <robin@robin.town> * Don't use unicode ellipses for translation reasons? Signed-off-by: Robin Townsend <robin@robin.town> * Fix tests Signed-off-by: Robin Townsend <robin@robin.town> * Fix tests, again Signed-off-by: Robin Townsend <robin@robin.town> * Remove unnecessary export Signed-off-by: Robin Townsend <robin@robin.town> * Ack Jitsi events when we wait for them Signed-off-by: Robin Townsend <robin@robin.town>
2022-03-22 22:14:11 +00:00
}
}).then(function() {
Prepare for Element Call integration (#9224) * Improve accessibility and testability of Tooltip Adding a role to Tooltip was motivated by React Testing Library's reliance on accessibility-related attributes to locate elements. * Make the ReadyWatchingStore constructor safer The ReadyWatchingStore constructor previously had a chance to immediately call onReady, which was dangerous because it was potentially calling the derived class's onReady at a point when the derived class hadn't even finished construction yet. In normal usage, I guess this never was a problem, but it was causing some of the tests I was writing to crash. This is solved by separating out the onReady call into a start method. * Rename 1:1 call components to 'LegacyCall' to reflect the fact that they're slated for removal, and to not clash with the new Call code. * Refactor VideoChannelStore into Call and CallStore Call is an abstract class that currently only has a Jitsi implementation, but this will make it easy to later add an Element Call implementation. * Remove WidgetReady, ClientReady, and ForceHangupCall hacks These are no longer used by the new Jitsi call implementation, and can be removed. * yarn i18n * Delete call map entries instead of inserting nulls * Allow multiple active calls and consolidate call listeners * Fix a race condition when creating a video room * Un-hardcode the media device fallback labels * Apply misc code review fixes * yarn i18n * Disconnect from calls more politely on logout * Fix some strict mode errors * Fix another updateRoom race condition
2022-08-30 19:13:39 +00:00
// NB we haven't necessarily blocked on the room promise, so we race
// here with the client knowing that the room exists, causing things
// like https://github.com/vector-im/vector-web/issues/1813
// Even if we were to block on the echo, servers tend to split the room
// state over multiple syncs so we can't atomically know when we have the
// entire thing.
if (opts.andView) {
dis.dispatch<ViewRoomPayload>({
action: Action.ViewRoom,
room_id: roomId,
should_peek: false,
// Creating a room will have joined us to the room,
// so we are expecting the room to come down the sync
// stream, if it hasn't already.
joining: true,
justCreatedOpts: opts,
metricsTrigger: "Created",
});
}
return roomId;
}, function(err) {
// Raise the error if the caller requested that we do so.
if (opts.inlineErrors) throw err;
// We also failed to join the room (this sets joining to false in RoomViewStore)
dis.dispatch({
2021-05-24 13:34:06 +00:00
action: Action.JoinRoomError,
roomId,
});
logger.error("Failed to create room " + roomId + " " + err);
let description = _t("Server may be unavailable, overloaded, or you hit a bug.");
if (err.errcode === "M_UNSUPPORTED_ROOM_VERSION") {
// Technically not possible with the UI as of April 2019 because there's no
// options for the user to change this. However, it's not a bad thing to report
// the error to the user for if/when the UI is available.
description = _t("The server does not support the room version specified.");
}
Modal.createDialog(ErrorDialog, {
title: _t("Failure to create room"),
description,
});
return null;
});
}
/*
* Ensure that for every user in a room, there is at least one device that we
* can encrypt to.
*/
export async function canEncryptToAllUsers(client: MatrixClient, userIds: string[]) {
try {
const usersDeviceMap = await client.downloadKeys(userIds);
// { "@user:host": { "DEVICE": {...}, ... }, ... }
return Object.values(usersDeviceMap).every((userDevices) =>
// { "DEVICE": {...}, ... }
Object.keys(userDevices).length > 0,
);
} catch (e) {
logger.error("Error determining if it's possible to encrypt to all users: ", e);
return false; // assume not
}
}
// Similar to ensureDMExists but also adds creation content
// without polluting ensureDMExists with unrelated stuff (also
// they're never encrypted).
export async function ensureVirtualRoomExists(
client: MatrixClient, userId: string, nativeRoomId: string,
): Promise<string> {
const existingDMRoom = findDMForUser(client, userId);
let roomId;
if (existingDMRoom) {
roomId = existingDMRoom.roomId;
} else {
roomId = await createRoom({
dmUserId: userId,
spinner: false,
andView: false,
createOpts: {
creation_content: {
// This allows us to recognise that the room is a virtual room
// when it comes down our sync stream (we also put the ID of the
// respective native room in there because why not?)
[VIRTUAL_ROOM_EVENT_TYPE]: nativeRoomId,
},
},
});
}
return roomId;
}
export async function ensureDMExists(client: MatrixClient, userId: string): Promise<string> {
const existingDMRoom = findDMForUser(client, userId);
let roomId;
if (existingDMRoom) {
roomId = existingDMRoom.roomId;
} else {
let encryption: boolean = undefined;
if (privateShouldBeEncrypted()) {
encryption = await canEncryptToAllUsers(client, [userId]);
}
2021-06-29 12:11:58 +00:00
roomId = await createRoom({ encryption, dmUserId: userId, spinner: false, andView: false });
await waitForMember(client, roomId, userId);
}
return roomId;
}