From 2f4fac0f37752023e71a7dae47f640729670e2a5 Mon Sep 17 00:00:00 2001 From: Arnei Date: Wed, 19 Oct 2022 12:51:31 +0200 Subject: [PATCH] Add leave room warning for last admin If the last room administrator leaves a room, other users cannot gain admin privilges anymore, leaving the room in an unmoderable state. To help in avoiding this scenario without actually preventing an admin from leaving the room if they really want, this commit adds a new warning message. Attempts to help with: https://github.com/vector-im/element-web/issues/2855 Signed-off-by: Arne Wilken arnepokemon@yahoo.de --- src/components/structures/MatrixChat.tsx | 24 ++++++++++++++++++++++++ src/i18n/strings/en_EN.json | 1 + src/utils/TypeUtils.ts | 11 +++++++++++ 3 files changed, 36 insertions(+) diff --git a/src/components/structures/MatrixChat.tsx b/src/components/structures/MatrixChat.tsx index 515355b63d..368a0492a5 100644 --- a/src/components/structures/MatrixChat.tsx +++ b/src/components/structures/MatrixChat.tsx @@ -138,6 +138,7 @@ import { UseCaseSelection } from '../views/elements/UseCaseSelection'; import { ValidatedServerConfig } from '../../utils/ValidatedServerConfig'; import { isLocalRoom } from '../../utils/localRoom/isLocalRoom'; import { viewUserDeviceSettings } from '../../actions/handlers/viewUserDeviceSettings'; +import { isNumberArray } from '../../utils/TypeUtils'; // legacy export export { default as Views } from "../../Views"; @@ -1125,6 +1126,29 @@ export default class MatrixChat extends React.PureComponent { )); } } + + const client = MatrixClientPeg.get(); + 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 (isNumberArray(userLevelValues)) { + const maxUserLevel = Math.max(...userLevelValues); + // If the user is the only user with highest power level + if (maxUserLevel === currentUserLevel && + userLevelValues.lastIndexOf(maxUserLevel) == userLevelValues.indexOf(maxUserLevel)) { + warnings.push(( + + { ' '/* Whitespace, otherwise the sentences get smashed together */ } + { _t("You are the sole person with the highest role in this room. " + + "If you leave, the room could become unmoderable. Consider giving " + + "another user your role.") } + + )); + } + } + return warnings; } diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index fa0b1690dc..661aae2c7a 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -3232,6 +3232,7 @@ "You are the only person here. If you leave, no one will be able to join in the future, including you.": "You are the only person here. If you leave, no one will be able to join in the future, including you.", "This space is not public. You will not be able to rejoin without an invite.": "This space is not public. You will not be able to rejoin without an invite.", "This room is not public. You will not be able to rejoin without an invite.": "This room is not public. You will not be able to rejoin without an invite.", + "You are the sole person with the highest role in this room. If you leave, the room could become unmoderable. Consider giving another user your role.": "You are the sole person with the highest role in this room. If you leave, the room could become unmoderable. Consider giving another user your role.", "Are you sure you want to leave the space '%(spaceName)s'?": "Are you sure you want to leave the space '%(spaceName)s'?", "Are you sure you want to leave the room '%(roomName)s'?": "Are you sure you want to leave the room '%(roomName)s'?", "Failed to forget room %(errCode)s": "Failed to forget room %(errCode)s", diff --git a/src/utils/TypeUtils.ts b/src/utils/TypeUtils.ts index abdd0eb2a0..286d844e9e 100644 --- a/src/utils/TypeUtils.ts +++ b/src/utils/TypeUtils.ts @@ -28,3 +28,14 @@ export function makeType(Type: any, opts: any) { Object.assign(c, opts); return c; } + +/** + * Typeguard that checks if an unknown variable is an array of numbers + * @param value the variable to check + * @returns true if the variable is an array of numbers, false otherwise + */ +export function isNumberArray(value: unknown): value is number[] { + return ( + Array.isArray(value) && value.every(element => typeof element === "number") + ); +}