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..67d4fc521 100644 --- a/frontend/src/ts/index.ts +++ b/frontend/src/ts/index.ts @@ -42,7 +42,7 @@ import "./states/connection"; import "./test/tts"; import "./elements/fps-counter"; import "./controllers/profile-search-controller"; -import { isDevEnvironment } from "./utils/misc"; +import { isDevEnvironment, addToGlobal } from "./utils/misc"; import * as VersionButton from "./elements/version-button"; import * as Focus from "./test/focus"; import { getDevOptionsModal } from "./utils/async-modules"; @@ -71,13 +71,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..3748846fc 100644 --- a/frontend/src/ts/utils/discord-avatar.ts +++ b/frontend/src/ts/utils/discord-avatar.ts @@ -1,26 +1,41 @@ -const cachedAvatarUrlByAvatarId: Map = new Map(); - 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 = `
`; + const loading = document.createElement("div"); + loading.className = "loading"; + loading.innerHTML = ''; + + const imageContainer = document.createElement("div"); + imageContainer.className = "discordImage"; + + const img = document.createElement("img"); + img.src = `${url}?size=${options?.size ?? 32}`; + + // Add event listeners directly to the img element + img.addEventListener("load", async () => { + loading.remove(); + }); + + img.addEventListener("error", () => { + knownBadUrls.add(url); + avatar.replaceWith(buildElement(null, options)); + }); + + imageContainer.appendChild(img); + avatar.appendChild(loading); + avatar.appendChild(imageContainer); } + return avatar; } @@ -43,40 +58,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/misc.ts b/frontend/src/ts/utils/misc.ts index 5920e7694..5a41ca8a1 100644 --- a/frontend/src/ts/utils/misc.ts +++ b/frontend/src/ts/utils/misc.ts @@ -761,4 +761,11 @@ export function scrollToCenterOrTop(el: HTMLElement | null): void { }); } +export function addToGlobal(items: Record): void { + for (const [name, item] of Object.entries(items)) { + //@ts-expect-error dev + window[name] = item; + } +} + // DO NOT ALTER GLOBAL OBJECTSONSTRUCTOR, IT WILL BREAK RESULT HASHES