mirror of
https://github.com/PR0M3TH3AN/bitvid.git
synced 2026-10-27 07:52:24 +00:00
107 lines
3.1 KiB
JavaScript
107 lines
3.1 KiB
JavaScript
import { DayDivider } from "./DayDivider.js";
|
|
import { MessageBubble } from "./MessageBubble.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 MessageThread({
|
|
document: doc,
|
|
contact = {},
|
|
messages = [],
|
|
state = "idle",
|
|
errorType = "",
|
|
privacyMode = "nip04",
|
|
} = {}) {
|
|
if (!doc) {
|
|
throw new Error("MessageThread requires a document reference.");
|
|
}
|
|
|
|
const root = createElement(doc, "section", "dm-message-thread");
|
|
|
|
const header = createElement(doc, "header", "dm-message-thread__header");
|
|
header.appendChild(
|
|
createElement(
|
|
doc,
|
|
"h3",
|
|
"dm-message-thread__title",
|
|
contact.name || "Direct Messages",
|
|
),
|
|
);
|
|
if (contact.status) {
|
|
header.appendChild(
|
|
createElement(doc, "span", "dm-message-thread__status", contact.status),
|
|
);
|
|
}
|
|
const normalizedPrivacy =
|
|
typeof privacyMode === "string" ? privacyMode.trim().toLowerCase() : "";
|
|
const isNip17 = normalizedPrivacy === "nip17" || normalizedPrivacy === "private";
|
|
const privacyLabel = `Privacy: ${isNip17 ? "NIP-17" : "NIP-04"}`;
|
|
const privacyHint = isNip17
|
|
? "NIP-17 gift-wraps your DM so relays only see the wrapper and relay hints."
|
|
: "NIP-04 sends a direct encrypted DM; relays can still see sender and recipient metadata.";
|
|
const privacyBadge = createElement(
|
|
doc,
|
|
"span",
|
|
"dm-message-thread__status",
|
|
privacyLabel,
|
|
);
|
|
privacyBadge.title = privacyHint;
|
|
header.appendChild(privacyBadge);
|
|
root.appendChild(header);
|
|
|
|
const timeline = createElement(doc, "div", "dm-message-thread__timeline");
|
|
timeline.setAttribute("role", "log");
|
|
timeline.setAttribute("aria-live", "polite");
|
|
|
|
const newMessagesBanner = createElement(
|
|
doc,
|
|
"button",
|
|
"dm-message-thread__new",
|
|
"New messages",
|
|
);
|
|
newMessagesBanner.type = "button";
|
|
newMessagesBanner.setAttribute("aria-label", "Jump to newest messages");
|
|
timeline.appendChild(newMessagesBanner);
|
|
|
|
const stateEl = createElement(doc, "div", "dm-message-thread__state");
|
|
|
|
if (state === "loading") {
|
|
stateEl.textContent = "Decrypting messages…";
|
|
timeline.appendChild(stateEl);
|
|
} else if (state === "empty") {
|
|
stateEl.textContent = "Say hello to start the conversation.";
|
|
timeline.appendChild(stateEl);
|
|
} else if (state === "error") {
|
|
const errorMessage =
|
|
errorType === "decrypt"
|
|
? "We couldn't decrypt some messages."
|
|
: "Something went wrong loading this thread.";
|
|
stateEl.textContent = errorMessage;
|
|
timeline.appendChild(stateEl);
|
|
} else {
|
|
messages.forEach((message) => {
|
|
if (message.type === "day") {
|
|
timeline.appendChild(DayDivider({ document: doc, label: message.label }));
|
|
return;
|
|
}
|
|
const bubble = MessageBubble({
|
|
document: doc,
|
|
message,
|
|
variant: message.direction === "outgoing" ? "outgoing" : "incoming",
|
|
});
|
|
timeline.appendChild(bubble);
|
|
});
|
|
}
|
|
|
|
root.appendChild(timeline);
|
|
return root;
|
|
}
|