4c8f888b3e
Правки пользователя: - в «Профиль → Основной» сверху квадратный аватар слева, справа от него настройки имени, «О себе», состояния и эмодзи во всю высоту; смена аватара — клик по нему, форматы (JPEG, PNG, WebP, GIF до 8 МБ) — в подсказке; - убраны дубли: часовой пояс только в «Язык и регион», статус — только в мини-профиле панели; - панель пользователя переехала в нижний левый угол под сайдбар, подсказка о выходе из сервера — на её прежнее место; панель ужата под 240 px; - шестерёнка сервера открывает новый пункт настроек «Сервер» (список серверов, общее, участники, роли, опасная зона), а «Профиль → Сервер» оставлен только для ника и «Применить ко всем серверам»; - окно настроек получило статический размер 70% рабочей области со скроллом; - ID пользователей скрыты везде, кроме раздела «Инстанс» у администратора; - список друзей свёрнут под кнопкой «Друзья (N)» и раскрывается снизу вверх; - 249 тестов Vitest (было 239), check/lint/test/build зелёные.
53 lines
1.8 KiB
TypeScript
53 lines
1.8 KiB
TypeScript
import { avatarUrl, hueFromId, initials } from '@/lib/identity';
|
|
|
|
interface AvatarProps {
|
|
name: string;
|
|
/** Стабильный id для оттенка заглушки. */
|
|
seed: string;
|
|
fileId?: string | undefined;
|
|
/** Локальный предпросмотр (свежевыбранный файл) вместо `fileId`. */
|
|
preview?: string | undefined;
|
|
size?: 'sm' | 'md' | 'lg' | 'xl';
|
|
/** Квадратная заглушка для серверов, круглая — для пользователей. */
|
|
shape?: 'circle' | 'square';
|
|
}
|
|
|
|
const sizes = {
|
|
sm: 'h-7 w-7 text-[11px]',
|
|
md: 'h-9 w-9 text-xs',
|
|
lg: 'h-14 w-14 text-lg',
|
|
/** Крупный аватар в настройках профиля: занимает родителя целиком. */
|
|
xl: 'h-full w-full text-4xl',
|
|
} as const;
|
|
|
|
/**
|
|
* Аватар: картинка файлового сервиса либо инициалы на цветной заглушке.
|
|
* `preview` перекрывает `fileId` — так выбранный файл видно до ответа сервера.
|
|
*/
|
|
export function Avatar({
|
|
name,
|
|
seed,
|
|
fileId,
|
|
preview,
|
|
size = 'md',
|
|
shape = 'circle',
|
|
}: AvatarProps) {
|
|
const src = preview ?? avatarUrl(fileId);
|
|
const hue = hueFromId(seed);
|
|
const radius = shape === 'circle' ? 'rounded-full' : 'rounded-[var(--radius-lg)]';
|
|
|
|
return (
|
|
<span
|
|
aria-hidden="true"
|
|
className={`inline-flex shrink-0 items-center justify-center overflow-hidden font-semibold text-white ${radius} ${sizes[size]}`}
|
|
style={src === null ? { backgroundColor: `oklch(0.55 0.13 ${String(hue)})` } : undefined}
|
|
>
|
|
{src === null ? (
|
|
initials(name)
|
|
) : (
|
|
<img src={src} alt="" className="h-full w-full object-cover" />
|
|
)}
|
|
</span>
|
|
);
|
|
}
|