fix: discord avatars not loading (@fehmer) (#6999)

fixes #6998

Seems it was fixed by discord in the meantime. Still I think this is a
better approach because it only takes one rest call to the discord cdn
instead of two for each avatar.

---------

Co-authored-by: Miodec <jack@monkeytype.com>
This commit is contained in:
Christian Fehmer 2025-10-15 13:07:57 +02:00 committed by GitHub
parent 015252120f
commit d9d375d05a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 54 additions and 63 deletions

View file

@ -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 {

View file

@ -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<string, unknown>): 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);

View file

@ -1,26 +1,41 @@
const cachedAvatarUrlByAvatarId: Map<string, string | null> = 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 = `<div class="loading"><i class="fas fa-circle-notch fa-spin"><i></div>`;
} else {
avatar.innerHTML = `<div class="userIcon"><i class="${
options?.userIcon ?? "fas fa-user-circle"
}"><i></div>`;
}
if (url === null || knownBadUrls.has(url)) {
avatar.innerHTML = `<div class="userIcon"><i class="${
options?.userIcon ?? "fas fa-user-circle"
}"></i></div>`;
} else {
avatar.innerHTML = `<div class="discordImage" style="background-image:url(${url}?size=${
options?.size ?? 32
})"></div>`;
const loading = document.createElement("div");
loading.className = "loading";
loading.innerHTML = '<i class="fas fa-circle-notch fa-spin"></i>';
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<string | null> {
// 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;
}

View file

@ -761,4 +761,11 @@ export function scrollToCenterOrTop(el: HTMLElement | null): void {
});
}
export function addToGlobal(items: Record<string, unknown>): 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