documentsmanager

This commit is contained in:
2025-11-19 23:36:04 +01:00
parent e1b47ed153
commit b76f1df27f
17 changed files with 564 additions and 300 deletions
+18 -99
View File
@@ -36,8 +36,13 @@ type NavigateHandler = (path: string, options?: { replace?: boolean }) => void;
interface UseDocumentPreviewArgs {
routeDocumentId?: DocumentId | null;
documents: DocumentLike[];
searchResults: DocumentLike[] | null;
documentsManager: {
getById: (id: DocumentId) => DocumentLike | null;
ensure: (id: DocumentId) => Promise<DocumentLike | null>;
getMany: (ids: DocumentId[]) => DocumentLike[];
subscribe: (listener: () => void) => () => void;
ingest: (docs: unknown[]) => { canonical: DocumentLike[]; changed: boolean };
};
selectedFolder?: FolderId | null;
assetManager: AssetManagerLike;
api: ApiClient;
@@ -55,7 +60,6 @@ interface UseDocumentPreviewArgs {
interface UseDocumentPreviewResult {
documentLinks: Map<DocumentId, DocumentLink>;
previewDocuments: Map<DocumentId, DocumentLike>;
ensureDownloadUrl: (documentId: DocumentId, options?: { force?: boolean }) => Promise<DocumentLink | null>;
ensurePreviewData: (documentId: DocumentId) => Promise<DocumentLike | null>;
openDocumentPreview: (documentId: DocumentId, options?: { replace?: boolean }) => void;
@@ -66,10 +70,9 @@ interface UseDocumentPreviewResult {
const useDocumentPreview = ({
routeDocumentId,
documents,
searchResults,
documentsManager,
selectedFolder,
assetManager,
assetManager: _assetManager,
api,
resolveApiPath,
notifyApiError,
@@ -80,7 +83,6 @@ const useDocumentPreview = ({
setActivePreviewId,
}: UseDocumentPreviewArgs): UseDocumentPreviewResult => {
const [documentLinks, setDocumentLinks] = useState<Map<DocumentId, DocumentLink>>(() => new Map());
const [previewDocuments, setPreviewDocuments] = useState<Map<DocumentId, DocumentLike>>(() => new Map());
const previewInflightRef = useRef<Map<DocumentId, Promise<DocumentLink | null>>>(new Map());
const previewReturnPathRef = useRef<string | null>(null);
@@ -110,35 +112,6 @@ const useDocumentPreview = ({
});
}, []);
const cachePreviewDocument = useCallback((doc: DocumentLike) => {
if (!doc?.id) {
return;
}
setPreviewDocuments((prev) => {
const existing = prev.get(doc.id);
if (existing === doc) {
return prev;
}
const next = new Map(prev);
next.set(doc.id, doc);
return next;
});
}, []);
const removeCachedPreviewDocument = useCallback((documentId: DocumentId) => {
if (!documentId) {
return;
}
setPreviewDocuments((prev) => {
if (!prev.has(documentId)) {
return prev;
}
const next = new Map(prev);
next.delete(documentId);
return next;
});
}, []);
const ensureDownloadUrl = useCallback(
async (documentId: DocumentId, { force = false }: { force?: boolean } = {}): Promise<DocumentLink | null> => {
if (!documentId) return null;
@@ -193,32 +166,22 @@ const useDocumentPreview = ({
async (documentId: DocumentId): Promise<DocumentLike | null> => {
if (!documentId) return null;
const findInCache = () => {
const pool = searchResults ?? documents;
return pool.find((item) => item.id === documentId) || null;
};
const findInCache = () => documentsManager.getById(documentId);
let doc = findInCache();
if (!doc) {
doc = await documentsManager.ensure(documentId);
}
if (!doc) {
const { data } = await api.get(`/documents/${documentId}`);
const hydratedDetail = assetManager.hydrateDetail(data);
const fetched = hydratedDetail?.document || (data as { document?: DocumentLike })?.document || data;
doc = fetched ? assetManager.hydrateDocument(fetched) || null : null;
const fetched = (data as { document?: DocumentLike })?.document || data;
const { canonical } = documentsManager.ingest([fetched as unknown]);
doc = (canonical[0] as DocumentLike | undefined) || null;
if (!doc) {
throw new Error('Document metadata unavailable.');
}
const existsInDocuments = documents.some((item) => item.id === doc?.id);
const existsInSearch = Array.isArray(searchResults)
? searchResults.some((item) => item.id === doc?.id)
: false;
if (doc?.id && (existsInDocuments || existsInSearch)) {
removeCachedPreviewDocument(doc.id);
} else {
cachePreviewDocument(doc);
}
}
if (!previewReturnPathRef.current) {
@@ -232,14 +195,10 @@ const useDocumentPreview = ({
return doc;
},
[
searchResults,
documents,
assetManager,
documentsManager,
ensureDownloadUrl,
setActivePreviewId,
api,
cachePreviewDocument,
removeCachedPreviewDocument,
],
);
@@ -270,21 +229,6 @@ const useDocumentPreview = ({
[navigate, selectedFolder],
);
useEffect(() => {
if (!routeDocumentId) {
return undefined;
}
const handleKeyDown = (event: KeyboardEvent) => {
if (event.key === 'Escape') {
closeDocumentPreview();
}
};
window.addEventListener('keydown', handleKeyDown);
return () => window.removeEventListener('keydown', handleKeyDown);
}, [routeDocumentId, closeDocumentPreview]);
useEffect(() => {
if (!routeDocumentId) {
return undefined;
@@ -305,33 +249,8 @@ const useDocumentPreview = ({
};
}, [routeDocumentId, ensurePreviewData, notifyApiError, closeDocumentPreview]);
useEffect(() => {
setPreviewDocuments((prev) => {
if (!prev.size) {
return prev;
}
const next = new Map(prev);
let changed = false;
const prune = (list?: DocumentLike[] | null) => {
if (!Array.isArray(list)) {
return;
}
list.forEach((doc) => {
if (doc?.id && next.has(doc.id)) {
next.delete(doc.id);
changed = true;
}
});
};
prune(documents);
prune(searchResults);
return changed ? next : prev;
});
}, [documents, searchResults]);
return {
documentLinks,
previewDocuments,
ensureDownloadUrl,
ensurePreviewData,
openDocumentPreview,