mirror of
https://github.com/PR0M3TH3AN/bitvid.git
synced 2026-10-28 00:12:25 +00:00
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]>
89 lines
2.4 KiB
JavaScript
89 lines
2.4 KiB
JavaScript
import { decorateAdminAvatar, isAdminActor } from "../adminBadge.js";
|
|
|
|
function createElement(doc, tag, className, text) {
|
|
const element = doc.createElement(tag);
|
|
if (className) {
|
|
element.className = className;
|
|
}
|
|
if (typeof text === "string") {
|
|
element.textContent = text;
|
|
}
|
|
return element;
|
|
}
|
|
|
|
export function MessageBubble({
|
|
document: doc,
|
|
message = {},
|
|
variant = "incoming",
|
|
avatarSrc = "",
|
|
adminId = "",
|
|
} = {}) {
|
|
if (!doc) {
|
|
throw new Error("MessageBubble requires a document reference.");
|
|
}
|
|
|
|
const {
|
|
body = "",
|
|
timestamp = "",
|
|
status = "sent",
|
|
} = message;
|
|
|
|
const row = createElement(doc, "div", "dm-message-row");
|
|
row.classList.add(`dm-message-row--${variant}`);
|
|
|
|
const avatar = createElement(doc, "img", "dm-message-avatar");
|
|
avatar.src = avatarSrc || "assets/svg/default-profile.svg";
|
|
avatar.alt = variant === "outgoing" ? "Me" : "Sender";
|
|
|
|
// The message avatar is a bare circular <img>; when its author is an admin,
|
|
// decorate it (ring + wrapped corner star). Non-admin bubbles keep the plain
|
|
// <img> so existing layout is untouched.
|
|
let avatarNode = avatar;
|
|
if (adminId && isAdminActor(adminId)) {
|
|
avatarNode = decorateAdminAvatar(avatar, adminId, { doc });
|
|
}
|
|
|
|
const bubble = createElement(doc, "div", "dm-message-bubble");
|
|
bubble.classList.add(`dm-message-bubble--${variant}`);
|
|
bubble.dataset.status = status;
|
|
|
|
const content = createElement(doc, "div", "dm-message-bubble__content", body);
|
|
bubble.appendChild(content);
|
|
|
|
const meta = createElement(doc, "div", "dm-message-bubble__meta");
|
|
if (timestamp) {
|
|
// Added 'text-muted' class for theme-respecting color (usually handles light/dark text)
|
|
const timeSpan = createElement(doc, "span", "dm-message-bubble__time text-muted", timestamp);
|
|
meta.appendChild(timeSpan);
|
|
}
|
|
|
|
if (variant === "outgoing") {
|
|
const statusLabel = createElement(
|
|
doc,
|
|
"span",
|
|
"dm-message-bubble__status",
|
|
status === "failed"
|
|
? "Failed to send"
|
|
: status === "pending"
|
|
? "Pending…"
|
|
: status === "sending"
|
|
? "Sending…"
|
|
: "Sent",
|
|
);
|
|
statusLabel.dataset.status = status;
|
|
meta.appendChild(statusLabel);
|
|
}
|
|
|
|
bubble.appendChild(meta);
|
|
|
|
if (variant === "outgoing") {
|
|
row.appendChild(bubble);
|
|
row.appendChild(avatarNode);
|
|
} else {
|
|
row.appendChild(avatarNode);
|
|
row.appendChild(bubble);
|
|
}
|
|
|
|
return row;
|
|
}
|