Add unread chat indicator
This commit is contained in:
@@ -162,6 +162,16 @@ function initDb() {
|
||||
CREATE INDEX IF NOT EXISTS idx_conversations_site_status ON conversations(site_id, status, last_message_at);
|
||||
CREATE INDEX IF NOT EXISTS idx_messages_conversation ON messages(conversation_id, created_at);
|
||||
`);
|
||||
if (ensureColumn("conversations", "admin_seen_at", "TEXT")) {
|
||||
db.prepare("UPDATE conversations SET admin_seen_at = last_message_at WHERE admin_seen_at IS NULL").run();
|
||||
}
|
||||
}
|
||||
|
||||
function ensureColumn(table, column, definition) {
|
||||
const exists = db.prepare(`PRAGMA table_info(${table})`).all().some((item) => item.name === column);
|
||||
if (exists) return false;
|
||||
db.exec(`ALTER TABLE ${table} ADD COLUMN ${column} ${definition}`);
|
||||
return true;
|
||||
}
|
||||
|
||||
function seedDefaults() {
|
||||
@@ -261,6 +271,12 @@ function adminConversations(res) {
|
||||
(SELECT body FROM messages WHERE conversation_id = c.id ORDER BY id DESC LIMIT 1) AS last_body,
|
||||
(SELECT COUNT(*) FROM messages WHERE conversation_id = c.id) AS message_count,
|
||||
(SELECT COUNT(*) FROM attachments WHERE conversation_id = c.id AND mime_type LIKE 'image/%') AS image_count,
|
||||
EXISTS(
|
||||
SELECT 1 FROM messages unread
|
||||
WHERE unread.conversation_id = c.id
|
||||
AND unread.sender_type = 'visitor'
|
||||
AND datetime(unread.created_at) > datetime(COALESCE(c.admin_seen_at, '1970-01-01 00:00:00'))
|
||||
) AS has_unread,
|
||||
(SELECT COUNT(*) FROM conversations vc WHERE vc.site_id = c.site_id AND vc.visitor_token = c.visitor_token) AS visitor_conversation_count
|
||||
FROM conversations c
|
||||
JOIN sites s ON s.id = c.site_id
|
||||
@@ -273,7 +289,8 @@ function adminConversations(res) {
|
||||
function adminConversation(res, url) {
|
||||
const conversation = findConversation(publicIdFrom(url));
|
||||
if (!conversation) return json(res, 404, { error: "conversation_not_found" });
|
||||
return json(res, 200, conversationDetails(conversation));
|
||||
markConversationSeen(conversation.id);
|
||||
return json(res, 200, conversationDetails(findConversation(conversation.public_id)));
|
||||
}
|
||||
|
||||
async function updateSite(req, res) {
|
||||
@@ -377,7 +394,7 @@ async function createConversation(req, res) {
|
||||
});
|
||||
const insert = insertConversation();
|
||||
|
||||
const conversation = db.prepare("SELECT * FROM conversations WHERE id = ?").get(insert);
|
||||
const conversation = findConversation(publicId);
|
||||
publish("admin", { type: "conversation:new", conversation: formatConversationSummary(conversation) });
|
||||
publishConversation(conversation, { type: "message:new", payload: conversationDetails(conversation) });
|
||||
return json(res, 201, conversationDetails(conversation));
|
||||
@@ -392,6 +409,7 @@ async function createVisitorMessage(req, res, url) {
|
||||
const messageId = insertMessage(conversation.id, "visitor", conversation.visitor_name || "Visitor", String(body.message).trim());
|
||||
saveAttachments(conversation.id, messageId, body.attachments || []);
|
||||
updateVisitorContext(conversation.id, body);
|
||||
markConversationUnread(conversation.id);
|
||||
bumpConversation(conversation.id, conversation.status === "resolved" ? "open" : conversation.status);
|
||||
const updated = findConversation(conversation.public_id);
|
||||
publishConversation(updated, { type: "message:new", payload: conversationDetails(updated) });
|
||||
@@ -407,6 +425,7 @@ async function createAdminMessage(req, res, url) {
|
||||
const messageId = insertMessage(conversation.id, "admin", req.adminUser.username, String(body.message || "").trim());
|
||||
saveAttachments(conversation.id, messageId, body.attachments || []);
|
||||
bumpConversation(conversation.id, conversation.status === "new" ? "open" : conversation.status);
|
||||
markConversationSeen(conversation.id);
|
||||
const updated = findConversation(conversation.public_id);
|
||||
publishConversation(updated, { type: "message:new", payload: conversationDetails(updated) });
|
||||
return json(res, 201, conversationDetails(updated));
|
||||
@@ -575,6 +594,7 @@ function formatConversationSummary(row) {
|
||||
lastBody: row.last_body,
|
||||
messageCount: row.message_count,
|
||||
imageCount: row.image_count || 0,
|
||||
hasUnread: Boolean(row.has_unread),
|
||||
pageCount: pageCount(row.browsing_history_json),
|
||||
sessionSeconds: sessionSeconds(row.browsing_history_json, row.created_at),
|
||||
visitorConversationCount: row.visitor_conversation_count || 1,
|
||||
@@ -622,6 +642,15 @@ function getDefaultSite() {
|
||||
function findConversation(publicId) {
|
||||
return db.prepare(`
|
||||
SELECT c.*, s.site_key,
|
||||
(SELECT body FROM messages WHERE conversation_id = c.id ORDER BY id DESC LIMIT 1) AS last_body,
|
||||
(SELECT COUNT(*) FROM messages WHERE conversation_id = c.id) AS message_count,
|
||||
(SELECT COUNT(*) FROM attachments WHERE conversation_id = c.id AND mime_type LIKE 'image/%') AS image_count,
|
||||
EXISTS(
|
||||
SELECT 1 FROM messages unread
|
||||
WHERE unread.conversation_id = c.id
|
||||
AND unread.sender_type = 'visitor'
|
||||
AND datetime(unread.created_at) > datetime(COALESCE(c.admin_seen_at, '1970-01-01 00:00:00'))
|
||||
) AS has_unread,
|
||||
(SELECT COUNT(*) FROM conversations vc WHERE vc.site_id = c.site_id AND vc.visitor_token = c.visitor_token) AS visitor_conversation_count
|
||||
FROM conversations c
|
||||
JOIN sites s ON s.id = c.site_id
|
||||
@@ -629,6 +658,14 @@ function findConversation(publicId) {
|
||||
`).get(publicId);
|
||||
}
|
||||
|
||||
function markConversationSeen(conversationId) {
|
||||
db.prepare("UPDATE conversations SET admin_seen_at = CURRENT_TIMESTAMP WHERE id = ?").run(conversationId);
|
||||
}
|
||||
|
||||
function markConversationUnread(conversationId) {
|
||||
db.prepare("UPDATE conversations SET admin_seen_at = NULL WHERE id = ?").run(conversationId);
|
||||
}
|
||||
|
||||
function publicIdFrom(url) {
|
||||
const parts = url.pathname.split("/").filter(Boolean);
|
||||
return parts[parts.indexOf("conversations") + 1];
|
||||
|
||||
Reference in New Issue
Block a user