Save workspace changes
This commit is contained in:
@@ -109,7 +109,7 @@ async function fetchPageData(url) {
|
||||
: null;
|
||||
|
||||
return {
|
||||
artworks,
|
||||
artworks: artworks.map(normalizeArtworkItem),
|
||||
nextCursor,
|
||||
nextPageUrl,
|
||||
hasMore,
|
||||
@@ -127,7 +127,7 @@ async function fetchPageData(url) {
|
||||
try { artworks = JSON.parse(el.dataset.artworks || '[]'); } catch { /* empty */ }
|
||||
|
||||
return {
|
||||
artworks,
|
||||
artworks: artworks.map(normalizeArtworkItem),
|
||||
nextCursor: el.dataset.nextCursor || null,
|
||||
nextPageUrl: el.dataset.nextPageUrl || null,
|
||||
hasMore: null,
|
||||
@@ -142,37 +142,61 @@ function SkeletonCard() {
|
||||
|
||||
// ── Ranking API helpers ───────────────────────────────────────────────────
|
||||
/**
|
||||
* Map a single ArtworkListResource item (from /api/rank/*) to the internal
|
||||
* artwork object shape used by ArtworkCard.
|
||||
* Normalize API / Blade artwork payloads into the internal shape used by the
|
||||
* gallery layout helpers and ArtworkCard.
|
||||
*/
|
||||
function mapRankApiArtwork(item) {
|
||||
const w = item.dimensions?.width ?? null;
|
||||
const h = item.dimensions?.height ?? null;
|
||||
const thumb = item.thumbnail_url ?? null;
|
||||
const webUrl = item.urls?.web ?? item.category?.url ?? null;
|
||||
function normalizeArtworkItem(item) {
|
||||
if (!item || typeof item !== 'object') return item;
|
||||
|
||||
const category = item.category && typeof item.category === 'object' ? item.category : null;
|
||||
const author = item.author && typeof item.author === 'object'
|
||||
? item.author
|
||||
: (item.creator && typeof item.creator === 'object' ? item.creator : null);
|
||||
const publisher = item.publisher && typeof item.publisher === 'object' ? item.publisher : null;
|
||||
const w = item.dimensions?.width ?? item.width ?? null;
|
||||
const h = item.dimensions?.height ?? item.height ?? null;
|
||||
const thumb = item.thumbnail_url ?? item.thumb_url ?? item.thumb ?? item.image ?? null;
|
||||
const canonicalUrl = item.canonical_url
|
||||
?? item.urls?.canonical
|
||||
?? item.urls?.direct
|
||||
?? item.url
|
||||
?? item.href
|
||||
?? item.urls?.web
|
||||
?? category?.url
|
||||
?? null;
|
||||
|
||||
return {
|
||||
...item,
|
||||
id: item.id ?? null,
|
||||
name: item.title ?? item.name ?? null,
|
||||
thumb: thumb,
|
||||
thumb_url: thumb,
|
||||
uname: item.author?.name ?? '',
|
||||
username: publisher?.type === 'group' ? '' : (item.author?.username ?? ''),
|
||||
avatar_url: item.author?.avatar_url ?? null,
|
||||
profile_url: publisher?.profile_url ?? item.author?.profile_url ?? null,
|
||||
published_as_type: publisher?.type ?? null,
|
||||
publisher: publisher,
|
||||
content_type_name: item.category?.content_type_name ?? item.category?.content_type_slug ?? item.category?.content_type ?? '',
|
||||
content_type_slug: item.category?.content_type_slug ?? item.category?.content_type ?? '',
|
||||
category_name: item.category?.name ?? '',
|
||||
category_slug: item.category?.slug ?? '',
|
||||
name: item.name ?? item.title ?? null,
|
||||
title: item.title ?? item.name ?? null,
|
||||
thumb: item.thumb ?? thumb,
|
||||
thumb_url: item.thumb_url ?? thumb,
|
||||
thumbnail_url: item.thumbnail_url ?? thumb,
|
||||
author,
|
||||
uname: item.uname ?? author?.name ?? item.author_name ?? '',
|
||||
username: item.username ?? (publisher?.type === 'group' ? '' : (author?.username ?? '')),
|
||||
avatar_url: item.avatar_url ?? author?.avatar_url ?? null,
|
||||
profile_url: item.profile_url ?? publisher?.profile_url ?? author?.profile_url ?? null,
|
||||
published_as_type: item.published_as_type ?? publisher?.type ?? null,
|
||||
publisher: publisher ?? null,
|
||||
content_type_name: item.content_type_name ?? category?.content_type_name ?? category?.content_type_slug ?? category?.content_type ?? '',
|
||||
content_type_slug: item.content_type_slug ?? category?.content_type_slug ?? category?.content_type ?? '',
|
||||
category_name: item.category_name ?? category?.name ?? (typeof item.category === 'string' ? item.category : ''),
|
||||
category_slug: item.category_slug ?? category?.slug ?? '',
|
||||
category: typeof item.category === 'string' ? item.category : (category?.name ?? item.category_name ?? ''),
|
||||
slug: item.slug ?? '',
|
||||
url: webUrl,
|
||||
canonical_url: item.canonical_url ?? item.urls?.canonical ?? item.urls?.direct ?? null,
|
||||
url: canonicalUrl,
|
||||
width: w,
|
||||
height: h,
|
||||
};
|
||||
}
|
||||
|
||||
function mapRankApiArtwork(item) {
|
||||
return normalizeArtworkItem(item);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch ranked artworks from the ranking API.
|
||||
* Returns { artworks: [...] } in internal shape, or { artworks: [] } on failure.
|
||||
@@ -261,7 +285,8 @@ function MasonryGallery({
|
||||
discoveryEndpoint = null,
|
||||
algoVersion: initialAlgoVersion = null,
|
||||
}) {
|
||||
const [artworks, setArtworks] = useState(initialArtworks);
|
||||
const normalizedInitialArtworks = initialArtworks.map(normalizeArtworkItem);
|
||||
const [artworks, setArtworks] = useState(normalizedInitialArtworks);
|
||||
const [nextCursor, setNextCursor] = useState(initialNextCursor);
|
||||
const [nextPageUrl, setNextPageUrl] = useState(initialNextPageUrl);
|
||||
const [loading, setLoading] = useState(false);
|
||||
@@ -279,7 +304,7 @@ function MasonryGallery({
|
||||
// client-side fetch from the ranking API to hydrate the grid.
|
||||
// Satisfies spec: "Fallback: Latest if ranking missing".
|
||||
useEffect(() => {
|
||||
if (initialArtworks.length > 0) return; // SSR artworks already present
|
||||
if (normalizedInitialArtworks.length > 0) return; // SSR artworks already present
|
||||
if (!rankApiEndpoint) return; // no API endpoint configured
|
||||
|
||||
let cancelled = false;
|
||||
|
||||
Reference in New Issue
Block a user