mirror of
https://github.com/MODSetter/SurfSense.git
synced 2026-04-25 08:46:22 +02:00
Replace PGlite aggregate SQL (SUM/CASE) with Zero useQuery + client-side status counting. State machine (idle/processing/success/error) preserved exactly. 167 → 79 lines.
81 lines
2.4 KiB
TypeScript
81 lines
2.4 KiB
TypeScript
"use client";
|
|
|
|
import { useEffect, useRef, useState } from "react";
|
|
import { queries } from "@/zero/queries";
|
|
import { useQuery } from "@rocicorp/zero/react";
|
|
|
|
export type DocumentsProcessingStatus = "idle" | "processing" | "success" | "error";
|
|
|
|
const SUCCESS_LINGER_MS = 5000;
|
|
|
|
/**
|
|
* Returns the processing status of documents in the search space:
|
|
* - "processing" — at least one doc is pending/processing (show spinner)
|
|
* - "error" — nothing processing, but failed docs exist (show red icon)
|
|
* - "success" — just transitioned from processing → all clear (green check, auto-dismisses)
|
|
* - "idle" — nothing noteworthy (show normal icon)
|
|
*/
|
|
export function useDocumentsProcessing(searchSpaceId: number | null): DocumentsProcessingStatus {
|
|
const [status, setStatus] = useState<DocumentsProcessingStatus>("idle");
|
|
const wasProcessingRef = useRef(false);
|
|
const successTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);
|
|
|
|
const [documents] = useQuery(
|
|
queries.documents.bySpace({ searchSpaceId: searchSpaceId ?? -1 })
|
|
);
|
|
|
|
useEffect(() => {
|
|
if (!searchSpaceId || !documents) return;
|
|
|
|
let processingCount = 0;
|
|
let failedCount = 0;
|
|
|
|
for (const doc of documents) {
|
|
const state = (doc.status as { state?: string } | null)?.state;
|
|
if (state === "pending" || state === "processing") {
|
|
processingCount++;
|
|
} else if (state === "failed") {
|
|
failedCount++;
|
|
}
|
|
}
|
|
|
|
if (processingCount > 0) {
|
|
wasProcessingRef.current = true;
|
|
if (successTimerRef.current) {
|
|
clearTimeout(successTimerRef.current);
|
|
successTimerRef.current = null;
|
|
}
|
|
setStatus("processing");
|
|
} else if (failedCount > 0) {
|
|
wasProcessingRef.current = false;
|
|
if (successTimerRef.current) {
|
|
clearTimeout(successTimerRef.current);
|
|
successTimerRef.current = null;
|
|
}
|
|
setStatus("error");
|
|
} else if (wasProcessingRef.current) {
|
|
wasProcessingRef.current = false;
|
|
setStatus("success");
|
|
if (successTimerRef.current) {
|
|
clearTimeout(successTimerRef.current);
|
|
}
|
|
successTimerRef.current = setTimeout(() => {
|
|
setStatus("idle");
|
|
successTimerRef.current = null;
|
|
}, SUCCESS_LINGER_MS);
|
|
} else {
|
|
setStatus("idle");
|
|
}
|
|
}, [searchSpaceId, documents]);
|
|
|
|
useEffect(() => {
|
|
return () => {
|
|
if (successTimerRef.current) {
|
|
clearTimeout(successTimerRef.current);
|
|
successTimerRef.current = null;
|
|
}
|
|
};
|
|
}, []);
|
|
|
|
return status;
|
|
}
|