User badges (#160)

* Users badges

* update readme
This commit is contained in:
Aine 2024-11-22 00:37:16 +02:00 committed by GitHub
parent cfd8238edc
commit 20417a67b9
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
14 changed files with 424 additions and 261 deletions

View File

@ -99,6 +99,7 @@ The following changes are already implemented:
* 🎨 [Add preferred theme colors to login page and footer](https://github.com/etkecc/synapse-admin/pull/155) * 🎨 [Add preferred theme colors to login page and footer](https://github.com/etkecc/synapse-admin/pull/155)
* 🔰 [Add "Assign Admin" button to the rooms](https://github.com/etkecc/synapse-admin/pull/156) * 🔰 [Add "Assign Admin" button to the rooms](https://github.com/etkecc/synapse-admin/pull/156)
* 🖼️ [Add rooms' avatars](https://github.com/etkecc/synapse-admin/pull/158) * 🖼️ [Add rooms' avatars](https://github.com/etkecc/synapse-admin/pull/158)
* 🤖 [User Badges](https://github.com/etkecc/synapse-admin/pull/160)
_the list will be updated as new changes are added_ _the list will be updated as new changes are added_

36
docs/user-badges.md Normal file
View File

@ -0,0 +1,36 @@
# User Badges
To help with identifying users with certain roles or permissions, we have implemented a badge system.
These badges are displayed on the user's avatar and have a handy tooltip that explains what the badge means.
## Available Badges
### 🧙‍ You
This badge is displayed on your user's avatar.
Tooltip for this badge will contain additional information, e.g.: `You (Admin)`.
### 👑 Admin
This badge is displayed on homeserver admins' avatars.
Tooltip for this badge is `Admin`.
### 🛡️ Appservice/System-managed
This badge is displayed on users that are managed by an appservices (or system), [more details](./system-users.md).
Tooltip for this badge will contain additional information, e.g.: `System-managed (Bot)`.
### 🤖 Bot
This badge is displayed on bots' avatars (users with the `user_type` set to `bot`).
Tooltip for this badge is `Bot`.
### 📞 Support
This badge is displayed on users that are part of the support team (users with the `user_type` set to `support`).
Tooltip for this badge is `Support`.
### 👤 Regular User
This badge is displayed on regular users' avatars.
Tooltip for this badge is `Regular User`.

View File

@ -1,8 +1,10 @@
import { get } from "lodash"; import { get } from "lodash";
import { Avatar, AvatarProps } from "@mui/material"; import { Avatar, AvatarProps, Badge, Tooltip } from "@mui/material";
import { FieldProps, useRecordContext } from "react-admin"; import { FieldProps, useRecordContext, useTranslate } from "react-admin";
import { useState, useEffect, useCallback } from "react"; import { useState, useEffect, useCallback } from "react";
import { fetchAuthenticatedMedia } from "../utils/fetchMedia"; import { fetchAuthenticatedMedia } from "../utils/fetchMedia";
import { isMXID, isASManaged } from "./mxid";
import storage from "../storage";
const AvatarField = ({ source, ...rest }: AvatarProps & FieldProps) => { const AvatarField = ({ source, ...rest }: AvatarProps & FieldProps) => {
const { alt, classes, sizes, sx, variant } = rest; const { alt, classes, sizes, sx, variant } = rest;
@ -45,6 +47,59 @@ const AvatarField = ({ source, ...rest }: AvatarProps & FieldProps) => {
letter = record.displayname[0].toUpperCase(); letter = record.displayname[0].toUpperCase();
} }
// hacky way to determine the user type
let badge = "";
let tooltip = "";
if (isMXID(record?.id)) {
const translate = useTranslate();
switch (record?.user_type) {
case "bot":
badge = "🤖";
tooltip = translate("resources.users.badge.bot");
break;
case "support":
badge = "📞";
tooltip = translate("resources.users.badge.support");
break;
default:
badge = "👤";
tooltip = translate("resources.users.badge.regular");
break;
}
if (record?.admin) {
badge = "👑";
tooltip = translate("resources.users.badge.admin");
}
if (isASManaged(record?.name)) {
badge = "🛡️";
tooltip = `${translate("resources.users.badge.system_managed")} (${tooltip})`;
}
if (storage.getItem("user_id") === record?.id) {
badge = "🧙‍";
tooltip = `${translate("resources.users.badge.you")} (${tooltip})`;
}
}
// if there is a badge, wrap the Avatar in a Badge and a Tooltip
if (badge) {
return (
<Tooltip title={tooltip}>
<Badge
badgeContent={badge}
overlap="circular"
sx={{ "& .MuiBadge-badge": { width: "10px" } }} // we deliberately set a very small width here, to make the badge actually circular
anchorOrigin={{
vertical: "bottom",
horizontal: "right",
}}
>
<Avatar alt={alt} classes={classes} sizes={sizes} src={src} sx={sx} variant={variant}>
{letter}
</Avatar>
</Badge>
</Tooltip>);
}
return (<Avatar alt={alt} classes={classes} sizes={sizes} src={src} sx={sx} variant={variant}> return (<Avatar alt={alt} classes={classes} sizes={sizes} src={src} sx={sx} variant={variant}>
{letter} {letter}
</Avatar>); </Avatar>);

View File

@ -1,6 +1,15 @@
import { Identifier } from "ra-core"; import { Identifier } from "ra-core";
import { GetConfig } from "./config"; import { GetConfig } from "./config";
const mxidPattern = /^@[^@:]+:[^@:]+$/;
/*
* Check if id is a valid Matrix ID (user)
* @param id The ID to check
* @returns Whether the ID is a valid Matrix ID
*/
export const isMXID = (id: string | Identifier): boolean => mxidPattern.test(id as string);
/** /**
* Check if a user is managed by an application service * Check if a user is managed by an application service
* @param id The user ID to check * @param id The user ID to check

View File

@ -187,6 +187,14 @@ const de: SynapseTranslationMessages = {
modify_managed_user_error: "Das Ändern eines vom System verwalteten Benutzers ist nicht zulässig.", modify_managed_user_error: "Das Ändern eines vom System verwalteten Benutzers ist nicht zulässig.",
username_available: "Benutzername verfügbar", username_available: "Benutzername verfügbar",
}, },
badge: {
you: "Sie",
bot: "Bot",
admin: "Administrator",
support: "Unterstützung",
regular: "Normaler Benutzer",
system_managed: "Systemverwalteter Benutzer",
},
action: { action: {
erase: "Lösche Benutzerdaten", erase: "Lösche Benutzerdaten",
erase_avatar: "Avatar löschen", erase_avatar: "Avatar löschen",

View File

@ -171,6 +171,14 @@ const en: SynapseTranslationMessages = {
overwrite_cancel: "Cancel", overwrite_cancel: "Cancel",
overwrite_confirm: "Overwrite", overwrite_confirm: "Overwrite",
}, },
badge: {
you: "You",
bot: "Bot",
admin: "Admin",
support: "Support",
regular: "Regular User",
system_managed: "System-managed",
},
limits: { limits: {
messages_per_second: "Messages per second", messages_per_second: "Messages per second",
messages_per_second_text: "The number of actions that can be performed in a second.", messages_per_second_text: "The number of actions that can be performed in a second.",

View File

@ -152,6 +152,14 @@ const fa: SynapseTranslationMessages = {
modify_managed_user_error: "لا يُسمح بتغيير المستخدم الذي يديره النظام.", modify_managed_user_error: "لا يُسمح بتغيير المستخدم الذي يديره النظام.",
username_available: "نام کاربری موجود", username_available: "نام کاربری موجود",
}, },
badge: {
you: "شما",
bot: "ربات",
admin: "مدیر",
support: "پشتیبان",
regular: "کاربر عادی",
system_managed: "مدیریت سیستم",
},
action: { action: {
erase: "پاک کردن اطلاعات کاربر", erase: "پاک کردن اطلاعات کاربر",
erase_avatar: "محو الصورة الرمزية", erase_avatar: "محو الصورة الرمزية",

View File

@ -155,6 +155,14 @@ const fr: SynapseTranslationMessages = {
modify_managed_user_error: "La modification d'un utilisateur géré par le système n'est pas autorisée.", modify_managed_user_error: "La modification d'un utilisateur géré par le système n'est pas autorisée.",
username_available: "Nom d'utilisateur disponible", username_available: "Nom d'utilisateur disponible",
}, },
badge: {
you: "Vous",
bot: "Bot",
admin: "Admin",
support: "Support",
regular: "Utilisateur régulier",
system_managed: "Géré par le système",
},
action: { action: {
erase: "Effacer les données de l'utilisateur", erase: "Effacer les données de l'utilisateur",
erase_avatar: "Effacer l'avatar", erase_avatar: "Effacer l'avatar",

8
src/i18n/index.d.ts vendored
View File

@ -163,6 +163,14 @@ interface SynapseTranslationMessages extends TranslationMessages {
overwrite_cancel: string; overwrite_cancel: string;
overwrite_confirm: string; overwrite_confirm: string;
}; };
badge: {
you: string;
bot: string;
admin: string;
support: string;
regular: string;
system_managed: string;
}
limits: { limits: {
messages_per_second: string; messages_per_second: string;
messages_per_second_text: string; messages_per_second_text: string;

View File

@ -153,6 +153,14 @@ const it: SynapseTranslationMessages = {
modify_managed_user_error: "La modifica di un utente gestito dal sistema non è consentita.", modify_managed_user_error: "La modifica di un utente gestito dal sistema non è consentita.",
username_available: "Nome utente disponibile", username_available: "Nome utente disponibile",
}, },
badge: {
you: "Tu",
bot: "Bot",
admin: "Amministratore",
support: "Supporto",
regular: "Utente normale",
system_managed: "Gestito dal sistema",
},
action: { action: {
erase: "Cancella i dati dell'utente", erase: "Cancella i dati dell'utente",
erase_avatar: "Cancella l'avatar dell'utente", erase_avatar: "Cancella l'avatar dell'utente",

View File

@ -190,6 +190,14 @@ const ru: SynapseTranslationMessages = {
modify_managed_user_error: "Изменение пользователя, управляемого системой, не допускается.", modify_managed_user_error: "Изменение пользователя, управляемого системой, не допускается.",
username_available: "Имя пользователя доступно", username_available: "Имя пользователя доступно",
}, },
badge: {
you: "Вы",
bot: "Бот",
admin: "Админ",
support: "Поддержка",
regular: "Обычный пользователь",
system_managed: "Управляемый системой",
},
action: { action: {
erase: "Удалить данные пользователя", erase: "Удалить данные пользователя",
erase_avatar: "Удалить аватар", erase_avatar: "Удалить аватар",
@ -468,6 +476,6 @@ const ru: SynapseTranslationMessages = {
}, },
helper: { length: "Длина токена, если токен не задан." }, helper: { length: "Длина токена, если токен не задан." },
}, },
}, },
}; };
export default ru; export default ru;

View File

@ -178,6 +178,14 @@ const zh: SynapseTranslationMessages = {
modify_managed_user_error: "不允许修改系统管理的用户。", modify_managed_user_error: "不允许修改系统管理的用户。",
username_available: "用户名可用", username_available: "用户名可用",
}, },
badge: {
you: "您",
bot: "机器人",
admin: "管理员",
support: "支持",
regular: "普通用户",
system_managed: "系统管理",
},
action: { action: {
erase: "抹除用户信息", erase: "抹除用户信息",
erase_avatar: "抹掉头像", erase_avatar: "抹掉头像",

View File

@ -320,9 +320,6 @@ const UserTitle = () => {
} }
let username = record ? (record.displayname ? `"${record.displayname}"` : `"${record.name}"`) : "" let username = record ? (record.displayname ? `"${record.displayname}"` : `"${record.name}"`) : ""
if (isASManaged(record?.id)) {
username += " 🤖";
}
return ( return (
<span> <span>
{translate("resources.users.name", { {translate("resources.users.name", {

View File

@ -1,6 +1,7 @@
import { Identifier, fetchUtils } from "react-admin"; import { Identifier, fetchUtils } from "react-admin";
import storage from "../storage"; import storage from "../storage";
import { isMXID } from "../components/mxid";
export const splitMxid = mxid => { export const splitMxid = mxid => {
const re = /^@(?<name>[a-zA-Z0-9._=\-/]+):(?<domain>[a-zA-Z0-9\-.]+\.[a-zA-Z]+)$/; const re = /^@(?<name>[a-zA-Z0-9._=\-/]+):(?<domain>[a-zA-Z0-9\-.]+\.[a-zA-Z]+)$/;
@ -77,8 +78,8 @@ export function returnMXID(input: string | Identifier): string {
// Check if the input already looks like a valid MXID (i.e., starts with "@" and contains ":") // Check if the input already looks like a valid MXID (i.e., starts with "@" and contains ":")
const mxidPattern = /^@[^@:]+:[^@:]+$/; const mxidPattern = /^@[^@:]+:[^@:]+$/;
if (typeof input === 'string' && mxidPattern.test(input)) { if (isMXID(input)) {
return input; // Already a valid MXID return input as string; // Already a valid MXID
} }
// If input is not a valid MXID, assume it's a localpart and construct the MXID // If input is not a valid MXID, assume it's a localpart and construct the MXID