From 7a2e9711f4f21c0536834197630ad8f15a2a539a Mon Sep 17 00:00:00 2001 From: Christian Fehmer Date: Fri, 26 Sep 2025 12:09:13 +0200 Subject: [PATCH] fix: discord avatars not loading (@fehmer) --- frontend/src/styles/core.scss | 12 +++- frontend/src/ts/index.ts | 8 +-- frontend/src/ts/utils/discord-avatar.ts | 83 ++++++++++--------------- frontend/src/ts/utils/global.ts | 6 ++ 4 files changed, 48 insertions(+), 61 deletions(-) create mode 100644 frontend/src/ts/utils/global.ts diff --git a/frontend/src/styles/core.scss b/frontend/src/styles/core.scss index ec0f9aaac..455732ea3 100644 --- a/frontend/src/styles/core.scss +++ b/frontend/src/styles/core.scss @@ -350,14 +350,20 @@ key { line-height: 1em; font-size: var(--size); border-radius: 100%; - background-position: center center; - background-size: contain; - background-repeat: no-repeat; grid-column: 1/2; grid-row: 1/2; place-self: center center; display: grid; place-content: center center; + overflow: hidden; + display: inline-block; + z-index: 1; //place above the loading spinner + img { + width: 100%; + height: 100%; + object-fit: cover; + display: block; + } } .loading { diff --git a/frontend/src/ts/index.ts b/frontend/src/ts/index.ts index 78f78a230..ae87d15c9 100644 --- a/frontend/src/ts/index.ts +++ b/frontend/src/ts/index.ts @@ -51,6 +51,7 @@ import * as Cookies from "./cookies"; import "./elements/psa"; import "./utils/url-handler"; import "./modals/last-signed-out-result"; +import { addToGlobal } from "./utils/global"; // Lock Math.random Object.defineProperty(Math, "random", { @@ -71,13 +72,6 @@ Object.defineProperty(window, "Math", { enumerable: true, }); -function addToGlobal(items: Record): void { - for (const [name, item] of Object.entries(items)) { - //@ts-expect-error dev - window[name] = item; - } -} - void loadFromLocalStorage(); void VersionButton.update(); Focus.set(true, true); diff --git a/frontend/src/ts/utils/discord-avatar.ts b/frontend/src/ts/utils/discord-avatar.ts index 9d1d10806..549f80b36 100644 --- a/frontend/src/ts/utils/discord-avatar.ts +++ b/frontend/src/ts/utils/discord-avatar.ts @@ -1,29 +1,40 @@ -const cachedAvatarUrlByAvatarId: Map = new Map(); +import { addToGlobal } from "./global"; type Options = { size?: number; userIcon?: string }; -function buildElement( - url: string | null, - options?: { loading?: boolean } & Options -): HTMLElement { +const knownBadUrls = new Set(); + +function buildElement(url: string | null, options?: Options): HTMLElement { const avatar = document.createElement("div"); avatar.classList.add("avatar"); - if (url === null) { - if (options?.loading) { - avatar.innerHTML = `
`; - } else { - avatar.innerHTML = `
`; - } + + if (url === null || knownBadUrls.has(url)) { + avatar.innerHTML = `
`; } else { - avatar.innerHTML = `
`; + avatar.innerHTML = ` +
+
+ +
`; } + return avatar; } +addToGlobal({ + onAvatarLoaded: (element: HTMLImageElement): void => { + element.closest(".avatar")?.querySelector(".loading")?.remove(); + }, + onAvatarError: (element: HTMLImageElement): void => { + knownBadUrls.add(element.src.split("?")[0]); + element.closest(".avatar")?.replaceWith(buildElement(null)); + }, +}); + export function getAvatarElement( { discordId, @@ -43,40 +54,10 @@ export function getAvatarElement( return buildElement(null, options); } - const cachedUrl = cachedAvatarUrlByAvatarId.get(discordAvatar); + const element = buildElement( + `https://cdn.discordapp.com/avatars/${discordId}/${discordAvatar}.png`, + options + ); - if (cachedUrl !== undefined) { - return buildElement(cachedUrl, options); - } else { - const element = buildElement(null, { loading: true }); - - void getDiscordAvatarUrl({ discordId, discordAvatar }).then((url) => { - cachedAvatarUrlByAvatarId.set(discordAvatar, url); - element.replaceWith(buildElement(url, options)); - }); - - return element; - } -} - -async function getDiscordAvatarUrl({ - discordId, - discordAvatar, -}: { - discordId: string; - discordAvatar: string; -}): Promise { - // An invalid request to this URL will return a 404. - try { - const avatarUrl = `https://cdn.discordapp.com/avatars/${discordId}/${discordAvatar}.png`; - - const response = await fetch(avatarUrl, { method: "HEAD" }); - if (!response.ok) { - return null; - } - - return avatarUrl; - } catch (error) {} - - return null; + return element; } diff --git a/frontend/src/ts/utils/global.ts b/frontend/src/ts/utils/global.ts new file mode 100644 index 000000000..9f8557b75 --- /dev/null +++ b/frontend/src/ts/utils/global.ts @@ -0,0 +1,6 @@ +export function addToGlobal(items: Record): void { + for (const [name, item] of Object.entries(items)) { + //@ts-expect-error dev + window[name] = item; + } +}