В «Аккаунте» появился блок «История игр» с двумя тумблерами — «Только лучшая партия» и «Подробные карточки»; выбор уходит в профиль, поэтому переживает перезаход и другое устройство. Новый компонент MatchHistory: подробный режим переиспользует список партий группы (MatchListView со всеми участниками), компактный рисует строку с результатом самого игрока — место, фракция, дата и длительность. Публичный профиль показывает ту же историю в режиме владельца и без контролов: страница намеренно read-only. schema.d.ts пересобран с живого бэкенда (npm run gen:api). #1 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
47 lines
2.0 KiB
TypeScript
47 lines
2.0 KiB
TypeScript
import { keepPreviousData, useQuery } from "@tanstack/react-query";
|
|
import { useEffect, useState } from "react";
|
|
|
|
import { api, unwrap } from "../api/client";
|
|
import { qk } from "../api/queryKeys";
|
|
|
|
/** Значение с задержкой: не дёргаем поиск на каждый введённый символ.
|
|
* 100 мс — почти незаметно глазу, но склеивает быструю печать в один запрос. */
|
|
function useDebounced<T>(value: T, ms = 100): T {
|
|
const [debounced, setDebounced] = useState(value);
|
|
useEffect(() => {
|
|
const t = setTimeout(() => setDebounced(value), ms);
|
|
return () => clearTimeout(t);
|
|
}, [value, ms]);
|
|
return debounced;
|
|
}
|
|
|
|
/** История партий игрока. Режим и подробность приходят вместе со списком:
|
|
* это витрина ВЛАДЕЛЬЦА профиля, её видят и гости. */
|
|
export function useUserMatches(userId: number | null) {
|
|
return useQuery({
|
|
queryKey: userId ? qk.userMatches(userId) : ["userMatches", "none"],
|
|
enabled: userId != null,
|
|
queryFn: async () =>
|
|
unwrap(
|
|
await api.GET("/api/users/{user_id}/matches", {
|
|
params: { path: { user_id: userId as number } },
|
|
}),
|
|
),
|
|
});
|
|
}
|
|
|
|
/** Подсказки по нику среди всех активных игроков (GET /api/users/search). */
|
|
export function useUserSearch(q: string, limit = 3) {
|
|
const dq = useDebounced(q.trim());
|
|
return useQuery({
|
|
queryKey: qk.userSearch(dq, limit),
|
|
enabled: dq.length > 0,
|
|
// Список не мигает пустым между буквами; при пустом q данные остаются —
|
|
// видимость выпадашки родитель гейтит по q, а не по data.
|
|
placeholderData: keepPreviousData,
|
|
staleTime: 30_000,
|
|
queryFn: async () =>
|
|
unwrap(await api.GET("/api/users/search", { params: { query: { q: dq, limit } } })),
|
|
});
|
|
}
|