Files
bitvid/js/ui/dm/Avatar.js
T
thePR0M3TH3ANandClaude Opus 4.8 37edbd18d2 Admin ring + star across avatars (identity, switcher, cards, player, DMs)
Add a reusable decorateAdminAvatar(avatarEl, id) that applies BOTH the gold
ring (box-shadow, not clipped by the avatar's overflow) and the corner star
(hung off a non-clipped .admin-avatar-wrap it ensures around the avatar).
Idempotent; works on attached or freshly-built detached avatars.

Wired onto:
- profile modal "Acting as" identity pill
- account-switcher saved-profile avatars
- video card creator avatars
- video player creator avatar
- DM conversation list + message bubbles (upgraded from star-only to ring+star)

adminBadge.js: split the star decision from its DOM (setAdminStar) so the
ring and star share one isAdminActor check; added ADMIN_RING_CLASS /
ADMIN_WRAP_CLASS. tests: decorateAdminAvatar (wrap/ring/star, idempotency,
non-admin, detached).

Co-Authored-By: Claude Opus 4.8 <[email protected]>
2026-07-07 14:24:16 -04:00

67 lines
1.7 KiB
JavaScript

import { decorateAdminAvatar, isAdminActor } from "../adminBadge.js";
const DEFAULT_AVATAR_ALT = "Avatar";
function getInitials(label = "") {
if (typeof label !== "string") {
return "";
}
const words = label.trim().split(/\s+/).filter(Boolean);
if (!words.length) {
return "";
}
const initials = words.slice(0, 2).map((word) => word[0].toUpperCase());
return initials.join("");
}
export function Avatar({
document: doc,
src = "",
alt = DEFAULT_AVATAR_ALT,
size = "md",
initials = "",
status = "",
adminId = "",
} = {}) {
if (!doc) {
throw new Error("Avatar requires a document reference.");
}
const avatar = doc.createElement("div");
// Explicit mapping ensures Tailwind scanner detects these classes
const sizeClass = {
sm: "dm-avatar--sm",
md: "dm-avatar--md",
}[size] || `dm-avatar--${size}`;
avatar.className = `dm-avatar ${sizeClass}`;
if (status) {
avatar.dataset.status = status;
}
const resolvedInitials = initials || getInitials(alt);
if (src) {
const img = doc.createElement("img");
img.className = "dm-avatar__image";
img.alt = alt || DEFAULT_AVATAR_ALT;
img.loading = "lazy";
img.decoding = "async";
img.src = src;
avatar.appendChild(img);
} else {
const fallback = doc.createElement("span");
fallback.className = "dm-avatar__fallback";
fallback.textContent = resolvedInitials || "?";
avatar.appendChild(fallback);
}
// Only admin avatars get decorated (ring + wrapped corner star); non-admin
// avatars are returned exactly as before to keep existing layouts untouched.
if (adminId && isAdminActor(adminId)) {
return decorateAdminAvatar(avatar, adminId, { doc });
}
return avatar;
}