225 lines · 7.1 KB
import { useState } from "react";
import { cn } from "~/utils/cn";
import type { StatusState } from "~/lib/types";

const AVATAR_COLORS = [
  "bg-orange-700",
  "bg-sky-700",
  "bg-emerald-700",
  "bg-rose-700",
  "bg-violet-700",
  "bg-amber-700",
  "bg-teal-700",
  "bg-fuchsia-700",
];
const SECONDS = 1000;
const MINUTE = 60 * SECONDS;
const HOUR = 60 * MINUTE;
const DAY = 24 * HOUR;
const MONTH = 30 * DAY;
const YEAR = 365 * DAY;
const KB = 1024;

function hash(value: string): number {
  let out = 0;
  for (const char of value) out = (out * 31 + char.charCodeAt(0)) | 0;
  return Math.abs(out);
}

const AVATAR_PIXELS = { sm: 40, md: 64, lg: 160 } as const;

/**
 * Shows an account's uploaded picture (looked up by name unless `src` is given) and falls back to
 * colored initials when there is none or it fails to load.
 */
export function Avatar({ name, size = "md", src }: { name: string; size?: "sm" | "md" | "lg"; src?: string | null }) {
  const [failedUrl, setFailedUrl] = useState<string | null>(null);
  const sizeClass = size === "sm" ? "size-5 text-[10px]" : size === "lg" ? "size-20 text-3xl" : "size-8 text-sm";
  const url = src === undefined ? `/avatars/u/${encodeURIComponent(name)}?s=${AVATAR_PIXELS[size]}` : src;
  if (url && url !== failedUrl) {
    return (