Files
ForbiddenStarsApp/frontend/src/components/AnnouncementDialog.tsx
T
NotBigGhostandClaude Opus 5 ab1fc3f00f Объявления: окно у игрока и редактор в админке
Окно — вариант E из макетов: заголовок в плашке-шапке, текст, точки очереди
и «Понятно», крестика нет. Очередь идёт от старого к новому; закрытое сразу
уходит из кэша, следующее открывается без ожидания сети. У повторно
показанного объявления под заголовком — красная пометка «обновлено».
AppShell показывает окно на любой странице и только когда пароль задан:
обязательное окно пароля всегда первое. Новые и изменённые объявления
приходят SSE-событием announcements, отложенное начало показа — перезапросом
раз в 5 минут.

Админка — вкладка «Объявления» с редактором R1: contenteditable и панель
(жирный, курсив, золотой и красный акцент, эмодзи, снятие оформления),
вставка и перетаскивание только простым текстом, счётчик символов, период
показа по МСК, переключатели «показывать новым игрокам» и «показать заново».
Предпросмотр — тем же окном, что у игрока. Список со статусом, счётчиком
«закрыли N из M» и действиями: изменить, снять с показа, дублировать,
удалить. Выделение текста в форме — полупрозрачный оранжевый вместо синего.

schema.d.ts пересобран из OpenAPI. #84

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-18 21:26:38 +03:00

41 lines
1.6 KiB
TypeScript

import { useEffect, useState } from "react";
import { useAckAnnouncement, usePendingAnnouncements } from "../hooks/announcements";
import { AnnouncementWindow } from "./AnnouncementWindow";
/**
* Очередь объявлений администрации поверх любой страницы: по одному, от старого
* к новому, следующее — после «Понятно» на предыдущем. AppShell монтирует её только
* когда пароль задан: обязательное окно пароля всегда первое.
*/
export function AnnouncementDialog() {
const { data } = usePendingAnnouncements(true);
const ack = useAckAnnouncement();
// Сколько уже закрыто в этой очереди — для точек «2 из 3». Закрытые сразу уходят
// из данных запроса, так что без счётчика точки показывали бы только остаток.
const [closed, setClosed] = useState(0);
const items = data ?? [];
useEffect(() => {
if (items.length === 0) setClosed(0);
}, [items.length]);
if (items.length === 0) return null;
const current = items[0];
return (
<AnnouncementWindow
key={current.id}
title={current.title}
bodyHtml={current.body_html}
updated={current.updated}
index={closed}
total={closed + items.length}
onOk={() => {
setClosed((c) => c + 1);
ack.mutate({ id: current.id, revision: current.revision });
}}
/>
);
}