Show visitor online state in chat list
This commit is contained in:
@@ -168,6 +168,9 @@ label { display: grid; gap: 6px; color: var(--muted); font-size: 13px; }
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.visitor-name-wrap .online-dot {
|
||||
flex: 0 0 auto;
|
||||
}
|
||||
.unread-dot {
|
||||
width: 9px;
|
||||
height: 9px;
|
||||
|
||||
+24
-1
@@ -3,6 +3,7 @@ const state = {
|
||||
snippet: "",
|
||||
conversations: [],
|
||||
visitors: [],
|
||||
visitorsLoaded: false,
|
||||
activeId: null,
|
||||
active: null,
|
||||
events: null,
|
||||
@@ -237,12 +238,16 @@ async function loadBootstrap() {
|
||||
async function loadConversations() {
|
||||
const data = await api("/api/admin/conversations");
|
||||
state.conversations = data.conversations;
|
||||
syncConversationVisitors();
|
||||
renderConversations();
|
||||
}
|
||||
|
||||
async function loadVisitors() {
|
||||
const data = await api("/api/admin/visitors");
|
||||
state.visitors = data.visitors;
|
||||
state.visitorsLoaded = true;
|
||||
syncConversationVisitors();
|
||||
renderConversations();
|
||||
renderVisitors();
|
||||
}
|
||||
|
||||
@@ -253,6 +258,7 @@ async function openConversation(id) {
|
||||
state.active = data;
|
||||
const index = state.conversations.findIndex((item) => item.id === id);
|
||||
if (index >= 0) state.conversations[index] = { ...state.conversations[index], ...data.conversation };
|
||||
syncConversationVisitors();
|
||||
renderConversations();
|
||||
renderActive();
|
||||
}
|
||||
@@ -271,6 +277,9 @@ function connectEvents() {
|
||||
}
|
||||
if (data.type === "visitors:update") {
|
||||
state.visitors = data.visitors;
|
||||
state.visitorsLoaded = true;
|
||||
syncConversationVisitors();
|
||||
renderConversations();
|
||||
renderVisitors();
|
||||
return;
|
||||
}
|
||||
@@ -311,7 +320,8 @@ function renderConversations() {
|
||||
<span class="row visitor-row">
|
||||
<span class="visitor-name-wrap">
|
||||
<span class="unread-dot" aria-label="${item.hasUnread ? "Neprecteno" : "Precteno"}"></span>
|
||||
<strong>${escapeHtml(item.visitor.name || item.visitor.email || "Navstevnik")}</strong>
|
||||
<strong>${escapeHtml(conversationVisitorName(item))}</strong>
|
||||
${item.visitorOnline ? `<span class="online-dot" aria-label="Online" title="Online"></span>` : ""}
|
||||
</span>
|
||||
${item.imageCount ? `<span class="photo-count">foto ${item.imageCount}</span>` : ""}
|
||||
</span>
|
||||
@@ -332,6 +342,19 @@ function renderConversations() {
|
||||
`).join("") : `<p class="empty-list">Zadny chat pro vybrany filtr.</p>`;
|
||||
}
|
||||
|
||||
function syncConversationVisitors() {
|
||||
if (!state.visitorsLoaded) return;
|
||||
const onlineIds = new Set(state.visitors.map((visitor) => visitor.id));
|
||||
state.conversations = state.conversations.map((item) => ({
|
||||
...item,
|
||||
visitorOnline: onlineIds.has(item.visitorToken)
|
||||
}));
|
||||
}
|
||||
|
||||
function conversationVisitorName(item) {
|
||||
return item.visitor?.name || item.visitor?.email || item.visitorLabel || "Navstevnik";
|
||||
}
|
||||
|
||||
function renderVisitors() {
|
||||
const visitors = state.hideBots ? state.visitors.filter((visitor) => !visitor.isBot) : state.visitors;
|
||||
const botCount = state.visitors.filter((visitor) => visitor.isBot).length;
|
||||
|
||||
@@ -304,6 +304,12 @@ function adminConversations(res) {
|
||||
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,
|
||||
EXISTS(
|
||||
SELECT 1 FROM visitor_sessions vs
|
||||
WHERE vs.site_id = c.site_id
|
||||
AND vs.visitor_token = c.visitor_token
|
||||
AND datetime(vs.last_seen_at) >= datetime('now', '-90 seconds')
|
||||
) AS visitor_online,
|
||||
(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
|
||||
@@ -689,6 +695,10 @@ function formatConversationSummary(row) {
|
||||
siteId: row.site_id,
|
||||
siteKey: row.site_key,
|
||||
status: row.status,
|
||||
visitorToken: row.visitor_token,
|
||||
visitorInitials: visitorInitials(row.visitor_token),
|
||||
visitorLabel: visitorLabel(row.visitor_token),
|
||||
visitorOnline: Boolean(row.visitor_online),
|
||||
visitor: {
|
||||
name: row.visitor_name,
|
||||
email: row.visitor_email,
|
||||
@@ -765,6 +775,12 @@ function findConversation(publicId) {
|
||||
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,
|
||||
EXISTS(
|
||||
SELECT 1 FROM visitor_sessions vs
|
||||
WHERE vs.site_id = c.site_id
|
||||
AND vs.visitor_token = c.visitor_token
|
||||
AND datetime(vs.last_seen_at) >= datetime('now', '-90 seconds')
|
||||
) AS visitor_online,
|
||||
(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
|
||||
|
||||
Reference in New Issue
Block a user