refactor
This commit is contained in:
@@ -8,12 +8,8 @@ import React, {
|
||||
useSyncExternalStore,
|
||||
} from 'react';
|
||||
import { createPortal } from 'react-dom';
|
||||
import { resolveDocumentAssetUrl, createAssetView } from '../asset_manager';
|
||||
import { resolveDocumentAssetUrl } from '../asset_manager';
|
||||
import { CloseIcon } from '../ui/icons';
|
||||
import SelectionFloatingActions from '../documents/SelectionFloatingActions';
|
||||
import { createDocumentsTableHeaderActions } from '../documents/DocumentsPanel';
|
||||
import createWorkspaceSurfaceConfig from '../documents/workspaceHeader';
|
||||
import DetailPanel from '../detail/DetailPanel';
|
||||
import { formatTransform } from './math';
|
||||
import useDocumentDrag from './useDocumentDrag';
|
||||
import PreviewZoomOverlay from '../detail/PreviewZoomOverlay';
|
||||
@@ -29,91 +25,11 @@ import {
|
||||
import useDeskPointer from './pointer/useDeskPointer';
|
||||
import useDeskTagInteractions from './tags/useDeskTagInteractions';
|
||||
import DesktopDocumentCard from './DesktopDocumentCard';
|
||||
import usePreviewMetadata from './hooks/usePreviewMetadata';
|
||||
import './DesktopWorkspace.css';
|
||||
|
||||
const DEBUG_DRAG = false;
|
||||
const DEBUG_FOCUS = false;
|
||||
|
||||
const usePreviewMetadata = (documents, getDocumentAsset, ensureAssetUrl) => {
|
||||
const [metadataMap, setMetadataMap] = useState(() => new Map());
|
||||
|
||||
useEffect(() => {
|
||||
let cancelled = false;
|
||||
const docs = Array.isArray(documents) ? documents : [];
|
||||
if (!docs.length) {
|
||||
setMetadataMap(new Map());
|
||||
return () => {
|
||||
cancelled = true;
|
||||
};
|
||||
}
|
||||
|
||||
const fetchMetadataForDoc = async (doc) => {
|
||||
if (!doc?.id) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const docId = String(doc.id);
|
||||
const resolveAsset = (type) => (typeof getDocumentAsset === 'function' ? getDocumentAsset(doc, type) : null);
|
||||
|
||||
let asset = resolveAsset('preview') || resolveAsset('thumbnail');
|
||||
let view = createAssetView(asset);
|
||||
let metadata = view.getPrimaryMetadata();
|
||||
|
||||
const hasDimensions = (meta) =>
|
||||
Number.isFinite(Number(meta?.width)) && Number.isFinite(Number(meta?.height)) &&
|
||||
Number(meta.width) > 0 && Number(meta.height) > 0;
|
||||
|
||||
if (!hasDimensions(metadata) && ensureAssetUrl && docId && asset?.id) {
|
||||
try {
|
||||
const ensured = await ensureAssetUrl(doc.id, asset, { start: 1, limit: 1 });
|
||||
if (ensured) {
|
||||
asset = ensured;
|
||||
view = createAssetView(asset);
|
||||
metadata = view.getPrimaryMetadata();
|
||||
}
|
||||
} catch (error) {
|
||||
console.warn('[desk] ensureDocumentSize metadata fetch failed', error);
|
||||
}
|
||||
}
|
||||
|
||||
if (!hasDimensions(metadata)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const width = Number(metadata.width);
|
||||
const height = Number(metadata.height);
|
||||
if (!Number.isFinite(width) || !Number.isFinite(height)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return [docId, { width, height }];
|
||||
};
|
||||
|
||||
Promise.all(docs.map((doc) => fetchMetadataForDoc(doc)))
|
||||
.then((entries) => {
|
||||
if (cancelled) return;
|
||||
const next = new Map();
|
||||
entries.forEach((entry) => {
|
||||
if (entry) {
|
||||
next.set(entry[0], entry[1]);
|
||||
}
|
||||
});
|
||||
setMetadataMap(next);
|
||||
})
|
||||
.catch(() => {
|
||||
if (!cancelled) {
|
||||
setMetadataMap(new Map());
|
||||
}
|
||||
});
|
||||
|
||||
return () => {
|
||||
cancelled = true;
|
||||
};
|
||||
}, [documents, getDocumentAsset, ensureAssetUrl]);
|
||||
|
||||
return metadataMap;
|
||||
};
|
||||
|
||||
const DesktopWorkspace = ({
|
||||
documents = [],
|
||||
searchResults = null,
|
||||
@@ -1082,109 +998,3 @@ const DesktopHelpOverlay = ({ open = false, onClose = null }) => {
|
||||
portalTarget,
|
||||
);
|
||||
};
|
||||
|
||||
export const createDesktopSurface = ({
|
||||
workspaceProps,
|
||||
renderSidebarToggle,
|
||||
parentBreadcrumb,
|
||||
onNavigateParent,
|
||||
detailProps = null,
|
||||
detailOpen = false,
|
||||
}) => {
|
||||
if (!workspaceProps) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const {
|
||||
currentFolderName,
|
||||
searchResults,
|
||||
onRefresh,
|
||||
viewMode,
|
||||
onViewModeChange,
|
||||
selectedDocumentIds,
|
||||
selectedFolderIds,
|
||||
onDeleteSelection,
|
||||
onClearSelection,
|
||||
tags,
|
||||
correspondents,
|
||||
documentLookup,
|
||||
tagLookupById,
|
||||
onBulkTagAdd,
|
||||
onBulkTagRemove,
|
||||
onBulkCorrespondentAdd,
|
||||
onBulkCorrespondentRemove,
|
||||
onBulkReanalyze,
|
||||
folderOptions,
|
||||
onMoveDocumentsToFolder,
|
||||
searchIncludeDescendants,
|
||||
onToggleSearchIncludeDescendants,
|
||||
} = workspaceProps;
|
||||
|
||||
const title = Array.isArray(searchResults) ? 'Search results' : currentFolderName;
|
||||
const subtitle = Array.isArray(searchResults)
|
||||
? `${searchResults.length} matching document${searchResults.length === 1 ? '' : 's'}`
|
||||
: null;
|
||||
|
||||
|
||||
const documentSelectionCount = Array.isArray(selectedDocumentIds) ? selectedDocumentIds.length : 0;
|
||||
const folderSelectionCount = Array.isArray(selectedFolderIds) ? selectedFolderIds.length : 0;
|
||||
const selectionCount = documentSelectionCount + folderSelectionCount;
|
||||
const actions = createDocumentsTableHeaderActions({
|
||||
viewMode: viewMode || 'desk',
|
||||
onViewModeChange,
|
||||
onRefresh,
|
||||
onShowDeskHelp: viewMode === 'desk' ? workspaceProps?.onOpenHelp : null,
|
||||
includeDescendants: searchIncludeDescendants,
|
||||
onToggleIncludeDescendants: onToggleSearchIncludeDescendants,
|
||||
});
|
||||
|
||||
const floatingActions = selectionCount > 0
|
||||
? (
|
||||
<SelectionFloatingActions
|
||||
selectionCount={selectionCount}
|
||||
selectedDocumentIds={selectedDocumentIds}
|
||||
selectedFolderIds={selectedFolderIds}
|
||||
documentLookup={documentLookup}
|
||||
tags={tags}
|
||||
tagLookupById={tagLookupById}
|
||||
correspondents={correspondents}
|
||||
onBulkTagAdd={onBulkTagAdd}
|
||||
onBulkTagRemove={onBulkTagRemove}
|
||||
onBulkCorrespondentAdd={onBulkCorrespondentAdd}
|
||||
onBulkCorrespondentRemove={onBulkCorrespondentRemove}
|
||||
onBulkReanalyze={onBulkReanalyze}
|
||||
onDeleteSelection={onDeleteSelection}
|
||||
onClearSelection={onClearSelection}
|
||||
folderOptions={folderOptions}
|
||||
onMoveDocumentsToFolder={onMoveDocumentsToFolder}
|
||||
/>
|
||||
)
|
||||
: null;
|
||||
|
||||
const sidebarToggle = renderSidebarToggle ? renderSidebarToggle() : null;
|
||||
const detail = detailOpen && detailProps ? <DetailPanel {...detailProps} /> : null;
|
||||
const surfaceConfig = createWorkspaceSurfaceConfig({
|
||||
key: 'workspace',
|
||||
variant: 'workspace',
|
||||
title,
|
||||
subtitle,
|
||||
sidebarToggle,
|
||||
parentBreadcrumb,
|
||||
onNavigateParent,
|
||||
actions,
|
||||
breadcrumbs: workspaceProps?.breadcrumbs || null,
|
||||
selectionLabel: null,
|
||||
floatingActions,
|
||||
content: (
|
||||
<DesktopWorkspace
|
||||
{...workspaceProps}
|
||||
/>
|
||||
),
|
||||
detail,
|
||||
});
|
||||
|
||||
return {
|
||||
...surfaceConfig,
|
||||
supportsDetail: Boolean(detailProps),
|
||||
};
|
||||
};
|
||||
|
||||
@@ -0,0 +1,113 @@
|
||||
import React from 'react';
|
||||
import SelectionFloatingActions from '../documents/SelectionFloatingActions';
|
||||
import { createDocumentsTableHeaderActions } from '../documents/DocumentsPanel';
|
||||
import createWorkspaceSurfaceConfig from '../documents/workspaceHeader';
|
||||
import DetailPanel from '../detail/DetailPanel';
|
||||
import DesktopWorkspace from './DesktopWorkspace';
|
||||
|
||||
const createDesktopSurface = ({
|
||||
workspaceProps,
|
||||
renderSidebarToggle,
|
||||
parentBreadcrumb,
|
||||
onNavigateParent,
|
||||
detailProps = null,
|
||||
detailOpen = false,
|
||||
}) => {
|
||||
if (!workspaceProps) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const {
|
||||
currentFolderName,
|
||||
searchResults,
|
||||
onRefresh,
|
||||
viewMode,
|
||||
onViewModeChange,
|
||||
selectedDocumentIds,
|
||||
selectedFolderIds,
|
||||
onDeleteSelection,
|
||||
onClearSelection,
|
||||
tags,
|
||||
correspondents,
|
||||
documentLookup,
|
||||
tagLookupById,
|
||||
onBulkTagAdd,
|
||||
onBulkTagRemove,
|
||||
onBulkCorrespondentAdd,
|
||||
onBulkCorrespondentRemove,
|
||||
onBulkReanalyze,
|
||||
folderOptions,
|
||||
onMoveDocumentsToFolder,
|
||||
searchIncludeDescendants,
|
||||
onToggleSearchIncludeDescendants,
|
||||
} = workspaceProps;
|
||||
|
||||
const title = Array.isArray(searchResults) ? 'Search results' : currentFolderName;
|
||||
const subtitle = Array.isArray(searchResults)
|
||||
? `${searchResults.length} matching document${searchResults.length === 1 ? '' : 's'}`
|
||||
: null;
|
||||
|
||||
const documentSelectionCount = Array.isArray(selectedDocumentIds) ? selectedDocumentIds.length : 0;
|
||||
const folderSelectionCount = Array.isArray(selectedFolderIds) ? selectedFolderIds.length : 0;
|
||||
const selectionCount = documentSelectionCount + folderSelectionCount;
|
||||
const actions = createDocumentsTableHeaderActions({
|
||||
viewMode: viewMode || 'desk',
|
||||
onViewModeChange,
|
||||
onRefresh,
|
||||
onShowDeskHelp: viewMode === 'desk' ? workspaceProps?.onOpenHelp : null,
|
||||
includeDescendants: searchIncludeDescendants,
|
||||
onToggleIncludeDescendants: onToggleSearchIncludeDescendants,
|
||||
});
|
||||
|
||||
const floatingActions = selectionCount > 0
|
||||
? (
|
||||
<SelectionFloatingActions
|
||||
selectionCount={selectionCount}
|
||||
selectedDocumentIds={selectedDocumentIds}
|
||||
selectedFolderIds={selectedFolderIds}
|
||||
documentLookup={documentLookup}
|
||||
tags={tags}
|
||||
tagLookupById={tagLookupById}
|
||||
correspondents={correspondents}
|
||||
onBulkTagAdd={onBulkTagAdd}
|
||||
onBulkTagRemove={onBulkTagRemove}
|
||||
onBulkCorrespondentAdd={onBulkCorrespondentAdd}
|
||||
onBulkCorrespondentRemove={onBulkCorrespondentRemove}
|
||||
onBulkReanalyze={onBulkReanalyze}
|
||||
onDeleteSelection={onDeleteSelection}
|
||||
onClearSelection={onClearSelection}
|
||||
folderOptions={folderOptions}
|
||||
onMoveDocumentsToFolder={onMoveDocumentsToFolder}
|
||||
/>
|
||||
)
|
||||
: null;
|
||||
|
||||
const sidebarToggle = renderSidebarToggle ? renderSidebarToggle() : null;
|
||||
const detail = detailOpen && detailProps ? <DetailPanel {...detailProps} /> : null;
|
||||
const surfaceConfig = createWorkspaceSurfaceConfig({
|
||||
key: 'workspace',
|
||||
variant: 'workspace',
|
||||
title,
|
||||
subtitle,
|
||||
sidebarToggle,
|
||||
parentBreadcrumb,
|
||||
onNavigateParent,
|
||||
actions,
|
||||
breadcrumbs: workspaceProps?.breadcrumbs || null,
|
||||
selectionLabel: null,
|
||||
floatingActions,
|
||||
content: (
|
||||
<DesktopWorkspace
|
||||
{...workspaceProps}
|
||||
/>
|
||||
),
|
||||
detail,
|
||||
});
|
||||
|
||||
return {
|
||||
...surfaceConfig,
|
||||
supportsDetail: Boolean(detailProps),
|
||||
};
|
||||
};
|
||||
|
||||
export default createDesktopSurface;
|
||||
@@ -0,0 +1,86 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
import { createAssetView } from '../../asset_manager';
|
||||
|
||||
const usePreviewMetadata = (documents, getDocumentAsset, ensureAssetUrl) => {
|
||||
const [metadataMap, setMetadataMap] = useState(() => new Map());
|
||||
|
||||
useEffect(() => {
|
||||
let cancelled = false;
|
||||
const docs = Array.isArray(documents) ? documents : [];
|
||||
if (!docs.length) {
|
||||
setMetadataMap(new Map());
|
||||
return () => {
|
||||
cancelled = true;
|
||||
};
|
||||
}
|
||||
|
||||
const fetchMetadataForDoc = async (doc) => {
|
||||
if (!doc?.id) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const docId = String(doc.id);
|
||||
const resolveAsset = (type) =>
|
||||
(typeof getDocumentAsset === 'function' ? getDocumentAsset(doc, type) : null);
|
||||
|
||||
let asset = resolveAsset('preview') || resolveAsset('thumbnail');
|
||||
let view = createAssetView(asset);
|
||||
let metadata = view.getPrimaryMetadata();
|
||||
|
||||
const hasDimensions = (meta) =>
|
||||
Number.isFinite(Number(meta?.width)) &&
|
||||
Number.isFinite(Number(meta?.height)) &&
|
||||
Number(meta.width) > 0 &&
|
||||
Number(meta.height) > 0;
|
||||
|
||||
if (!hasDimensions(metadata) && ensureAssetUrl && docId && asset?.id) {
|
||||
try {
|
||||
const ensured = await ensureAssetUrl(doc.id, asset, { start: 1, limit: 1 });
|
||||
if (ensured) {
|
||||
asset = ensured;
|
||||
view = createAssetView(asset);
|
||||
metadata = view.getPrimaryMetadata();
|
||||
}
|
||||
} catch (error) {
|
||||
console.warn('[desk] ensureDocumentSize metadata fetch failed', error);
|
||||
}
|
||||
}
|
||||
|
||||
if (!hasDimensions(metadata)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return {
|
||||
docId,
|
||||
width: Number(metadata.width),
|
||||
height: Number(metadata.height),
|
||||
};
|
||||
};
|
||||
|
||||
let mounted = true;
|
||||
(async () => {
|
||||
const entries = await Promise.all(docs.map(fetchMetadataForDoc));
|
||||
if (!mounted || cancelled) {
|
||||
return;
|
||||
}
|
||||
const next = new Map();
|
||||
entries.forEach((entry) => {
|
||||
if (entry && entry.docId) {
|
||||
next.set(entry.docId, entry);
|
||||
}
|
||||
});
|
||||
if (!cancelled) {
|
||||
setMetadataMap(next);
|
||||
}
|
||||
})();
|
||||
|
||||
return () => {
|
||||
cancelled = true;
|
||||
mounted = false;
|
||||
};
|
||||
}, [documents, getDocumentAsset, ensureAssetUrl]);
|
||||
|
||||
return metadataMap;
|
||||
};
|
||||
|
||||
export default usePreviewMetadata;
|
||||
Reference in New Issue
Block a user