feat(web): чат Фазы 2 — лента, композер, реакции, ответы, поиск и вложения
Полноценный текстовый чат в веб-клиенте:
- стор сообщений по комнатам (кэш 200, оптимистичная отправка с nonce,
применение всех событий Gateway, пагинация курсором, непрочитанные и
упоминания, сброс при логауте и INVALID_SESSION);
- лента: группировка подряд идущих сообщений одного автора, разделители дат,
разделитель «новые сообщения», подгрузка истории вверх, пустые состояния;
- разметка: жирный/курсив/код/блок кода/зачёркнутый/цитата/ссылки (только
http(s)) и упоминания <@id> — безопасный рендер React-узлами без
dangerouslySetInnerHTML;
- реакции чипами с подсветкой своих, быстрые эмодзи и пикер, панель действий и
контекстное меню (ответ, реакция, закрепление, правка, удаление, копирование);
- ответы с переходом к оригиналу, inline-правка, «печатает…» с POST /typing раз
в 3 с, пины в модалке, поиск по комнате с подсветкой и переходом;
- композер: автовысота, Enter/Shift+Enter, лимит 4000, drag&drop и скрепка
(POST /channels/{id}/files → attachment_ids), обработка 429 с таймером и
perm.denied с причиной, read-only без SEND_MESSAGES;
- бейджи непрочитанных и упоминаний в сайдбаре, авто-POST /ack при активном окне;
- 176 тестов Vitest (было 100), check/lint/test/build зелёные.
This commit is contained in:
@@ -50,7 +50,13 @@
|
||||
"guild.member_limit_reached": "На сервере достигнут лимит участников.",
|
||||
"channel.not_found": "Комната не найдена.",
|
||||
"validation.failed": "Проверьте заполненные поля.",
|
||||
"rate.limited": "Слишком много запросов. Подождите немного."
|
||||
"rate.limited": "Слишком много запросов. Подождите немного.",
|
||||
"perm.denied": "Недостаточно прав для этого действия.",
|
||||
"rate_limited": "Слишком много запросов. Подождите немного.",
|
||||
"message.edit_window_expired": "Сообщение нельзя изменить: прошло больше 24 часов.",
|
||||
"file.too_large": "Файл больше допустимого размера.",
|
||||
"request.bad": "Некорректный запрос.",
|
||||
"file.not_found": "Файл не найден."
|
||||
},
|
||||
"theme": {
|
||||
"switchToLight": "Светлая тема",
|
||||
@@ -210,7 +216,100 @@
|
||||
"notInGuild": {
|
||||
"title": "Сервер недоступен",
|
||||
"body": "Похоже, вы не состоите в этом сервере."
|
||||
}
|
||||
},
|
||||
"selectUnread": "Открыть комнату {{name}}, непрочитанных: {{count}}",
|
||||
"selectMentions": "Открыть комнату {{name}}, упоминаний: {{count}}",
|
||||
"slowmode": "медленный режим {{seconds}} с"
|
||||
},
|
||||
"chat": {
|
||||
"log": "Лента сообщений комнаты {{name}}",
|
||||
"loadMore": "Загрузить ещё",
|
||||
"loadingMore": "Загружаем…",
|
||||
"today": "Сегодня",
|
||||
"yesterday": "Вчера",
|
||||
"edited": "(изменено)",
|
||||
"pending": "отправляется…",
|
||||
"newMessages": "Новые сообщения",
|
||||
"adminBadge": "админ",
|
||||
"unknownAuthor": "неизвестный",
|
||||
"start": {
|
||||
"title": "Это начало комнаты #{{name}}",
|
||||
"body": "Здесь пока нет сообщений. Напишите первым — сообщение увидят все участники комнаты."
|
||||
},
|
||||
"message": {
|
||||
"by": "Сообщение от {{name}} в комнате {{channel}}",
|
||||
"sentAt": "Отправлено {{time}}"
|
||||
},
|
||||
"reply": {
|
||||
"to": "ответ {{name}}",
|
||||
"missing": "сообщение удалено",
|
||||
"composer": "Ответ {{name}}",
|
||||
"cancel": "Отменить ответ"
|
||||
},
|
||||
"actions": {
|
||||
"menu": "Действия с сообщением",
|
||||
"reply": "Ответить",
|
||||
"react": "Реакция",
|
||||
"pin": "Закрепить",
|
||||
"unpin": "Открепить",
|
||||
"edit": "Изменить",
|
||||
"delete": "Удалить",
|
||||
"deleteConfirm": "Удалить это сообщение?",
|
||||
"copy": "Копировать текст"
|
||||
},
|
||||
"reactions": {
|
||||
"title": "Реакции",
|
||||
"add": "Поставить реакцию {{emoji}}",
|
||||
"toggle": "Реакция {{emoji}}: {{count}}"
|
||||
},
|
||||
"emoji": {
|
||||
"title": "Выбор эмодзи",
|
||||
"quick": "Быстрые реакции",
|
||||
"popular": "Популярные эмодзи",
|
||||
"pick": "Поставить реакцию {{emoji}}"
|
||||
},
|
||||
"edit": {
|
||||
"label": "Редактирование сообщения",
|
||||
"hint": "Enter — сохранить, Escape — отменить",
|
||||
"save": "Сохранить",
|
||||
"cancel": "Отмена"
|
||||
},
|
||||
"composer": {
|
||||
"label": "Сообщение в комнату {{name}}",
|
||||
"placeholder": "Написать в #{{name}}",
|
||||
"send": "Отправить",
|
||||
"hint": "Enter — отправить, Shift+Enter — новая строка",
|
||||
"attach": "Прикрепить файл",
|
||||
"fileInput": "Выбрать файлы",
|
||||
"uploading": "Загружаем вложения…",
|
||||
"attachments": "Вложения к сообщению",
|
||||
"removeAttachment": "Убрать вложение {{name}}",
|
||||
"counter": "{{count}}/{{max}}",
|
||||
"tooLarge": "Файл «{{name}}» больше допустимых {{limit}}",
|
||||
"rateLimited": "Слишком часто. Отправить можно будет через {{seconds}} с.",
|
||||
"denied": "Нет права отправлять сообщения в эту комнату.",
|
||||
"readOnly": "В эту комнату нельзя писать."
|
||||
},
|
||||
"typing_one": "{{names}} печатает…",
|
||||
"typing_few": "{{names}} печатают…",
|
||||
"typing_many": "{{names}} печатают…",
|
||||
"typing_other": "{{names}} печатают…",
|
||||
"pins": {
|
||||
"open": "Закреплённые сообщения",
|
||||
"title": "Закреплённые в #{{name}}",
|
||||
"empty": "В этой комнате пока нет закреплённых сообщений.",
|
||||
"jump": "Перейти"
|
||||
},
|
||||
"search": {
|
||||
"open": "Поиск по комнате",
|
||||
"title": "Поиск по сообщениям",
|
||||
"label": "Строка поиска",
|
||||
"placeholder": "Поиск по сообщениям",
|
||||
"hint": "Введите не меньше двух символов.",
|
||||
"empty": "Ничего не найдено."
|
||||
},
|
||||
"unread": "Непрочитанных сообщений: {{count}}",
|
||||
"mentions": "Упоминаний: {{count}}"
|
||||
},
|
||||
"user": {
|
||||
"panelLabel": "Панель пользователя",
|
||||
|
||||
Reference in New Issue
Block a user