mirror of
https://github.com/vector-im/element-web.git
synced 2025-08-06 14:27:10 +02:00
Update for compatibility with v12 rooms (#30452)
* Update for compatibility with v12 rooms Stop using powerLevelNorm and reading PL events manually. To support https://github.com/matrix-org/matrix-js-sdk/pull/4937 * Add test for leave space dialog * Don't compute stuff if we don't need it * Use room.client * Use getSafeUserId * Remove client arg * Use getJoinedMembers and add doc * Fix tests * Fix more tests * Fix other test * Clarify comment Co-authored-by: Michael Telatynski <7t3chguy@gmail.com> --------- Co-authored-by: Michael Telatynski <7t3chguy@gmail.com>
This commit is contained in:
parent
12927cc4a7
commit
6a8493c6eb
@ -141,6 +141,7 @@ import { type OpenForwardDialogPayload } from "../../dispatcher/payloads/OpenFor
|
|||||||
import { ShareFormat, type SharePayload } from "../../dispatcher/payloads/SharePayload";
|
import { ShareFormat, type SharePayload } from "../../dispatcher/payloads/SharePayload";
|
||||||
import Markdown from "../../Markdown";
|
import Markdown from "../../Markdown";
|
||||||
import { sanitizeHtmlParams } from "../../Linkify";
|
import { sanitizeHtmlParams } from "../../Linkify";
|
||||||
|
import { isOnlyAdmin } from "../../utils/membership";
|
||||||
|
|
||||||
// legacy export
|
// legacy export
|
||||||
export { default as Views } from "../../Views";
|
export { default as Views } from "../../Views";
|
||||||
@ -1255,18 +1256,12 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
|
|||||||
|
|
||||||
const client = MatrixClientPeg.get();
|
const client = MatrixClientPeg.get();
|
||||||
if (client && roomToLeave) {
|
if (client && roomToLeave) {
|
||||||
const plEvent = roomToLeave.currentState.getStateEvents(EventType.RoomPowerLevels, "");
|
|
||||||
const plContent = plEvent ? plEvent.getContent() : {};
|
|
||||||
const userLevels = plContent.users || {};
|
|
||||||
const currentUserLevel = userLevels[client.getUserId()!];
|
|
||||||
const userLevelValues = Object.values(userLevels);
|
|
||||||
if (userLevelValues.every((x) => typeof x === "number")) {
|
|
||||||
const maxUserLevel = Math.max(...(userLevelValues as number[]));
|
|
||||||
// If the user is the only user with highest power level
|
// If the user is the only user with highest power level
|
||||||
if (
|
if (isOnlyAdmin(roomToLeave)) {
|
||||||
maxUserLevel === currentUserLevel &&
|
const userLevelValues = roomToLeave.getJoinedMembers().map((m) => m.powerLevel);
|
||||||
userLevelValues.lastIndexOf(maxUserLevel) == userLevelValues.indexOf(maxUserLevel)
|
|
||||||
) {
|
const maxUserLevel = Math.max(...(userLevelValues as number[]));
|
||||||
|
|
||||||
const warning =
|
const warning =
|
||||||
maxUserLevel >= 100
|
maxUserLevel >= 100
|
||||||
? _t("leave_room_dialog|room_leave_admin_warning")
|
? _t("leave_room_dialog|room_leave_admin_warning")
|
||||||
@ -1279,7 +1274,6 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return warnings;
|
return warnings;
|
||||||
}
|
}
|
||||||
|
@ -15,23 +15,13 @@ import BaseDialog from "../dialogs/BaseDialog";
|
|||||||
import SpaceStore from "../../../stores/spaces/SpaceStore";
|
import SpaceStore from "../../../stores/spaces/SpaceStore";
|
||||||
import SpaceChildrenPicker from "../spaces/SpaceChildrenPicker";
|
import SpaceChildrenPicker from "../spaces/SpaceChildrenPicker";
|
||||||
import { filterBoolean } from "../../../utils/arrays";
|
import { filterBoolean } from "../../../utils/arrays";
|
||||||
|
import { isOnlyAdmin } from "../../../utils/membership";
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
space: Room;
|
space: Room;
|
||||||
onFinished(leave: boolean, rooms?: Room[]): void;
|
onFinished(leave: boolean, rooms?: Room[]): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
const isOnlyAdmin = (room: Room): boolean => {
|
|
||||||
const userId = room.client.getSafeUserId();
|
|
||||||
if (room.getMember(userId)?.powerLevelNorm !== 100) {
|
|
||||||
return false; // user is not an admin
|
|
||||||
}
|
|
||||||
return room.getJoinedMembers().every((member) => {
|
|
||||||
// return true if every other member has a lower power level (we are highest)
|
|
||||||
return member.userId === userId || member.powerLevelNorm < 100;
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const LeaveSpaceDialog: React.FC<IProps> = ({ space, onFinished }) => {
|
const LeaveSpaceDialog: React.FC<IProps> = ({ space, onFinished }) => {
|
||||||
const spaceChildren = useMemo(() => {
|
const spaceChildren = useMemo(() => {
|
||||||
const roomSet = new Set(SpaceStore.instance.getSpaceFilteredRoomIds(space.roomId));
|
const roomSet = new Set(SpaceStore.instance.getSpaceFilteredRoomIds(space.roomId));
|
||||||
|
@ -131,3 +131,23 @@ export async function waitForMember(
|
|||||||
client.removeListener(RoomStateEvent.NewMember, handler);
|
client.removeListener(RoomStateEvent.NewMember, handler);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if the user is the only joined admin in the room
|
||||||
|
* This function will *not* cause lazy loading of room members, so if these should be included then
|
||||||
|
* the caller needs to make sure members have been loaded.
|
||||||
|
* @param room The room to check if the user is the only admin.
|
||||||
|
* @returns True if the user is the only user with the highest power level, false otherwise
|
||||||
|
*/
|
||||||
|
export function isOnlyAdmin(room: Room): boolean {
|
||||||
|
const currentUserLevel = room.getMember(room.client.getSafeUserId())?.powerLevel;
|
||||||
|
|
||||||
|
const userLevelValues = room.getJoinedMembers().map((m) => m.powerLevel);
|
||||||
|
|
||||||
|
const maxUserLevel = Math.max(...userLevelValues.filter((x) => typeof x === "number"));
|
||||||
|
// If the user is the only user with highest power level
|
||||||
|
return (
|
||||||
|
maxUserLevel === currentUserLevel &&
|
||||||
|
userLevelValues.lastIndexOf(maxUserLevel) == userLevelValues.indexOf(maxUserLevel)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
@ -691,6 +691,8 @@ describe("<MatrixChat />", () => {
|
|||||||
jest.spyOn(spaceRoom, "isSpaceRoom").mockReturnValue(true);
|
jest.spyOn(spaceRoom, "isSpaceRoom").mockReturnValue(true);
|
||||||
|
|
||||||
jest.spyOn(ReleaseAnnouncementStore.instance, "getReleaseAnnouncement").mockReturnValue(null);
|
jest.spyOn(ReleaseAnnouncementStore.instance, "getReleaseAnnouncement").mockReturnValue(null);
|
||||||
|
(room as any).client = mockClient;
|
||||||
|
(spaceRoom as any).client = mockClient;
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("forget_room", () => {
|
describe("forget_room", () => {
|
||||||
@ -775,6 +777,22 @@ describe("<MatrixChat />", () => {
|
|||||||
),
|
),
|
||||||
).toBeInTheDocument();
|
).toBeInTheDocument();
|
||||||
});
|
});
|
||||||
|
it("should warn when user is the last admin", async () => {
|
||||||
|
jest.spyOn(room, "getJoinedMembers").mockReturnValue([
|
||||||
|
{ powerLevel: 100 } as unknown as MatrixJs.RoomMember,
|
||||||
|
{ powerLevel: 0 } as unknown as MatrixJs.RoomMember,
|
||||||
|
]);
|
||||||
|
jest.spyOn(room, "getMember").mockReturnValue({
|
||||||
|
powerLevel: 100,
|
||||||
|
} as unknown as MatrixJs.RoomMember);
|
||||||
|
dispatchAction();
|
||||||
|
await screen.findByRole("dialog");
|
||||||
|
expect(
|
||||||
|
screen.getByText(
|
||||||
|
"You're the only administrator in this room. If you leave, nobody will be able to change room settings or take other important actions.",
|
||||||
|
),
|
||||||
|
).toBeInTheDocument();
|
||||||
|
});
|
||||||
it("should do nothing on cancel", async () => {
|
it("should do nothing on cancel", async () => {
|
||||||
dispatchAction();
|
dispatchAction();
|
||||||
const dialog = await screen.findByRole("dialog");
|
const dialog = await screen.findByRole("dialog");
|
||||||
|
@ -0,0 +1,50 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2025 New Vector Ltd.
|
||||||
|
|
||||||
|
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
|
||||||
|
Please see LICENSE files in the repository root for full details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import React from "react";
|
||||||
|
import { render, screen } from "jest-matrix-react";
|
||||||
|
import { MatrixEvent, type RoomMember } from "matrix-js-sdk/src/matrix";
|
||||||
|
|
||||||
|
import LeaveSpaceDialog from "../../../../../src/components/views/dialogs/LeaveSpaceDialog";
|
||||||
|
import { createTestClient, mkStubRoom } from "../../../../test-utils";
|
||||||
|
|
||||||
|
describe("LeaveSpaceDialog", () => {
|
||||||
|
it("should warn about not being able to rejoin non-public space", () => {
|
||||||
|
const mockClient = createTestClient();
|
||||||
|
const mockSpace = mkStubRoom("myfakeroom", "myfakeroom", mockClient) as any;
|
||||||
|
jest.spyOn(mockSpace.currentState, "getStateEvents").mockReturnValue(
|
||||||
|
new MatrixEvent({
|
||||||
|
type: "m.room.join_rules",
|
||||||
|
content: {
|
||||||
|
join_rule: "invite",
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
render(<LeaveSpaceDialog space={mockSpace} onFinished={jest.fn()} />);
|
||||||
|
|
||||||
|
expect(screen.getByText(/You won't be able to rejoin unless you are re-invited/)).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should warn if user is the only admin", () => {
|
||||||
|
const mockClient = createTestClient();
|
||||||
|
const mockSpace = mkStubRoom("myfakeroom", "myfakeroom", mockClient) as any;
|
||||||
|
jest.spyOn(mockSpace, "getJoinedMembers").mockReturnValue([
|
||||||
|
{ powerLevel: 100 } as unknown as RoomMember,
|
||||||
|
{ powerLevel: 0 } as unknown as RoomMember,
|
||||||
|
]);
|
||||||
|
jest.spyOn(mockSpace, "getMember").mockReturnValue({
|
||||||
|
powerLevel: 100,
|
||||||
|
} as unknown as RoomMember);
|
||||||
|
|
||||||
|
render(<LeaveSpaceDialog space={mockSpace} onFinished={jest.fn()} />);
|
||||||
|
|
||||||
|
expect(
|
||||||
|
screen.getByText(/You're the only admin of this space. Leaving it will mean no one has control over it./),
|
||||||
|
).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
});
|
Loading…
Reference in New Issue
Block a user