All checks were successful
Build and Publish Docker Image / build-and-push (push) Successful in 40s
60 lines
1.7 KiB
TypeScript
60 lines
1.7 KiB
TypeScript
import { useContext, useState } from "react";
|
|
import { API_URL } from "../config/api.ts";
|
|
import { AvatarOverrideContext } from "../contexts/AvatarOverrideContext.ts";
|
|
|
|
interface AvatarProps {
|
|
userId: string;
|
|
username: string;
|
|
hasAvatar: boolean;
|
|
size?: number;
|
|
version?: number;
|
|
}
|
|
|
|
export function Avatar(
|
|
{ userId, username, hasAvatar, size = 36, version }: AvatarProps,
|
|
) {
|
|
const [imgFailed, setImgFailed] = useState(false);
|
|
const sizeStyle = { width: size, height: size };
|
|
|
|
// A live `user_updated` override (avatar add/remove/change) wins over the
|
|
// possibly-stale props the surrounding list was rendered with.
|
|
const override = useContext(AvatarOverrideContext)[userId];
|
|
const effectiveHasAvatar = override ? override.hasAvatar : hasAvatar;
|
|
const effectiveVersion = override ? override.version : version;
|
|
|
|
// A new image (different version/presence) is a fresh thing to try — clear
|
|
// any prior load failure during render rather than in an effect.
|
|
const imgKey = `${effectiveHasAvatar}:${effectiveVersion ?? ""}`;
|
|
const [prevImgKey, setPrevImgKey] = useState(imgKey);
|
|
if (prevImgKey !== imgKey) {
|
|
setPrevImgKey(imgKey);
|
|
setImgFailed(false);
|
|
}
|
|
|
|
if (effectiveHasAvatar && !imgFailed) {
|
|
const src = effectiveVersion
|
|
? `${API_URL}/api/avatars/${userId}?v=${effectiveVersion}`
|
|
: `${API_URL}/api/avatars/${userId}`;
|
|
return (
|
|
<img
|
|
src={src}
|
|
alt={username}
|
|
title={username}
|
|
style={sizeStyle}
|
|
className="avatar-img"
|
|
onError={() => setImgFailed(true)}
|
|
/>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<div
|
|
className="avatar-initials"
|
|
title={username}
|
|
style={{ ...sizeStyle, fontSize: size * 0.45 }}
|
|
>
|
|
{username.charAt(0).toUpperCase()}
|
|
</div>
|
|
);
|
|
}
|