documentsmanager
This commit is contained in:
@@ -1,7 +1,17 @@
|
||||
import { Dispatch, SetStateAction, useCallback, useState } from 'react';
|
||||
import {
|
||||
Dispatch,
|
||||
SetStateAction,
|
||||
useCallback,
|
||||
useEffect,
|
||||
useRef,
|
||||
useState,
|
||||
} from 'react';
|
||||
import DocumentsManager from '../../documents/DocumentsManager';
|
||||
|
||||
type DocumentId = string | number;
|
||||
|
||||
interface DocumentLike {
|
||||
id?: string | number;
|
||||
id?: DocumentId;
|
||||
[key: string]: unknown;
|
||||
}
|
||||
|
||||
@@ -11,36 +21,74 @@ interface FolderContentsEntry {
|
||||
}
|
||||
|
||||
interface UseDocumentsOptions {
|
||||
setSearchResults: Dispatch<SetStateAction<DocumentLike[] | null>>;
|
||||
setFolderContents: Dispatch<SetStateAction<Map<string, FolderContentsEntry>>>;
|
||||
fetchDocumentById?: (id: DocumentId) => Promise<DocumentLike | null>;
|
||||
hydrateDocument?: (payload: unknown) => DocumentLike | null;
|
||||
hydrateDocuments?: (payload: unknown[]) => DocumentLike[];
|
||||
extractDocument?: (payload: unknown) => DocumentLike | null;
|
||||
}
|
||||
|
||||
const useDocuments = ({ setSearchResults, setFolderContents }: UseDocumentsOptions) => {
|
||||
const [documents, setDocuments] = useState<DocumentLike[]>([]);
|
||||
const useDocuments = ({
|
||||
setFolderContents,
|
||||
fetchDocumentById,
|
||||
hydrateDocument,
|
||||
hydrateDocuments,
|
||||
extractDocument,
|
||||
}: UseDocumentsOptions) => {
|
||||
const managerRef = useRef(
|
||||
new DocumentsManager<DocumentLike>(fetchDocumentById, {
|
||||
hydrateDocument,
|
||||
hydrateDocuments,
|
||||
extractDocument,
|
||||
}),
|
||||
);
|
||||
const [documents, setDocumentsState] = useState<DocumentLike[]>([]);
|
||||
|
||||
useEffect(() => {
|
||||
managerRef.current.setFetcher(fetchDocumentById);
|
||||
}, [fetchDocumentById]);
|
||||
|
||||
const setDocuments = useCallback(
|
||||
(value: DocumentLike[] | ((prev: DocumentLike[]) => DocumentLike[])) => {
|
||||
setDocumentsState((prev) => {
|
||||
const resolved = typeof value === 'function' ? value(prev) : value;
|
||||
if (!Array.isArray(resolved)) {
|
||||
return resolved;
|
||||
}
|
||||
const { canonical } = managerRef.current.ingest(resolved);
|
||||
return canonical;
|
||||
});
|
||||
},
|
||||
[],
|
||||
);
|
||||
|
||||
const mapDocumentCaches = useCallback(
|
||||
(mapper: (doc: DocumentLike) => DocumentLike | undefined) => {
|
||||
managerRef.current.map(mapper);
|
||||
const lookupSnapshot = managerRef.current.getSnapshot();
|
||||
|
||||
const applyToList = (list?: DocumentLike[] | null) => {
|
||||
let changed = false;
|
||||
const safeList = Array.isArray(list) ? list : [];
|
||||
const next = safeList.map((doc) => {
|
||||
const updated = mapper(doc);
|
||||
if (updated === undefined || updated === doc) {
|
||||
return doc;
|
||||
}
|
||||
changed = true;
|
||||
return updated;
|
||||
});
|
||||
return changed ? next : safeList;
|
||||
};
|
||||
|
||||
setDocuments((prev) => applyToList(prev));
|
||||
setSearchResults((prev) => {
|
||||
if (!Array.isArray(prev)) {
|
||||
setDocumentsState((prev) => {
|
||||
if (!Array.isArray(prev) || prev.length === 0) {
|
||||
return prev;
|
||||
}
|
||||
return applyToList(prev);
|
||||
let changed = false;
|
||||
const next = prev.map((doc) => {
|
||||
const id = doc?.id;
|
||||
if (id != null && lookupSnapshot.has(id as DocumentId)) {
|
||||
const canonical = lookupSnapshot.get(id as DocumentId) as DocumentLike;
|
||||
if (canonical !== doc) {
|
||||
changed = true;
|
||||
}
|
||||
return canonical;
|
||||
}
|
||||
const updated = mapper(doc);
|
||||
const nextDoc = updated === undefined ? doc : updated;
|
||||
if (nextDoc !== doc) {
|
||||
changed = true;
|
||||
}
|
||||
return nextDoc;
|
||||
});
|
||||
return changed ? next : prev;
|
||||
});
|
||||
setFolderContents((prev) => {
|
||||
if (!prev.size) {
|
||||
@@ -56,12 +104,20 @@ const useDocuments = ({ setSearchResults, setFolderContents }: UseDocumentsOptio
|
||||
}
|
||||
let docsChanged = false;
|
||||
const updatedDocs = docs.map((doc) => {
|
||||
const updated = mapper(doc);
|
||||
if (updated === undefined || updated === doc) {
|
||||
return doc;
|
||||
const id = doc?.id;
|
||||
if (id != null && lookupSnapshot.has(id as DocumentId)) {
|
||||
const canonical = lookupSnapshot.get(id as DocumentId) as DocumentLike;
|
||||
if (canonical !== doc) {
|
||||
docsChanged = true;
|
||||
}
|
||||
return canonical;
|
||||
}
|
||||
docsChanged = true;
|
||||
return updated;
|
||||
const updated = mapper(doc);
|
||||
const nextDoc = updated === undefined ? doc : updated;
|
||||
if (nextDoc !== doc) {
|
||||
docsChanged = true;
|
||||
}
|
||||
return nextDoc;
|
||||
});
|
||||
if (docsChanged) {
|
||||
changed = true;
|
||||
@@ -73,7 +129,7 @@ const useDocuments = ({ setSearchResults, setFolderContents }: UseDocumentsOptio
|
||||
return changed ? next : prev;
|
||||
});
|
||||
},
|
||||
[setFolderContents, setSearchResults],
|
||||
[setFolderContents],
|
||||
);
|
||||
|
||||
const updateDocumentCaches = useCallback(
|
||||
@@ -93,11 +149,23 @@ const useDocuments = ({ setSearchResults, setFolderContents }: UseDocumentsOptio
|
||||
[mapDocumentCaches],
|
||||
);
|
||||
|
||||
const removeDocumentsFromLookup = useCallback(
|
||||
(documentIds: Array<DocumentId>) => {
|
||||
if (!Array.isArray(documentIds) || !documentIds.length) {
|
||||
return;
|
||||
}
|
||||
managerRef.current.remove(documentIds);
|
||||
},
|
||||
[],
|
||||
);
|
||||
|
||||
return {
|
||||
documents,
|
||||
setDocuments,
|
||||
removeDocumentsFromLookup,
|
||||
mapDocumentCaches,
|
||||
updateDocumentCaches,
|
||||
documentsManager: managerRef.current,
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user