feat(frontend): watches, webhooks and alerts panels read humanly, with chat picker, url filter, test send and delivery log

This commit is contained in:
hh committed 2026-10-19 02:57:27 +00:00
1 parent 6a4fd6ad47
commit ea3a7c0a61
19 files changed
+3699 -458

No files matched your search

+4
View File
@@ -1,15 +1,19 @@
import { account } from "#lib/i18n/messages/account.js";
import { alerts } from "#lib/i18n/messages/alerts.js";
import { common } from "#lib/i18n/messages/common.js";
import { menu } from "#lib/i18n/messages/menu.js";
import { read } from "#lib/i18n/messages/read.js";
import { settings } from "#lib/i18n/messages/settings.js";
import { watches } from "#lib/i18n/messages/watches.js";
export const messages = {
...account,
...alerts,
...common,
...menu,
...read,
...settings,
...watches,
};
export type MessageKey = keyof typeof messages;
+47
View File
@@ -0,0 +1,47 @@
import type { Messages } from "#lib/i18n/types.js";
export const alerts = {
"alerts.all": { en: "All", ru: "Все" },
"alerts.badge": {
en: { one: "{count} unread alert", other: "{count} unread alerts" },
ru: {
few: "{count} непрочитанных оповещения",
many: "{count} непрочитанных оповещений",
one: "{count} непрочитанное оповещение",
other: "{count} непрочитанных оповещения",
},
},
"alerts.empty.description": {
en: "They show up here when a watch fires",
ru: "Они появятся здесь, когда сработает наблюдение",
},
"alerts.empty.title": { en: "No alerts", ru: "Оповещений нет" },
"alerts.emptyUnseen.title": { en: "All read", ru: "Всё прочитано" },
"alerts.event.delete": { en: "Deleted", ru: "Удалено" },
"alerts.event.edit": { en: "Edited", ru: "Изменено" },
"alerts.event.message": { en: "New", ru: "Новое" },
"alerts.event.online": { en: "Online", ru: "В сети" },
"alerts.event.transcript": { en: "In a voice message", ru: "В голосовом" },
"alerts.loadFailed": {
en: "Couldn't load alerts",
ru: "Не удалось загрузить оповещения",
},
"alerts.markAll": { en: "Mark all as read", ru: "Отметить все прочитанными" },
"alerts.markSeen": { en: "Mark as read", ru: "Отметить прочитанным" },
"alerts.more": { en: "Show more", ru: "Показать ещё" },
"alerts.noText": { en: "No text", ru: "Без текста" },
"alerts.notify": {
en: "Browser notifications",
ru: "Уведомления в браузере",
},
"alerts.notifyDenied": {
en: "The browser blocked notifications for this site",
ru: "Браузер запретил уведомления для этого сайта",
},
"alerts.onlineAfter": { en: "after {away} away", ru: "не было {away}" },
"alerts.open": { en: "Go to message", ru: "Перейти к сообщению" },
"alerts.openChat": { en: "Open chat", ru: "Открыть чат" },
"alerts.retry": { en: "Retry", ru: "Повторить" },
"alerts.setup": { en: "Set up watches", ru: "Настроить наблюдения" },
"alerts.unseen": { en: "Unread", ru: "Непрочитанные" },
} satisfies Messages;
+206
View File
@@ -0,0 +1,206 @@
import type { Messages } from "#lib/i18n/types.js";
export const watches = {
"watches.add": { en: "New watch", ru: "Новое наблюдение" },
"watches.anyChat": { en: "Any chat", ru: "Любой чат" },
"watches.anyDm": { en: "Any private chat", ru: "Любая личка" },
"watches.anySender": { en: "Anyone", ru: "Кто угодно" },
"watches.chat": { en: "Chat", ru: "Чат" },
"watches.choose": { en: "Choose", ru: "Выбрать" },
"watches.clear": { en: "Clear", ru: "Убрать" },
"watches.delete": { en: "Delete", ru: "Удалить" },
"watches.deleteConfirm": {
en: "Delete the watch with its alerts?",
ru: "Удалить наблюдение вместе с оповещениями?",
},
"watches.describe.deletedDm": {
en: "Deleted messages in private chats",
ru: "Удалённые сообщения в личке",
},
"watches.describe.deletedIn": {
en: "Deleted messages in {chat}",
ru: "Удалённые сообщения в чате «{chat}»",
},
"watches.describe.editedDm": {
en: "Edited messages in private chats",
ru: "Изменённые сообщения в личке",
},
"watches.describe.editedIn": {
en: "Edited messages in {chat}",
ru: "Изменённые сообщения в чате «{chat}»",
},
"watches.describe.keyword": {
en: "{what} in any chat",
ru: "{what} в любом чате",
},
"watches.describe.keywordFrom": {
en: "{what} from {person}",
ru: "{what}, отправитель {person}",
},
"watches.describe.keywordFromIn": {
en: "{what} from {person} in {chat}",
ru: "{what} в чате «{chat}», отправитель {person}",
},
"watches.describe.keywordIn": {
en: "{what} in {chat}",
ru: "{what} в чате «{chat}»",
},
"watches.describe.newFrom": {
en: "New messages from {person}",
ru: "Новые сообщения, отправитель {person}",
},
"watches.describe.newFromIn": {
en: "New messages from {person} in {chat}",
ru: "Новые сообщения в чате «{chat}», отправитель {person}",
},
"watches.describe.newIn": {
en: "New messages in {chat}",
ru: "Новые сообщения в чате «{chat}»",
},
"watches.describe.online": {
en: "{person} is online",
ru: "{person} в сети",
},
"watches.describe.regex": {
en: "Pattern “{pattern}”",
ru: "Шаблон «{pattern}»",
},
"watches.describe.word": { en: "Word “{pattern}”", ru: "Слово «{pattern}»" },
"watches.disable": { en: "Turn off", ru: "Выключить" },
"watches.edit": { en: "Edit", ru: "Изменить" },
"watches.empty.description": {
en: "A watch looks for a word, a person or deleted messages and raises an alert",
ru: "Наблюдение ищет слово, человека или удалённые сообщения и присылает оповещение",
},
"watches.empty.title": { en: "No watches yet", ru: "Наблюдений пока нет" },
"watches.enable": { en: "Turn on", ru: "Включить" },
"watches.error.action": {
en: "That didn't work, try again",
ru: "Не получилось, попробуйте ещё раз",
},
"watches.error.badRegex": {
en: "Can't read the expression: {detail}",
ru: "Не получилось разобрать выражение: {detail}",
},
"watches.error.failed": {
en: "Couldn't save: {detail}",
ru: "Не удалось сохранить: {detail}",
},
"watches.error.needPattern": {
en: "Enter a word or a phrase",
ru: "Впишите слово или фразу",
},
"watches.error.needPerson": {
en: "Choose a person",
ru: "Выберите человека",
},
"watches.error.needScope": {
en: "Choose a chat or a sender",
ru: "Выберите чат или отправителя",
},
"watches.error.slowRegex": {
en: "A repeat inside a repeat, like (a+)+, can hang. Make the expression simpler",
ru: "Повтор внутри повтора, вроде (a+)+, может зависнуть. Упростите выражение",
},
"watches.expression": { en: "Expression", ru: "Выражение" },
"watches.fired": {
en: {
one: "{count} alert, last {date}",
other: "{count} alerts, last {date}",
},
ru: {
few: "{count} оповещения, последнее {date}",
many: "{count} оповещений, последнее {date}",
one: "{count} оповещение, последнее {date}",
other: "{count} оповещения, последнее {date}",
},
},
"watches.kind": { en: "What to watch", ru: "Что ищем" },
"watches.kind.keyword": { en: "A word or a pattern", ru: "Слово или шаблон" },
"watches.kind.message_deleted": {
en: "Deleted messages",
ru: "Удалённые сообщения",
},
"watches.kind.message_edited": {
en: "Edited messages",
ru: "Изменённые сообщения",
},
"watches.kind.new_message": { en: "New messages", ru: "Новые сообщения" },
"watches.kind.peer_online": { en: "Coming online", ru: "Появление в сети" },
"watches.loadFailed": {
en: "Couldn't load watches",
ru: "Не удалось загрузить наблюдения",
},
"watches.name": { en: "Name, optional", ru: "Название, если нужно" },
"watches.never": { en: "Hasn't fired yet", ru: "Ещё не срабатывало" },
"watches.nothing": { en: "Nothing found", ru: "Ничего не найдено" },
"watches.off": { en: "Off", ru: "Выключено" },
"watches.pattern": { en: "Word or phrase", ru: "Слово или фраза" },
"watches.person": { en: "Person", ru: "Человек" },
"watches.regex": { en: "Regular expression", ru: "Регулярное выражение" },
"watches.retry": { en: "Retry", ru: "Повторить" },
"watches.search": { en: "Search chats", ru: "Поиск чата" },
"watches.searchPeople": { en: "Search people", ru: "Поиск человека" },
"watches.sender": { en: "Sender", ru: "Отправитель" },
"watches.unknownChat": { en: "Chat {id}", ru: "Чат {id}" },
"watches.unknownPerson": { en: "User {id}", ru: "Пользователь {id}" },
"watches.webhooks": { en: "Webhooks", ru: "Вебхуки" },
"webhooks.add": { en: "Add a webhook", ru: "Добавить вебхук" },
"webhooks.allWatches": { en: "All watches", ru: "Все наблюдения" },
"webhooks.attempts": {
en: { one: "{count} attempt", other: "{count} attempts" },
ru: {
few: "{count} попытки",
many: "{count} попыток",
one: "{count} попытка",
other: "{count} попытки",
},
},
"webhooks.badUrl": {
en: "Needs an http or https address",
ru: "Нужен адрес http или https",
},
"webhooks.copied": { en: "Copied", ru: "Скопировано" },
"webhooks.copy": { en: "Copy", ru: "Скопировать" },
"webhooks.deleteConfirm": {
en: "Delete the webhook?",
ru: "Удалить вебхук?",
},
"webhooks.empty.description": {
en: "Every alert goes out as a signed POST to your address",
ru: "Каждое оповещение уйдёт POST-запросом с подписью на ваш адрес",
},
"webhooks.empty.title": { en: "No webhooks", ru: "Вебхуков нет" },
"webhooks.enabled": { en: "Send alerts", ru: "Отправлять оповещения" },
"webhooks.event.alert": { en: "Alert", ru: "Оповещение" },
"webhooks.event.test": { en: "Test", ru: "Проверка" },
"webhooks.filter": { en: "Which watches", ru: "Какие наблюдения" },
"webhooks.log": { en: "Recent deliveries", ru: "Последние доставки" },
"webhooks.logEmpty": {
en: "Nothing sent yet",
ru: "Пока ничего не отправлено",
},
"webhooks.never": { en: "Nothing sent yet", ru: "Ещё не отправлялся" },
"webhooks.off": { en: "Off", ru: "Выключен" },
"webhooks.rotate": { en: "New secret", ru: "Новый секрет" },
"webhooks.secret": { en: "Signing secret", ru: "Секрет подписи" },
"webhooks.signature": {
en: "Header X-Beavergram-Signature: sha256=HMAC of the body",
ru: "Заголовок X-Beavergram-Signature: sha256=HMAC тела",
},
"webhooks.status.delivered": { en: "Delivered", ru: "Доставлено" },
"webhooks.status.failed": { en: "Not delivered", ru: "Не доставлено" },
"webhooks.status.pending": { en: "Retry at {time}", ru: "Повтор в {time}" },
"webhooks.status.sending": { en: "Sending", ru: "Отправляется" },
"webhooks.test": { en: "Send a test", ru: "Проверить" },
"webhooks.testFailed": {
en: "Not delivered: {error}",
ru: "Не доставлено: {error}",
},
"webhooks.testing": { en: "Sending…", ru: "Отправляю…" },
"webhooks.testOk": {
en: "Delivered, response {code}",
ru: "Доставлено, ответ {code}",
},
"webhooks.url": { en: "Address", ru: "Адрес" },
} satisfies Messages;