backend: split assets into objects

This commit is contained in:
2025-10-20 16:28:57 +02:00
parent 96a6d0ee5d
commit c44ba91ec7
9 changed files with 163 additions and 105 deletions
+22 -4
View File
@@ -172,7 +172,7 @@ class AssetManager {
return next;
}
ensureAsset(documentId, asset, { force = false } = {}) {
ensureAsset(documentId, asset, { force = false, start = null, limit = null } = {}) {
if (!documentId || !asset?.id) {
return Promise.resolve(asset || null);
}
@@ -189,7 +189,7 @@ class AssetManager {
return Promise.resolve({ ...asset, ...cached });
}
const inflightKey = `${documentId}:${asset.id}`;
const inflightKey = `${documentId}:${asset.id}:${start ?? 'd'}:${limit ?? 'd'}`;
if (!force && this.assetInflight.has(inflightKey)) {
return this.assetInflight.get(inflightKey);
}
@@ -198,13 +198,31 @@ class AssetManager {
return Promise.reject(new Error('AssetManager API client is not configured.'));
}
const params = {};
if (Number.isInteger(start) && start > 0) {
params.start = start;
}
if (Number.isInteger(limit) && limit > 0) {
params.limit = limit;
}
const requestConfig = Object.keys(params).length ? { params } : undefined;
const request = this.api
.get(`/documents/${documentId}/assets/${asset.id}`)
.get(`/assets/${asset.id}`, requestConfig)
.then(({ data }) => {
const objects = Array.isArray(data.objects) ? data.objects : [];
const primaryObject = objects[0] || null;
const expiresAt = typeof primaryObject?.expires_at === 'number'
? primaryObject.expires_at
: Date.now() + this.assetPresignTtlMs;
const entry = {
...asset,
...data,
expiresAt: Date.now() + this.assetPresignTtlMs,
objects,
url: primaryObject?.url || null,
expiresAt,
};
this.rememberAsset(entry);
return entry;
+4 -2
View File
@@ -446,8 +446,10 @@ const DetailPanel = ({
return null;
}
const asset = getDocumentAsset(doc, 'preview');
const width = Number(asset?.metadata?.width) || 0;
const height = Number(asset?.metadata?.height) || 0;
const primaryObject = asset?.objects?.[0] || null;
const primaryMetadata = primaryObject?.metadata || asset?.metadata || {};
const width = Number(primaryMetadata?.width) || 0;
const height = Number(primaryMetadata?.height) || 0;
const orientation = width > 0 && height > 0 ? (width >= height ? 'landscape' : 'portrait') : 'landscape';
return {
id: doc.id,
+4 -2
View File
@@ -22,8 +22,10 @@ const DocumentThumbnailImage = ({
}) => {
const resolvedMaxSize = Math.max(1, Math.round(maxSize || 1));
const thumbnailAsset = useMemo(() => getAssetFromVersion(document?.current_version, 'thumbnail'), [document?.current_version]);
const assetWidth = Number(thumbnailAsset?.metadata?.width);
const assetHeight = Number(thumbnailAsset?.metadata?.height);
const primaryObject = thumbnailAsset?.objects?.[0] || null;
const primaryMetadata = primaryObject?.metadata || thumbnailAsset?.metadata || {};
const assetWidth = Number(primaryMetadata?.width);
const assetHeight = Number(primaryMetadata?.height);
const dimensions = useMemo(() => {
if (!Number.isFinite(assetWidth) || assetWidth <= 0 || !Number.isFinite(assetHeight) || assetHeight <= 0) {
+4 -2
View File
@@ -413,11 +413,13 @@ const AppLayout = () => {
const isAssetEquivalent = (lhs, rhs) => {
if (!lhs || !rhs) return false;
const lhsPrimaryMetadata = lhs?.objects?.[0]?.metadata || lhs?.metadata;
const rhsPrimaryMetadata = rhs?.objects?.[0]?.metadata || rhs?.metadata;
return (
lhs.id === rhs.id &&
lhs.url === rhs.url &&
lhs?.metadata?.width === rhs?.metadata?.width &&
lhs?.metadata?.height === rhs?.metadata?.height &&
lhsPrimaryMetadata?.width === rhsPrimaryMetadata?.width &&
lhsPrimaryMetadata?.height === rhsPrimaryMetadata?.height &&
lhs.mime_type === rhs.mime_type &&
lhs.asset_type === rhs.asset_type &&
lhs.created_at === rhs.created_at
+4 -2
View File
@@ -502,8 +502,10 @@ const SkeuomorphicWorkspace = ({
(doc) => {
if (!doc) return null;
const asset = resolvePreviewAsset(doc);
const width = asset?.metadata?.width;
const height = asset?.metadata?.height;
const primaryObject = asset?.objects?.[0] || null;
const primaryMetadata = primaryObject?.metadata || asset?.metadata || {};
const width = primaryMetadata?.width;
const height = primaryMetadata?.height;
if (typeof width === 'number' && typeof height === 'number') {
return { width, height };
}
+1 -2
View File
@@ -751,7 +751,6 @@ button.icon-button.ghost:hover:not([disabled]) {
}
.column + .column {
border-left: none;
padding-left: 1.25rem;
}
@@ -847,7 +846,6 @@ button.icon-button.ghost:hover:not([disabled]) {
color: inherit;
transition: background 0.12s ease, color 0.12s ease;
position: relative;
display: inline-block;
}
.folder-row span.name {
@@ -1511,6 +1509,7 @@ button.icon-button.ghost:hover:not([disabled]) {
position: relative;
padding: 1.25rem;
overflow-y: auto;
border-left: 1px solid var(--border);
}
.detail-panel .column-body {