85 lines
2.9 KiB
TypeScript
85 lines
2.9 KiB
TypeScript
import { useCallback, useEffect, useMemo, useState } from "react";
|
|
import { referenceOf, useCaminoQuery, type ExactReference } from "@quixos/camino-react";
|
|
import { queries } from "./gen/client.js";
|
|
import { asInterface, type JobNode } from "./model.js";
|
|
import { mergeCachedAnnotationEntries } from "./work.js";
|
|
|
|
type Entry = { node: JobNode };
|
|
|
|
/** Successes outlive request history. Read every retained page instead of
|
|
* treating the first bounded graph query as the entire semantic cache. */
|
|
export function useCachedAnnotations(reference?: ExactReference | null) {
|
|
const query = useCaminoQuery(queries.DocumentView, {
|
|
enabled: !!reference,
|
|
root: reference ? asInterface(reference, "document") : undefined,
|
|
input: {},
|
|
admission: { query: queries.DocumentInputs, input: {} },
|
|
});
|
|
const [pages, setPages] = useState<{ object: string; entries: Record<string, readonly Entry[]> }>({
|
|
object: "",
|
|
entries: {},
|
|
});
|
|
const report = useCallback(
|
|
(cursor: string, entries: readonly Entry[] | null) => {
|
|
const object = reference?.object ?? "";
|
|
setPages((previous) => {
|
|
const next = previous.object === object ? { ...previous.entries } : {};
|
|
if (entries) next[cursor] = entries;
|
|
else delete next[cursor];
|
|
return { object, entries: next };
|
|
});
|
|
},
|
|
[reference?.object],
|
|
);
|
|
const node =
|
|
query.status === "ready" && referenceOf(query.data.root).object === reference?.object ? query.data.root : null;
|
|
const entries = useMemo(
|
|
() =>
|
|
mergeCachedAnnotationEntries(
|
|
[
|
|
...(node?.results.entries ?? []),
|
|
...(pages.object === reference?.object ? Object.values(pages.entries).flat() : []),
|
|
],
|
|
node?.jobs.entries ?? [],
|
|
),
|
|
[node, pages, reference?.object],
|
|
);
|
|
return {
|
|
ready: !!node,
|
|
entries,
|
|
continuation:
|
|
node?.results.pageInfo.hasNextPage && node.results.pageInfo.endCursor ? (
|
|
<CachedResultsTail
|
|
key={reference!.object}
|
|
reference={reference!}
|
|
cursor={node.results.pageInfo.endCursor}
|
|
report={report}
|
|
/>
|
|
) : null,
|
|
};
|
|
}
|
|
|
|
function CachedResultsTail({
|
|
reference,
|
|
cursor,
|
|
report,
|
|
}: {
|
|
reference: ExactReference;
|
|
cursor: string;
|
|
report(cursor: string, entries: readonly Entry[] | null): void;
|
|
}) {
|
|
const query = useCaminoQuery(queries.CachedResultsView, {
|
|
root: asInterface(reference, "document"),
|
|
input: { after: cursor },
|
|
admission: { query: queries.CachedResultsInputs, input: { after: cursor } },
|
|
});
|
|
const results = query.status === "ready" ? query.data.root.results : null;
|
|
useEffect(() => {
|
|
if (results) report(cursor, results.entries);
|
|
return () => report(cursor, null);
|
|
}, [results, cursor, report]);
|
|
return results?.pageInfo.hasNextPage && results.pageInfo.endCursor ? (
|
|
<CachedResultsTail reference={reference} cursor={results.pageInfo.endCursor} report={report} />
|
|
) : null;
|
|
}
|