cleanup
This commit is contained in:
@@ -0,0 +1,300 @@
|
||||
import { useCallback, useEffect, useMemo } from 'react';
|
||||
import { resolveDocumentAssetUrl } from '../asset_manager';
|
||||
import { useDetailPanel } from '../app/useDetailPanel';
|
||||
import {
|
||||
DEFAULT_FOLDER_NAME,
|
||||
getRowId,
|
||||
isDocumentRowKey,
|
||||
} from '../app/appLayoutUtils';
|
||||
|
||||
const useDetailWorkspace = ({
|
||||
documents,
|
||||
searchResults,
|
||||
focusedDocumentId,
|
||||
selectionOrder,
|
||||
selectedDocumentIds,
|
||||
documentLookup,
|
||||
folderNodes,
|
||||
ensureFolderData,
|
||||
detailPanelControlRef,
|
||||
detailFolderFetchRef,
|
||||
previewEntries,
|
||||
previewDocumentId,
|
||||
activePreviewId,
|
||||
openDocumentPreview,
|
||||
promoteSelectionOrder,
|
||||
handleDocumentTitleUpdate,
|
||||
handleDocumentIssuedUpdate,
|
||||
handleDocumentTagAdd,
|
||||
handleTagRemove,
|
||||
ensureAssetUrl,
|
||||
getDocumentAsset,
|
||||
ensurePreviewData,
|
||||
correspondents,
|
||||
handleCorrespondentAdd,
|
||||
handleCorrespondentRemove,
|
||||
resolveApiPath,
|
||||
selectFolder,
|
||||
tags,
|
||||
tagLookupById,
|
||||
}) => {
|
||||
const selectedDocument = useMemo(() => {
|
||||
if (!focusedDocumentId) {
|
||||
return null;
|
||||
}
|
||||
const pool = searchResults ?? documents;
|
||||
return pool.find((doc) => doc.id === focusedDocumentId) || null;
|
||||
}, [focusedDocumentId, searchResults, documents]);
|
||||
|
||||
const orderedSelectedDocuments = useMemo(() => {
|
||||
const ordered = [];
|
||||
const seen = new Set();
|
||||
|
||||
const pushDoc = (doc) => {
|
||||
if (doc?.id && !seen.has(doc.id)) {
|
||||
ordered.push(doc);
|
||||
seen.add(doc.id);
|
||||
}
|
||||
};
|
||||
|
||||
selectionOrder.forEach((key) => {
|
||||
if (!isDocumentRowKey(key)) {
|
||||
return;
|
||||
}
|
||||
const docId = getRowId(key);
|
||||
const doc = documentLookup.get(docId) || null;
|
||||
pushDoc(doc);
|
||||
});
|
||||
|
||||
selectedDocumentIds.forEach((docId) => {
|
||||
if (seen.has(docId)) {
|
||||
return;
|
||||
}
|
||||
const doc = documentLookup.get(docId) || null;
|
||||
pushDoc(doc);
|
||||
});
|
||||
|
||||
return ordered;
|
||||
}, [selectionOrder, documentLookup, selectedDocumentIds]);
|
||||
|
||||
const {
|
||||
detailPanelOpen,
|
||||
detailPanelDocument,
|
||||
openDetailPanel,
|
||||
closeDetailPanel,
|
||||
} = useDetailPanel({
|
||||
documentLookup,
|
||||
orderedSelectedDocuments,
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
detailPanelControlRef.current = {
|
||||
open: openDetailPanel,
|
||||
close: closeDetailPanel,
|
||||
};
|
||||
}, [detailPanelControlRef, openDetailPanel, closeDetailPanel]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!orderedSelectedDocuments.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
const visited = new Set();
|
||||
|
||||
orderedSelectedDocuments.forEach((doc) => {
|
||||
const folderId = doc?.folder_id;
|
||||
if (!folderId) {
|
||||
return;
|
||||
}
|
||||
|
||||
let currentId = folderId;
|
||||
let guard = 0;
|
||||
|
||||
while (currentId && currentId !== 'root' && guard < 32) {
|
||||
guard += 1;
|
||||
if (visited.has(currentId)) {
|
||||
break;
|
||||
}
|
||||
visited.add(currentId);
|
||||
|
||||
const node = folderNodes.get(currentId);
|
||||
if (!node) {
|
||||
if (!detailFolderFetchRef.current.has(currentId)) {
|
||||
detailFolderFetchRef.current.add(currentId);
|
||||
ensureFolderData(currentId, { force: false, includeDocuments: false })
|
||||
.catch((error) => {
|
||||
console.warn('Failed to preload folder metadata for detail path', currentId, error);
|
||||
})
|
||||
.finally(() => {
|
||||
detailFolderFetchRef.current.delete(currentId);
|
||||
});
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
const parentId = node.parentId ?? 'root';
|
||||
if (!parentId || parentId === 'root') {
|
||||
break;
|
||||
}
|
||||
currentId = parentId;
|
||||
}
|
||||
});
|
||||
}, [orderedSelectedDocuments, folderNodes, ensureFolderData, detailFolderFetchRef]);
|
||||
|
||||
const resolveFolderPath = useCallback(
|
||||
(folderId) => {
|
||||
if (!folderId || folderId === 'root') {
|
||||
return [];
|
||||
}
|
||||
|
||||
const segments = [];
|
||||
const visited = new Set();
|
||||
let currentId = folderId;
|
||||
let guard = 0;
|
||||
|
||||
while (currentId && guard < 32 && !visited.has(currentId)) {
|
||||
guard += 1;
|
||||
visited.add(currentId);
|
||||
|
||||
if (currentId === 'root') {
|
||||
break;
|
||||
}
|
||||
|
||||
const node = folderNodes.get(currentId);
|
||||
if (!node) {
|
||||
segments.push({ id: currentId, name: '…' });
|
||||
break;
|
||||
}
|
||||
|
||||
segments.push({ id: node.id, name: node.name || 'Folder' });
|
||||
|
||||
const parentId = node.parentId ?? 'root';
|
||||
if (!parentId || parentId === 'root') {
|
||||
segments.push({ id: 'root', name: DEFAULT_FOLDER_NAME });
|
||||
break;
|
||||
}
|
||||
|
||||
currentId = parentId;
|
||||
}
|
||||
|
||||
if (!segments.some((segment) => segment.id === 'root')) {
|
||||
segments.push({ id: 'root', name: DEFAULT_FOLDER_NAME });
|
||||
}
|
||||
|
||||
return segments.reverse();
|
||||
},
|
||||
[folderNodes],
|
||||
);
|
||||
|
||||
const selectedPreviewEntry = useMemo(() => {
|
||||
if (!selectedDocument) {
|
||||
return null;
|
||||
}
|
||||
return previewEntries.get(selectedDocument.id) || null;
|
||||
}, [selectedDocument, previewEntries]);
|
||||
|
||||
const previewWorkspaceEntry = useMemo(() => {
|
||||
if (!previewDocumentId) {
|
||||
return null;
|
||||
}
|
||||
return previewEntries.get(previewDocumentId) || null;
|
||||
}, [previewDocumentId, previewEntries]);
|
||||
|
||||
const previewWorkspaceDocument = useMemo(() => {
|
||||
if (!previewDocumentId) {
|
||||
return null;
|
||||
}
|
||||
const pool = searchResults ?? documents;
|
||||
return pool.find((doc) => doc.id === previewDocumentId) || null;
|
||||
}, [previewDocumentId, searchResults, documents]);
|
||||
|
||||
const previewActive = Boolean(previewDocumentId && previewWorkspaceDocument);
|
||||
|
||||
const resolveThumbnailUrlForDoc = useCallback(
|
||||
(doc) =>
|
||||
resolveDocumentAssetUrl(doc, 'thumbnail', {
|
||||
ensureAssetUrl,
|
||||
getAsset: getDocumentAsset,
|
||||
}),
|
||||
[ensureAssetUrl, getDocumentAsset],
|
||||
);
|
||||
|
||||
const inspectDocument = useCallback(
|
||||
(documentId) => {
|
||||
if (!documentId) {
|
||||
return;
|
||||
}
|
||||
openDetailPanel({ documentIds: [documentId] });
|
||||
},
|
||||
[openDetailPanel],
|
||||
);
|
||||
|
||||
const handleDetailPanelClose = useCallback(() => {
|
||||
closeDetailPanel();
|
||||
}, [closeDetailPanel]);
|
||||
|
||||
const detailPanelProps = useMemo(
|
||||
() => ({
|
||||
document: detailPanelDocument,
|
||||
tags,
|
||||
tagLookupById,
|
||||
onTagAdd: handleDocumentTagAdd,
|
||||
onTagRemove: handleTagRemove,
|
||||
previewEntry: selectedPreviewEntry,
|
||||
onOpenPreview: openDocumentPreview,
|
||||
onPromoteSelection: promoteSelectionOrder,
|
||||
activePreviewId,
|
||||
onUpdateTitle: handleDocumentTitleUpdate,
|
||||
onUpdateIssued: handleDocumentIssuedUpdate,
|
||||
ensureAssetUrl,
|
||||
getDocumentAsset,
|
||||
ensurePreviewData,
|
||||
correspondents,
|
||||
onCorrespondentAdd: handleCorrespondentAdd,
|
||||
onCorrespondentRemove: handleCorrespondentRemove,
|
||||
resolveApiPath,
|
||||
onFolderNavigate: selectFolder,
|
||||
onClose: handleDetailPanelClose,
|
||||
resolveFolderPath,
|
||||
}),
|
||||
[
|
||||
activePreviewId,
|
||||
correspondents,
|
||||
detailPanelDocument,
|
||||
ensureAssetUrl,
|
||||
ensurePreviewData,
|
||||
getDocumentAsset,
|
||||
handleCorrespondentAdd,
|
||||
handleCorrespondentRemove,
|
||||
handleDetailPanelClose,
|
||||
handleDocumentTagAdd,
|
||||
handleDocumentIssuedUpdate,
|
||||
handleDocumentTitleUpdate,
|
||||
handleTagRemove,
|
||||
openDocumentPreview,
|
||||
promoteSelectionOrder,
|
||||
resolveApiPath,
|
||||
resolveFolderPath,
|
||||
selectFolder,
|
||||
selectedPreviewEntry,
|
||||
tags,
|
||||
tagLookupById,
|
||||
],
|
||||
);
|
||||
|
||||
return {
|
||||
detailPanelProps,
|
||||
detailPanelOpen,
|
||||
openDetailPanel,
|
||||
closeDetailPanel,
|
||||
handleDetailPanelClose,
|
||||
inspectDocument,
|
||||
previewActive,
|
||||
previewWorkspaceDocument,
|
||||
previewWorkspaceEntry,
|
||||
resolveThumbnailUrlForDoc,
|
||||
resolveFolderPath,
|
||||
};
|
||||
};
|
||||
|
||||
export default useDetailWorkspace;
|
||||
Reference in New Issue
Block a user