From f9cda3b6cdb424f4b0afaf46ca2c1d2c8b70e014 Mon Sep 17 00:00:00 2001 From: Gabe Martin-Dempesy Date: Sun, 26 May 2024 09:11:17 -0700 Subject: [PATCH] fix(favicons): cache shows wrong Google Docs icons When this plugin fetches favicon URLs, it uses a basic Map to cache the favicon response bodies in memory. Previously this Map was keyed using the hostname of the URL in the block content. However, this caused an incompatibility with hard-coded path-specific exceptions that this plugin implements for Google Docs. So, if you have the following Logseq page: ```md - https://docs.google.com/document/xxx - https://docs.google.com/spreadsheets/xxx - https://docs.google.com/presentation/xxx ``` The cache would side-step the path-specific checks, and all the links would display the same icon. This change shifts the cache around just the fetch of the favicon, and uses the favicon's URL as the cache key. Note this means the cache is no longer covers the conditionals in getFaviconData, but this should be a minimal tradeoff for displaying the correct icon. --- src/modules/favIcons/favIcons.ts | 34 +++++++++++--------------------- src/modules/utils.ts | 9 +++++++++ 2 files changed, 21 insertions(+), 22 deletions(-) diff --git a/src/modules/favIcons/favIcons.ts b/src/modules/favIcons/favIcons.ts index 1fa6785..2db9a27 100644 --- a/src/modules/favIcons/favIcons.ts +++ b/src/modules/favIcons/favIcons.ts @@ -1,7 +1,7 @@ import { doc, globals } from '../globals'; // import { stopLinksObserver } from '../linksObserver/linksObserver'; import { getPropsByPageName } from '../pageIcons/queries'; -import { getBase64FromUrl, isNeedLowContrastFix } from '../utils'; +import { getBase64FromUrlWithCache, isNeedLowContrastFix } from '../utils'; import './favIcons.css'; @@ -34,20 +34,9 @@ const setIconToExtItem = async (extLinkItem: HTMLAnchorElement) => { format: 'img', src: '' }; - const { hostname } = new URL(url); - if (!hostname) { - // skip cache for strange URIs - faviconData = await getFaviconData(url); - } else { - if (globals.favIconsCache.has(hostname)) { - // try from cache - faviconData = globals.favIconsCache.get(hostname); - } else { - // no? get fresh + save to cache - faviconData = await getFaviconData(url); - globals.favIconsCache.set(hostname, faviconData); - } - } + + faviconData = await getFaviconData(url); + if (faviconData.format === 'img') { // use IMG const fav = doc.createElement('img'); @@ -107,31 +96,32 @@ const getFaviconData = async (url: string): Promise => { if (hostname === 'youtu.be') { return favIcon = { format: 'img', - src: await getBase64FromUrl(`https://t3.gstatic.com/faviconV2?client=SOCIAL&type=FAVICON&fallback_opts=TYPE,SIZE,URL&url=http://youtube.com&size=32`) + src: await getBase64FromUrlWithCache(`https://t3.gstatic.com/faviconV2?client=SOCIAL&type=FAVICON&fallback_opts=TYPE,SIZE,URL&url=http://youtube.com&size=32`) }; + } if (hostname === 'gmail.com' || hostname === 'mail.google.com') { return favIcon = { format: 'img', - src: await getBase64FromUrl(`https://ssl.gstatic.com/ui/v1/icons/mail/rfr/gmail.ico`) + src: await getBase64FromUrlWithCache(`https://ssl.gstatic.com/ui/v1/icons/mail/rfr/gmail.ico`) } } if (url.includes('docs.google.com/document')) { return favIcon = { format: 'img', - src: await getBase64FromUrl(`https://ssl.gstatic.com/docs/documents/images/kix-favicon7.ico`) + src: await getBase64FromUrlWithCache(`https://ssl.gstatic.com/docs/documents/images/kix-favicon7.ico`) } } if (url.includes('docs.google.com/spreadsheets')) { return favIcon = { format: 'img', - src: await getBase64FromUrl(`https://ssl.gstatic.com/docs/spreadsheets/favicon3.ico`) + src: await getBase64FromUrlWithCache(`https://ssl.gstatic.com/docs/spreadsheets/favicon3.ico`) } } if (url.includes('docs.google.com/presentation')) { return favIcon = { format: 'img', - src: await getBase64FromUrl(`https://ssl.gstatic.com/docs/presentations/images/favicon5.ico`) + src: await getBase64FromUrlWithCache(`https://ssl.gstatic.com/docs/presentations/images/favicon5.ico`) } } if (url.includes('.atlassian.net/jira/') || url.includes('.atlassian.net/browse/')) { @@ -144,13 +134,13 @@ const getFaviconData = async (url: string): Promise => { if (protocol === 'http:' || protocol === 'https:') { favIcon = { format: 'img', - src: await getBase64FromUrl(`https://t3.gstatic.com/faviconV2?client=SOCIAL&type=FAVICON&fallback_opts=TYPE,SIZE,URL&url=http://${hostname}&size=32`) + src: await getBase64FromUrlWithCache(`https://t3.gstatic.com/faviconV2?client=SOCIAL&type=FAVICON&fallback_opts=TYPE,SIZE,URL&url=http://${hostname}&size=32`) } if (favIcon.src === 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAACXBIWXMAAAsSAAALEgHS3X78AAACiElEQVQ4EaVTzU8TURCf2tJuS7tQtlRb6UKBIkQwkRRSEzkQgyEc6lkOKgcOph78Y+CgjXjDs2i44FXY9AMTlQRUELZapVlouy3d7kKtb0Zr0MSLTvL2zb75eL838xtTvV6H/xELBptMJojeXLCXyobnyog4YhzXYvmCFi6qVSfaeRdXdrfaU1areV5KykmX06rcvzumjY/1ggkR3Jh+bNf1mr8v1D5bLuvR3qDgFbvbBJYIrE1mCIoCrKxsHuzK+Rzvsi29+6DEbTZz9unijEYI8ObBgXOzlcrx9OAlXyDYKUCzwwrDQx1wVDGg089Dt+gR3mxmhcUnaWeoxwMbm/vzDFzmDEKMMNhquRqduT1KwXiGt0vre6iSeAUHNDE0d26NBtAXY9BACQyjFusKuL2Ry+IPb/Y9ZglwuVscdHaknUChqLF/O4jn3V5dP4mhgRJgwSYm+gV0Oi3XrvYB30yvhGa7BS70eGFHPoTJyQHhMK+F0ZesRVVznvXw5Ixv7/C10moEo6OZXbWvlFAF9FVZDOqEABUMRIkMd8GnLwVWg9/RkJF9sA4oDfYQAuzzjqzwvnaRUFxn/X2ZlmGLXAE7AL52B4xHgqAUqrC1nSNuoJkQtLkdqReszz/9aRvq90NOKdOS1nch8TpL555WDp49f3uAMXhACRjD5j4ykuCtf5PP7Fm1b0DIsl/VHGezzP1KwOiZQobFF9YyjSRYQETRENSlVzI8iK9mWlzckpSSCQHVALmN9Az1euDho9Xo8vKGd2rqooA8yBcrwHgCqYR0kMkWci08t/R+W4ljDCanWTg9TJGwGNaNk3vYZ7VUdeKsYJGFNkfSzjXNrSX20s4/h6kB81/271ghG17l+rPTAAAAAElFTkSuQmCC') { // is common ugly icon? favIcon = { format: 'img', - src: await getBase64FromUrl(`https://icons.duckduckgo.com/ip3/${hostname}`) + src: await getBase64FromUrlWithCache(`https://icons.duckduckgo.com/ip3/${hostname}`) } if (favIcon.src === 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAAwCAYAAABXAvmHAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyhpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuNS1jMDIxIDc5LjE1NTc3MiwgMjAxNC8wMS8xMy0xOTo0NDowMCAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENDIDIwMTQgKE1hY2ludG9zaCkiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6QkI4OUQxMDdDQTYwMTFFNEJGMThCRkI4NTA4NTkyNkYiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6QkI4OUQxMDhDQTYwMTFFNEJGMThCRkI4NTA4NTkyNkYiPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDpCQjg5RDEwNUNBNjAxMUU0QkYxOEJGQjg1MDg1OTI2RiIgc3RSZWY6ZG9jdW1lbnRJRD0ieG1wLmRpZDpCQjg5RDEwNkNBNjAxMUU0QkYxOEJGQjg1MDg1OTI2RiIvPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/PjGq5lQAAAI0SURBVHja1Jo9jsIwEIW9Fh17gBxgD0B63EPPBXIBDkAP/Ub0XIAeeuiTA3CAHID061mNkWXlPzN28qQRQojkffZkPEn8dT6fBYFiHSv8jDCqVGBkOnL8HKXFSNNbHUrHsuN/DNwKv5c6HjpuQ2EWA40nlokxAvANBszIpS9IHwAYub2OteARDMivjqeOFFOtVbLjwRWODpd5W2s8l6ICgFE/9shzQZRaRzz3KICDjp0Ipx16GARwwIsrtDZNELIhbaZg3obYdwVQgdOmKZ1UG0DUlnOBdXBXeVmROssJAyzdVJLOCktV51867ozrRFy1EieE5mGU3tYFSK3EtBzS6SapzZ+YZsJ0vh+ALYN5wQyxtQEUEcC75jcOCGUAYqLKs2kpwdQQ4DmWRLkfCmIl7ZI0Q4j/GYh8N2CEEBEXgC+ISApesUNwA7BD+ABglQ+AO44yy52fnLN5A1DM1Tx45wLwYf4DkM3UPCgDgHym5kG5mYFyhuZLMwOgB8EBf3R8ezL/8WwAbkQAaQUE1xO+mw2QEV0LLgSX+dy9qQddiA5uIDifrV6qVmIgehJCcJl/2qXfbSVSoorEpRI91vZCRUspDK2T2znImvJ0naD5a1W5r+tGU8H3bHPoIpn2badPE4FoXOFlh5y7Bk6bxmuyy3viFMsW1HVf7w5KNN7a4nS9I4MDJYTrRFudT7r2Z33e1Bc4C5RbDdz2gHWrgb1iZ2LYZo+qVPG+2cMFESLgdps/AQYA9D2Sc4DqpGYAAAAASUVORK5CYII=') { favIcon = { diff --git a/src/modules/utils.ts b/src/modules/utils.ts index acd44db..1767915 100644 --- a/src/modules/utils.ts +++ b/src/modules/utils.ts @@ -30,6 +30,15 @@ export const checkUpdate = async () => { } } +export const getBase64FromUrlWithCache = async (url: string): Promise => { + if (globals.favIconsCache[url]) { + return globals.favIconsCache[url]; + } + const favicon = await getBase64FromUrl(url); + globals.favIconsCache[url] = favicon; + return favicon; +} + // Generate Base64 from image URL export const getBase64FromUrl = async (url: string): Promise => { let data;