Files
flotilla/src/app/components/ProfileCircle.svelte
T
2026-06-15 10:39:01 -07:00

49 lines
1.6 KiB
Svelte

<script lang="ts">
import cx from "classnames"
import {removeUndefined} from "@welshman/lib"
import {deriveProfile, deriveProfileDisplay} from "@welshman/app"
import {getColor, getBlobVariant} from "@app/theme"
import ImageIcon from "@lib/components/ImageIcon.svelte"
type Props = {
pubkey?: string
class?: string
size?: number
url?: string
shape?: "blob" | "circle"
style?: string
}
const {pubkey, url, size = 7, shape = "blob", style = "", ...props}: Props = $props()
const profile = deriveProfile(pubkey, removeUndefined([url]))
const display = deriveProfileDisplay(pubkey)
// Organic, hand-drawn-feeling mask. The variant is stable per pubkey so a
// person's silhouette never changes; `shape="circle"` opts back into a disc.
const shapeClass =
shape === "circle"
? "rounded-full"
: ["avatar-blob", "avatar-blob-2", "avatar-blob-3"][getBlobVariant(pubkey) - 1]
const color = getColor(pubkey)
const px = $derived(size * 4)
const initial = $derived([...($display || "")].find(c => c.trim()) || "?")
</script>
{#if $profile?.picture}
<ImageIcon {size} alt="" {style} class={cx(props.class, shapeClass)} src={$profile.picture} />
{:else}
<!-- Fallback: a subtle gradient derived from the pubkey + the person's initial. -->
<div
class={cx(
props.class,
shapeClass,
"font-display flex shrink-0 items-center justify-center font-bold text-white uppercase select-none",
)}
style="width:{px}px;height:{px}px;font-size:{px *
0.45}px;background-image:linear-gradient(135deg,{color},color-mix(in oklab,{color},#000 28%));{style}">
{initial}
</div>
{/if}