/* * * Copyright 2024 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. * / */ import React, { useState, JSX, PropsWithChildren } from "react"; import { Button } from "@vector-im/compound-web"; import { compare } from "matrix-js-sdk/src/utils"; import { useMatrixClientContext } from "../../../contexts/MatrixClientContext"; import PowerSelector from "../elements/PowerSelector"; import { _t } from "../../../languageHandler"; import SettingsFieldset from "./SettingsFieldset"; /** * Display in a fieldset, the power level of the users and allow to change them. * The apply button is disabled until the power level of an user is changed. * If there is no user to display, the children is displayed instead. */ interface PowerLevelSelectorProps { /** * The power levels of the users * The key is the user id and the value is the power level */ userLevels: Record; /** * Whether the user can change the power levels of other users */ canChangeLevels: boolean; /** * The current user power level */ currentUserLevel: number; /** * The callback when the apply button is clicked * @param value - new power level for the user * @param userId - the user id */ onClick: (value: number, userId: string) => void; /** * Filter the users to display * @param user */ filter: (user: string) => boolean; /** * The title of the fieldset */ title: string; } export function PowerLevelSelector({ userLevels, canChangeLevels, currentUserLevel, onClick, filter, title, children, }: PropsWithChildren): JSX.Element | null { const matrixClient = useMatrixClientContext(); const [currentPowerLevel, setCurrentPowerLevel] = useState<{ value: number; userId: string } | null>(null); // If the power level has changed, we need to enable the apply button const powerLevelChanged = Boolean( currentPowerLevel && currentPowerLevel.value !== userLevels[currentPowerLevel?.userId], ); // We sort the users by power level, then we filter them const users = Object.keys(userLevels) .sort((userA, userB) => sortUser(userA, userB, userLevels)) .filter(filter); // No user to display, we return the children into fragment to convert it to JSX.Element type if (!users.length) return <>{children}; return ( {users.map((userId) => { // We only want to display users with a valid power level aka an integer if (!Number.isInteger(userLevels[userId])) return; const isMe = userId === matrixClient.getUserId(); // If I can change levels, I can change the level of anyone with a lower level than mine const canChange = canChangeLevels && (userLevels[userId] < currentUserLevel || isMe); // When the new power level is selected, the fields are rerendered and we need to keep the current value const userLevel = currentPowerLevel?.userId === userId ? currentPowerLevel?.value : userLevels[userId]; return ( setCurrentPowerLevel({ value, userId })} /> ); })} ); } /** * Sort the users by power level, then by name * @param userA * @param userB * @param userLevels */ function sortUser(userA: string, userB: string, userLevels: PowerLevelSelectorProps["userLevels"]): number { const powerLevelDiff = userLevels[userA] - userLevels[userB]; return powerLevelDiff !== 0 ? powerLevelDiff : compare(userA.toLocaleLowerCase(), userB.toLocaleLowerCase()); }