This commit is contained in:
2025-11-16 11:36:11 +01:00
parent 7e0768a09a
commit dae66703f2
17 changed files with 1110 additions and 160 deletions
+1 -1
View File
@@ -62,7 +62,7 @@ const DesktopPreviewCard = ({
}: DesktopPreviewCardProps): JSX.Element => {
const navigator = useAssetNavigator({
document: doc,
assetType: 'preview',
assetType: 'thumbnail',
ensureAssetUrl: shouldLoad ? ensureAssetUrl : null,
getAsset: getDocumentAsset,
prefetch,
+94 -32
View File
@@ -8,6 +8,7 @@ import React, {
useSyncExternalStore,
} from 'react';
import { resolveDocumentAssetUrl } from '../asset_manager';
import type { EnsureAssetUrl, GetAsset } from '../asset_manager';
import { formatTransform } from './math';
import useDocumentDrag from './useDocumentDrag';
import PreviewZoomOverlay from '../detail/PreviewZoomOverlay';
@@ -31,6 +32,8 @@ import '../styles/workspace/workspace-cards.css';
type Identifier = string | number;
type TagLike = { id?: Identifier | null; label?: string; color?: string | null } | null;
type PreviewEntryLike = { url?: string | null; contentType?: string | null };
type OverlaySource = { url: string; alt?: string | null; contentType?: string | null };
export interface DeskDocument {
id?: Identifier | null;
@@ -49,6 +52,7 @@ interface NavigatorSnapshot {
ordinal?: number | null;
width?: number | null;
height?: number | null;
contentType?: string | null;
}
type OverlayOriginHint = {
@@ -124,8 +128,8 @@ interface DesktopWorkspaceProps {
onDocumentStackSelect?: (docIds: Identifier[]) => void;
onPromoteSelection?: (...args: unknown[]) => void;
onAssignTagToDocument?: (...args: unknown[]) => void;
ensureAssetUrl?: (...args: unknown[]) => Promise<unknown> | unknown;
getDocumentAsset?: (...args: unknown[]) => unknown;
ensureAssetUrl?: EnsureAssetUrl;
getDocumentAsset?: GetAsset;
activeTagIds?: Array<Identifier | null>;
selectedDocumentIds?: Identifier[];
onClearSelection?: () => void;
@@ -193,6 +197,8 @@ interface DesktopWorkspaceViewProps {
const DEBUG_DRAG = false;
const DEBUG_FOCUS = false;
const defaultGetDocumentAsset: GetAsset = () => null;
const DesktopWorkspace: React.FC<DesktopWorkspaceProps> = ({
documents = [],
searchResults = null,
@@ -202,7 +208,7 @@ const DesktopWorkspace: React.FC<DesktopWorkspaceProps> = ({
onPromoteSelection = null,
onAssignTagToDocument = null,
ensureAssetUrl = null,
getDocumentAsset = () => null,
getDocumentAsset = defaultGetDocumentAsset,
activeTagIds = [],
selectedDocumentIds = [],
onClearSelection = null,
@@ -210,6 +216,8 @@ const DesktopWorkspace: React.FC<DesktopWorkspaceProps> = ({
onCloseDetailPanel = null,
tenantId = null,
viewId = 'default',
previewEntries,
ensureDownloadUrl,
}) => {
const items = useMemo<DeskDocument[]>(
() => (searchResults ? searchResults : documents),
@@ -217,6 +225,7 @@ const DesktopWorkspace: React.FC<DesktopWorkspaceProps> = ({
);
const allowLayoutPersistence = Boolean(tenantId && viewId && viewId.startsWith('folder:'));
const previewEntryMap = previewEntries instanceof Map ? previewEntries : null;
const containerRef = useRef<HTMLDivElement | null>(null);
const itemRefs = useRef<Map<string, HTMLDivElement | null>>(new Map());
@@ -226,9 +235,8 @@ const DesktopWorkspace: React.FC<DesktopWorkspaceProps> = ({
const [overlayOriginTransform, setOverlayOriginTransform] = useState<OverlayOriginTransform | null>(
null,
);
const [previewSnapshots, setPreviewSnapshots] = useState<Map<string, NavigatorSnapshot>>(
() => new Map(),
);
const [overlaySource, setOverlaySource] = useState<OverlaySource | null>(null);
const [, setPreviewSnapshots] = useState<Map<string, NavigatorSnapshot>>(() => new Map());
const [docSizeVersion, setDocSizeVersion] = useState(0);
const docSizeMapRef = useRef<Map<string, DocumentSizeInfo>>(new Map());
const ensureDocumentSize = useCallback((doc: DeskDocument | null): DocumentSizeInfo | null => {
@@ -444,7 +452,7 @@ const DesktopWorkspace: React.FC<DesktopWorkspaceProps> = ({
if (!doc) {
return;
}
resolveDocumentAssetUrl(doc, 'preview', {
resolveDocumentAssetUrl(doc, 'thumbnail', {
ensureAssetUrl,
getAsset: getDocumentAsset,
});
@@ -546,30 +554,77 @@ const DesktopWorkspace: React.FC<DesktopWorkspaceProps> = ({
}
}, [items, previewMetadata]);
const overlayDisplay = useMemo<OverlayDisplay | null>(() => {
useEffect(() => {
let cancelled = false;
if (!overlayDocId) {
return null;
setOverlaySource(null);
return () => {
cancelled = true;
};
}
const snapshot = previewSnapshots.get(overlayDocId);
if (!snapshot || !snapshot.url) {
return null;
const doc = documentLookup.get(overlayDocId) || null;
const docIdentifier = doc?.id ?? null;
if (!docIdentifier || !doc) {
setOverlaySource(null);
return () => {
cancelled = true;
};
}
const doc = documentLookup.get(overlayDocId);
const alt = snapshot.alt || (doc?.title as string | undefined);
return {
url: snapshot.url,
alt,
canGoPrev: snapshot.canGoPrev,
canGoNext: snapshot.canGoNext,
goPrev: snapshot.goPrev,
goNext: snapshot.goNext,
const docContentType = doc?.content_type ?? null;
const versionContentType = (doc?.current_version as { version?: { content_type?: string | null } } | null)?.version?.content_type ?? null;
const applyEntry = (entry?: PreviewEntryLike | null) => {
if (!entry?.url) {
setOverlaySource(null);
return;
}
setOverlaySource({
url: entry.url,
alt: doc.title as string | undefined,
contentType: entry.contentType || docContentType || versionContentType || undefined,
});
};
}, [overlayDocId, previewSnapshots, documentLookup]);
const cachedEntry = previewEntryMap?.get(docIdentifier) || null;
if (cachedEntry?.url) {
applyEntry(cachedEntry);
return () => {
cancelled = true;
};
}
if (!ensureDownloadUrl) {
setOverlaySource(null);
return () => {
cancelled = true;
};
}
ensureDownloadUrl(docIdentifier)
.then((entry) => {
if (cancelled) {
return;
}
applyEntry(entry);
})
.catch(() => {
if (!cancelled) {
setOverlaySource(null);
}
});
return () => {
cancelled = true;
};
}, [overlayDocId, documentLookup, previewEntryMap, ensureDownloadUrl]);
const closeOverlay = useCallback(() => {
setOverlayDocId(null);
setOverlayOriginRect(null);
setOverlayOriginTransform(null);
setOverlaySource(null);
}, []);
useEffect(() => {
@@ -611,24 +666,32 @@ const DesktopWorkspace: React.FC<DesktopWorkspaceProps> = ({
}
}, [draggingId, items, setDraggingId]);
const overlayDisplay = useMemo<OverlayDisplay | null>(() => {
if (!overlaySource) {
return null;
}
return {
...overlaySource,
canGoPrev: false,
canGoNext: false,
};
}, [overlaySource]);
const openOverlayForDoc = useCallback(
(docId: Identifier | null, originInfo: OverlayOriginHint | null = null) => {
if (!docId) {
return;
}
const docKey = String(docId);
const snapshot = previewSnapshots.get(docKey);
if (!snapshot || !snapshot.url) {
return;
}
const container = itemRefs.current.get(docKey);
const imageNode = container
? container.querySelector<HTMLImageElement>('.desk-item__card img')
: null;
if (!container || !imageNode) {
if (!container) {
return;
}
const imageNode = container.querySelector<HTMLImageElement>('.desk-item__card img');
const rect = (imageNode || container).getBoundingClientRect();
if (!rect) {
return;
}
const rect = imageNode.getBoundingClientRect();
let originTransform = null;
if (originInfo) {
const { rotation = 0, scale = 1, width: originWidth, height: originHeight } = originInfo;
@@ -666,7 +729,6 @@ const DesktopWorkspace: React.FC<DesktopWorkspaceProps> = ({
},
[
bringToFront,
previewSnapshots,
itemRefs,
setOverlayOriginTransform,
ensureDocumentSize,
@@ -61,6 +61,8 @@ interface UseDeskWorkspacePropsArgs {
searchQuery?: string;
activeCorrespondentFilters?: Identifier[];
selectedFolder?: Identifier | string | null;
previewEntries?: Map<Identifier, { url?: string | null; contentType?: string | null }>;
ensureDownloadUrl?: (documentId: Identifier, options?: { force?: boolean }) => Promise<{ url?: string | null; contentType?: string | null } | null>;
}
const useDeskWorkspaceProps = ({
@@ -108,6 +110,8 @@ const useDeskWorkspaceProps = ({
searchQuery = '',
activeCorrespondentFilters = [],
selectedFolder,
previewEntries,
ensureDownloadUrl,
}: UseDeskWorkspacePropsArgs) => {
const handleDeskDocumentStackSelect: DeskDocumentStackSelectHandler = useCallback(
(docIds) => {
@@ -200,6 +204,8 @@ const useDeskWorkspaceProps = ({
onMoveDocumentsToFolder: moveDocumentsToFolder,
searchIncludeDescendants,
onToggleSearchIncludeDescendants: toggleSearchIncludeDescendants,
previewEntries,
ensureDownloadUrl,
}),
[
documents,
@@ -240,6 +246,8 @@ const useDeskWorkspaceProps = ({
moveDocumentsToFolder,
searchIncludeDescendants,
toggleSearchIncludeDescendants,
previewEntries,
ensureDownloadUrl,
],
);
};