7
This commit is contained in:
@@ -6,6 +6,7 @@ import React, {
|
||||
useRef,
|
||||
useState,
|
||||
} from 'react';
|
||||
import { resolveDocumentAssetUrl } from './asset_manager';
|
||||
import './skeuomorphic_ws.css';
|
||||
|
||||
const ITEM_WIDTH = 220;
|
||||
@@ -258,31 +259,6 @@ const useDocumentDrag = ({
|
||||
};
|
||||
};
|
||||
|
||||
const resolvePreviewDimensions = (doc) => {
|
||||
if (!doc) return null;
|
||||
|
||||
const group = doc?.current_version?.assets || doc?.assets;
|
||||
let asset = null;
|
||||
if (group && !Array.isArray(group) && group?.preview) {
|
||||
asset = group.preview;
|
||||
} else if (Array.isArray(group)) {
|
||||
asset = group.find((entry) => entry?.asset_type === 'preview') || null;
|
||||
}
|
||||
|
||||
if (!asset) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const width = asset?.metadata?.width;
|
||||
const height = asset?.metadata?.height;
|
||||
|
||||
if (Number.isFinite(width) && Number.isFinite(height)) {
|
||||
return { width, height };
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
const seededRandom = (input) => {
|
||||
const text = String(input);
|
||||
let hash = 2166136261;
|
||||
@@ -336,37 +312,6 @@ const getContrastingTextColor = (hex) => {
|
||||
return luminance > 0.6 ? '#1b1f24' : '#ffffff';
|
||||
};
|
||||
|
||||
const absolutizeAssetUrl = (input, resolver) => {
|
||||
if (!input) return null;
|
||||
if (/^(?:https?:|data:|blob:)/i.test(input)) {
|
||||
return input;
|
||||
}
|
||||
if (typeof resolver === 'function') {
|
||||
return resolver(input);
|
||||
}
|
||||
return input;
|
||||
};
|
||||
|
||||
const resolvePreviewUrl = (doc, resolver) => {
|
||||
if (!doc) return null;
|
||||
const group = doc?.current_version?.assets || doc?.assets;
|
||||
let asset = null;
|
||||
if (group && !Array.isArray(group) && group?.preview) {
|
||||
asset = group.preview;
|
||||
} else if (Array.isArray(group)) {
|
||||
asset = group.find((entry) => entry?.asset_type === 'preview') || null;
|
||||
}
|
||||
if (!asset?.url) {
|
||||
return null;
|
||||
}
|
||||
const now = Date.now();
|
||||
const expiresAt = typeof asset.expiresAt === 'number' ? asset.expiresAt : null;
|
||||
if (expiresAt && expiresAt <= now) {
|
||||
return null;
|
||||
}
|
||||
return absolutizeAssetUrl(asset.url, resolver);
|
||||
};
|
||||
|
||||
const SkeuomorphicWorkspace = ({
|
||||
documents = [],
|
||||
searchResults = null,
|
||||
@@ -380,13 +325,13 @@ const SkeuomorphicWorkspace = ({
|
||||
onCreateTag = null,
|
||||
onAssignTagToDocument = null,
|
||||
onRemoveTagFromDocument = null,
|
||||
resolveAssetPath = null,
|
||||
ensureAssetUrl = null,
|
||||
getDocumentAsset = () => null,
|
||||
}) => {
|
||||
const items = useMemo(() => (searchResults ? searchResults : documents), [documents, searchResults]);
|
||||
const showingSearchResults = searchResults !== null;
|
||||
|
||||
|
||||
const containerRef = useRef(null);
|
||||
const layoutRef = useRef(new Map());
|
||||
const itemRefs = useRef(new Map());
|
||||
@@ -406,47 +351,51 @@ const SkeuomorphicWorkspace = ({
|
||||
const docSizeMapRef = useRef(new Map());
|
||||
const removalCursorActiveRef = useRef(false);
|
||||
|
||||
const resolvePreviewAsset = useCallback(
|
||||
(doc) => {
|
||||
if (!doc) return null;
|
||||
return getDocumentAsset(doc, 'preview');
|
||||
},
|
||||
[getDocumentAsset],
|
||||
);
|
||||
|
||||
const resolvePreviewDimensions = useCallback(
|
||||
(doc) => {
|
||||
if (!doc) return null;
|
||||
const asset = resolvePreviewAsset(doc);
|
||||
const width = asset?.metadata?.width;
|
||||
const height = asset?.metadata?.height;
|
||||
if (typeof width === 'number' && typeof height === 'number') {
|
||||
return { width, height };
|
||||
}
|
||||
return null;
|
||||
},
|
||||
[resolvePreviewAsset],
|
||||
);
|
||||
|
||||
const resolvePreviewUrl = useCallback(
|
||||
(doc) => {
|
||||
if (!doc) return null;
|
||||
return resolveDocumentAssetUrl(doc, 'preview', {
|
||||
ensureAssetUrl,
|
||||
getAsset: getDocumentAsset,
|
||||
});
|
||||
},
|
||||
[ensureAssetUrl, getDocumentAsset],
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (!ensureAssetUrl) {
|
||||
return;
|
||||
}
|
||||
|
||||
const now = Date.now();
|
||||
const seen = new Set();
|
||||
const queueAsset = (docId, asset) => {
|
||||
if (!docId || !asset?.id || seen.has(asset.id)) {
|
||||
return;
|
||||
}
|
||||
const type = asset.asset_type;
|
||||
if (type && type !== 'thumbnail' && type !== 'preview') {
|
||||
return;
|
||||
}
|
||||
const expiresAt = typeof asset.expiresAt === 'number' ? asset.expiresAt : null;
|
||||
const hasFreshUrl = asset.url && (!expiresAt || expiresAt > now);
|
||||
if (hasFreshUrl) {
|
||||
return;
|
||||
}
|
||||
const force = Boolean(asset.url && expiresAt && expiresAt <= now);
|
||||
seen.add(asset.id);
|
||||
ensureAssetUrl(docId, asset, { force }).catch(() => {});
|
||||
};
|
||||
|
||||
items.forEach((doc) => {
|
||||
const docId = doc?.id;
|
||||
if (!docId) {
|
||||
return;
|
||||
}
|
||||
|
||||
const previewAsset = getDocumentAsset(doc, 'preview');
|
||||
if (previewAsset) {
|
||||
queueAsset(docId, previewAsset);
|
||||
}
|
||||
const thumbnailAsset = getDocumentAsset(doc, 'thumbnail');
|
||||
if (thumbnailAsset) {
|
||||
queueAsset(docId, thumbnailAsset);
|
||||
}
|
||||
resolveDocumentAssetUrl(doc, 'preview', {
|
||||
ensureAssetUrl,
|
||||
getAsset: getDocumentAsset,
|
||||
});
|
||||
});
|
||||
}, [items, ensureAssetUrl]);
|
||||
}, [items, ensureAssetUrl, getDocumentAsset]);
|
||||
|
||||
const focusCanvas = useCallback(() => {
|
||||
const canvas = containerRef.current;
|
||||
@@ -656,7 +605,7 @@ const SkeuomorphicWorkspace = ({
|
||||
const size = { width, height };
|
||||
docSizeMapRef.current.set(key, size);
|
||||
return size;
|
||||
}, []);
|
||||
}, [resolvePreviewDimensions]);
|
||||
|
||||
const documentLookup = useMemo(() => {
|
||||
const map = new Map();
|
||||
@@ -683,8 +632,8 @@ const SkeuomorphicWorkspace = ({
|
||||
}, [activeShelfTagId, availableTags]);
|
||||
|
||||
|
||||
const resolveZoomMetrics = useCallback(
|
||||
(doc, cardWidth, cardHeight) => {
|
||||
const resolveZoomMetrics = useCallback(
|
||||
(doc, cardWidth, cardHeight) => {
|
||||
const canvasWidth = canvasSize.width || DEFAULT_CANVAS_WIDTH;
|
||||
const canvasHeight = canvasSize.height || DEFAULT_CANVAS_HEIGHT;
|
||||
const safeCardWidth = cardWidth || CARD_WIDTH_MIN;
|
||||
@@ -745,7 +694,7 @@ const resolveZoomMetrics = useCallback(
|
||||
zoomCenterY,
|
||||
};
|
||||
},
|
||||
[canvasSize.width, canvasSize.height],
|
||||
[canvasSize.width, canvasSize.height, resolvePreviewDimensions],
|
||||
);
|
||||
|
||||
const resolveBaseMetrics = useCallback(
|
||||
@@ -769,7 +718,7 @@ const resolveZoomMetrics = useCallback(
|
||||
baseScale: 1,
|
||||
};
|
||||
},
|
||||
[],
|
||||
[resolvePreviewDimensions],
|
||||
);
|
||||
|
||||
const syncLayoutSnapshot = useCallback(() => {
|
||||
@@ -1396,7 +1345,7 @@ const resolveZoomMetrics = useCallback(
|
||||
<div
|
||||
className="skeuo-canvas"
|
||||
ref={containerRef}
|
||||
tabIndex={0}
|
||||
tabIndex={1}
|
||||
data-active-tag={activeShelfTagId || undefined}
|
||||
onDragOver={handleCanvasDragOver}
|
||||
onDragLeave={handleCanvasDragLeave}
|
||||
@@ -1455,7 +1404,7 @@ const resolveZoomMetrics = useCallback(
|
||||
const totalScale = zoomScale > 0 ? zoomScale : 1;
|
||||
const inverseTagScale = totalScale > 0 ? 1 / totalScale : 1;
|
||||
const tagsStyle = { '--tag-scale': inverseTagScale };
|
||||
const previewUrl = resolvePreviewUrl(doc, resolveAssetPath);
|
||||
const previewUrl = resolvePreviewUrl(doc);
|
||||
const imageUrl = previewUrl;
|
||||
const title = doc.title || doc.original_name || 'Document';
|
||||
const dragging = draggingId === doc.id;
|
||||
|
||||
Reference in New Issue
Block a user