xtablo-source/backend/static/discussion-sse.js

110 lines
3.7 KiB
JavaScript
Raw Permalink Normal View History

2026-05-16 08:18:33 +00:00
(function () {
function messageExists(messageId) {
return Boolean(document.querySelector('[data-message-id="' + CSS.escape(messageId) + '"]'));
}
function isDiscussionMessageForm(element) {
return Boolean(element && element.matches && element.matches('form[action$="/discussion/messages"]'));
}
function requestElement(detail) {
return detail && detail.requestConfig && detail.requestConfig.elt;
}
function isSuccessfulDiscussionPost(detail) {
var status = detail && detail.xhr && detail.xhr.status;
return status >= 200 && status < 300;
}
function messageIdFromHTML(html) {
if (!html) return "";
var template = document.createElement("template");
template.innerHTML = html.trim();
var message = template.content.querySelector("[data-message-id]");
return message ? message.dataset.messageId : "";
}
2026-05-16 08:18:33 +00:00
function ensureMessageList(container) {
var messages = container.querySelector("#discussion-messages");
if (!messages) return null;
var list = messages.querySelector(".divide-y");
if (list) return list;
messages.innerHTML = "";
list = document.createElement("div");
list.className = "divide-y divide-slate-100";
messages.appendChild(list);
return list;
}
function scrollToBottom(container) {
var messages = container.querySelector("#discussion-messages");
if (messages) messages.scrollTop = messages.scrollHeight;
}
2026-05-16 08:18:33 +00:00
function appendMessage(container, event) {
if (!event || !event.messageId || !event.messageHtml || messageExists(event.messageId)) {
return;
}
// Skip messages authored by the current user — HTMX already appended them
// with IsOwn=true. Let SSE handle only messages from other users.
var currentUserId = container.dataset.currentUserId;
if (currentUserId && event.authorUserId === currentUserId) {
return;
}
2026-05-16 08:18:33 +00:00
var list = ensureMessageList(container);
if (!list) return;
var template = document.createElement("template");
template.innerHTML = event.messageHtml.trim();
list.appendChild(template.content);
scrollToBottom(container);
2026-05-16 08:18:33 +00:00
}
function connectDiscussion(container) {
if (!container || container.dataset.discussionStreamConnected === "true") return;
var streamURL = container.dataset.discussionStreamUrl;
if (!streamURL || !window.EventSource) return;
scrollToBottom(container);
2026-05-16 08:18:33 +00:00
container.dataset.discussionStreamConnected = "true";
var source = new EventSource(streamURL);
source.addEventListener("discussion-message", function (message) {
try {
appendMessage(container, JSON.parse(message.data));
} catch (_) {
return;
}
});
}
function connectDiscussionStreams() {
document.querySelectorAll("[data-discussion-stream-url]").forEach(connectDiscussion);
}
document.addEventListener("DOMContentLoaded", connectDiscussionStreams);
document.body.addEventListener("htmx:afterSwap", connectDiscussionStreams);
document.body.addEventListener("htmx:beforeSwap", function (event) {
if (!isDiscussionMessageForm(requestElement(event.detail))) return;
var messageId = messageIdFromHTML(event.detail.xhr && event.detail.xhr.responseText);
if (messageId && messageExists(messageId)) {
event.detail.shouldSwap = false;
event.preventDefault();
}
});
document.body.addEventListener("htmx:afterRequest", function (event) {
var form = requestElement(event.detail);
if (!isDiscussionMessageForm(form) || !isSuccessfulDiscussionPost(event.detail)) return;
form.reset();
var textarea = form.querySelector("#discussion-message-body");
if (textarea) textarea.value = "";
var tab = form.closest("[data-discussion-stream-url]");
if (tab) scrollToBottom(tab);
});
2026-05-16 08:18:33 +00:00
})();