Allow image-only visitor messages
This commit is contained in:
+9
-2
@@ -69,21 +69,28 @@ $("#settingsForm").addEventListener("submit", async (event) => {
|
|||||||
$("#replyForm").addEventListener("submit", async (event) => {
|
$("#replyForm").addEventListener("submit", async (event) => {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
if (!state.activeId) return;
|
if (!state.activeId) return;
|
||||||
|
const textarea = event.currentTarget.querySelector("textarea[name='message']");
|
||||||
|
const originalMessage = textarea.value;
|
||||||
const form = new FormData(event.currentTarget);
|
const form = new FormData(event.currentTarget);
|
||||||
const payload = {
|
const payload = {
|
||||||
message: form.get("message") || "",
|
message: form.get("message") || "",
|
||||||
attachments: await Promise.all(state.replyFiles.slice(0, 3).map(fileToPayload))
|
attachments: await Promise.all(state.replyFiles.slice(0, 3).map(fileToPayload))
|
||||||
};
|
};
|
||||||
if (!payload.message.trim() && !payload.attachments.length) return;
|
if (!payload.message.trim() && !payload.attachments.length) return;
|
||||||
|
textarea.value = "";
|
||||||
|
try {
|
||||||
await api(`/api/admin/conversations/${state.activeId}/messages`, {
|
await api(`/api/admin/conversations/${state.activeId}/messages`, {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
body: payload
|
body: payload
|
||||||
});
|
});
|
||||||
event.currentTarget.querySelector("textarea[name='message']").value = "";
|
|
||||||
event.currentTarget.reset();
|
event.currentTarget.reset();
|
||||||
state.replyFiles = [];
|
state.replyFiles = [];
|
||||||
renderReplyAttachments();
|
renderReplyAttachments();
|
||||||
await openConversation(state.activeId);
|
await openConversation(state.activeId);
|
||||||
|
} catch (error) {
|
||||||
|
textarea.value = originalMessage;
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
$("#replyForm textarea").addEventListener("keydown", (event) => {
|
$("#replyForm textarea").addEventListener("keydown", (event) => {
|
||||||
@@ -299,7 +306,7 @@ function renderActive() {
|
|||||||
$("#messages").innerHTML = messages.map((message) => `
|
$("#messages").innerHTML = messages.map((message) => `
|
||||||
<article class="message ${message.senderType}">
|
<article class="message ${message.senderType}">
|
||||||
<div class="by">${escapeHtml(message.senderName || message.senderType)} · ${escapeHtml(message.createdAt)}</div>
|
<div class="by">${escapeHtml(message.senderName || message.senderType)} · ${escapeHtml(message.createdAt)}</div>
|
||||||
<p>${escapeHtml(message.body)}</p>
|
${message.body ? `<p>${escapeHtml(message.body)}</p>` : ""}
|
||||||
${renderAttachments(message.attachments)}
|
${renderAttachments(message.attachments)}
|
||||||
</article>
|
</article>
|
||||||
`).join("");
|
`).join("");
|
||||||
|
|||||||
+3
-3
@@ -37,7 +37,7 @@
|
|||||||
<div class="mf-typing" hidden>Operator pise...</div>
|
<div class="mf-typing" hidden>Operator pise...</div>
|
||||||
<form class="mf-form">
|
<form class="mf-form">
|
||||||
<div class="mf-composer">
|
<div class="mf-composer">
|
||||||
<textarea name="message" required rows="3" placeholder="${escapeHtml(copy.placeholder)}"></textarea>
|
<textarea name="message" rows="3" placeholder="${escapeHtml(copy.placeholder)}"></textarea>
|
||||||
<button class="mf-send" type="submit" aria-label="${escapeHtml(copy.sendLabel)}" title="${escapeHtml(copy.sendLabel)}">
|
<button class="mf-send" type="submit" aria-label="${escapeHtml(copy.sendLabel)}" title="${escapeHtml(copy.sendLabel)}">
|
||||||
<svg viewBox="0 0 24 24" aria-hidden="true">
|
<svg viewBox="0 0 24 24" aria-hidden="true">
|
||||||
<path d="M3.7 20.3 21 12 3.7 3.7l2.5 7.1L14 12l-7.8 1.2-2.5 7.1Z"></path>
|
<path d="M3.7 20.3 21 12 3.7 3.7l2.5 7.1L14 12l-7.8 1.2-2.5 7.1Z"></path>
|
||||||
@@ -121,7 +121,7 @@
|
|||||||
form.addEventListener("submit", async (event) => {
|
form.addEventListener("submit", async (event) => {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
const body = await payloadFromForm(form, selectedFiles);
|
const body = await payloadFromForm(form, selectedFiles);
|
||||||
if (!body.message.trim()) return;
|
if (!body.message.trim() && !body.attachments.length) return;
|
||||||
const submitButton = root.querySelector(".mf-send");
|
const submitButton = root.querySelector(".mf-send");
|
||||||
submitButton.disabled = true;
|
submitButton.disabled = true;
|
||||||
|
|
||||||
@@ -200,7 +200,7 @@
|
|||||||
function addMessage(messages, sender, body, attachments) {
|
function addMessage(messages, sender, body, attachments) {
|
||||||
const item = document.createElement("article");
|
const item = document.createElement("article");
|
||||||
item.className = `mf-message ${sender}`;
|
item.className = `mf-message ${sender}`;
|
||||||
item.innerHTML = `<p>${escapeHtml(body)}</p>${attachments.map((a) => `<a class="mf-photo" href="${apiBase}${a.url}" target="_blank" rel="noreferrer"><img src="${apiBase}${a.url}" alt="${escapeHtml(a.name)}"></a>`).join("")}`;
|
item.innerHTML = `${body ? `<p>${escapeHtml(body)}</p>` : ""}${attachments.map((a) => `<a class="mf-photo" href="${apiBase}${a.url}" target="_blank" rel="noreferrer"><img src="${apiBase}${a.url}" alt="${escapeHtml(a.name)}"></a>`).join("")}`;
|
||||||
messages.append(item);
|
messages.append(item);
|
||||||
messages.scrollTop = messages.scrollHeight;
|
messages.scrollTop = messages.scrollHeight;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -354,7 +354,7 @@ function getVisitorConversation(res, url) {
|
|||||||
|
|
||||||
async function createConversation(req, res) {
|
async function createConversation(req, res) {
|
||||||
const body = await readJson(req, 8_000_000);
|
const body = await readJson(req, 8_000_000);
|
||||||
if (!body.message || !String(body.message).trim()) return json(res, 400, { error: "message_required" });
|
if ((!body.message || !String(body.message).trim()) && !(body.attachments || []).length) return json(res, 400, { error: "message_or_image_required" });
|
||||||
if (!attachmentsAreAllowed(body.attachments || [])) return json(res, 400, { error: "only_images_allowed" });
|
if (!attachmentsAreAllowed(body.attachments || [])) return json(res, 400, { error: "only_images_allowed" });
|
||||||
const site = db.prepare("SELECT * FROM sites WHERE site_key = ?").get(body.siteKey || "9b-plus");
|
const site = db.prepare("SELECT * FROM sites WHERE site_key = ?").get(body.siteKey || "9b-plus");
|
||||||
if (!site) return json(res, 404, { error: "site_not_found" });
|
if (!site) return json(res, 404, { error: "site_not_found" });
|
||||||
@@ -389,7 +389,7 @@ async function createConversation(req, res) {
|
|||||||
nullish(countryFromHeaders(req))
|
nullish(countryFromHeaders(req))
|
||||||
).lastInsertRowid;
|
).lastInsertRowid;
|
||||||
|
|
||||||
const messageId = insertMessage(conversationId, "visitor", info.name || "Visitor", String(body.message).trim());
|
const messageId = insertMessage(conversationId, "visitor", info.name || "Visitor", String(body.message || "").trim());
|
||||||
saveAttachments(conversationId, messageId, body.attachments || []);
|
saveAttachments(conversationId, messageId, body.attachments || []);
|
||||||
return conversationId;
|
return conversationId;
|
||||||
});
|
});
|
||||||
@@ -403,11 +403,11 @@ async function createConversation(req, res) {
|
|||||||
|
|
||||||
async function createVisitorMessage(req, res, url) {
|
async function createVisitorMessage(req, res, url) {
|
||||||
const body = await readJson(req, 8_000_000);
|
const body = await readJson(req, 8_000_000);
|
||||||
if (!body.message || !String(body.message).trim()) return json(res, 400, { error: "message_required" });
|
if ((!body.message || !String(body.message).trim()) && !(body.attachments || []).length) return json(res, 400, { error: "message_or_image_required" });
|
||||||
if (!attachmentsAreAllowed(body.attachments || [])) return json(res, 400, { error: "only_images_allowed" });
|
if (!attachmentsAreAllowed(body.attachments || [])) return json(res, 400, { error: "only_images_allowed" });
|
||||||
const conversation = findConversation(publicIdFrom(url));
|
const conversation = findConversation(publicIdFrom(url));
|
||||||
if (!conversation) return json(res, 404, { error: "conversation_not_found" });
|
if (!conversation) return json(res, 404, { error: "conversation_not_found" });
|
||||||
const messageId = insertMessage(conversation.id, "visitor", conversation.visitor_name || "Visitor", String(body.message).trim());
|
const messageId = insertMessage(conversation.id, "visitor", conversation.visitor_name || "Visitor", String(body.message || "").trim());
|
||||||
saveAttachments(conversation.id, messageId, body.attachments || []);
|
saveAttachments(conversation.id, messageId, body.attachments || []);
|
||||||
updateVisitorContext(conversation.id, body);
|
updateVisitorContext(conversation.id, body);
|
||||||
markConversationUnread(conversation.id);
|
markConversationUnread(conversation.id);
|
||||||
|
|||||||
Reference in New Issue
Block a user