Окно — вариант E из макетов: заголовок в плашке-шапке, текст, точки очереди и «Понятно», крестика нет. Очередь идёт от старого к новому; закрытое сразу уходит из кэша, следующее открывается без ожидания сети. У повторно показанного объявления под заголовком — красная пометка «обновлено». AppShell показывает окно на любой странице и только когда пароль задан: обязательное окно пароля всегда первое. Новые и изменённые объявления приходят SSE-событием announcements, отложенное начало показа — перезапросом раз в 5 минут. Админка — вкладка «Объявления» с редактором R1: contenteditable и панель (жирный, курсив, золотой и красный акцент, эмодзи, снятие оформления), вставка и перетаскивание только простым текстом, счётчик символов, период показа по МСК, переключатели «показывать новым игрокам» и «показать заново». Предпросмотр — тем же окном, что у игрока. Список со статусом, счётчиком «закрыли N из M» и действиями: изменить, снять с показа, дублировать, удалить. Выделение текста в форме — полупрозрачный оранжевый вместо синего. schema.d.ts пересобран из OpenAPI. #84 Перенесено в main без рейтинга из ab1fc3f; schema.d.ts сверен с OpenAPI этой ветки. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
45 lines
2.0 KiB
TypeScript
45 lines
2.0 KiB
TypeScript
export const qk = {
|
|
me: ["me"] as const,
|
|
adminMe: ["adminMe"] as const,
|
|
home: ["home"] as const,
|
|
leaderboard: ["leaderboard"] as const,
|
|
expansions: ["expansions"] as const,
|
|
factions: ["factions"] as const,
|
|
myStats: ["myStats"] as const,
|
|
publicProfile: (id: number) => ["publicProfile", id] as const,
|
|
userMatches: (id: number) => ["userMatches", id] as const,
|
|
userSearch: (q: string, limit: number) => ["userSearch", q, limit] as const,
|
|
groups: ["groups"] as const,
|
|
group: (id: number) => ["group", id] as const,
|
|
groupFactions: (id: number) => ["group", id, "factions"] as const,
|
|
groupMembers: (id: number) => ["group", id, "members"] as const,
|
|
groupMatches: (id: number) => ["group", id, "matches"] as const,
|
|
groupStats: (id: number) => ["group", id, "stats"] as const,
|
|
match: (id: number) => ["match", id] as const,
|
|
authConfig: ["authConfig"] as const,
|
|
invitations: ["invitations"] as const,
|
|
notifications: ["notifications"] as const,
|
|
announcements: ["announcements"] as const,
|
|
devUsers: ["devUsers"] as const,
|
|
adminUsers: ["adminUsers"] as const,
|
|
adminGroups: ["adminGroups"] as const,
|
|
adminMatches: ["adminMatches"] as const,
|
|
adminLogs: ["adminLogs"] as const,
|
|
adminAchievements: ["adminAchievements"] as const,
|
|
adminAnnouncements: ["adminAnnouncements"] as const,
|
|
};
|
|
|
|
/**
|
|
* Ключи, которые протухают от любой партии: конкретных участников мы не знаем
|
|
* (событие приходит на всю группу), поэтому инвалидируем по префиксу. Один
|
|
* список на SSE-обработчик и на завершение партии — иначе переименование ключа
|
|
* в этом файле тихо разойдётся с местами, где он написан строкой.
|
|
*/
|
|
export const matchAffectedKeys = [
|
|
qk.home,
|
|
qk.leaderboard,
|
|
qk.myStats,
|
|
["userMatches"],
|
|
["publicProfile"],
|
|
] as const;
|