Skip to content

Commit 617cfff

Browse files
JSKittyclaude
andcommitted
fix(emoji): re-resolve custom emojis after a cache clear
Clearing the image cache (Cache slice or full Clear) left the frontend's url-to-path emoji memos pointing at deleted files, short-circuiting the re-download forever and rendering broken images until an app restart. Clearing now drops the memos and rebinds every emoji img on screen, so they re-download on sight. Binds stash their cache kind so the rebind lands in the same cache subdir. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
1 parent b92b355 commit 617cfff

2 files changed

Lines changed: 24 additions & 1 deletion

File tree

src/js/picker.js

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -512,6 +512,20 @@ function _isCacheableEmojiUrl(url) {
512512
return typeof url === 'string' && url.startsWith('https://');
513513
}
514514

515+
/**
516+
* Drop every memoized emoji path and re-resolve all emoji <img>s on screen.
517+
* Called after the disk cache is cleared: the memos (and any rendered srcs)
518+
* point at deleted files, and left alone they'd short-circuit the re-download
519+
* forever, leaving broken images until a full reload.
520+
*/
521+
function reloadCachedEmojiImgs() {
522+
_emojiCacheMemo.clear();
523+
_emojiFailReason.clear();
524+
document.querySelectorAll('img[data-cache-token]').forEach(img => {
525+
bindCachedEmojiImg(img, img.dataset.cacheToken, img.dataset.cacheKind || 'emoji');
526+
});
527+
}
528+
515529
/** Returns the memoized local path for `url`, or null if not yet cached
516530
* in this session. Synchronous — safe to call from render-fast paths. */
517531
function cachedEmojiPath(url) {
@@ -573,15 +587,18 @@ function bindCachedEmojiImg(img, url, kind = 'emoji', onUnavailable = null) {
573587
};
574588
if (!_isCacheableEmojiUrl(url)) {
575589
delete img.dataset.cacheToken;
590+
delete img.dataset.cacheKind;
576591
unavailable();
577592
return;
578593
}
579594
// Token guard against the re-bind race: if this same <img> gets
580595
// rebound to a different URL before our async resolve lands, the
581596
// stale `.then` would overwrite the newer src. Reused elements
582597
// (e.g. the naming-overlay preview cycling through a batch) are the
583-
// common offenders.
598+
// common offenders. The kind rides along so a cache-clear rebind
599+
// resolves into the same cache subdir.
584600
img.dataset.cacheToken = url;
601+
img.dataset.cacheKind = kind;
585602
const memo = _emojiCacheMemo.get(url);
586603
if (memo) {
587604
img.src = convertFileSrc(memo);

src/js/settings.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1513,6 +1513,9 @@ async function clearStorage() {
15131513
clearStorageBtn.disabled = true;
15141514
clearStorageBtn.textContent = 'Clearing...';
15151515
await invoke('clear_storage');
1516+
// Full clear nukes the image cache too; drop the emoji memos so
1517+
// rendered emojis re-download instead of pointing at deleted files
1518+
reloadCachedEmojiImgs();
15161519
clearStorageBtn.textContent = strPrevText;
15171520
clearStorageBtn.disabled = false;
15181521
return true;
@@ -1817,6 +1820,9 @@ function renderStorageDonut(typeDistribution) {
18171820
try {
18181821
const res = await invoke('clear_storage_category', { category, exts });
18191822
showToast(`Freed ${res.freed_formatted}`);
1823+
// The emoji memos and any rendered <img>s point at the deleted
1824+
// cache files; re-resolve so they re-download on sight
1825+
if (category === 'cache') reloadCachedEmojiImgs();
18201826
} catch (e) {
18211827
await popupConfirm('Delete Failed', `Could not delete: ${escapeHtml(String(e))}`, true, '', 'vector_warning.svg');
18221828
}

0 commit comments

Comments
 (0)