parent
cfd8238edc
commit
20417a67b9
@ -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 "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)
|
||||
* 🤖 [User Badges](https://github.com/etkecc/synapse-admin/pull/160)
|
||||
|
||||
_the list will be updated as new changes are added_
|
||||
|
||||
|
36
docs/user-badges.md
Normal file
36
docs/user-badges.md
Normal 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`.
|
@ -1,8 +1,10 @@
|
||||
import { get } from "lodash";
|
||||
import { Avatar, AvatarProps } from "@mui/material";
|
||||
import { FieldProps, useRecordContext } from "react-admin";
|
||||
import { Avatar, AvatarProps, Badge, Tooltip } from "@mui/material";
|
||||
import { FieldProps, useRecordContext, useTranslate } from "react-admin";
|
||||
import { useState, useEffect, useCallback } from "react";
|
||||
import { fetchAuthenticatedMedia } from "../utils/fetchMedia";
|
||||
import { isMXID, isASManaged } from "./mxid";
|
||||
import storage from "../storage";
|
||||
|
||||
const AvatarField = ({ source, ...rest }: AvatarProps & FieldProps) => {
|
||||
const { alt, classes, sizes, sx, variant } = rest;
|
||||
@ -45,6 +47,59 @@ const AvatarField = ({ source, ...rest }: AvatarProps & FieldProps) => {
|
||||
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}>
|
||||
{letter}
|
||||
</Avatar>);
|
||||
|
@ -1,6 +1,15 @@
|
||||
import { Identifier } from "ra-core";
|
||||
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
|
||||
* @param id The user ID to check
|
||||
|
@ -187,6 +187,14 @@ const de: SynapseTranslationMessages = {
|
||||
modify_managed_user_error: "Das Ändern eines vom System verwalteten Benutzers ist nicht zulässig.",
|
||||
username_available: "Benutzername verfügbar",
|
||||
},
|
||||
badge: {
|
||||
you: "Sie",
|
||||
bot: "Bot",
|
||||
admin: "Administrator",
|
||||
support: "Unterstützung",
|
||||
regular: "Normaler Benutzer",
|
||||
system_managed: "Systemverwalteter Benutzer",
|
||||
},
|
||||
action: {
|
||||
erase: "Lösche Benutzerdaten",
|
||||
erase_avatar: "Avatar löschen",
|
||||
|
@ -171,6 +171,14 @@ const en: SynapseTranslationMessages = {
|
||||
overwrite_cancel: "Cancel",
|
||||
overwrite_confirm: "Overwrite",
|
||||
},
|
||||
badge: {
|
||||
you: "You",
|
||||
bot: "Bot",
|
||||
admin: "Admin",
|
||||
support: "Support",
|
||||
regular: "Regular User",
|
||||
system_managed: "System-managed",
|
||||
},
|
||||
limits: {
|
||||
messages_per_second: "Messages per second",
|
||||
messages_per_second_text: "The number of actions that can be performed in a second.",
|
||||
|
@ -152,6 +152,14 @@ const fa: SynapseTranslationMessages = {
|
||||
modify_managed_user_error: "لا يُسمح بتغيير المستخدم الذي يديره النظام.",
|
||||
username_available: "نام کاربری موجود",
|
||||
},
|
||||
badge: {
|
||||
you: "شما",
|
||||
bot: "ربات",
|
||||
admin: "مدیر",
|
||||
support: "پشتیبان",
|
||||
regular: "کاربر عادی",
|
||||
system_managed: "مدیریت سیستم",
|
||||
},
|
||||
action: {
|
||||
erase: "پاک کردن اطلاعات کاربر",
|
||||
erase_avatar: "محو الصورة الرمزية",
|
||||
|
@ -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.",
|
||||
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: {
|
||||
erase: "Effacer les données de l'utilisateur",
|
||||
erase_avatar: "Effacer l'avatar",
|
||||
|
8
src/i18n/index.d.ts
vendored
8
src/i18n/index.d.ts
vendored
@ -163,6 +163,14 @@ interface SynapseTranslationMessages extends TranslationMessages {
|
||||
overwrite_cancel: string;
|
||||
overwrite_confirm: string;
|
||||
};
|
||||
badge: {
|
||||
you: string;
|
||||
bot: string;
|
||||
admin: string;
|
||||
support: string;
|
||||
regular: string;
|
||||
system_managed: string;
|
||||
}
|
||||
limits: {
|
||||
messages_per_second: string;
|
||||
messages_per_second_text: string;
|
||||
|
@ -153,6 +153,14 @@ const it: SynapseTranslationMessages = {
|
||||
modify_managed_user_error: "La modifica di un utente gestito dal sistema non è consentita.",
|
||||
username_available: "Nome utente disponibile",
|
||||
},
|
||||
badge: {
|
||||
you: "Tu",
|
||||
bot: "Bot",
|
||||
admin: "Amministratore",
|
||||
support: "Supporto",
|
||||
regular: "Utente normale",
|
||||
system_managed: "Gestito dal sistema",
|
||||
},
|
||||
action: {
|
||||
erase: "Cancella i dati dell'utente",
|
||||
erase_avatar: "Cancella l'avatar dell'utente",
|
||||
|
@ -190,6 +190,14 @@ const ru: SynapseTranslationMessages = {
|
||||
modify_managed_user_error: "Изменение пользователя, управляемого системой, не допускается.",
|
||||
username_available: "Имя пользователя доступно",
|
||||
},
|
||||
badge: {
|
||||
you: "Вы",
|
||||
bot: "Бот",
|
||||
admin: "Админ",
|
||||
support: "Поддержка",
|
||||
regular: "Обычный пользователь",
|
||||
system_managed: "Управляемый системой",
|
||||
},
|
||||
action: {
|
||||
erase: "Удалить данные пользователя",
|
||||
erase_avatar: "Удалить аватар",
|
||||
@ -468,6 +476,6 @@ const ru: SynapseTranslationMessages = {
|
||||
},
|
||||
helper: { length: "Длина токена, если токен не задан." },
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
export default ru;
|
||||
|
@ -178,6 +178,14 @@ const zh: SynapseTranslationMessages = {
|
||||
modify_managed_user_error: "不允许修改系统管理的用户。",
|
||||
username_available: "用户名可用",
|
||||
},
|
||||
badge: {
|
||||
you: "您",
|
||||
bot: "机器人",
|
||||
admin: "管理员",
|
||||
support: "支持",
|
||||
regular: "普通用户",
|
||||
system_managed: "系统管理",
|
||||
},
|
||||
action: {
|
||||
erase: "抹除用户信息",
|
||||
erase_avatar: "抹掉头像",
|
||||
|
@ -320,9 +320,6 @@ const UserTitle = () => {
|
||||
}
|
||||
|
||||
let username = record ? (record.displayname ? `"${record.displayname}"` : `"${record.name}"`) : ""
|
||||
if (isASManaged(record?.id)) {
|
||||
username += " 🤖";
|
||||
}
|
||||
return (
|
||||
<span>
|
||||
{translate("resources.users.name", {
|
||||
|
@ -1,6 +1,7 @@
|
||||
import { Identifier, fetchUtils } from "react-admin";
|
||||
|
||||
import storage from "../storage";
|
||||
import { isMXID } from "../components/mxid";
|
||||
|
||||
export const splitMxid = mxid => {
|
||||
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 ":")
|
||||
const mxidPattern = /^@[^@:]+:[^@:]+$/;
|
||||
if (typeof input === 'string' && mxidPattern.test(input)) {
|
||||
return input; // Already a valid MXID
|
||||
if (isMXID(input)) {
|
||||
return input as string; // Already a valid MXID
|
||||
}
|
||||
|
||||
// If input is not a valid MXID, assume it's a localpart and construct the MXID
|
||||
|
Loading…
x
Reference in New Issue
Block a user