Add widget reply notification setting

This commit is contained in:
rajchales-monito
2026-07-23 23:29:54 +02:00
parent f8874c86ef
commit 4125a512ad
4 changed files with 38 additions and 7 deletions
+25 -5
View File
@@ -68,7 +68,10 @@
const attachmentList = root.querySelector(".mf-attachment-list");
let selectedFiles = [];
launcher.addEventListener("click", () => panel.classList.toggle("open"));
launcher.addEventListener("click", () => {
panel.classList.toggle("open");
if (panel.classList.contains("open")) launcher.classList.remove("mf-notify");
});
close.addEventListener("click", () => panel.classList.remove("open"));
form.message.addEventListener("input", debounce(() => {
@@ -133,7 +136,7 @@
selectedFiles = [];
renderSelectedFiles(attachmentList, selectedFiles);
renderConversation(messages, data);
connectEvents(messages, typing);
connectEvents(messages, typing, panel, launcher, settings);
} catch {
showFormError(root, "Zpravu se nepodarilo odeslat. Zkuste to prosim znovu.");
} finally {
@@ -143,7 +146,7 @@
rememberPage();
if (session.conversationId) {
loadExistingConversation(messages).finally(() => connectEvents(messages, typing));
loadExistingConversation(messages).finally(() => connectEvents(messages, typing, panel, launcher, settings));
}
}
@@ -155,7 +158,7 @@
};
}
function connectEvents(messages, typing) {
function connectEvents(messages, typing, panel, launcher, settings) {
if (session.events || !session.conversationId) return;
session.events = new EventSource(`${apiBase}/api/widget/conversations/${session.conversationId}/events`);
session.events.addEventListener("message", (event) => {
@@ -165,7 +168,14 @@
clearTimeout(connectEvents.typingTimer);
connectEvents.typingTimer = setTimeout(() => { typing.hidden = true; }, 1800);
}
if (data.type === "message:new") renderConversation(messages, data.payload);
if (data.type === "message:new") {
renderConversation(messages, data.payload);
const eventMessages = data.payload?.messages || [];
const lastMessage = eventMessages[eventMessages.length - 1];
if (settings.notifications?.pulseOnAdminReply !== false && lastMessage?.senderType === "admin" && !panel.classList.contains("open")) {
launcher.classList.add("mf-notify");
}
}
});
}
@@ -276,6 +286,11 @@
.mf-launcher span { width: 26px; margin: 0 auto; }
.mf-launcher span:before { transform: translateY(-8px); }
.mf-launcher span:after { transform: translateY(5px); width: 18px; }
.mf-launcher.mf-notify { animation: mfPulse 1.15s ease-out infinite; }
.mf-launcher.mf-notify:after {
position: absolute; top: 5px; right: 5px; width: 13px; height: 13px; border-radius: 50%;
background: #ff3b30; border: 2px solid ${c.surface}; content: "";
}
.mf-panel {
position: fixed; z-index: 2147483001; ${desktop.side}: ${desktop.sideOffsetPx}px; bottom: ${desktop.bottomPx + 72}px;
width: min(${desktop.widthPx}px, calc(100vw - 24px)); max-height: min(680px, calc(100vh - 110px)); display: none;
@@ -313,6 +328,11 @@
.mf-note.error { color: #b42318; }
.mf-powered { justify-self: center; color: ${c.muted}; font-size: 11px; text-decoration: none; }
.mf-powered:hover { color: ${c.brand}; text-decoration: underline; }
@keyframes mfPulse {
0% { box-shadow: 0 12px 30px rgb(0 0 0 / 22%), 0 0 0 0 rgb(15 143 111 / 42%); transform: scale(1); }
70% { box-shadow: 0 12px 30px rgb(0 0 0 / 22%), 0 0 0 14px rgb(15 143 111 / 0%); transform: scale(1.04); }
100% { box-shadow: 0 12px 30px rgb(0 0 0 / 22%), 0 0 0 0 rgb(15 143 111 / 0%); transform: scale(1); }
}
@media (max-width: 640px) {
.mf-launcher { ${mobile.side}: ${mobile.sideOffsetPx}px; bottom: ${mobile.bottomPx}px; }
.mf-panel { ${mobile.side}: ${mobile.sideOffsetPx}px; bottom: ${mobile.bottomPx + 68}px; width: min(${mobile.widthPx}px, calc(100vw - 24px)); max-height: calc(100vh - 96px); }