import { useQuery, useQueryClient } from "@tanstack/react-query"; import { useEffect, useRef } from "react"; import { api } from "../../../api/client.ts"; export interface WikiBatch { id: number; status: string } export interface WikiStatus { root: string; root_title: string | null; synced_at: string | null; history_at?: string | null; pages: number; error: string | null; batch: WikiBatch | null; } export interface WikiNode { code: string; title: string | null; depth: number; parent_code: string | null; size: number | null } export interface WikiAttachment { id: number; name: string | null; state: string; size: number | null; error?: string | null; image?: boolean } export interface WikiPageData { code: string; title: string | null; path: string; url: string | null; synced_at: string | null; markdown: string; attachments: WikiAttachment[]; } export interface WikiHit { code: string; title: string | null; path: string; snippet: string } export interface WikiAttachmentState { id: number; name: string | null; state: string; error: string | null; size: number | null } export interface WikiChange { at: string; page_code: string; title: string | null; path: string | null; version: string; author: { login: string | null; name: string | null }; added: number; removed: number; } export interface WikiChanges { since: string; until: string; synced_at: string | null; history_at: string | null; truncated: boolean; changes: WikiChange[]; } export const POLL_MS = 3000; // опрос хода синхронизации, пока пакет не завершён export const SEARCH_LIMIT = 20; // ключи начинаются с ["wiki", проект]: одной инвалидацией обновляется всё про проект export const wikiQk = { project: (p: string) => ["wiki", p] as const, status: (p: string) => ["wiki", p, "status"] as const, tree: (p: string) => ["wiki", p, "tree"] as const, page: (p: string, code: string) => ["wiki", p, "page", code] as const, search: (p: string, q: string) => ["wiki", p, "search", q] as const, changes: (p: string, period: string) => ["wiki", p, "changes", period] as const, }; const terminal = (b: WikiBatch | null) => b === null || b.status === "done" || b.status === "cancelled"; // ход синхронизации; пока пакет идёт — опрос раз в 3 с, по его завершении дерево и страницы перечитываются export function useWikiStatus(project: string, enabled = true) { const qc = useQueryClient(); const q = useQuery({ queryKey: wikiQk.status(project), enabled, queryFn: () => api.get("/wiki/status", { project }), refetchInterval: (query) => (terminal(query.state.data?.batch ?? null) ? false : POLL_MS), }); const batch = q.data?.batch ?? null; const wasRunning = useRef(false); useEffect(() => { const running = !terminal(batch); if (wasRunning.current && !running) { for (const key of ["tree", "page", "search"]) void qc.invalidateQueries({ queryKey: [...wikiQk.project(project), key] }); } wasRunning.current = running; }, [batch, project, qc]); return q; } export const useWikiTree = (project: string, enabled: boolean) => useQuery({ queryKey: wikiQk.tree(project), enabled, queryFn: () => api.get<{ pages: WikiNode[] }>("/wiki/tree", { project }).then((r) => r.pages) }); export const useWikiPage = (project: string, code: string | null) => useQuery({ queryKey: wikiQk.page(project, code ?? ""), enabled: code !== null, queryFn: () => api.get(`/wiki/pages/${encodeURIComponent(code!)}`, { project }), }); export const useWikiSearch = (project: string, q: string) => useQuery({ queryKey: wikiQk.search(project, q), enabled: q !== "", queryFn: () => api.get<{ results: WikiHit[] }>("/wiki/search", { project, q, limit: SEARCH_LIMIT }).then((r) => r.results), }); // правки за период для ленты; границы считает запрос в момент выполнения (перечитывание берёт «до сейчас») export const WIKI_CHANGES_LIMIT = 2000; export const useWikiChanges = (project: string, period: string, bounds: () => { since: Date; until: Date }, enabled: boolean) => useQuery({ queryKey: wikiQk.changes(project, period), enabled, refetchOnWindowFocus: true, queryFn: () => { const { since, until } = bounds(); return api.get("/wiki/changes", { project, since: since.toISOString(), until: until.toISOString(), limit: WIKI_CHANGES_LIMIT }); }, });