Fix room settings roles tab getting confused if power level change fails (#31768)

* Fix room settings roles tab getting confused if power level change fails

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>

* Iterate

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>

---------

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
This commit is contained in:
Michael Telatynski
2026-01-19 12:08:42 +00:00
committed by GitHub
parent 3f021472c2
commit b8ad0b93db
4 changed files with 14 additions and 12 deletions
@@ -14,6 +14,7 @@ import Field from "./Field";
import { KeyBindingAction } from "../../../accessibility/KeyboardShortcuts"; import { KeyBindingAction } from "../../../accessibility/KeyboardShortcuts";
import { getKeyBindingsManager } from "../../../KeyBindingsManager"; import { getKeyBindingsManager } from "../../../KeyBindingsManager";
import { objectHasDiff } from "../../../utils/objects"; import { objectHasDiff } from "../../../utils/objects";
import { arrayHasDiff } from "../../../utils/arrays.ts";
const CUSTOM_VALUE = "SELECT_VALUE_CUSTOM"; const CUSTOM_VALUE = "SELECT_VALUE_CUSTOM";
@@ -38,7 +39,6 @@ interface Props<K extends undefined | string> {
} }
interface IState { interface IState {
levelRoleMap: Partial<Record<number | "undefined", string>>;
// List of power levels to show in the drop-down // List of power levels to show in the drop-down
options: number[]; options: number[];
@@ -47,7 +47,7 @@ interface IState {
custom?: boolean; custom?: boolean;
} }
export default class PowerSelector<K extends undefined | string> extends React.Component<Props<K>, IState> { export default class PowerSelector<K extends undefined | string> extends React.PureComponent<Props<K>, IState> {
public static defaultProps: Partial<Props<any>> = { public static defaultProps: Partial<Props<any>> = {
maxValue: Infinity, maxValue: Infinity,
usersDefault: 0, usersDefault: 0,
@@ -58,7 +58,6 @@ export default class PowerSelector<K extends undefined | string> extends React.C
super(props); super(props);
this.state = { this.state = {
levelRoleMap: {},
// List of power levels to show in the drop-down // List of power levels to show in the drop-down
options: [], options: [],
@@ -85,6 +84,7 @@ export default class PowerSelector<K extends undefined | string> extends React.C
private initStateFromProps(): void { private initStateFromProps(): void {
// This needs to be done now because levelRoleMap has translated strings // This needs to be done now because levelRoleMap has translated strings
const levelRoleMap = Roles.levelRoleMap(this.props.usersDefault); const levelRoleMap = Roles.levelRoleMap(this.props.usersDefault);
const options = Object.keys(levelRoleMap) const options = Object.keys(levelRoleMap)
.filter((level) => { .filter((level) => {
return ( return (
@@ -93,11 +93,13 @@ export default class PowerSelector<K extends undefined | string> extends React.C
}) })
.map((level) => parseInt(level)); .map((level) => parseInt(level));
if (arrayHasDiff(options, this.state.options)) {
this.setState({ options });
}
const isCustom = levelRoleMap[this.props.value] === undefined; const isCustom = levelRoleMap[this.props.value] === undefined;
this.setState({ this.setState({
levelRoleMap,
options,
custom: isCustom, custom: isCustom,
customValue: this.props.value, customValue: this.props.value,
selectValue: isCustom ? CUSTOM_VALUE : this.props.value, selectValue: isCustom ? CUSTOM_VALUE : this.props.value,
@@ -32,7 +32,7 @@ interface PowerLevelSelectorProps {
*/ */
canChangeLevels: boolean; canChangeLevels: boolean;
/** /**
* The current user power level * The current user's own power level
*/ */
currentUserLevel: number; currentUserLevel: number;
/** /**
@@ -105,6 +105,7 @@ export function PowerLevelSelector({
disabled={!canChange} disabled={!canChange}
label={userId} label={userId}
key={userId} key={userId}
maxValue={currentUserLevel}
onChange={async (value) => { onChange={async (value) => {
const userLevelsTmp = Object.assign({}, userLevels); const userLevelsTmp = Object.assign({}, userLevels);
userLevelsTmp[userId] = value; userLevelsTmp[userId] = value;
@@ -26,6 +26,7 @@ import { SettingsSection } from "../../shared/SettingsSection";
import MatrixClientContext from "../../../../../contexts/MatrixClientContext"; import MatrixClientContext from "../../../../../contexts/MatrixClientContext";
import { PowerLevelSelector } from "../../PowerLevelSelector"; import { PowerLevelSelector } from "../../PowerLevelSelector";
import { ElementCallEventType, ElementCallMemberEventType } from "../../../../../call-types"; import { ElementCallEventType, ElementCallMemberEventType } from "../../../../../call-types";
import { objectClone } from "../../../../../utils/objects.ts";
interface IEventShowOpts { interface IEventShowOpts {
isState?: boolean; isState?: boolean;
@@ -221,12 +222,9 @@ export default class RolesRoomSettingsTab extends React.Component<IProps, RolesR
const plEvent = room.currentState.getStateEvents(EventType.RoomPowerLevels, ""); const plEvent = room.currentState.getStateEvents(EventType.RoomPowerLevels, "");
let plContent = plEvent?.getContent<RoomPowerLevelsEventContent>() ?? {}; let plContent = plEvent?.getContent<RoomPowerLevelsEventContent>() ?? {};
// Clone the power levels just in case // Clone the power levels so we can modify it without clobbering the js-sdk
plContent = Object.assign({}, plContent); plContent = objectClone({ users: {}, ...plContent });
plContent["users"]![powerLevelKey] = value;
// powerLevelKey should be a user ID
if (!plContent["users"]) plContent["users"] = {};
plContent["users"][powerLevelKey] = value;
try { try {
await client.sendStateEvent(this.props.room.roomId, EventType.RoomPowerLevels, plContent); await client.sendStateEvent(this.props.room.roomId, EventType.RoomPowerLevels, plContent);
@@ -233,6 +233,7 @@ describe("RolesRoomSettingsTab", () => {
return null; return null;
}); });
mocked(room.currentState.mayClientSendStateEvent).mockReturnValue(true); mocked(room.currentState.mayClientSendStateEvent).mockReturnValue(true);
mocked(room.getMember).mockReturnValue({ powerLevel: 100 } as any);
const { container } = await renderTab(); const { container } = await renderTab();
const selector = container.querySelector(`[placeholder="${cli.getUserId()}"]`)!; const selector = container.querySelector(`[placeholder="${cli.getUserId()}"]`)!;