Skip to content

Commit 6ba2845

Browse files
author
jagdeep sidhu
committed
Fix token icon refresh
1 parent 8b477e1 commit 6ba2845

File tree

1 file changed

+41
-4
lines changed

1 file changed

+41
-4
lines changed

source/components/TokenIcon.tsx

Lines changed: 41 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,12 @@ import { NFT_FALLBACK_IMAGE } from 'utils/nftFallback';
99
// Add a simple LRU cap to prevent unbounded growth
1010
const MAX_CACHE_ENTRIES = 200;
1111
const tokenImageCache = new Map<string, string | null>();
12+
// Track when a cache key last failed so we can retry later
13+
const tokenImageFailureTs = new Map<string, number>();
14+
// Retry failed icon fetches after a short cooldown (prevents permanent "stuck" fallbacks)
15+
const FAILURE_RETRY_MS = 5 * 60 * 1000; // 5 minutes
16+
// If an image is too large to safely base64-cache, fall back to rendering the URL directly
17+
const MAX_CACHED_BLOB_BYTES = 256 * 1024; // 256KB
1218

1319
// Track in-flight requests to prevent duplicate fetches
1420
const pendingTokenRequests = new Map<string, Promise<string | null>>();
@@ -70,6 +76,26 @@ export const TokenIcon: React.FC<ITokenIconProps> = React.memo(
7076
// Check cache first
7177
if (tokenImageCache.has(cacheKey)) {
7278
const cached = tokenImageCache.get(cacheKey);
79+
// If we previously cached a failure (null), allow retry after cooldown
80+
if (cached === null) {
81+
const failedAt = tokenImageFailureTs.get(cacheKey) || 0;
82+
if (failedAt && Date.now() - failedAt >= FAILURE_RETRY_MS) {
83+
tokenImageCache.delete(cacheKey);
84+
tokenImageFailureTs.delete(cacheKey);
85+
} else {
86+
return {
87+
url: null,
88+
error: true,
89+
loading: false,
90+
};
91+
}
92+
} else {
93+
return {
94+
url: cached,
95+
error: false,
96+
loading: false,
97+
};
98+
}
7399
return {
74100
url: cached,
75101
error: cached === null,
@@ -136,10 +162,18 @@ export const TokenIcon: React.FC<ITokenIconProps> = React.memo(
136162
if (!response.ok) throw new Error('Failed to fetch');
137163

138164
const blob = await response.blob();
139-
// Skip caching very large images to avoid memory bloat (~256KB)
140-
if (blob.size > 256 * 1024) {
141-
tokenImageCache.set(cacheKey, null);
142-
return null;
165+
// If it's too large for base64 caching, just cache the URL so it can still render reliably
166+
if (blob.size > MAX_CACHED_BLOB_BYTES) {
167+
// Enforce LRU cap
168+
if (tokenImageCache.size >= MAX_CACHE_ENTRIES) {
169+
const oldestKey = tokenImageCache.keys().next().value as
170+
| string
171+
| undefined;
172+
if (oldestKey !== undefined) tokenImageCache.delete(oldestKey);
173+
}
174+
tokenImageCache.set(cacheKey, logo);
175+
tokenImageFailureTs.delete(cacheKey);
176+
return logo;
143177
}
144178
const reader = new FileReader();
145179

@@ -154,6 +188,7 @@ export const TokenIcon: React.FC<ITokenIconProps> = React.memo(
154188
if (oldestKey !== undefined) tokenImageCache.delete(oldestKey);
155189
}
156190
tokenImageCache.set(cacheKey, dataUrl);
191+
tokenImageFailureTs.delete(cacheKey);
157192
resolve(dataUrl);
158193
};
159194

@@ -165,6 +200,7 @@ export const TokenIcon: React.FC<ITokenIconProps> = React.memo(
165200
if (oldestKey !== undefined) tokenImageCache.delete(oldestKey);
166201
}
167202
tokenImageCache.set(cacheKey, null);
203+
tokenImageFailureTs.set(cacheKey, Date.now());
168204
resolve(null);
169205
};
170206

@@ -173,6 +209,7 @@ export const TokenIcon: React.FC<ITokenIconProps> = React.memo(
173209
} catch (err) {
174210
// Cache the failure to prevent repeated attempts
175211
tokenImageCache.set(cacheKey, null);
212+
tokenImageFailureTs.set(cacheKey, Date.now());
176213
return null;
177214
}
178215
};

0 commit comments

Comments
 (0)