mirror of
https://github.com/etkecc/synapse-admin.git
synced 2025-03-15 02:38:35 +03:00
parent
cfd8238edc
commit
20417a67b9
14 changed files with 424 additions and 261 deletions
|
@ -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",
|
||||
|
|
516
src/i18n/ru.ts
516
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;
|
||||
|
|
|
@ -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…
Add table
Reference in a new issue