Files
GIA/core/static/js/compose-panel.js

192 lines
5.7 KiB
JavaScript

(function () {
if (window.GIAComposePanel) {
window.GIAComposePanel.initAll(document);
return;
}
const core = window.GIAComposePanelCore;
const threadModule = window.GIAComposePanelThread;
const sendModule = window.GIAComposePanelSend;
if (!core || !threadModule || !sendModule) {
return;
}
const destroyPanelState = function (state) {
if (!state) {
return;
}
if (state.pollTimer) {
clearInterval(state.pollTimer);
}
if (state.pendingCommandPoll) {
clearInterval(state.pendingCommandPoll);
}
if (state.socket) {
try {
state.socket.close();
} catch (_err) {
// Ignore socket close failures.
}
}
if (state.eventHandler) {
document.body.removeEventListener("composeMessageSent", state.eventHandler);
}
if (state.sendResultHandler) {
document.body.removeEventListener(
"composeSendResult",
state.sendResultHandler
);
}
if (state.commandIdHandler) {
document.body.removeEventListener(
"composeSendCommandId",
state.commandIdHandler
);
}
delete window.giaComposePanels[state.panelId];
};
const initPanel = function (panel) {
if (!panel) {
return;
}
const panelId = String(panel.id || "").trim();
if (!panelId) {
return;
}
if (panel.dataset.composeInitialized === "1" && window.giaComposePanels[panelId]) {
return;
}
destroyPanelState(window.giaComposePanels[panelId]);
const thread = document.getElementById(panelId + "-thread");
const form = document.getElementById(panelId + "-form");
const textarea = document.getElementById(panelId + "-textarea");
if (!thread || !form || !textarea) {
return;
}
const state = {
panelId: panelId,
pollTimer: null,
polling: false,
socket: null,
websocketReady: false,
pendingCommandPoll: null,
pendingCommandId: "",
pendingCommandAttempts: 0,
pendingCommandStartedAt: 0,
pendingCommandInFlight: false,
eventHandler: null,
sendResultHandler: null,
commandIdHandler: null,
};
window.giaComposePanels[panelId] = state;
panel.dataset.composeInitialized = "1";
const statusBox = document.getElementById(panelId + "-status");
const setStatus = function (message, level) {
if (!statusBox) {
return;
}
statusBox.innerHTML = "";
if (!message) {
return;
}
statusBox.appendChild(
core.createNode(
"p",
"compose-status-line is-" + String(level || "info"),
String(message)
)
);
};
const flashCompose = function (className) {
panel.classList.remove("is-send-success", "is-send-fail");
void panel.offsetWidth;
panel.classList.add(className);
window.setTimeout(function () {
panel.classList.remove(className);
}, 420);
};
const autosize = function () {
textarea.style.height = "auto";
textarea.style.height = Math.min(Math.max(textarea.scrollHeight, 44), 128) + "px";
};
const threadController = threadModule.createController({
contactSelect: document.getElementById(panelId + "-contact-select"),
hiddenIdentifier: document.getElementById(panelId + "-input-identifier"),
hiddenPerson: document.getElementById(panelId + "-input-person"),
hiddenReplyTo: form.querySelector('input[name="reply_to_message_id"]'),
hiddenService: document.getElementById(panelId + "-input-service"),
metaLine: document.getElementById(panelId + "-meta-line"),
panel: panel,
panelId: panelId,
platformSelect: document.getElementById(panelId + "-platform-select"),
renderMode: String(panel.dataset.renderMode || "page"),
replyBanner: document.getElementById(panelId + "-reply-banner"),
replyBannerText: document.getElementById(panelId + "-reply-text"),
replyClearBtn: document.getElementById(panelId + "-reply-clear"),
state: state,
textarea: textarea,
thread: thread,
typingNode: document.getElementById(panelId + "-typing"),
});
const sendController = sendModule.createController({
armInput: form.querySelector('input[name="failsafe_arm"]'),
autosize: autosize,
clearReplyTarget: threadController.clearReplyTarget,
confirmInput: form.querySelector('input[name="failsafe_confirm"]'),
csrfToken: String(panel.dataset.csrfToken || ""),
flashCompose: flashCompose,
form: form,
manualConfirm: form.querySelector(".manual-confirm"),
panel: panel,
panelId: panelId,
poll: threadController.poll,
queryParams: threadController.queryParams,
sendButton: form.querySelector(".compose-send-btn"),
sendCapable: String(panel.dataset.capabilitySend || "").toLowerCase() === "true",
setStatus: setStatus,
state: state,
statusBox: statusBox,
textarea: textarea,
thread: thread,
});
threadController.setBeforeContextReset(sendController.resetForContextSwitch);
autosize();
textarea.addEventListener("input", autosize);
threadController.init();
sendController.init();
};
const initAll = function (root) {
core.collectPanels(root).forEach(function (panel) {
initPanel(panel);
});
};
window.GIAComposePanel = {
destroyPanel: function (panelId) {
destroyPanelState(window.giaComposePanels[panelId]);
},
initAll: initAll,
initPanel: initPanel,
};
document.addEventListener("DOMContentLoaded", function () {
initAll(document);
});
if (document.body) {
document.body.addEventListener("htmx:afterSwap", function (event) {
initAll((event && event.target) || document);
});
}
initAll(document);
})();