Партия: совместное заполнение формы завершения на клиенте

Раскладка, комментарии, причина победы и комментарий о партии уходят в общий
черновик через ~0.6с после последнего действия, а чужие правки подтягиваются по
SSE. Чужой черновик применяется, только если с момента моего последнего действия
прошло больше 1.5с — иначе правка соседа перетирала бы тайл прямо под рукой.

Перед «Завершить» отложенная запись дожимается: иначе последняя правка попала бы
в результаты, но не в черновик, и второй участник увидел бы не то, что записалось.
Само завершение по-прежнему шлёт тело запроса, так что работает и без черновика.

Под подсказкой о перетаскивании появилась строка «Результаты заполняет также
<Ник>», а если партию завершил кто-то другой — тост вместо молча исчезающей формы.

В админке у идущей партии вместо формы правки — пояснение и переход на страницу
партии: сервер её результаты всё равно не примет.

#28

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BoiJK9ux8peeyjLb8TYjFf
This commit is contained in:
2026-09-09 19:39:42 +03:00
co-authored by Claude Opus 5
parent 8be9a23c0c
commit 32d64c94af
6 changed files with 254 additions and 8 deletions
+27 -1
View File
@@ -2,7 +2,13 @@ import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
import { api, unwrap } from "../api/client";
import { matchAffectedKeys, qk } from "../api/queryKeys";
import type { FactionRead, MatchCreate, MatchFinish, MatchRead } from "../domain/types";
import type {
FactionRead,
MatchCreate,
MatchFinish,
MatchFinishDraftData,
MatchRead,
} from "../domain/types";
import { uploadFile } from "../lib/upload";
export function useMatch(matchId: number | null) {
@@ -20,6 +26,26 @@ export function useMatch(matchId: number | null) {
});
}
/**
* Общий черновик формы завершения: то, что видят все, кто заполняет партию.
* Ответ — свежая партия целиком, поэтому кладём его прямо в кэш, без инвалидации:
* перезапрос на каждое движение тайла был бы расточительным.
*/
export function useSaveFinishDraft(matchId: number | null) {
const qc = useQueryClient();
return useMutation({
mutationFn: async (data: MatchFinishDraftData): Promise<MatchRead> =>
unwrap(
await api.PUT("/api/matches/{match_id}/finish-draft", {
params: { path: { match_id: matchId as number } },
body: data,
}),
),
onSuccess: (m) => qc.setQueryData(qk.match(m.id), m),
});
}
export function useRandomizeFaction() {
return useMutation({
mutationFn: async (args: { group_id: number; exclude_faction_ids: number[] }): Promise<FactionRead> => {