diff --git a/README.md b/README.md index a190e14..030b914 100644 --- a/README.md +++ b/README.md @@ -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_ diff --git a/docs/user-badges.md b/docs/user-badges.md new file mode 100644 index 0000000..8025535 --- /dev/null +++ b/docs/user-badges.md @@ -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`. diff --git a/src/components/AvatarField.tsx b/src/components/AvatarField.tsx index 880c84f..a7988b1 100644 --- a/src/components/AvatarField.tsx +++ b/src/components/AvatarField.tsx @@ -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 ( + + + + {letter} + + + ); + } + return ( {letter} ); diff --git a/src/components/mxid.tsx b/src/components/mxid.tsx index da7dc32..ba850ea 100644 --- a/src/components/mxid.tsx +++ b/src/components/mxid.tsx @@ -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 diff --git a/src/i18n/de.ts b/src/i18n/de.ts index 0cd4bd1..eae2070 100644 --- a/src/i18n/de.ts +++ b/src/i18n/de.ts @@ -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", diff --git a/src/i18n/en.ts b/src/i18n/en.ts index 6fa422d..91fba59 100644 --- a/src/i18n/en.ts +++ b/src/i18n/en.ts @@ -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.", diff --git a/src/i18n/fa.ts b/src/i18n/fa.ts index 80ac563..d1d1ccb 100644 --- a/src/i18n/fa.ts +++ b/src/i18n/fa.ts @@ -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: "محو الصورة الرمزية", diff --git a/src/i18n/fr.ts b/src/i18n/fr.ts index 7804aaa..7834aaf 100644 --- a/src/i18n/fr.ts +++ b/src/i18n/fr.ts @@ -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", diff --git a/src/i18n/index.d.ts b/src/i18n/index.d.ts index 8812e54..f69689a 100644 --- a/src/i18n/index.d.ts +++ b/src/i18n/index.d.ts @@ -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; diff --git a/src/i18n/it.ts b/src/i18n/it.ts index 7975e3d..c9ab358 100644 --- a/src/i18n/it.ts +++ b/src/i18n/it.ts @@ -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", diff --git a/src/i18n/ru.ts b/src/i18n/ru.ts index 3f2d133..04f4d5e 100644 --- a/src/i18n/ru.ts +++ b/src/i18n/ru.ts @@ -147,10 +147,10 @@ const ru: SynapseTranslationMessages = { helper: { send: "Это API удаляет локальные файлы с вашего собственного сервера, включая локальные миниатюры и копии скачанных файлов. \ Данный API не затрагивает файлы, загруженные во внешние хранилища.", - }, - }, - resources: { - users: { + }, + }, + resources: { + users: { name: "Пользователь |||| Пользователи", email: "Почта", msisdn: "Телефон", @@ -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: "Удалить аватар", @@ -208,266 +216,266 @@ const ru: SynapseTranslationMessages = { burst_count_text: "Количество действий, которые могут быть выполнены до ограничения.", } }, - rooms: { - name: "Комната |||| Комнаты", - fields: { - room_id: "ID комнаты", - name: "Название", - canonical_alias: "Псевдоним", - joined_members: "Участники", - joined_local_members: "Локальные участники", - joined_local_devices: "Локальные устройства", - state_events: "События состояния / Сложность", - version: "Версия", - is_encrypted: "Зашифровано", - encryption: "Шифрование", - federatable: "Федерация", - public: "Отображается в каталоге комнат", - creator: "Создатель", - join_rules: "Правила входа", - guest_access: "Гостевой доступ", - history_visibility: "Видимость истории", - topic: "Тема", - avatar: "Аватар", - actions: "Действия", - }, - helper: { - forward_extremities: - "Оконечности — это события-листья в конце ориентированного ациклического графа (DAG) в комнате, т.е. события без дочерних элементов. \ - Чем больше их в комнате, тем больше Synapse работает над разрешением состояния (это дорогостоящая операция). \ - Хотя Synapse старается не допускать существования слишком большого числа таких событий в комнате, из-за ошибок они иногда снова появляются. \ - Если в комнате >10 оконечностей, стоит найти комнату-виновника и попробовать удалить их с помощью SQL-запросов из #1760.", - }, - enums: { - join_rules: { - public: "Для всех", - knock: "Надо постучать", - invite: "По приглашению", - private: "Приватная", - }, - guest_access: { - can_join: "Гости могут войти", - forbidden: "Гости не могут войти", - }, - history_visibility: { - invited: "С момента приглашения", - joined: "С момента входа", - shared: "С момента открытия доступа", - world_readable: "Для всех", - }, - unencrypted: "Без шифрования", - }, - action: { - erase: { - title: "Удалить комнату", - content: - "Действительно удалить эту комнату? Это действие будет невозможно отменить. Все сообщения и файлы в комнате будут удалены с сервера!", - fields: { - block: "Заблокировать и запретить пользователям присоединяться к комнате", - }, - success: "Комната/ы успешно удалены", - failure: "Комната/ы не могут быть удалены.", - }, - make_admin: { - assign_admin: "Назначить администратора", - title: "Назначить администратора комнате %{roomName}", - confirm: "Назначить администратора", - content: "Введите полную MXID пользователя, которого нужно назначить администратором.\nПредупреждение: для этого должен быть назначен хотя бы один локальный участник в качестве администратора.", - success: "Пользователь назначен администратором комнаты.", - failure: "Пользователь не может быть назначен администратором комнаты. %{errMsg}", - } - }, + rooms: { + name: "Комната |||| Комнаты", + fields: { + room_id: "ID комнаты", + name: "Название", + canonical_alias: "Псевдоним", + joined_members: "Участники", + joined_local_members: "Локальные участники", + joined_local_devices: "Локальные устройства", + state_events: "События состояния / Сложность", + version: "Версия", + is_encrypted: "Зашифровано", + encryption: "Шифрование", + federatable: "Федерация", + public: "Отображается в каталоге комнат", + creator: "Создатель", + join_rules: "Правила входа", + guest_access: "Гостевой доступ", + history_visibility: "Видимость истории", + topic: "Тема", + avatar: "Аватар", + actions: "Действия", }, - reports: { - name: "Жалоба |||| Жалобы", - fields: { - id: "ID", - received_ts: "Дата и время жалобы", - user_id: "Автор жалобы", - name: "Название комнаты", - score: "Баллы", - reason: "Причина", - event_id: "ID события", - event_json: { - origin: "Исходнный сервер", - origin_server_ts: "Дата и время отправки", - type: "Тип события", - content: { - msgtype: "Тип содержимого", - body: "Содержимое", - format: "Формат", - formatted_body: "Форматированное содержимое", - algorithm: "Алгоритм", - url: "Ссылка", - info: { - mimetype: "Тип", - }, - }, - }, - }, - action: { - erase: { - title: "Удалить жалобу", - content: "Действительно удалить жалобу? Это действие будет невозможно отменить.", - }, - }, + helper: { + forward_extremities: + "Оконечности — это события-листья в конце ориентированного ациклического графа (DAG) в комнате, т.е. события без дочерних элементов. \ + Чем больше их в комнате, тем больше Synapse работает над разрешением состояния (это дорогостоящая операция). \ + Хотя Synapse старается не допускать существования слишком большого числа таких событий в комнате, из-за ошибок они иногда снова появляются. \ + Если в комнате >10 оконечностей, стоит найти комнату-виновника и попробовать удалить их с помощью SQL-запросов из #1760.", }, - connections: { - name: "Подключения", - fields: { - last_seen: "Дата", - ip: "IP адрес", - user_agent: "Юзер-агент", + enums: { + join_rules: { + public: "Для всех", + knock: "Надо постучать", + invite: "По приглашению", + private: "Приватная", }, + guest_access: { + can_join: "Гости могут войти", + forbidden: "Гости не могут войти", + }, + history_visibility: { + invited: "С момента приглашения", + joined: "С момента входа", + shared: "С момента открытия доступа", + world_readable: "Для всех", + }, + unencrypted: "Без шифрования", }, - devices: { - name: "Устройство |||| Устройства", - fields: { - device_id: "ID устройства", - display_name: "Название", - last_seen_ts: "Дата и время", - last_seen_ip: "IP адрес", - }, - action: { - erase: { - title: "Удаление %{id}", - content: 'Действительно удалить устройство "%{name}"?', - success: "Устройство успешно удалено.", - failure: "Произошла ошибка.", - }, - }, - }, - users_media: { - name: "Файлы", - fields: { - media_id: "ID файла", - media_length: "Размер файла (в байтах)", - media_type: "Тип", - upload_name: "Имя файла", - quarantined_by: "На карантине", - safe_from_quarantine: "Защитить от карантина", - created_ts: "Создано", - last_access_ts: "Последний доступ", - }, - action: { - open: "Открыть файл в новом окне", - }, - }, - protect_media: { - action: { - create: "Не защищён, установить защиту", - delete: "Защищён, снять защиту", - none: "На карантине", - send_success: "Статус защиты успешно изменён.", - send_failure: "Произошла ошибка.", - }, - }, - quarantine_media: { - action: { - name: "Карантин", - create: "Поместить на карантин", - delete: "На карантине, снять карантин", - none: "Защищено от карантина", - send_success: "Статус карантина успешно изменён.", - send_failure: "Произошла ошибка.", - }, - }, - pushers: { - name: "Пушер |||| Пушеры", - fields: { - app: "Приложение", - app_display_name: "Название приложения", - app_id: "ID приложения", - device_display_name: "Название устройства", - kind: "Вид", - lang: "Язык", - profile_tag: "Тег профиля", - pushkey: "Ключ", - data: { url: "URL" }, - }, - }, - servernotices: { - name: "Серверные уведомления", - send: "Отправить серверные уведомления", - fields: { - body: "Сообщение", - }, - action: { - send: "Отправить", - send_success: "Серверное уведомление успешно отправлено.", - send_failure: "Произошла ошибка.", - }, - helper: { - send: 'Отправить серверное уведомление выбранным пользователям. На сервере должна быть активна функция "Server Notices".', - }, - }, - user_media_statistics: { - name: "Файлы пользователей", - fields: { - media_count: "Количество файлов", - media_length: "Размер файлов", - }, - }, - forward_extremities: { - name: "Оконечности", - fields: { - id: "ID события", - received_ts: "Дата и время", - depth: "Глубина", - state_group: "Группа состояния", - }, - }, - room_state: { - name: "События состояния", - fields: { - type: "Тип", - content: "Содержимое", - origin_server_ts: "Дата отправки", - sender: "Отправитель", - }, - }, - room_directory: { - name: "Каталог комнат", - fields: { - world_readable: "Гости могут просматривать без входа", - guest_can_join: "Гости могут войти", - }, - action: { - title: - "Удалить комнату из каталога |||| Удалить %{smart_count} комнаты из каталога |||| Удалить %{smart_count} комнат из каталога", + action: { + erase: { + title: "Удалить комнату", content: - "Действительно удалить комнату из каталога? |||| Действительно удалить %{smart_count} комнаты из каталога? |||| Действительно удалить %{smart_count} комнат из каталога?", - erase: "Удалить из каталога комнат", - create: "Опубликовать в каталоге комнат", - send_success: "Комната успешно опубликована.", - send_failure: "Произошла ошибка.", + "Действительно удалить эту комнату? Это действие будет невозможно отменить. Все сообщения и файлы в комнате будут удалены с сервера!", + fields: { + block: "Заблокировать и запретить пользователям присоединяться к комнате", + }, + success: "Комната/ы успешно удалены", + failure: "Комната/ы не могут быть удалены.", + }, + make_admin: { + assign_admin: "Назначить администратора", + title: "Назначить администратора комнате %{roomName}", + confirm: "Назначить администратора", + content: "Введите полную MXID пользователя, которого нужно назначить администратором.\nПредупреждение: для этого должен быть назначен хотя бы один локальный участник в качестве администратора.", + success: "Пользователь назначен администратором комнаты.", + failure: "Пользователь не может быть назначен администратором комнаты. %{errMsg}", + } + }, + }, + reports: { + name: "Жалоба |||| Жалобы", + fields: { + id: "ID", + received_ts: "Дата и время жалобы", + user_id: "Автор жалобы", + name: "Название комнаты", + score: "Баллы", + reason: "Причина", + event_id: "ID события", + event_json: { + origin: "Исходнный сервер", + origin_server_ts: "Дата и время отправки", + type: "Тип события", + content: { + msgtype: "Тип содержимого", + body: "Содержимое", + format: "Формат", + formatted_body: "Форматированное содержимое", + algorithm: "Алгоритм", + url: "Ссылка", + info: { + mimetype: "Тип", + }, + }, }, }, - destinations: { - name: "Федерация", - fields: { - destination: "Назначение", - failure_ts: "Дата и время ошибки", - retry_last_ts: "Дата и время последней попытки", - retry_interval: "Интервал между попытками", - last_successful_stream_ordering: "Последний успешный поток", - stream_ordering: "Поток", + action: { + erase: { + title: "Удалить жалобу", + content: "Действительно удалить жалобу? Это действие будет невозможно отменить.", }, - action: { reconnect: "Переподключиться" }, }, - registration_tokens: { - name: "Токены регистрации", - fields: { - token: "Токен", - valid: "Рабочий токен", - uses_allowed: "Количество использований", - pending: "Ожидает", - completed: "Завершено", - expiry_time: "Дата окончания", - length: "Длина", + }, + connections: { + name: "Подключения", + fields: { + last_seen: "Дата", + ip: "IP адрес", + user_agent: "Юзер-агент", + }, + }, + devices: { + name: "Устройство |||| Устройства", + fields: { + device_id: "ID устройства", + display_name: "Название", + last_seen_ts: "Дата и время", + last_seen_ip: "IP адрес", + }, + action: { + erase: { + title: "Удаление %{id}", + content: 'Действительно удалить устройство "%{name}"?', + success: "Устройство успешно удалено.", + failure: "Произошла ошибка.", }, - helper: { length: "Длина токена, если токен не задан." }, }, -}, + }, + users_media: { + name: "Файлы", + fields: { + media_id: "ID файла", + media_length: "Размер файла (в байтах)", + media_type: "Тип", + upload_name: "Имя файла", + quarantined_by: "На карантине", + safe_from_quarantine: "Защитить от карантина", + created_ts: "Создано", + last_access_ts: "Последний доступ", + }, + action: { + open: "Открыть файл в новом окне", + }, + }, + protect_media: { + action: { + create: "Не защищён, установить защиту", + delete: "Защищён, снять защиту", + none: "На карантине", + send_success: "Статус защиты успешно изменён.", + send_failure: "Произошла ошибка.", + }, + }, + quarantine_media: { + action: { + name: "Карантин", + create: "Поместить на карантин", + delete: "На карантине, снять карантин", + none: "Защищено от карантина", + send_success: "Статус карантина успешно изменён.", + send_failure: "Произошла ошибка.", + }, + }, + pushers: { + name: "Пушер |||| Пушеры", + fields: { + app: "Приложение", + app_display_name: "Название приложения", + app_id: "ID приложения", + device_display_name: "Название устройства", + kind: "Вид", + lang: "Язык", + profile_tag: "Тег профиля", + pushkey: "Ключ", + data: { url: "URL" }, + }, + }, + servernotices: { + name: "Серверные уведомления", + send: "Отправить серверные уведомления", + fields: { + body: "Сообщение", + }, + action: { + send: "Отправить", + send_success: "Серверное уведомление успешно отправлено.", + send_failure: "Произошла ошибка.", + }, + helper: { + send: 'Отправить серверное уведомление выбранным пользователям. На сервере должна быть активна функция "Server Notices".', + }, + }, + user_media_statistics: { + name: "Файлы пользователей", + fields: { + media_count: "Количество файлов", + media_length: "Размер файлов", + }, + }, + forward_extremities: { + name: "Оконечности", + fields: { + id: "ID события", + received_ts: "Дата и время", + depth: "Глубина", + state_group: "Группа состояния", + }, + }, + room_state: { + name: "События состояния", + fields: { + type: "Тип", + content: "Содержимое", + origin_server_ts: "Дата отправки", + sender: "Отправитель", + }, + }, + room_directory: { + name: "Каталог комнат", + fields: { + world_readable: "Гости могут просматривать без входа", + guest_can_join: "Гости могут войти", + }, + action: { + title: + "Удалить комнату из каталога |||| Удалить %{smart_count} комнаты из каталога |||| Удалить %{smart_count} комнат из каталога", + content: + "Действительно удалить комнату из каталога? |||| Действительно удалить %{smart_count} комнаты из каталога? |||| Действительно удалить %{smart_count} комнат из каталога?", + erase: "Удалить из каталога комнат", + create: "Опубликовать в каталоге комнат", + send_success: "Комната успешно опубликована.", + send_failure: "Произошла ошибка.", + }, + }, + destinations: { + name: "Федерация", + fields: { + destination: "Назначение", + failure_ts: "Дата и время ошибки", + retry_last_ts: "Дата и время последней попытки", + retry_interval: "Интервал между попытками", + last_successful_stream_ordering: "Последний успешный поток", + stream_ordering: "Поток", + }, + action: { reconnect: "Переподключиться" }, + }, + registration_tokens: { + name: "Токены регистрации", + fields: { + token: "Токен", + valid: "Рабочий токен", + uses_allowed: "Количество использований", + pending: "Ожидает", + completed: "Завершено", + expiry_time: "Дата окончания", + length: "Длина", + }, + helper: { length: "Длина токена, если токен не задан." }, + }, + }, }; export default ru; diff --git a/src/i18n/zh.ts b/src/i18n/zh.ts index 6e29bfc..919654c 100644 --- a/src/i18n/zh.ts +++ b/src/i18n/zh.ts @@ -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: "抹掉头像", diff --git a/src/resources/users.tsx b/src/resources/users.tsx index 46c8cd5..19c6316 100644 --- a/src/resources/users.tsx +++ b/src/resources/users.tsx @@ -320,9 +320,6 @@ const UserTitle = () => { } let username = record ? (record.displayname ? `"${record.displayname}"` : `"${record.name}"`) : "" - if (isASManaged(record?.id)) { - username += " 🤖"; - } return ( {translate("resources.users.name", { diff --git a/src/synapse/synapse.ts b/src/synapse/synapse.ts index 4fa025a..63e07f3 100644 --- a/src/synapse/synapse.ts +++ b/src/synapse/synapse.ts @@ -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 = /^@(?[a-zA-Z0-9._=\-/]+):(?[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