Skip to content

Commit 5ebb78c

Browse files
Refactor game list rendering and lazy fetch covers
Separate games that already include name/cover from those represented only by IDs; render full-data games immediately and fetch per-game JSON ({base_path}/games/{id}.json) for the rest. Add renderGameCard helper to consolidate card DOM creation, use igdbImageUrl for cover thumbnails, and provide a placeholder/fallback label when data is missing. This simplifies rendering logic and enables lazy loading of cover art.
1 parent cc7cad7 commit 5ebb78c

1 file changed

Lines changed: 83 additions & 45 deletions

File tree

gh-pages-template/assets/js/item_detail.js

Lines changed: 83 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,7 @@ function loadItemDetail(endpoint, renderFn) {
125125

126126
/**
127127
* Render a list of game cards (compact) into a container element.
128-
* Games are fetched from the buckets to resolve names.
128+
* Games are fetched from individual game files when needed to get cover art.
129129
* @param {HTMLElement} container
130130
* @param {Array<number|object>} games - array of game IDs or game objects with {id, name, cover}
131131
*/
@@ -139,54 +139,92 @@ function renderGameList(container, games) {
139139
row.className = "row row-cols-2 row-cols-sm-3 row-cols-md-4 row-cols-lg-6 g-2";
140140
container.appendChild(row);
141141

142+
// Separate games into those with full data and those that need fetching
143+
const gamesToFetch = [];
144+
const gamesWithData = [];
145+
142146
games.forEach(game => {
143147
const gameId = typeof game === "object" ? game.id : game;
144-
const gameName = typeof game === "object" && game.name ? game.name : null;
145-
const coverUrl = typeof game === "object" && game.cover ? igdbImageUrl(game.cover.url, "t_cover_small") : null;
146-
147-
const col = document.createElement("div");
148-
col.className = "col";
149-
row.appendChild(col);
150-
151-
const card = document.createElement("a");
152-
card.className = "card h-100 text-decoration-none shadow-sm border-0 rounded-0 game-card";
153-
card.href = `${base_path}/browse/games/?id=${gameId}`;
154-
col.appendChild(card);
155-
156-
if (coverUrl) {
157-
const img = document.createElement("img");
158-
img.className = "card-img-top rounded-0";
159-
img.src = coverUrl;
160-
img.alt = gameName || "";
161-
img.loading = "lazy";
162-
card.appendChild(img);
163-
} else {
164-
const placeholder = document.createElement("div");
165-
placeholder.className = "card-img-top bg-secondary d-flex align-items-center justify-content-center";
166-
placeholder.style.height = "120px";
167-
const icon = document.createElement("span");
168-
icon.className = "material-symbols-outlined text-white";
169-
icon.textContent = "sports_esports";
170-
placeholder.appendChild(icon);
171-
card.appendChild(placeholder);
172-
}
148+
const hasFullData = typeof game === "object" && game.name && game.cover;
173149

174-
const cardBody = document.createElement("div");
175-
cardBody.className = "card-body p-1";
176-
card.appendChild(cardBody);
177-
178-
if (gameName) {
179-
const nameEl = document.createElement("p");
180-
nameEl.className = "card-text small mb-0 text-truncate";
181-
nameEl.textContent = gameName;
182-
nameEl.title = gameName;
183-
cardBody.appendChild(nameEl);
150+
if (hasFullData) {
151+
gamesWithData.push(game);
184152
} else {
185-
// Fetch the name from the buckets lazily - just show ID for now
186-
const nameEl = document.createElement("p");
187-
nameEl.className = "card-text small mb-0 text-muted";
188-
nameEl.textContent = `#${gameId}`;
189-
cardBody.appendChild(nameEl);
153+
gamesToFetch.push(gameId);
190154
}
191155
});
156+
157+
// Render games that already have full data
158+
gamesWithData.forEach(game => {
159+
renderGameCard(row, game.id, game.name, game.cover ? igdbImageUrl(game.cover.url, "t_cover_small") : null);
160+
});
161+
162+
// Fetch and render games that only have IDs
163+
if (gamesToFetch.length > 0) {
164+
// Fetch each game's data
165+
const fetchPromises = gamesToFetch.map(gameId => {
166+
return fetch(`${base_path}/games/${gameId}.json`)
167+
.then(r => r.ok ? r.json() : null)
168+
.then(gameData => ({ id: gameId, data: gameData }))
169+
.catch(() => ({ id: gameId, data: null }));
170+
});
171+
172+
Promise.all(fetchPromises).then(results => {
173+
results.forEach(({ id, data }) => {
174+
const name = data ? data.name : null;
175+
const coverUrl = data && data.cover ? igdbImageUrl(data.cover.url, "t_cover_small") : null;
176+
renderGameCard(row, id, name, coverUrl);
177+
});
178+
});
179+
}
180+
}
181+
182+
/**
183+
* Helper function to render a single game card
184+
*/
185+
function renderGameCard(row, gameId, gameName, coverUrl) {
186+
const col = document.createElement("div");
187+
col.className = "col";
188+
row.appendChild(col);
189+
190+
const card = document.createElement("a");
191+
card.className = "card h-100 text-decoration-none shadow-sm border-0 rounded-0 game-card";
192+
card.href = `${base_path}/browse/games/?id=${gameId}`;
193+
col.appendChild(card);
194+
195+
if (coverUrl) {
196+
const img = document.createElement("img");
197+
img.className = "card-img-top rounded-0";
198+
img.src = coverUrl;
199+
img.alt = gameName || "";
200+
img.loading = "lazy";
201+
card.appendChild(img);
202+
} else {
203+
const placeholder = document.createElement("div");
204+
placeholder.className = "card-img-top bg-secondary d-flex align-items-center justify-content-center";
205+
placeholder.style.height = "120px";
206+
const icon = document.createElement("span");
207+
icon.className = "material-symbols-outlined text-white";
208+
icon.textContent = "sports_esports";
209+
placeholder.appendChild(icon);
210+
card.appendChild(placeholder);
211+
}
212+
213+
const cardBody = document.createElement("div");
214+
cardBody.className = "card-body p-1";
215+
card.appendChild(cardBody);
216+
217+
if (gameName) {
218+
const nameEl = document.createElement("p");
219+
nameEl.className = "card-text small mb-0 text-truncate";
220+
nameEl.textContent = gameName;
221+
nameEl.title = gameName;
222+
cardBody.appendChild(nameEl);
223+
} else {
224+
// Show game ID as fallback
225+
const nameEl = document.createElement("p");
226+
nameEl.className = "card-text small mb-0 text-muted";
227+
nameEl.textContent = `Game #${gameId}`;
228+
cardBody.appendChild(nameEl);
229+
}
192230
}

0 commit comments

Comments
 (0)