29 lines
1.3 KiB
TypeScript
29 lines
1.3 KiB
TypeScript
/**
|
|
* 头像的轻量显示降级:指定URL加载失败时用名字首段的大写表示,新的URL可重新尝试。
|
|
* 失败标记仅属当前组件,不写用户资料,也不把一次图片失败当成账户失效。
|
|
*/
|
|
"use client";
|
|
|
|
import { useState } from "react";
|
|
import styles from "./workspace.module.css";
|
|
|
|
/** 只记住最近一次失败URL;large仅控制尺寸,图片不发送Referrer,alt保留用户身份文本。
|
|
* 换 URL 可以再试,若之后回到较早失败的 URL 也可能重试;不是永久失败地址集合。 */
|
|
export function UserAvatar({ name, url, large = false }: {
|
|
name: string;
|
|
url?: string | null;
|
|
large?: boolean;
|
|
}) {
|
|
const [failedUrl, setFailedUrl] = useState<string | null>(null);
|
|
// fallback 用 slice(0,1) 取首个 UTF-16 代码单元,并非完整 Unicode 字素;空名字得到空占位。
|
|
const showImage = Boolean(url && url !== failedUrl);
|
|
return (
|
|
<span className={`${styles.avatar} ${large ? styles.avatarLarge : ""}`}>
|
|
{showImage ? (
|
|
<img src={url!} alt={`${name}的头像`} width={large ? 36 : 24} height={large ? 36 : 24}
|
|
referrerPolicy="no-referrer" onError={() => setFailedUrl(url!)} />
|
|
) : <span aria-hidden="true">{name.slice(0, 1).toUpperCase()}</span>}
|
|
</span>
|
|
);
|
|
}
|