Bail out of RoomSettingsDialog when room is not found (#10662)

* hack to fix console noise from unfaked timers and clearAllModals

* remove old debug logging in AsyncWrapper

* pass room to room settings tabs

* add errorboundary for roomsettingsdialog

* apply strictnullchecks to tabs/room

* dedupe code to set toom in roomsettingdialog

* add unit tests

* test SecurityRoomSettingsTab

* remove snapshot

* strict fixes

* more tests

* 2% more test coverage

* remove roomName from RoomSettingsDialogs state
This commit is contained in:
Kerry 2023-04-27 13:20:02 +12:00 committed by GitHub
parent f6e8ffe750
commit 223892bf0e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
19 changed files with 1077 additions and 121 deletions

View file

@ -1,6 +1,8 @@
/*
Copyright 2019 New Vector Ltd
Copyright 2019 Michael Telatynski <7t3chguy@gmail.com>
Copyright 2023 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.
@ -16,7 +18,7 @@ limitations under the License.
*/
import React from "react";
import { RoomEvent } from "matrix-js-sdk/src/models/room";
import { RoomEvent, Room } from "matrix-js-sdk/src/models/room";
import TabbedView, { Tab } from "../../structures/TabbedView";
import { _t, _td } from "../../../languageHandler";
@ -36,6 +38,7 @@ import { VoipRoomSettingsTab } from "../settings/tabs/room/VoipRoomSettingsTab";
import { ActionPayload } from "../../../dispatcher/payloads";
import { NonEmptyArray } from "../../../@types/common";
import { PollHistoryTab } from "../settings/tabs/room/PollHistoryTab";
import ErrorBoundary from "../elements/ErrorBoundary";
export const ROOM_GENERAL_TAB = "ROOM_GENERAL_TAB";
export const ROOM_VOIP_TAB = "ROOM_VOIP_TAB";
@ -53,15 +56,17 @@ interface IProps {
}
interface IState {
roomName: string;
room: Room;
}
export default class RoomSettingsDialog extends React.Component<IProps, IState> {
class RoomSettingsDialog extends React.Component<IProps, IState> {
private dispatcherRef: string;
public constructor(props: IProps) {
super(props);
this.state = { roomName: "" };
const room = this.getRoom();
this.state = { room };
}
public componentDidMount(): void {
@ -70,6 +75,13 @@ export default class RoomSettingsDialog extends React.Component<IProps, IState>
this.onRoomName();
}
public componentDidUpdate(): void {
if (this.props.roomId !== this.state.room.roomId) {
const room = this.getRoom();
this.setState({ room });
}
}
public componentWillUnmount(): void {
if (this.dispatcherRef) {
dis.unregister(this.dispatcherRef);
@ -78,6 +90,21 @@ export default class RoomSettingsDialog extends React.Component<IProps, IState>
MatrixClientPeg.get().removeListener(RoomEvent.Name, this.onRoomName);
}
/**
* Get room from client
* @returns Room
* @throws when room is not found
*/
private getRoom(): Room {
const room = MatrixClientPeg.get().getRoom(this.props.roomId)!;
// something is really wrong if we encounter this
if (!room) {
throw new Error(`Cannot find room ${this.props.roomId}`);
}
return room;
}
private onAction = (payload: ActionPayload): void => {
// When view changes below us, close the room settings
// whilst the modal is open this can only be triggered when someone hits Leave Room
@ -87,9 +114,8 @@ export default class RoomSettingsDialog extends React.Component<IProps, IState>
};
private onRoomName = (): void => {
this.setState({
roomName: MatrixClientPeg.get().getRoom(this.props.roomId)?.name ?? "",
});
// rerender when the room name changes
this.forceUpdate();
};
private getTabs(): NonEmptyArray<Tab> {
@ -100,7 +126,7 @@ export default class RoomSettingsDialog extends React.Component<IProps, IState>
ROOM_GENERAL_TAB,
_td("General"),
"mx_RoomSettingsDialog_settingsIcon",
<GeneralRoomSettingsTab roomId={this.props.roomId} />,
<GeneralRoomSettingsTab room={this.state.room} />,
"RoomSettingsGeneral",
),
);
@ -110,7 +136,7 @@ export default class RoomSettingsDialog extends React.Component<IProps, IState>
ROOM_VOIP_TAB,
_td("Voice & Video"),
"mx_RoomSettingsDialog_voiceIcon",
<VoipRoomSettingsTab roomId={this.props.roomId} />,
<VoipRoomSettingsTab room={this.state.room} />,
),
);
}
@ -119,12 +145,7 @@ export default class RoomSettingsDialog extends React.Component<IProps, IState>
ROOM_SECURITY_TAB,
_td("Security & Privacy"),
"mx_RoomSettingsDialog_securityIcon",
(
<SecurityRoomSettingsTab
roomId={this.props.roomId}
closeSettingsFn={() => this.props.onFinished(true)}
/>
),
<SecurityRoomSettingsTab room={this.state.room} closeSettingsFn={() => this.props.onFinished(true)} />,
"RoomSettingsSecurityPrivacy",
),
);
@ -133,7 +154,7 @@ export default class RoomSettingsDialog extends React.Component<IProps, IState>
ROOM_ROLES_TAB,
_td("Roles & Permissions"),
"mx_RoomSettingsDialog_rolesIcon",
<RolesRoomSettingsTab roomId={this.props.roomId} />,
<RolesRoomSettingsTab room={this.state.room} />,
"RoomSettingsRolesPermissions",
),
);
@ -144,7 +165,7 @@ export default class RoomSettingsDialog extends React.Component<IProps, IState>
"mx_RoomSettingsDialog_notificationsIcon",
(
<NotificationSettingsTab
roomId={this.props.roomId}
roomId={this.state.room.roomId}
closeSettingsFn={() => this.props.onFinished(true)}
/>
),
@ -158,7 +179,7 @@ export default class RoomSettingsDialog extends React.Component<IProps, IState>
ROOM_BRIDGES_TAB,
_td("Bridges"),
"mx_RoomSettingsDialog_bridgesIcon",
<BridgeSettingsTab roomId={this.props.roomId} />,
<BridgeSettingsTab room={this.state.room} />,
"RoomSettingsBridges",
),
);
@ -169,7 +190,7 @@ export default class RoomSettingsDialog extends React.Component<IProps, IState>
ROOM_POLL_HISTORY_TAB,
_td("Poll history"),
"mx_RoomSettingsDialog_pollsIcon",
<PollHistoryTab roomId={this.props.roomId} onFinished={() => this.props.onFinished(true)} />,
<PollHistoryTab room={this.state.room} onFinished={() => this.props.onFinished(true)} />,
),
);
@ -181,7 +202,7 @@ export default class RoomSettingsDialog extends React.Component<IProps, IState>
"mx_RoomSettingsDialog_warningIcon",
(
<AdvancedRoomSettingsTab
roomId={this.props.roomId}
room={this.state.room}
closeSettingsFn={() => this.props.onFinished(true)}
/>
),
@ -194,7 +215,7 @@ export default class RoomSettingsDialog extends React.Component<IProps, IState>
}
public render(): React.ReactNode {
const roomName = this.state.roomName;
const roomName = this.state.room.name;
return (
<BaseDialog
className="mx_RoomSettingsDialog"
@ -213,3 +234,11 @@ export default class RoomSettingsDialog extends React.Component<IProps, IState>
);
}
}
const WrappedRoomSettingsDialog: React.FC<IProps> = (props) => (
<ErrorBoundary>
<RoomSettingsDialog {...props} />
</ErrorBoundary>
);
export default WrappedRoomSettingsDialog;

