Remove asset/document hydration pipeline and introduce client-side cache updates for correspondents, tags, and search results
This commit is contained in:
@@ -18,18 +18,21 @@ interface UseDocumentCorrespondentActionsArgs {
|
||||
apiClient: ApiClient;
|
||||
correspondents: CorrespondentOption[];
|
||||
handleCorrespondentCreate: (payload: { name: string }) => Promise<CorrespondentOption | null>;
|
||||
refreshCurrentFolder: () => Promise<void>;
|
||||
notifyApiError: (error: unknown, fallback: string) => void;
|
||||
setStatusMessage: (message: string, variant?: string) => void;
|
||||
updateDocumentCaches?: (
|
||||
id: Identifier,
|
||||
updater: (doc: { correspondents?: CorrespondentOption[] } | null) => { correspondents?: CorrespondentOption[] } | null,
|
||||
) => void;
|
||||
}
|
||||
|
||||
const useDocumentCorrespondentActions = ({
|
||||
apiClient,
|
||||
correspondents,
|
||||
handleCorrespondentCreate,
|
||||
refreshCurrentFolder,
|
||||
notifyApiError,
|
||||
setStatusMessage,
|
||||
updateDocumentCaches,
|
||||
}: UseDocumentCorrespondentActionsArgs) => {
|
||||
const correspondentLookupByName = useMemo(() => {
|
||||
const map = new Map<string, CorrespondentOption>();
|
||||
@@ -44,7 +47,7 @@ const useDocumentCorrespondentActions = ({
|
||||
const handleDocumentCorrespondentAttach = useCallback(
|
||||
async (
|
||||
{ documentId, correspondentId }: { documentId: Identifier; correspondentId: Identifier },
|
||||
{ notify = true, refresh = true }: { notify?: boolean; refresh?: boolean } = {},
|
||||
{ notify = true }: { notify?: boolean } = {},
|
||||
) => {
|
||||
if (documentId == null || correspondentId == null) {
|
||||
throw new Error('Missing document or correspondent.');
|
||||
@@ -54,8 +57,21 @@ const useDocumentCorrespondentActions = ({
|
||||
assignments: [{ correspondent_id: correspondentId }],
|
||||
replace: false,
|
||||
});
|
||||
if (refresh) {
|
||||
await refreshCurrentFolder();
|
||||
if (updateDocumentCaches) {
|
||||
const correspondent = correspondents.find((entry) => entry?.id === correspondentId) || null;
|
||||
updateDocumentCaches(documentId, (doc) => {
|
||||
if (!doc) {
|
||||
return doc;
|
||||
}
|
||||
const current = Array.isArray(doc.correspondents) ? doc.correspondents : [];
|
||||
if (current.some((entry) => entry?.id === correspondentId)) {
|
||||
return doc;
|
||||
}
|
||||
const nextEntry = correspondent
|
||||
? { id: correspondent.id, name: correspondent.name }
|
||||
: { id: correspondentId };
|
||||
return { ...doc, correspondents: [...current, nextEntry] };
|
||||
});
|
||||
}
|
||||
if (notify) {
|
||||
setStatusMessage('Correspondent assigned.', 'success');
|
||||
@@ -67,21 +83,27 @@ const useDocumentCorrespondentActions = ({
|
||||
throw new Error(message);
|
||||
}
|
||||
},
|
||||
[apiClient, refreshCurrentFolder, notifyApiError, setStatusMessage],
|
||||
[apiClient, correspondents, notifyApiError, setStatusMessage, updateDocumentCaches],
|
||||
);
|
||||
|
||||
const handleCorrespondentRemove = useCallback(
|
||||
async (
|
||||
{ documentId, correspondentId }: { documentId: Identifier; correspondentId: Identifier },
|
||||
{ notify = true, refresh = true }: { notify?: boolean; refresh?: boolean } = {},
|
||||
{ notify = true }: { notify?: boolean } = {},
|
||||
) => {
|
||||
if (documentId == null || correspondentId == null) {
|
||||
throw new Error('Missing document or correspondent.');
|
||||
}
|
||||
try {
|
||||
await apiClient.delete(`/documents/${documentId}/correspondents/${correspondentId}`);
|
||||
if (refresh) {
|
||||
await refreshCurrentFolder();
|
||||
if (updateDocumentCaches) {
|
||||
updateDocumentCaches(documentId, (doc) => {
|
||||
if (!doc || !Array.isArray(doc.correspondents)) {
|
||||
return doc;
|
||||
}
|
||||
const filtered = doc.correspondents.filter((entry) => entry?.id !== correspondentId);
|
||||
return filtered.length === doc.correspondents.length ? doc : { ...doc, correspondents: filtered };
|
||||
});
|
||||
}
|
||||
if (notify) {
|
||||
setStatusMessage('Correspondent removed.', 'success');
|
||||
@@ -93,7 +115,7 @@ const useDocumentCorrespondentActions = ({
|
||||
throw new Error(message);
|
||||
}
|
||||
},
|
||||
[apiClient, refreshCurrentFolder, notifyApiError, setStatusMessage],
|
||||
[apiClient, notifyApiError, setStatusMessage, updateDocumentCaches],
|
||||
);
|
||||
|
||||
const normalizeOption = (
|
||||
|
||||
@@ -134,6 +134,7 @@ interface UseDocumentMutationsArgs {
|
||||
closeDocumentPreview: CloseDocumentPreview;
|
||||
previewDocumentId?: DocumentId | null;
|
||||
refreshCurrentFolder: () => Promise<void>;
|
||||
refreshVisibleDocuments: () => Promise<void>;
|
||||
documentsViewMode?: string;
|
||||
updateDocumentCaches: UpdateDocumentCaches;
|
||||
tagLookupById: Map<DocumentId, Tag>;
|
||||
@@ -141,6 +142,7 @@ interface UseDocumentMutationsArgs {
|
||||
refreshTags: () => Promise<void>;
|
||||
tagManager: TagManager;
|
||||
extractDocumentFromResponse?: (payload: unknown) => DocumentLike | null;
|
||||
ingestDocuments?: (docs: unknown[]) => { canonical: DocumentLike[]; changed: boolean };
|
||||
}
|
||||
|
||||
interface UseDocumentMutationsResult {
|
||||
@@ -210,6 +212,7 @@ const useDocumentMutations = ({
|
||||
closeDocumentPreview,
|
||||
previewDocumentId,
|
||||
refreshCurrentFolder,
|
||||
refreshVisibleDocuments,
|
||||
documentsViewMode,
|
||||
updateDocumentCaches,
|
||||
tagLookupById,
|
||||
@@ -217,6 +220,7 @@ const useDocumentMutations = ({
|
||||
refreshTags,
|
||||
tagManager,
|
||||
extractDocumentFromResponse,
|
||||
ingestDocuments,
|
||||
}: UseDocumentMutationsArgs): UseDocumentMutationsResult => {
|
||||
const moveDocumentsToFolder = useCallback(
|
||||
async (documentIds: Array<DocumentId | DocumentLike>, targetFolderId?: NullableFolderId) => {
|
||||
@@ -492,12 +496,16 @@ const useDocumentMutations = ({
|
||||
const { data } = await api.patch(`/documents/${documentId}`, { title: trimmed });
|
||||
const updatedDocument = extractDocumentFromResponse?.(data);
|
||||
|
||||
updateDocumentCaches(documentId, (doc) => {
|
||||
if (updatedDocument) {
|
||||
return { ...doc, ...updatedDocument };
|
||||
}
|
||||
return { ...doc, title: trimmed };
|
||||
});
|
||||
if (updatedDocument && ingestDocuments) {
|
||||
ingestDocuments([updatedDocument]);
|
||||
} else {
|
||||
updateDocumentCaches(documentId, (doc) => {
|
||||
if (updatedDocument) {
|
||||
return { ...doc, ...updatedDocument };
|
||||
}
|
||||
return { ...doc, title: trimmed };
|
||||
});
|
||||
}
|
||||
|
||||
setStatusMessage('Document title updated.', 'success');
|
||||
return true;
|
||||
@@ -509,7 +517,15 @@ const useDocumentMutations = ({
|
||||
setLoading(false);
|
||||
}
|
||||
},
|
||||
[api, extractDocumentFromResponse, notifyApiError, setLoading, setStatusMessage, updateDocumentCaches],
|
||||
[
|
||||
api,
|
||||
extractDocumentFromResponse,
|
||||
ingestDocuments,
|
||||
notifyApiError,
|
||||
setLoading,
|
||||
setStatusMessage,
|
||||
updateDocumentCaches,
|
||||
],
|
||||
);
|
||||
|
||||
const handleDocumentIssuedUpdate = useCallback(
|
||||
@@ -520,12 +536,16 @@ const useDocumentMutations = ({
|
||||
const { data } = await api.patch(`/documents/${documentId}`, payload);
|
||||
const updatedDocument = extractDocumentFromResponse?.(data);
|
||||
|
||||
updateDocumentCaches(documentId, (doc) => {
|
||||
if (updatedDocument) {
|
||||
return { ...doc, ...updatedDocument };
|
||||
}
|
||||
return { ...doc, issued_at: payload.issued_at };
|
||||
});
|
||||
if (updatedDocument && ingestDocuments) {
|
||||
ingestDocuments([updatedDocument]);
|
||||
} else {
|
||||
updateDocumentCaches(documentId, (doc) => {
|
||||
if (updatedDocument) {
|
||||
return { ...doc, ...updatedDocument };
|
||||
}
|
||||
return { ...doc, issued_at: payload.issued_at };
|
||||
});
|
||||
}
|
||||
|
||||
const message = payload.issued_at ? 'Issued date updated.' : 'Issued date cleared.';
|
||||
setStatusMessage(message, 'success');
|
||||
@@ -538,7 +558,56 @@ const useDocumentMutations = ({
|
||||
setLoading(false);
|
||||
}
|
||||
},
|
||||
[api, extractDocumentFromResponse, notifyApiError, setLoading, setStatusMessage, updateDocumentCaches],
|
||||
[
|
||||
api,
|
||||
extractDocumentFromResponse,
|
||||
ingestDocuments,
|
||||
notifyApiError,
|
||||
setLoading,
|
||||
setStatusMessage,
|
||||
updateDocumentCaches,
|
||||
],
|
||||
);
|
||||
|
||||
const attachTagToDocument = useCallback(
|
||||
async ({
|
||||
documentId,
|
||||
tag,
|
||||
}: {
|
||||
documentId?: DocumentId;
|
||||
tag?: Tag | null;
|
||||
}) => {
|
||||
if (!documentId || !tag?.id) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const cachedTag: Tag = {
|
||||
id: tag.id,
|
||||
label: tag.label,
|
||||
color: Object.prototype.hasOwnProperty.call(tag, 'color') ? tag.color ?? null : null,
|
||||
};
|
||||
|
||||
try {
|
||||
await api.post(`/documents/${documentId}/tags`, { tag_ids: [cachedTag.id] });
|
||||
updateDocumentCaches(documentId, (doc) => {
|
||||
if (!doc) {
|
||||
return doc;
|
||||
}
|
||||
const currentTags = Array.isArray(doc.tags) ? doc.tags : [];
|
||||
if (currentTags.some((entry) => entry?.id === cachedTag.id)) {
|
||||
return doc;
|
||||
}
|
||||
return { ...doc, tags: [...currentTags, cachedTag] };
|
||||
});
|
||||
setStatusMessage('Tag assigned.', 'success');
|
||||
return true;
|
||||
} catch (error) {
|
||||
const message = (error as Record<string, any>)?.response?.data?.error || 'Failed to assign tag.';
|
||||
notifyApiError(error, message);
|
||||
return false;
|
||||
}
|
||||
},
|
||||
[api, notifyApiError, setStatusMessage, updateDocumentCaches],
|
||||
);
|
||||
|
||||
const handleDocumentTagAdd = useCallback(
|
||||
@@ -561,17 +630,18 @@ const useDocumentMutations = ({
|
||||
tag = data as Tag;
|
||||
await refreshTags();
|
||||
}
|
||||
await api.post(`/documents/${document.id}/tags`, { tag_ids: [tag.id] });
|
||||
setStatusMessage('Tag assigned.', 'success');
|
||||
await attachTagToDocument({
|
||||
documentId: document.id as DocumentId,
|
||||
tag,
|
||||
});
|
||||
if (input && Object(input) === input && 'value' in (input as Record<string, unknown>)) {
|
||||
(input as { value?: string }).value = '';
|
||||
}
|
||||
await refreshCurrentFolder();
|
||||
} catch (error) {
|
||||
notifyApiError(error, 'Failed to assign tag.');
|
||||
}
|
||||
},
|
||||
[api, tags, refreshTags, refreshCurrentFolder, notifyApiError, setStatusMessage, tagManager],
|
||||
[api, tags, refreshTags, attachTagToDocument, notifyApiError, setStatusMessage, tagManager],
|
||||
);
|
||||
|
||||
const handleDocumentTagAttach = useCallback(
|
||||
@@ -597,40 +667,14 @@ const useDocumentMutations = ({
|
||||
};
|
||||
};
|
||||
|
||||
try {
|
||||
await api.post(`/documents/${documentId}/tags`, { tag_ids: [tagId] });
|
||||
updateDocumentCaches(documentId, (doc) => {
|
||||
if (!doc) {
|
||||
return doc;
|
||||
}
|
||||
const currentTags = Array.isArray(doc.tags) ? doc.tags : [];
|
||||
if (currentTags.some((existing) => existing?.id === tagId)) {
|
||||
return doc;
|
||||
}
|
||||
const resolvedTag = resolveTagForCache();
|
||||
if (!resolvedTag) {
|
||||
return doc;
|
||||
}
|
||||
return { ...doc, tags: [...currentTags, resolvedTag] };
|
||||
});
|
||||
setStatusMessage('Tag assigned.', 'success');
|
||||
if (documentsViewMode !== 'desk') {
|
||||
await refreshCurrentFolder();
|
||||
}
|
||||
return true;
|
||||
} catch (error) {
|
||||
const message = (error as Record<string, any>)?.response?.data?.error || 'Failed to assign tag.';
|
||||
notifyApiError(error, message);
|
||||
return false;
|
||||
}
|
||||
const resolvedTag = resolveTagForCache();
|
||||
return attachTagToDocument({
|
||||
documentId,
|
||||
tag: resolvedTag,
|
||||
});
|
||||
},
|
||||
[
|
||||
api,
|
||||
refreshCurrentFolder,
|
||||
documentsViewMode,
|
||||
notifyApiError,
|
||||
setStatusMessage,
|
||||
updateDocumentCaches,
|
||||
attachTagToDocument,
|
||||
tagLookupById,
|
||||
],
|
||||
);
|
||||
|
||||
@@ -21,11 +21,11 @@ interface UseDocumentTaggingArgs {
|
||||
tags: TagRecord[];
|
||||
tagManager: TagManager;
|
||||
refreshTags: () => Promise<void> | void;
|
||||
refreshCurrentFolder: () => Promise<void> | void;
|
||||
resolveTargetDocumentIds: (ids?: Identifier[] | null) => Identifier[];
|
||||
notifyApiError: (error: unknown, message: string) => void;
|
||||
setStatusMessage: (message: string, variant?: string) => void;
|
||||
setLoading: (state: boolean) => void;
|
||||
updateDocumentCaches?: (id: Identifier, updater: (doc: TagRecord | null) => TagRecord | null) => void;
|
||||
}
|
||||
|
||||
interface BulkTagOperationArgs {
|
||||
@@ -47,11 +47,11 @@ const useDocumentTagging = ({
|
||||
tags,
|
||||
tagManager,
|
||||
refreshTags,
|
||||
refreshCurrentFolder,
|
||||
resolveTargetDocumentIds,
|
||||
notifyApiError,
|
||||
setStatusMessage,
|
||||
setLoading,
|
||||
updateDocumentCaches,
|
||||
}: UseDocumentTaggingArgs) => {
|
||||
const bulkTagOperation = useCallback(
|
||||
async ({ labels, action, documentIds }: BulkTagOperationArgs): Promise<BulkTagOperationResult> => {
|
||||
@@ -82,22 +82,59 @@ const useDocumentTagging = ({
|
||||
|
||||
setLoading(true);
|
||||
try {
|
||||
if (action === 'add') {
|
||||
const createdIds: Identifier[] = [];
|
||||
for (const label of normalized) {
|
||||
let tag = tags.find((item) => item.label.toLowerCase() === label.toLowerCase()) || null;
|
||||
if (!tag) {
|
||||
const payload = tagManager.buildPayload({ label });
|
||||
const response = await apiClient.post<{ id?: Identifier; label: string }>('/tags', payload);
|
||||
tag = 'data' in response ? response.data : response;
|
||||
await refreshTags();
|
||||
}
|
||||
createdIds.push(tag.id);
|
||||
if (action === 'add') {
|
||||
const createdIds: Identifier[] = [];
|
||||
const createdTags: TagRecord[] = [];
|
||||
for (const label of normalized) {
|
||||
let tag = tags.find((item) => item.label.toLowerCase() === label.toLowerCase()) || null;
|
||||
if (!tag) {
|
||||
const payload = tagManager.buildPayload({ label });
|
||||
const response = await apiClient.post<{ id?: Identifier; label: string }>('/tags', payload);
|
||||
tag = 'data' in response ? response.data : response;
|
||||
await refreshTags();
|
||||
}
|
||||
tagIds = Array.from(new Set(createdIds));
|
||||
createdIds.push(tag.id);
|
||||
createdTags.push(tag);
|
||||
}
|
||||
tagIds = Array.from(new Set(createdIds));
|
||||
|
||||
tagIds = Array.from(new Set(tagIds));
|
||||
if (updateDocumentCaches) {
|
||||
const tagById = new Map<Identifier, TagRecord>();
|
||||
tags.forEach((tag) => {
|
||||
if (tag?.id != null) {
|
||||
tagById.set(tag.id, tag);
|
||||
}
|
||||
});
|
||||
createdTags.forEach((tag) => {
|
||||
if (tag?.id != null) {
|
||||
tagById.set(tag.id, tag);
|
||||
}
|
||||
});
|
||||
targetDocumentIds.forEach((docId) => {
|
||||
tagIds.forEach((tagId) => {
|
||||
const cachedTag = tagById.get(tagId);
|
||||
if (!cachedTag) {
|
||||
return;
|
||||
}
|
||||
updateDocumentCaches(docId, (doc) => {
|
||||
if (!doc) {
|
||||
return doc;
|
||||
}
|
||||
const currentTags = Array.isArray((doc as any).tags) ? (doc as any).tags : [];
|
||||
if (currentTags.some((entry: any) => entry?.id === tagId)) {
|
||||
return doc;
|
||||
}
|
||||
return {
|
||||
...(doc as any),
|
||||
tags: [...currentTags, { ...cachedTag }],
|
||||
};
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
tagIds = Array.from(new Set(tagIds));
|
||||
|
||||
if (!tagIds.length) {
|
||||
return { ok: false, reason: 'no-tags' };
|
||||
@@ -109,7 +146,23 @@ const useDocumentTagging = ({
|
||||
action,
|
||||
});
|
||||
|
||||
await refreshCurrentFolder();
|
||||
if (updateDocumentCaches) {
|
||||
targetDocumentIds.forEach((docId) => {
|
||||
tagIds.forEach((tagId) => {
|
||||
updateDocumentCaches(docId, (doc) => {
|
||||
if (!doc || !Array.isArray((doc as any).tags)) {
|
||||
return doc;
|
||||
}
|
||||
const currentTags = (doc as any).tags;
|
||||
if (action === 'remove') {
|
||||
const filtered = currentTags.filter((entry: any) => entry?.id !== tagId);
|
||||
return filtered.length === currentTags.length ? doc : { ...(doc as any), tags: filtered };
|
||||
}
|
||||
return doc;
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
ok: true,
|
||||
@@ -130,11 +183,11 @@ const useDocumentTagging = ({
|
||||
resolveTargetDocumentIds,
|
||||
tags,
|
||||
refreshTags,
|
||||
refreshCurrentFolder,
|
||||
notifyApiError,
|
||||
setLoading,
|
||||
tagManager,
|
||||
apiClient,
|
||||
updateDocumentCaches,
|
||||
],
|
||||
);
|
||||
|
||||
|
||||
@@ -23,24 +23,14 @@ interface FolderContentsEntry {
|
||||
interface UseDocumentsOptions {
|
||||
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 = ({
|
||||
setFolderContents,
|
||||
fetchDocumentById,
|
||||
hydrateDocument,
|
||||
hydrateDocuments,
|
||||
extractDocument,
|
||||
}: UseDocumentsOptions) => {
|
||||
const managerRef = useRef(
|
||||
new DocumentsManager<DocumentLike>(fetchDocumentById, {
|
||||
hydrateDocument,
|
||||
hydrateDocuments,
|
||||
extractDocument,
|
||||
}),
|
||||
new DocumentsManager<DocumentLike>(fetchDocumentById),
|
||||
);
|
||||
const [documents, setDocumentsState] = useState<DocumentLike[]>([]);
|
||||
|
||||
|
||||
@@ -318,9 +318,6 @@ const useDocumentsWorkspace = ({
|
||||
} = useDocuments({
|
||||
setFolderContents,
|
||||
fetchDocumentById,
|
||||
hydrateDocument: (payload) => assetManager.hydrateDocument(payload),
|
||||
hydrateDocuments: (payload) => assetManager.hydrateDocuments(payload),
|
||||
extractDocument: extractDocumentFromResponse,
|
||||
});
|
||||
|
||||
const documentLookup = useSyncExternalStore(
|
||||
@@ -348,7 +345,6 @@ const useDocumentsWorkspace = ({
|
||||
isInvalidFolderDrop,
|
||||
} = useFolderTree({
|
||||
initialSelectedFolder: routeFolderId || 'root',
|
||||
assetManager,
|
||||
apiClient: api,
|
||||
tenantIdRef,
|
||||
documentsSortFieldRef: activeSortFieldRef,
|
||||
@@ -370,6 +366,7 @@ const useDocumentsWorkspace = ({
|
||||
activeCorrespondentFilters,
|
||||
setActiveCorrespondentFilters,
|
||||
isFilterActive,
|
||||
refetchSearchResults,
|
||||
documentsFilterValue,
|
||||
} = useDocumentsSearch({
|
||||
api,
|
||||
@@ -444,7 +441,6 @@ const useDocumentsWorkspace = ({
|
||||
|
||||
const {
|
||||
documentLinks,
|
||||
ensurePreviewData,
|
||||
ensureDownloadUrl,
|
||||
openDocumentPreview,
|
||||
closeDocumentPreview,
|
||||
@@ -454,7 +450,6 @@ const useDocumentsWorkspace = ({
|
||||
routeDocumentId: previewDocumentId,
|
||||
documentsManager,
|
||||
selectedFolder,
|
||||
assetManager,
|
||||
api,
|
||||
resolveApiPath,
|
||||
notifyApiError,
|
||||
@@ -607,6 +602,14 @@ const useDocumentsWorkspace = ({
|
||||
}
|
||||
}, [selectedFolder, ensureFolderData, applySelectedFolder, notifyApiError]);
|
||||
|
||||
const refreshVisibleDocuments = useCallback(async () => {
|
||||
if (showingSearchResults) {
|
||||
await refetchSearchResults();
|
||||
return;
|
||||
}
|
||||
await refreshCurrentFolder();
|
||||
}, [showingSearchResults, refetchSearchResults, refreshCurrentFolder]);
|
||||
|
||||
const {
|
||||
handleBulkTagAddFromDetail,
|
||||
handleBulkTagRemoveFromDetail,
|
||||
@@ -616,11 +619,11 @@ const useDocumentsWorkspace = ({
|
||||
tags,
|
||||
tagManager,
|
||||
refreshTags,
|
||||
refreshCurrentFolder,
|
||||
resolveTargetDocumentIds,
|
||||
notifyApiError,
|
||||
setStatusMessage,
|
||||
setLoading,
|
||||
updateDocumentCaches,
|
||||
});
|
||||
|
||||
const {
|
||||
@@ -671,9 +674,9 @@ const useDocumentsWorkspace = ({
|
||||
apiClient: api,
|
||||
correspondents,
|
||||
handleCorrespondentCreate,
|
||||
refreshCurrentFolder,
|
||||
notifyApiError,
|
||||
setStatusMessage,
|
||||
updateDocumentCaches,
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
@@ -851,6 +854,7 @@ const useDocumentsWorkspace = ({
|
||||
closeDocumentPreview,
|
||||
previewDocumentId,
|
||||
refreshCurrentFolder,
|
||||
refreshVisibleDocuments,
|
||||
documentsViewMode,
|
||||
updateDocumentCaches,
|
||||
tagLookupById,
|
||||
@@ -858,6 +862,7 @@ const useDocumentsWorkspace = ({
|
||||
refreshTags,
|
||||
tagManager,
|
||||
extractDocumentFromResponse,
|
||||
ingestDocuments: (docs) => documentsManager.ingest(docs),
|
||||
});
|
||||
|
||||
const {
|
||||
@@ -1007,7 +1012,6 @@ const useDocumentsWorkspace = ({
|
||||
resolveTargetDocumentIds,
|
||||
correspondentLookupByName,
|
||||
handleCorrespondentCreate,
|
||||
refreshCurrentFolder,
|
||||
setStatusMessage,
|
||||
selectedDocumentIds,
|
||||
selectedFolderIds,
|
||||
@@ -1015,6 +1019,7 @@ const useDocumentsWorkspace = ({
|
||||
handleFolderDelete,
|
||||
clearDocumentSelection,
|
||||
setLoading,
|
||||
updateDocumentCaches,
|
||||
});
|
||||
|
||||
|
||||
@@ -1036,9 +1041,7 @@ const useDocumentsWorkspace = ({
|
||||
return null;
|
||||
}
|
||||
|
||||
setDocuments((prev) =>
|
||||
prev.map((doc) => (doc.id === documentId ? mergeAssetIntoDocument(doc, entry) : doc)),
|
||||
);
|
||||
updateDocumentCaches(documentId, (doc) => mergeAssetIntoDocument(doc, entry));
|
||||
|
||||
return entry;
|
||||
} catch (error) {
|
||||
@@ -1046,7 +1049,7 @@ const useDocumentsWorkspace = ({
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
[assetManager, setDocuments, notifyApiError],
|
||||
[assetManager, updateDocumentCaches, notifyApiError],
|
||||
);
|
||||
|
||||
|
||||
@@ -1264,7 +1267,6 @@ const useDocumentsWorkspace = ({
|
||||
handleTagRemove,
|
||||
ensureAssetUrl,
|
||||
getDocumentAsset,
|
||||
ensurePreviewData,
|
||||
correspondents,
|
||||
handleCorrespondentAdd,
|
||||
handleCorrespondentRemove,
|
||||
@@ -1617,7 +1619,6 @@ const useDocumentsWorkspace = ({
|
||||
documentsTableProps,
|
||||
detailPanelProps,
|
||||
documentsViewMode,
|
||||
ensurePreviewData,
|
||||
ensureAssetUrl,
|
||||
resolveFolderPath,
|
||||
getDocumentAsset,
|
||||
@@ -1669,7 +1670,6 @@ const useDocumentsWorkspace = ({
|
||||
documentsTableProps,
|
||||
detailPanelProps,
|
||||
documentsViewMode,
|
||||
ensurePreviewData,
|
||||
ensureAssetUrl,
|
||||
resolveFolderPath,
|
||||
getDocumentAsset,
|
||||
|
||||
@@ -48,11 +48,6 @@ interface FolderTreeNode extends FolderSummary {
|
||||
hasChildren?: boolean;
|
||||
}
|
||||
|
||||
interface AssetManagerLike {
|
||||
hydrateDocuments: (docs: DocumentLike[]) => DocumentLike[];
|
||||
hydrateFolderContents: (payload: FolderContentsEntry) => FolderContentsEntry;
|
||||
}
|
||||
|
||||
interface ApiClient {
|
||||
get<T = FolderContentsEntry>(path: string, config?: { params?: Record<string, unknown> }): Promise<{ data: T }>;
|
||||
}
|
||||
@@ -68,7 +63,6 @@ interface SelectionHelpers {
|
||||
|
||||
interface UseFolderTreeOptions {
|
||||
initialSelectedFolder?: FolderId;
|
||||
assetManager: AssetManagerLike;
|
||||
apiClient: ApiClient;
|
||||
tenantIdRef: MutableRefObject<Identifier | null>;
|
||||
documentsSortFieldRef: MutableRefObject<string>;
|
||||
@@ -86,7 +80,6 @@ interface FolderOption {
|
||||
|
||||
const useFolderTree = ({
|
||||
initialSelectedFolder = 'root',
|
||||
assetManager,
|
||||
apiClient,
|
||||
tenantIdRef,
|
||||
documentsSortFieldRef,
|
||||
@@ -117,7 +110,7 @@ const useFolderTree = ({
|
||||
const applySelectedFolder = useCallback(
|
||||
(folderId: FolderId, contents?: FolderContentsEntry | null) => {
|
||||
const subfolders = Array.isArray(contents?.subfolders) ? contents.subfolders : [];
|
||||
const docs = assetManager.hydrateDocuments(contents?.documents ?? []);
|
||||
const docs = Array.isArray(contents?.documents) ? contents.documents : [];
|
||||
const folderInfo = contents?.folder ?? null;
|
||||
|
||||
setCurrentSubfolders(subfolders);
|
||||
@@ -166,7 +159,6 @@ const useFolderTree = ({
|
||||
setSelectionOrder(mergedSelection);
|
||||
},
|
||||
[
|
||||
assetManager,
|
||||
focusedDocumentId,
|
||||
selectionAnchorRef,
|
||||
selectionOrderRef,
|
||||
@@ -258,14 +250,13 @@ const useFolderTree = ({
|
||||
}
|
||||
const requestConfig = Object.keys(params).length ? { params } : {};
|
||||
const { data } = await apiClient.get<FolderContentsEntry>(`/folders/${path}/contents`, requestConfig);
|
||||
const hydrated = assetManager.hydrateFolderContents(data);
|
||||
const childFolders = Array.isArray(data.subfolders) ? data.subfolders : [];
|
||||
const childIds = childFolders
|
||||
.map((child) => (child?.id ?? null) as FolderId | null)
|
||||
.filter((id): id is FolderId => Boolean(id));
|
||||
|
||||
const enriched = {
|
||||
...hydrated,
|
||||
...data,
|
||||
__includesDocuments: includeDocuments,
|
||||
__sortField: includeDocuments ? sortField : cachedSortField,
|
||||
__sortDirection: includeDocuments ? sortDirection : cachedSortDirection,
|
||||
@@ -353,10 +344,10 @@ const useFolderTree = ({
|
||||
if (existingEntry) {
|
||||
next.set(folderId, {
|
||||
...existingEntry,
|
||||
...hydrated,
|
||||
...data,
|
||||
documents: existingEntry.__includesDocuments
|
||||
? existingEntry.documents
|
||||
: hydrated.documents,
|
||||
: data.documents,
|
||||
__includesDocuments: existingEntry.__includesDocuments || false,
|
||||
__sortField: existingEntry.__sortField ?? enriched.__sortField,
|
||||
__sortDirection: existingEntry.__sortDirection ?? enriched.__sortDirection,
|
||||
@@ -372,7 +363,6 @@ const useFolderTree = ({
|
||||
},
|
||||
[
|
||||
apiClient,
|
||||
assetManager,
|
||||
documentsSortDirectionRef,
|
||||
documentsSortFieldRef,
|
||||
tenantIdRef,
|
||||
|
||||
Reference in New Issue
Block a user