85 lines
4.5 KiB
TypeScript
85 lines
4.5 KiB
TypeScript
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<WikiStatus>("/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<WikiPageData>(`/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<WikiChanges>("/wiki/changes", { project, since: since.toISOString(), until: until.toISOString(), limit: WIKI_CHANGES_LIMIT });
|
||
},
|
||
});
|