Fetch image_ids lazily with first image, remove from initial cards load
ci/woodpecker/push/woodpecker Pipeline was successful

This commit is contained in:
2026-05-15 22:18:04 +02:00
parent 42d1981da9
commit f24bd7a93b
3 changed files with 37 additions and 36 deletions
+10 -5
View File
@@ -5,7 +5,8 @@ export const CardContext = createContext();
export function CardProvider({ children }) { export function CardProvider({ children }) {
const [ownedAmounts, setOwnedAmounts] = useState({}); const [ownedAmounts, setOwnedAmounts] = useState({});
const [expandedCardId, setExpandedCardId] = useState(null); const [expandedCardId, setExpandedCardId] = useState(null);
// cardImages[cardId] is an array of base64 strings, one per artwork index // cardImages[cardId].blobs: string[] (base64 per artwork index)
// cardImages[cardId].ids: number[] (image_ids from API)
const [cardImages, setCardImages] = useState({}); const [cardImages, setCardImages] = useState({});
const updateAmount = (card_id, key, amount) => { const updateAmount = (card_id, key, amount) => {
@@ -15,11 +16,15 @@ export function CardProvider({ children }) {
})); }));
}; };
const setCardImage = (card_id, index, blob) => { const setCardImage = (card_id, index, blob, image_ids) => {
setCardImages(prev => { setCardImages(prev => {
const existing = prev[card_id] ? [...prev[card_id]] : []; const existing = prev[card_id] ?? { blobs: [], ids: [] };
existing[index] = blob; const blobs = [...existing.blobs];
return { ...prev, [card_id]: existing }; blobs[index] = blob;
return {
...prev,
[card_id]: { blobs, ids: image_ids ?? existing.ids }
};
}); });
}; };
+19 -22
View File
@@ -45,14 +45,14 @@ function HomePage() {
useEffect(() => { useEffect(() => {
if (!expandedCardId) return; if (!expandedCardId) return;
const card = cards.find(c => c.id === expandedCardId); const imageIds = cardImages[expandedCardId]?.ids;
if (!card) return; const imageId = imageIds?.[artworkIndex];
const imageId = card.image_ids?.[artworkIndex]; if (cardImages[expandedCardId]?.blobs[artworkIndex]) return;
if (!imageId || cardImages[expandedCardId]?.[artworkIndex]) return; // imageId may be undefined on first load (ids not yet fetched)
fetchCardImage(expandedCardId, imageId) fetchCardImage(expandedCardId, imageId)
.then(image => setCardImage(expandedCardId, artworkIndex, image)) .then(({ image, image_ids }) => setCardImage(expandedCardId, artworkIndex, image, image_ids))
.catch(err => console.error('Failed to load card image', err)); .catch(err => console.error('Failed to load card image', err));
}, [expandedCardId, artworkIndex, cards, cardImages, setCardImage]); }, [expandedCardId, artworkIndex, cardImages, setCardImage]);
const getTotal = useCallback((card) => const getTotal = useCallback((card) =>
card.printings?.reduce((sum, p) => { card.printings?.reduce((sum, p) => {
@@ -134,30 +134,27 @@ function HomePage() {
<div style={{ flex: 1, padding: '16px', overflowY: 'auto', display: 'flex', flexDirection: 'column', alignItems: 'center', gap: '10px' }}> <div style={{ flex: 1, padding: '16px', overflowY: 'auto', display: 'flex', flexDirection: 'column', alignItems: 'center', gap: '10px' }}>
{expandedCard ? ( {expandedCard ? (
<> <>
{(() => {
const imgs = cardImages[expandedCardId];
const blob = imgs?.blobs[artworkIndex];
const ids = imgs?.ids ?? [];
return (
<div style={{ position: 'relative', width: '100%', display: 'flex', alignItems: 'center', justifyContent: 'center' }}> <div style={{ position: 'relative', width: '100%', display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
{cardImages[expandedCardId]?.[artworkIndex] ? ( {blob ? (
<img src={cardImages[expandedCardId][artworkIndex]} alt={expandedCard.name} style={{ maxWidth: '100%' }} /> <img src={blob} alt={expandedCard.name} style={{ maxWidth: '100%' }} />
) : ( ) : (
<div style={{ color: '#444', fontSize: '12px', padding: '2rem' }}>Loading image</div> <div style={{ color: '#444', fontSize: '12px', padding: '2rem' }}>Loading image</div>
)} )}
{(expandedCard.image_ids?.length ?? 0) > 1 && ( {ids.length > 1 && (
<div style={{ position: 'absolute', bottom: '6px', display: 'flex', alignItems: 'center', gap: '8px' }}> <div style={{ position: 'absolute', bottom: '6px', display: 'flex', alignItems: 'center', gap: '8px' }}>
<button <button className="icon-btn" onClick={() => setArtworkIndex(i => Math.max(0, i - 1))} disabled={artworkIndex === 0}></button>
className="icon-btn" <span style={{ fontSize: '11px', color: '#555' }}>{artworkIndex + 1} / {ids.length}</span>
onClick={() => setArtworkIndex(i => Math.max(0, i - 1))} <button className="icon-btn" onClick={() => setArtworkIndex(i => Math.min(ids.length - 1, i + 1))} disabled={artworkIndex === ids.length - 1}></button>
disabled={artworkIndex === 0}
></button>
<span style={{ fontSize: '11px', color: '#555' }}>
{artworkIndex + 1} / {expandedCard.image_ids.length}
</span>
<button
className="icon-btn"
onClick={() => setArtworkIndex(i => Math.min(expandedCard.image_ids.length - 1, i + 1))}
disabled={artworkIndex === expandedCard.image_ids.length - 1}
></button>
</div> </div>
)} )}
</div> </div>
);
})()}
<div style={{ width: '100%', borderTop: '1px solid #222', paddingTop: '10px', display: 'flex', flexDirection: 'column', gap: '5px' }}> <div style={{ width: '100%', borderTop: '1px solid #222', paddingTop: '10px', display: 'flex', flexDirection: 'column', gap: '5px' }}>
<span style={{ fontSize: '15px', fontWeight: 600, color: '#e0e0e0' }}>{expandedCard.name}</span> <span style={{ fontSize: '15px', fontWeight: 600, color: '#e0e0e0' }}>{expandedCard.name}</span>
<div style={{ display: 'flex', gap: '6px', flexWrap: 'wrap' }}> <div style={{ display: 'flex', gap: '6px', flexWrap: 'wrap' }}>
+1 -2
View File
@@ -13,8 +13,7 @@ export async function fetchCardImage(cardId, imageId) {
: `${API_BASE}/cardImage/${cardId}`; : `${API_BASE}/cardImage/${cardId}`;
const response = await fetch(url); const response = await fetch(url);
if (!response.ok) throw new Error('Failed to fetch card image'); if (!response.ok) throw new Error('Failed to fetch card image');
const data = await response.json(); return await response.json(); // { image, image_ids }
return data.image;
} }
export async function updateCardAmount(cardId, setId, rarityId, amount) { export async function updateCardAmount(cardId, setId, rarityId, amount) {