Files
wehub-resource-sync 6ede33ccdb
Build and Push Docker Images / create_manifest (web, surfsense-web, , cpu) (push) Has been cancelled
Build and Push Docker Images / finalize_release (push) Has been cancelled
Obsidian Plugin Lint / lint (push) Has been cancelled
Build and Push Docker Images / build (./surfsense_web, cpu, ./surfsense_web/Dockerfile, web, surfsense-web, ubuntu-24.04-arm, linux/arm64, arm64, , runner, false, cpu) (push) Has been cancelled
Build and Push Docker Images / build (./surfsense_web, cpu, ./surfsense_web/Dockerfile, web, surfsense-web, ubuntu-latest, linux/amd64, amd64, , runner, false, cpu) (push) Has been cancelled
Build and Push Docker Images / compute_version (push) Has been cancelled
Build and Push Docker Images / build (./surfsense_backend, cpu, ./surfsense_backend/Dockerfile, backend, surfsense-backend, ubuntu-24.04-arm, linux/arm64, arm64, , production, false, cpu) (push) Has been cancelled
Build and Push Docker Images / build (./surfsense_backend, cpu, ./surfsense_backend/Dockerfile, backend, surfsense-backend, ubuntu-latest, linux/amd64, amd64, , production, false, cpu) (push) Has been cancelled
Build and Push Docker Images / build (./surfsense_backend, cu126, ./surfsense_backend/Dockerfile, backend, surfsense-backend, ubuntu-24.04-arm, linux/arm64, arm64, -cuda126, production, true, cuda126) (push) Has been cancelled
Build and Push Docker Images / build (./surfsense_backend, cu126, ./surfsense_backend/Dockerfile, backend, surfsense-backend, ubuntu-latest, linux/amd64, amd64, -cuda126, production, true, cuda126) (push) Has been cancelled
Build and Push Docker Images / build (./surfsense_backend, cu128, ./surfsense_backend/Dockerfile, backend, surfsense-backend, ubuntu-24.04-arm, linux/arm64, arm64, -cuda, production, true, cuda) (push) Has been cancelled
Build and Push Docker Images / build (./surfsense_backend, cu128, ./surfsense_backend/Dockerfile, backend, surfsense-backend, ubuntu-latest, linux/amd64, amd64, -cuda, production, true, cuda) (push) Has been cancelled
Build and Push Docker Images / verify_digests (push) Has been cancelled
Build and Push Docker Images / create_manifest (backend, surfsense-backend, , cpu) (push) Has been cancelled
Build and Push Docker Images / create_manifest (backend, surfsense-backend, -cuda, cuda) (push) Has been cancelled
Build and Push Docker Images / create_manifest (backend, surfsense-backend, -cuda126, cuda126) (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 13:33:44 +08:00

128 lines
3.6 KiB
TypeScript

"use client";
import { useCallback, useEffect, useRef, useState } from "react";
import type { DocumentTypeEnum } from "@/contracts/types/document.types";
import { documentsApiService } from "@/lib/apis/documents-api.service";
import { type DocumentDisplay, toDisplayDoc } from "./use-documents";
const SEARCH_INITIAL_SIZE = 20;
const SEARCH_SCROLL_SIZE = 5;
/**
* Paginated document search hook.
*
* Handles title-based search with server-side filtering,
* pagination via skip/page_size, and staleness detection
* so fast typing never renders stale results.
*
* @param workspaceId - The workspace to search within
* @param query - The debounced search query
* @param activeTypes - Document types to filter by
* @param enabled - When false the hook resets and stops fetching
*/
export function useDocumentSearch(
workspaceId: number,
query: string,
activeTypes: DocumentTypeEnum[],
enabled: boolean
) {
const [documents, setDocuments] = useState<DocumentDisplay[]>([]);
const [loading, setLoading] = useState(false);
const [loadingMore, setLoadingMore] = useState(false);
const [hasMore, setHasMore] = useState(false);
const [error, setError] = useState(false);
const apiLoadedRef = useRef(0);
const queryRef = useRef(query);
const isActive = enabled && !!query.trim();
const activeTypesKey = activeTypes.join(",");
// biome-ignore lint/correctness/useExhaustiveDependencies: activeTypesKey serializes activeTypes
useEffect(() => {
if (!isActive || !workspaceId) {
setDocuments([]);
setHasMore(false);
setError(false);
apiLoadedRef.current = 0;
return;
}
let cancelled = false;
queryRef.current = query;
setLoading(true);
setError(false);
documentsApiService
.searchDocuments({
queryParams: {
workspace_id: workspaceId,
page: 0,
page_size: SEARCH_INITIAL_SIZE,
title: query.trim(),
...(activeTypes.length > 0 && { document_types: activeTypes }),
},
})
.then((response) => {
if (cancelled || queryRef.current !== query) return;
setDocuments(response.items.map(toDisplayDoc));
setHasMore(response.has_more);
apiLoadedRef.current = response.items.length;
})
.catch((err) => {
if (cancelled) return;
console.error("[useDocumentSearch] Search failed:", err);
setError(true);
})
.finally(() => {
if (!cancelled) setLoading(false);
});
return () => {
cancelled = true;
};
}, [query, workspaceId, isActive, activeTypesKey]);
// biome-ignore lint/correctness/useExhaustiveDependencies: activeTypesKey serializes activeTypes
const loadMore = useCallback(async () => {
if (loadingMore || !isActive || !hasMore) return;
setLoadingMore(true);
try {
const response = await documentsApiService.searchDocuments({
queryParams: {
workspace_id: workspaceId,
skip: apiLoadedRef.current,
page_size: SEARCH_SCROLL_SIZE,
title: query.trim(),
...(activeTypes.length > 0 && { document_types: activeTypes }),
},
});
if (queryRef.current !== query) return;
setDocuments((prev) => [...prev, ...response.items.map(toDisplayDoc)]);
setHasMore(response.has_more);
apiLoadedRef.current += response.items.length;
} catch (err) {
console.error("[useDocumentSearch] Load more failed:", err);
} finally {
setLoadingMore(false);
}
}, [loadingMore, isActive, hasMore, workspaceId, query, activeTypesKey]);
const removeItems = useCallback((ids: number[]) => {
const idSet = new Set(ids);
setDocuments((prev) => prev.filter((item) => !idSet.has(item.id)));
}, []);
return {
documents,
loading,
loadingMore,
hasMore,
loadMore,
error,
removeItems,
};
}