diff --git a/docs/icons.md b/docs/icons.md index e0ab48b1..01f071c3 100644 --- a/docs/icons.md +++ b/docs/icons.md @@ -80,7 +80,7 @@ sections: --- ## Generative Icons -To uses a unique and programmatically generated icon for a given service just set `icon: generative`. This is particularly useful when you have a lot of similar services with a different IP or port, and no specific icon. These icons are generated with [DiceBear](https://avatars.dicebear.com/), and use a hash of the services domain/ ip for entropy, so each domain will always have the same icon. +To uses a unique and programmatically generated icon for a given service just set `icon: generative`. This is particularly useful when you have a lot of similar services with a different IP or port, and no specific icon. These icons are generated with [DiceBear](https://avatars.dicebear.com/) (or [Evatar](https://evatar.io/) for fallback), and use a hash of the services domain/ ip for entropy, so each domain will have a unique icon.

diff --git a/src/components/LinkItems/ItemIcon.vue b/src/components/LinkItems/ItemIcon.vue index f3c296e0..5e3a9667 100644 --- a/src/components/LinkItems/ItemIcon.vue +++ b/src/components/LinkItems/ItemIcon.vue @@ -49,12 +49,14 @@ export default { }, /* Gets the icon path, dependent on icon type */ iconPath: function iconPath() { + if (this.broken) return this.getFallbackIcon(); return this.getIconPath(this.icon, this.url); }, }, data() { return { broken: false, // If true, was unable to resolve icon + attemptedFallback: false, }; }, methods: { @@ -94,12 +96,12 @@ export default { }, /* Get favicon URL, for items which use the favicon as their icon */ getFavicon(fullUrl, specificApi) { - if (this.shouldUseDefaultFavicon(fullUrl)) { // Check if we should use local icon + const faviconApi = specificApi || this.appConfig.faviconApi || defaultFaviconApi; + if (this.shouldUseDefaultFavicon(fullUrl) || faviconApi === 'local') { // Check if we should use local icon const urlParts = fullUrl.split('/'); if (urlParts.length >= 2) return `${urlParts[0]}/${urlParts[1]}/${urlParts[2]}/${iconCdns.faviconName}`; } else if (fullUrl.includes('http')) { // Service is running publicly const host = this.getHostName(fullUrl); - const faviconApi = specificApi || this.appConfig.faviconApi || defaultFaviconApi; const endpoint = faviconApiEndpoints[faviconApi]; return endpoint.replace('$URL', host); } @@ -130,9 +132,9 @@ export default { return `${iconCdns.localPath}/${img}`; }, /* Formats the URL for fetching the generative icons */ - getGenerativeIcon(url) { + getGenerativeIcon(url, cdn) { const host = encodeURI(url) || Math.random().toString(); - return iconCdns.generative.replace('{icon}', asciiHash(host)); + return (cdn || iconCdns.generative).replace('{icon}', asciiHash(host)); }, /* Returns the SVG path content */ getSimpleIcon(img) { @@ -187,6 +189,23 @@ export default { this.broken = true; ErrorHandler(`The path to '${this.icon}' could not be resolved`); }, + /* Called when initial icon has resulted in 404. Attempts to find new icon */ + getFallbackIcon() { + if (this.attemptedFallback) return undefined; // If this is second attempt, then give up + const { iconType } = this; + const markAsSttempted = () => { + this.broken = false; + this.attemptedFallback = true; + }; + if (iconType.includes('favicon')) { // Specify fallback for favicon-based icons + markAsSttempted(); + return this.getFavicon(this.url, 'local'); + } else if (iconType === 'generative') { + markAsSttempted(); + return this.getGenerativeIcon(this.url, iconCdns.generativeFallback); + } + return undefined; + }, }, }; diff --git a/src/utils/defaults.js b/src/utils/defaults.js index f81467c8..0a5a8cf3 100644 --- a/src/utils/defaults.js +++ b/src/utils/defaults.js @@ -184,6 +184,7 @@ module.exports = { mdi: 'https://cdn.jsdelivr.net/npm/@mdi/font@5.9.55/css/materialdesignicons.min.css', si: 'https://unpkg.com/simple-icons@v5/icons', generative: 'https://avatars.dicebear.com/api/identicon/{icon}.svg', + generativeFallback: 'https://evatar.io/{icon}', localPath: './item-icons', faviconName: 'favicon.ico', homeLabIcons: 'https://raw.githubusercontent.com/WalkxCode/dashboard-icons/master/png/{icon}.png',