@@ -4,6 +4,8 @@ import Notification from "@/components/Notification";
44import PriceTrendIndicator from "@/components/Yugioh/PriceTrendIndicator" ;
55
66const FALLBACK_IMAGE = "/images/yugioh-card.png" ;
7+ const LOCAL_IMAGE_BASE_PATH = "/images/yugiohImages" ;
8+ const LOCAL_CROPPED_IMAGE_BASE_PATH = "/images/yugiohImagesCropped" ;
79
810const formatCurrency = ( value ) => {
911 const numeric = Number ( value ) ;
@@ -28,6 +30,67 @@ const getPrimaryCardImage = ( card ) => (
2830 : null
2931) ;
3032
33+ const getUniqueStrings = ( values ) => {
34+ const seen = new Set ( ) ;
35+
36+ return values . reduce ( ( list , value ) => {
37+ const normalized = normalizeOptionalString ( value ) ;
38+ if ( ! normalized || seen . has ( normalized ) ) {
39+ return list ;
40+ }
41+
42+ seen . add ( normalized ) ;
43+ list . push ( normalized ) ;
44+ return list ;
45+ } , [ ] ) ;
46+ } ;
47+
48+ const getImageIdFromUrl = ( value ) => {
49+ const normalized = normalizeOptionalString ( value ) ;
50+ if ( ! normalized ) {
51+ return null ;
52+ }
53+
54+ const path = normalized . split ( / [ ? # ] / ) [ 0 ] ;
55+ const fileName = path . split ( "/" ) . pop ( ) || "" ;
56+ const match = fileName . match ( / ^ ( .+ ?) \. (?: j p e ? g | p n g | w e b p ) $ / i ) ;
57+ return normalizeOptionalString ( match ?. [ 1 ] ) ;
58+ } ;
59+
60+ const normalizeImageId = ( value ) => {
61+ const normalized = normalizeOptionalString ( value ) ;
62+ if ( ! normalized ) {
63+ return null ;
64+ }
65+
66+ const idFromUrl = getImageIdFromUrl ( normalized ) ;
67+ if ( idFromUrl ) {
68+ return idFromUrl ;
69+ }
70+
71+ if ( / ^ (?: h t t p s ? : ) ? \/ \/ / i. test ( normalized ) || normalized . startsWith ( "/" ) ) {
72+ return null ;
73+ }
74+
75+ return normalized . replace ( / \. (?: j p e ? g | p n g | w e b p ) $ / i, "" ) ;
76+ } ;
77+
78+ const buildLocalImagePath = ( basePath , imageId ) => `${ basePath } /${ encodeURIComponent ( String ( imageId ) ) } .jpg` ;
79+
80+ const getNextImageSources = ( image ) => {
81+ const rawSources = image . dataset . nextSrcs ;
82+ if ( ! rawSources ) {
83+ return [ ] ;
84+ }
85+
86+ try {
87+ const parsed = JSON . parse ( rawSources ) ;
88+ return Array . isArray ( parsed ) ? parsed . filter ( Boolean ) : [ ] ;
89+ } catch ( error ) {
90+ return rawSources . split ( "|" ) . filter ( Boolean ) ;
91+ }
92+ } ;
93+
3194const GridView = ( { aggregatedData, onDeleteCard, onUpdateCard, sortConfig, handleSortChange, selectedCardIds, onSelectedCardIdsChange, folderNameMap = { } } ) => {
3295 const [ edit , setEdit ] = useState ( { } ) ;
3396 const [ editValues , setEditValues ] = useState ( { } ) ;
@@ -127,43 +190,54 @@ const GridView = ( { aggregatedData, onDeleteCard, onUpdateCard, sortConfig, han
127190 } ) ;
128191 } , [ ] ) ;
129192
130- const getFullImagePath = useCallback ( ( cardId ) => `/images/yugiohImages/${ String ( cardId ) } .jpg` , [ ] ) ;
193+ const getFullImagePath = useCallback ( ( cardId ) => buildLocalImagePath ( LOCAL_IMAGE_BASE_PATH , cardId ) , [ ] ) ;
194+ const getCroppedImagePath = useCallback ( ( cardId ) => buildLocalImagePath ( LOCAL_CROPPED_IMAGE_BASE_PATH , cardId ) , [ ] ) ;
131195
132196 const getCardImageSources = useCallback (
133197 ( card ) => {
134198 const primaryCardImage = getPrimaryCardImage ( card ) ;
135- const imageId =
136- normalizeOptionalString ( primaryCardImage ?. id ) ||
137- normalizeOptionalString ( card ?. cardImageId ) ||
138- normalizeOptionalString ( card ?. cardId ) ||
139- normalizeOptionalString ( card ?. cardDetailId ) ||
140- normalizeOptionalString ( card ?. id ) ;
141- const localImageSrc = imageId ? getFullImagePath ( imageId ) : null ;
142- const remoteImageSrc =
143- normalizeOptionalString ( card ?. remoteImageUrl ) ||
144- normalizeOptionalString ( card ?. image_url ) ||
145- normalizeOptionalString ( primaryCardImage ?. image_url ) ||
146- normalizeOptionalString ( primaryCardImage ?. image_url_cropped ) ||
147- normalizeOptionalString ( primaryCardImage ?. image_url_small ) ;
148- const primaryImageSrc = remoteImageSrc || localImageSrc || FALLBACK_IMAGE ;
149- const secondaryImageSrc = primaryImageSrc === remoteImageSrc ? localImageSrc : remoteImageSrc ;
199+ const remoteImageSources = getUniqueStrings ( [
200+ card ?. remoteImageUrl ,
201+ card ?. image_url ,
202+ primaryCardImage ?. image_url ,
203+ primaryCardImage ?. image_url_cropped ,
204+ primaryCardImage ?. image_url_small ,
205+ ] ) ;
206+ const imageIds = getUniqueStrings ( [
207+ normalizeImageId ( primaryCardImage ?. id ) ,
208+ normalizeImageId ( card ?. cardImageId ) ,
209+ normalizeImageId ( card ?. cardId ) ,
210+ normalizeImageId ( card ?. cardDetailId ) ,
211+ normalizeImageId ( card ?. id ) ,
212+ ...remoteImageSources . map ( getImageIdFromUrl ) ,
213+ ] ) ;
214+ const localImageSources = imageIds . flatMap ( ( imageId ) => [
215+ getFullImagePath ( imageId ) ,
216+ getCroppedImagePath ( imageId ) ,
217+ ] ) ;
218+ const imageSources = getUniqueStrings ( [
219+ ...remoteImageSources ,
220+ ...localImageSources ,
221+ FALLBACK_IMAGE ,
222+ ] ) ;
223+ const primaryImageSrc = imageSources [ 0 ] || FALLBACK_IMAGE ;
150224
151225 return {
152226 primaryImageSrc,
153- secondaryImageSrc : secondaryImageSrc && secondaryImageSrc !== primaryImageSrc ? secondaryImageSrc : null ,
227+ backupImageSrcs : imageSources . slice ( 1 ) . filter ( ( source ) => source !== FALLBACK_IMAGE ) ,
154228 } ;
155229 } ,
156- [ getFullImagePath ] ,
230+ [ getCroppedImagePath , getFullImagePath ] ,
157231 ) ;
158232
159233 const handleCardImageError = useCallback ( ( event ) => {
160234 const image = event . currentTarget ;
161- const nextSrc = image . dataset . nextSrc ;
235+ const [ nextSrc , ... remainingSources ] = getNextImageSources ( image ) ;
162236 const fallbackSrc = image . dataset . fallbackSrc ;
163237
164238 if ( nextSrc ) {
165239 image . src = nextSrc ;
166- image . dataset . nextSrc = "" ;
240+ image . dataset . nextSrcs = JSON . stringify ( remainingSources ) ;
167241 return ;
168242 }
169243
@@ -276,7 +350,7 @@ const GridView = ( { aggregatedData, onDeleteCard, onUpdateCard, sortConfig, han
276350 { memoizedAggregatedData . map ( ( card ) => {
277351 if ( ! card ) return null ;
278352
279- const { primaryImageSrc, secondaryImageSrc } = getCardImageSources ( card ) ;
353+ const { primaryImageSrc, backupImageSrcs } = getCardImageSources ( card ) ;
280354 const primaryCardImage = getPrimaryCardImage ( card ) ;
281355 const detailCardId =
282356 normalizeOptionalString ( primaryCardImage ?. id ) ||
@@ -413,7 +487,7 @@ const GridView = ( { aggregatedData, onDeleteCard, onUpdateCard, sortConfig, han
413487 src = { primaryImageSrc }
414488 alt = { `Card Image - ${ card . productName } ` }
415489 loading = "lazy"
416- data-next-src = { secondaryImageSrc || "" }
490+ data-next-srcs = { JSON . stringify ( backupImageSrcs ) }
417491 data-fallback-src = { FALLBACK_IMAGE }
418492 onError = { handleCardImageError }
419493 />
0 commit comments