View file

@ -70,14 +70,14 @@ const SpaceSettingsDialog: React.FC<IProps> = ({ matrixClient: cli, space, onFin
SpaceSettingsTab.Roles,
_td("Roles & Permissions"),
"mx_RoomSettingsDialog_rolesIcon",
<RolesRoomSettingsTab roomId={space.roomId} />,
<RolesRoomSettingsTab room={space} />,
),
SettingsStore.getValue(UIFeature.AdvancedSettings)
? new Tab(
SpaceSettingsTab.Advanced,
_td("Advanced"),
"mx_RoomSettingsDialog_warningIcon",
<AdvancedRoomSettingsTab roomId={space.roomId} closeSettingsFn={onFinished} />,
<AdvancedRoomSettingsTab room={space} closeSettingsFn={onFinished} />,
)
: null,
].filter(Boolean) as NonEmptyArray<Tab>;

View file

@ -16,9 +16,9 @@ limitations under the License.
import React from "react";
import { EventType } from "matrix-js-sdk/src/@types/event";
import { Room } from "matrix-js-sdk/src/matrix";
import { _t } from "../../../../../languageHandler";
import { MatrixClientPeg } from "../../../../../MatrixClientPeg";
import AccessibleButton, { ButtonEvent } from "../../../elements/AccessibleButton";
import RoomUpgradeDialog from "../../../dialogs/RoomUpgradeDialog";
import Modal from "../../../../../Modal";
@ -29,7 +29,7 @@ import { ViewRoomPayload } from "../../../../../dispatcher/payloads/ViewRoomPayl
import SettingsStore from "../../../../../settings/SettingsStore";
interface IProps {
roomId: string;
room: Room;
closeSettingsFn(): void;
}
@ -64,8 +64,8 @@ export default class AdvancedRoomSettingsTab extends React.Component<IProps, ISt
this.state = {};
// we handle lack of this object gracefully later, so don't worry about it failing here.
const room = MatrixClientPeg.get().getRoom(this.props.roomId);
room?.getRecommendedVersion().then((v) => {
const room = this.props.room;
room.getRecommendedVersion().then((v) => {
const tombstone = room.currentState.getStateEvents(EventType.RoomTombstone, "");
const additionalStateChanges: Partial<IState> = {};
@ -85,8 +85,7 @@ export default class AdvancedRoomSettingsTab extends React.Component<IProps, ISt
}
private upgradeRoom = (): void => {
const room = MatrixClientPeg.get().getRoom(this.props.roomId);
if (room) Modal.createDialog(RoomUpgradeDialog, { room });
Modal.createDialog(RoomUpgradeDialog, { room: this.props.room });
};
private onOldRoomClicked = (e: ButtonEvent): void => {
@ -105,12 +104,11 @@ export default class AdvancedRoomSettingsTab extends React.Component<IProps, ISt
};
public render(): React.ReactNode {
const client = MatrixClientPeg.get();
const room = client.getRoom(this.props.roomId);
const isSpace = room?.isSpaceRoom();
const room = this.props.room;
const isSpace = room.isSpaceRoom();
let unfederatableSection: JSX.Element | undefined;
if (room?.currentState.getStateEvents(EventType.RoomCreate, "")?.getContent()["m.federate"] === false) {
if (room.currentState.getStateEvents(EventType.RoomCreate, "")?.getContent()["m.federate"] === false) {
unfederatableSection = <div>{_t("This room is not accessible by remote Matrix servers")}</div>;
}
@ -143,9 +141,9 @@ export default class AdvancedRoomSettingsTab extends React.Component<IProps, ISt
if (this.state.oldRoomId) {
let copy: string;
if (isSpace) {
copy = _t("View older version of %(spaceName)s.", { spaceName: room?.name ?? this.state.oldRoomId });
copy = _t("View older version of %(spaceName)s.", { spaceName: room.name ?? this.state.oldRoomId });
} else {
copy = _t("View older messages in %(roomName)s.", { roomName: room?.name ?? this.state.oldRoomId });
copy = _t("View older messages in %(roomName)s.", { roomName: room.name ?? this.state.oldRoomId });
}
oldRoomLink = (
@ -160,11 +158,13 @@ export default class AdvancedRoomSettingsTab extends React.Component<IProps, ISt
<div className="mx_SettingsTab_heading">{_t("Advanced")}</div>
<div className="mx_SettingsTab_section mx_SettingsTab_subsectionText">
<span className="mx_SettingsTab_subheading">
{room?.isSpaceRoom() ? _t("Space information") : _t("Room information")}
{room.isSpaceRoom() ? _t("Space information") : _t("Room information")}
</span>
<div>
<span>{_t("Internal room ID")}</span>
<CopyableText getTextToCopy={() => this.props.roomId}>{this.props.roomId}</CopyableText>
<CopyableText getTextToCopy={() => this.props.room.roomId}>
{this.props.room.roomId}
</CopyableText>
</div>
{unfederatableSection}
</div>
@ -172,7 +172,7 @@ export default class AdvancedRoomSettingsTab extends React.Component<IProps, ISt
<span className="mx_SettingsTab_subheading">{_t("Room version")}</span>
<div>
<span>{_t("Room version:")}</span>&nbsp;
{room?.getVersion()}
{room.getVersion()}
</div>
{oldRoomLink}
{roomUpgradeButton}

View file

@ -30,7 +30,7 @@ const BRIDGE_EVENT_TYPES = [
const BRIDGES_LINK = "https://matrix.org/bridges/";
interface IProps {
roomId: string;
room: Room;
}
export default class BridgeSettingsTab extends React.Component<IProps> {
@ -51,9 +51,8 @@ export default class BridgeSettingsTab extends React.Component<IProps> {
public render(): React.ReactNode {
// This settings tab will only be invoked if the following function returns more
// than 0 events, so no validation is needed at this stage.
const bridgeEvents = BridgeSettingsTab.getBridgeStateEvents(this.props.roomId);
const client = MatrixClientPeg.get();
const room = client.getRoom(this.props.roomId);
const bridgeEvents = BridgeSettingsTab.getBridgeStateEvents(this.props.room.roomId);
const room = this.props.room;
let content: JSX.Element;
if (bridgeEvents.length > 0) {

View file

@ -15,6 +15,7 @@ limitations under the License.
*/
import React, { ContextType } from "react";
import { Room } from "matrix-js-sdk/src/matrix";
import { _t } from "../../../../../languageHandler";
import RoomProfileSettings from "../../../room_settings/RoomProfileSettings";
@ -28,7 +29,7 @@ import AliasSettings from "../../../room_settings/AliasSettings";
import PosthogTrackers from "../../../../../PosthogTrackers";
interface IProps {
roomId: string;
room: Room;
}
interface IState {
@ -50,7 +51,7 @@ export default class GeneralRoomSettingsTab extends React.Component<IProps, ISta
private onLeaveClick = (ev: ButtonEvent): void => {
dis.dispatch({
action: "leave_room",
room_id: this.props.roomId,
room_id: this.props.room.roomId,
});
PosthogTrackers.trackInteraction("WebRoomSettingsLeaveButton", ev);
@ -58,17 +59,18 @@ export default class GeneralRoomSettingsTab extends React.Component<IProps, ISta
public render(): React.ReactNode {
const client = this.context;
const room = client.getRoom(this.props.roomId);
const room = this.props.room;
const canSetAliases = true; // Previously, we arbitrarily only allowed admins to do this
const canSetCanonical = room?.currentState.mayClientSendStateEvent("m.room.canonical_alias", client);
const canonicalAliasEv = room?.currentState.getStateEvents("m.room.canonical_alias", "") ?? undefined;
const canSetCanonical = room.currentState.mayClientSendStateEvent("m.room.canonical_alias", client);
const canonicalAliasEv = room.currentState.getStateEvents("m.room.canonical_alias", "") ?? undefined;
const urlPreviewSettings =
room && SettingsStore.getValue(UIFeature.URLPreviews) ? <UrlPreviewSettings room={room} /> : null;
const urlPreviewSettings = SettingsStore.getValue(UIFeature.URLPreviews) ? (
<UrlPreviewSettings room={room} />
) : null;
let leaveSection;
if (room?.getMyMembership() === "join") {
if (room.getMyMembership() === "join") {
leaveSection = (
<>
<span className="mx_SettingsTab_subheading">{_t("Leave room")}</span>
@ -85,12 +87,12 @@ export default class GeneralRoomSettingsTab extends React.Component<IProps, ISta
<div className="mx_SettingsTab mx_GeneralRoomSettingsTab">
<div className="mx_SettingsTab_heading">{_t("General")}</div>
<div className="mx_SettingsTab_section mx_GeneralRoomSettingsTab_profileSection">
<RoomProfileSettings roomId={this.props.roomId} />
<RoomProfileSettings roomId={room.roomId} />
</div>
<div className="mx_SettingsTab_heading">{_t("Room Addresses")}</div>
<AliasSettings
roomId={this.props.roomId}
roomId={room.roomId}
canSetCanonicalAlias={canSetCanonical}
canSetAliases={canSetAliases}
canonicalAliasEvent={canonicalAliasEv}

View file

@ -53,7 +53,7 @@ export default class NotificationsSettingsTab extends React.Component<IProps, IS
public constructor(props: IProps, context: React.ContextType<typeof MatrixClientContext>) {
super(props, context);
this.roomProps = EchoChamber.forRoom(context.getRoom(this.props.roomId));
this.roomProps = EchoChamber.forRoom(context.getRoom(this.props.roomId)!);
let currentSound = "default";
const soundData = Notifier.getSoundForRoom(this.props.roomId);

View file

@ -15,23 +15,20 @@ limitations under the License.
*/
import React, { useContext } from "react";
import { Room } from "matrix-js-sdk/src/matrix";
import MatrixClientContext from "../../../../../contexts/MatrixClientContext";
import { PollHistory } from "../../../polls/pollHistory/PollHistory";
import { RoomPermalinkCreator } from "../../../../../utils/permalinks/Permalinks";
interface IProps {
roomId: string;
room: Room;
onFinished: () => void;
}
export const PollHistoryTab: React.FC<IProps> = ({ roomId, onFinished }) => {
export const PollHistoryTab: React.FC<IProps> = ({ room, onFinished }) => {
const matrixClient = useContext(MatrixClientContext);
const room = matrixClient.getRoom(roomId);
if (!room) {
return null;
}
const permalinkCreator = new RoomPermalinkCreator(room, roomId);
const permalinkCreator = new RoomPermalinkCreator(room, room.roomId);
return (
<div className="mx_SettingsTab">

View file

@ -22,6 +22,7 @@ import { logger } from "matrix-js-sdk/src/logger";
import { throttle, get } from "lodash";
import { compare } from "matrix-js-sdk/src/utils";
import { IContent } from "matrix-js-sdk/src/models/event";
import { Room } from "matrix-js-sdk/src/matrix";
import { _t, _td } from "../../../../../languageHandler";
import { MatrixClientPeg } from "../../../../../MatrixClientPeg";
@ -129,7 +130,7 @@ export class BannedUser extends React.Component<IBannedUserProps> {
}
interface IProps {
roomId: string;
room: Room;
}
export default class RolesRoomSettingsTab extends React.Component<IProps> {
@ -145,7 +146,7 @@ export default class RolesRoomSettingsTab extends React.Component<IProps> {
}
private onRoomStateUpdate = (state: RoomState): void => {
if (state.roomId !== this.props.roomId) return;
if (state.roomId !== this.props.room.roomId) return;
this.onThisRoomMembership();
};
@ -171,8 +172,8 @@ export default class RolesRoomSettingsTab extends React.Component<IProps> {
private onPowerLevelsChanged = (value: number, powerLevelKey: string): void => {
const client = MatrixClientPeg.get();
const room = client.getRoom(this.props.roomId);
const plEvent = room?.currentState.getStateEvents(EventType.RoomPowerLevels, "");
const room = this.props.room;
const plEvent = room.currentState.getStateEvents(EventType.RoomPowerLevels, "");
let plContent = plEvent?.getContent() ?? {};
// Clone the power levels just in case
@ -186,7 +187,7 @@ export default class RolesRoomSettingsTab extends React.Component<IProps> {
plContent["events"][powerLevelKey.slice(eventsLevelPrefix.length)] = value;
} else {
const keyPath = powerLevelKey.split(".");
let parentObj: IContent | undefined;
let parentObj: IContent = {};
let currentObj = plContent;
for (const key of keyPath) {
if (!currentObj[key]) {
@ -198,7 +199,7 @@ export default class RolesRoomSettingsTab extends React.Component<IProps> {
parentObj[keyPath[keyPath.length - 1]] = value;
}
client.sendStateEvent(this.props.roomId, EventType.RoomPowerLevels, plContent).catch((e) => {
client.sendStateEvent(this.props.room.roomId, EventType.RoomPowerLevels, plContent).catch((e) => {
logger.error(e);
Modal.createDialog(ErrorDialog, {
@ -213,8 +214,8 @@ export default class RolesRoomSettingsTab extends React.Component<IProps> {
private onUserPowerLevelChanged = (value: number, powerLevelKey: string): void => {
const client = MatrixClientPeg.get();
const room = client.getRoom(this.props.roomId);
const plEvent = room?.currentState.getStateEvents(EventType.RoomPowerLevels, "");
const room = this.props.room;
const plEvent = room.currentState.getStateEvents(EventType.RoomPowerLevels, "");
let plContent = plEvent?.getContent() ?? {};
// Clone the power levels just in case
@ -224,7 +225,7 @@ export default class RolesRoomSettingsTab extends React.Component<IProps> {
if (!plContent["users"]) plContent["users"] = {};
plContent["users"][powerLevelKey] = value;
client.sendStateEvent(this.props.roomId, EventType.RoomPowerLevels, plContent).catch((e) => {
client.sendStateEvent(this.props.room.roomId, EventType.RoomPowerLevels, plContent).catch((e) => {
logger.error(e);
Modal.createDialog(ErrorDialog, {
@ -239,12 +240,12 @@ export default class RolesRoomSettingsTab extends React.Component<IProps> {
public render(): React.ReactNode {
const client = MatrixClientPeg.get();
const room = client.getRoom(this.props.roomId);
const isSpaceRoom = room?.isSpaceRoom();
const room = this.props.room;
const isSpaceRoom = room.isSpaceRoom();
const plEvent = room?.currentState.getStateEvents(EventType.RoomPowerLevels, "");
const plEvent = room.currentState.getStateEvents(EventType.RoomPowerLevels, "");
const plContent = plEvent ? plEvent.getContent() || {} : {};
const canChangeLevels = room?.currentState.mayClientSendStateEvent(EventType.RoomPowerLevels, client);
const canChangeLevels = room.currentState.mayClientSendStateEvent(EventType.RoomPowerLevels, client);
const plEventsToLabels: Record<EventType | string, string | null> = {
// These will be translated for us later.
@ -392,7 +393,7 @@ export default class RolesRoomSettingsTab extends React.Component<IProps> {
}
}
const banned = room?.getMembersWithMembership("ban");
const banned = room.getMembersWithMembership("ban");
let bannedUsersSection: JSX.Element | undefined;
if (banned?.length) {
const canBanUsers = currentUserLevel >= banLevel;
@ -401,16 +402,16 @@ export default class RolesRoomSettingsTab extends React.Component<IProps> {
<ul>
{banned.map((member) => {
const banEvent = member.events.member?.getContent();
const sender = room?.getMember(member.events.member.getSender());
let bannedBy = member.events.member?.getSender(); // start by falling back to mxid
if (sender) bannedBy = sender.name;
const bannedById = member.events.member?.getSender();
const sender = bannedById ? room.getMember(bannedById) : undefined;
const bannedBy = sender?.name || bannedById; // fallback to mxid
return (
<BannedUser
key={member.userId}
canUnban={canBanUsers}
member={member}
reason={banEvent?.reason}
by={bannedBy}
by={bannedBy!}
/>
);
})}
@ -443,7 +444,7 @@ export default class RolesRoomSettingsTab extends React.Component<IProps> {
.filter(Boolean);
// hide the power level selector for enabling E2EE if it the room is already encrypted
if (client.isRoomEncrypted(this.props.roomId)) {
if (client.isRoomEncrypted(this.props.room.roomId)) {
delete eventsLevels[EventType.RoomEncryption];
}
@ -481,9 +482,7 @@ export default class RolesRoomSettingsTab extends React.Component<IProps> {
<div className="mx_SettingsTab mx_RolesRoomSettingsTab">
<div className="mx_SettingsTab_heading">{_t("Roles & Permissions")}</div>
{privilegedUsersSection}
{canChangeLevels && room !== null && (
<AddPrivilegedUsers room={room} defaultUserLevel={defaultUserLevel} />
)}
{canChangeLevels && <AddPrivilegedUsers room={room} defaultUserLevel={defaultUserLevel} />}
{mutedUsersSection}
{bannedUsersSection}
<SettingsFieldset

View file

@ -20,6 +20,7 @@ import { MatrixEvent } from "matrix-js-sdk/src/models/event";
import { RoomStateEvent } from "matrix-js-sdk/src/models/room-state";
import { EventType } from "matrix-js-sdk/src/@types/event";
import { logger } from "matrix-js-sdk/src/logger";
import { Room } from "matrix-js-sdk/src/matrix";
import { Icon as WarningIcon } from "../../../../../../res/img/warning.svg";
import { _t } from "../../../../../languageHandler";
@ -42,7 +43,7 @@ import PosthogTrackers from "../../../../../PosthogTrackers";
import MatrixClientContext from "../../../../../contexts/MatrixClientContext";
interface IProps {
roomId: string;
room: Room;
closeSettingsFn: () => void;
}
@ -61,7 +62,7 @@ export default class SecurityRoomSettingsTab extends React.Component<IProps, ISt
public constructor(props: IProps, context: React.ContextType<typeof MatrixClientContext>) {
super(props, context);
const state = context.getRoom(this.props.roomId)?.currentState;
const state = this.props.room.currentState;
this.state = {
guestAccess: this.pullContentPropertyFromEvent<GuestAccess>(
@ -75,7 +76,7 @@ export default class SecurityRoomSettingsTab extends React.Component<IProps, ISt
HistoryVisibility.Shared,
),
hasAliases: false, // async loaded in componentDidMount
encrypted: context.isRoomEncrypted(this.props.roomId),
encrypted: context.isRoomEncrypted(this.props.room.roomId),
showAdvancedSection: false,
};
}
@ -104,7 +105,7 @@ export default class SecurityRoomSettingsTab extends React.Component<IProps, ISt
};
private onEncryptionChange = async (): Promise<void> => {
if (this.context.getRoom(this.props.roomId)?.getJoinRule() === JoinRule.Public) {
if (this.props.room.getJoinRule() === JoinRule.Public) {
const dialog = Modal.createDialog(QuestionDialog, {
title: _t("Are you sure you want to add encryption to this public room?"),
description: (
@ -172,7 +173,9 @@ export default class SecurityRoomSettingsTab extends React.Component<IProps, ISt
const beforeEncrypted = this.state.encrypted;
this.setState({ encrypted: true });
this.context
.sendStateEvent(this.props.roomId, EventType.RoomEncryption, { algorithm: "m.megolm.v1.aes-sha2" })
.sendStateEvent(this.props.room.roomId, EventType.RoomEncryption, {
algorithm: "m.megolm.v1.aes-sha2",
})
.catch((e) => {
logger.error(e);
this.setState({ encrypted: beforeEncrypted });
@ -190,7 +193,7 @@ export default class SecurityRoomSettingsTab extends React.Component<IProps, ISt
this.context
.sendStateEvent(
this.props.roomId,
this.props.room.roomId,
EventType.RoomGuestAccess,
{
guest_access: guestAccess,
@ -222,7 +225,7 @@ export default class SecurityRoomSettingsTab extends React.Component<IProps, ISt
this.setState({ history: history });
this.context
.sendStateEvent(
this.props.roomId,
this.props.room.roomId,
EventType.RoomHistoryVisibility,
{
history_visibility: history,
@ -236,22 +239,21 @@ export default class SecurityRoomSettingsTab extends React.Component<IProps, ISt
};
private updateBlacklistDevicesFlag = (checked: boolean): void => {
this.context.getRoom(this.props.roomId)?.setBlacklistUnverifiedDevices(checked);
this.props.room.setBlacklistUnverifiedDevices(checked);
};
private async hasAliases(): Promise<boolean> {
const cli = this.context;
const response = await cli.getLocalAliases(this.props.roomId);
const response = await cli.getLocalAliases(this.props.room.roomId);
const localAliases = response.aliases;
return Array.isArray(localAliases) && localAliases.length !== 0;
}
private renderJoinRule(): JSX.Element {
const client = this.context;
const room = client.getRoom(this.props.roomId);
const room = this.props.room;
let aliasWarning: JSX.Element | undefined;
if (room?.getJoinRule() === JoinRule.Public && !this.state.hasAliases) {
if (room.getJoinRule() === JoinRule.Public && !this.state.hasAliases) {
aliasWarning = (
<div className="mx_SecurityRoomSettingsTab_warning">
<WarningIcon width={15} height={15} />
@ -260,7 +262,7 @@ export default class SecurityRoomSettingsTab extends React.Component<IProps, ISt
);
}
const description = _t("Decide who can join %(roomName)s.", {
roomName: room?.name,
roomName: room.name,
});
return (
@ -342,7 +344,7 @@ export default class SecurityRoomSettingsTab extends React.Component<IProps, ISt
const client = this.context;
const history = this.state.history;
const state = client.getRoom(this.props.roomId)?.currentState;
const state = this.props.room.currentState;
const canChangeHistory = state?.mayClientSendStateEvent(EventType.RoomHistoryVisibility, client);
const options = [
@ -393,7 +395,7 @@ export default class SecurityRoomSettingsTab extends React.Component<IProps, ISt
private renderAdvanced(): JSX.Element {
const client = this.context;
const guestAccess = this.state.guestAccess;
const state = client.getRoom(this.props.roomId)?.currentState;
const state = this.props.room.currentState;
const canSetGuestAccess = state?.mayClientSendStateEvent(EventType.RoomGuestAccess, client);
return (
@ -416,9 +418,9 @@ export default class SecurityRoomSettingsTab extends React.Component<IProps, ISt
public render(): React.ReactNode {
const client = this.context;
const room = client.getRoom(this.props.roomId);
const room = this.props.room;
const isEncrypted = this.state.encrypted;
const hasEncryptionPermission = room?.currentState.mayClientSendStateEvent(EventType.RoomEncryption, client);
const hasEncryptionPermission = room.currentState.mayClientSendStateEvent(EventType.RoomEncryption, client);
const canEnableEncryption = !isEncrypted && hasEncryptionPermission;
let encryptionSettings: JSX.Element | undefined;
@ -428,7 +430,7 @@ export default class SecurityRoomSettingsTab extends React.Component<IProps, ISt
name="blacklistUnverifiedDevices"
level={SettingLevel.ROOM_DEVICE}
onChange={this.updateBlacklistDevicesFlag}
roomId={this.props.roomId}
roomId={this.props.room.roomId}
/>
);
}
@ -436,7 +438,7 @@ export default class SecurityRoomSettingsTab extends React.Component<IProps, ISt
const historySection = this.renderHistory();
let advanced: JSX.Element | undefined;
if (room?.getJoinRule() === JoinRule.Public) {
if (room.getJoinRule() === JoinRule.Public) {
advanced = (
<div className="mx_SettingsTab_section">
<AccessibleButton

View file

@ -18,6 +18,7 @@ import React, { useCallback, useMemo, useState } from "react";
import { JoinRule } from "matrix-js-sdk/src/@types/partials";
import { EventType } from "matrix-js-sdk/src/@types/event";
import { RoomState } from "matrix-js-sdk/src/models/room-state";
import { Room } from "matrix-js-sdk/src/matrix";
import { _t } from "../../../../../languageHandler";
import { MatrixClientPeg } from "../../../../../MatrixClientPeg";
@ -29,14 +30,13 @@ import { useRoomState } from "../../../../../hooks/useRoomState";
import SdkConfig, { DEFAULTS } from "../../../../../SdkConfig";
interface ElementCallSwitchProps {
roomId: string;
room: Room;
}
const ElementCallSwitch: React.FC<ElementCallSwitchProps> = ({ roomId }) => {
const room = useMemo(() => MatrixClientPeg.get().getRoom(roomId), [roomId]);
const isPublic = useMemo(() => room?.getJoinRule() === JoinRule.Public, [room]);
const ElementCallSwitch: React.FC<ElementCallSwitchProps> = ({ room }) => {
const isPublic = useMemo(() => room.getJoinRule() === JoinRule.Public, [room]);
const [content, events, maySend] = useRoomState(
room ?? undefined,
room,
useCallback((state: RoomState) => {
const content = state?.getStateEvents(EventType.RoomPowerLevels, "")?.getContent();
return [
@ -68,12 +68,12 @@ const ElementCallSwitch: React.FC<ElementCallSwitchProps> = ({ roomId }) => {
events[ElementCall.MEMBER_EVENT_TYPE.name] = adminLevel;
}
MatrixClientPeg.get().sendStateEvent(roomId, EventType.RoomPowerLevels, {
MatrixClientPeg.get().sendStateEvent(room.roomId, EventType.RoomPowerLevels, {
events: events,
...content,
});
},
[roomId, content, events, isPublic],
[room.roomId, content, events, isPublic],
);
const brand = SdkConfig.get("element_call").brand ?? DEFAULTS.element_call.brand;
@ -95,14 +95,14 @@ const ElementCallSwitch: React.FC<ElementCallSwitchProps> = ({ roomId }) => {
};
interface Props {
roomId: string;
room: Room;
}
export const VoipRoomSettingsTab: React.FC<Props> = ({ roomId }) => {
export const VoipRoomSettingsTab: React.FC<Props> = ({ room }) => {
return (
<SettingsTab heading={_t("Voice & Video")}>
<SettingsSubsection heading={_t("Call type")}>
<ElementCallSwitch roomId={roomId} />
<ElementCallSwitch room={room} />
</SettingsSubsection>
</SettingsTab>
);