f7support: точка непрочитанного по изменению карточки (fingerprint)
Если API не отдаёт has_unread или не обновляет его в списке, показываем зелёную точку при смене activity/updated_at или preview относительно последнего зафиксированного состояния. Учёт альтернативных полей hasUnread/unread/unread_count. После открытия чата — fetchTickets для актуального отпечатка.
This commit is contained in:
+57
-2
@@ -71,6 +71,8 @@
|
||||
wsReconnectTimer: null,
|
||||
ticketsPollTimer: null,
|
||||
pendingFile: null,
|
||||
/** @type {Record<string, string>} последний известный «отпечаток» карточки по номеру тикета (для точки без has_unread с API) */
|
||||
ticketSeenFingerprint: {},
|
||||
};
|
||||
|
||||
function clientIdentityHeaders() {
|
||||
@@ -711,9 +713,55 @@
|
||||
window.setTimeout(run, 320);
|
||||
}
|
||||
|
||||
function ticketBoardFingerprint(t) {
|
||||
const act = String(t.activity_at || t.updated_at || t.last_activity_at || t.created_at || "");
|
||||
const prev = t.preview_text != null ? String(t.preview_text) : "";
|
||||
return `${act}\u0001${prev}`;
|
||||
}
|
||||
|
||||
/** Для открытого чата учитываем последнее сообщение, чтобы отпечаток не отставал от превью в списке. */
|
||||
function effectiveBoardFingerprint(t) {
|
||||
const base = ticketBoardFingerprint(t);
|
||||
const tn = String(t.ticket_number);
|
||||
if (state.chatModalOpen && state.currentTicket != null && String(state.currentTicket) === tn) {
|
||||
const msgs = state.messages;
|
||||
if (msgs && msgs.length) {
|
||||
const last = msgs[msgs.length - 1];
|
||||
return `${base}\u0002${last.id ?? ""}\u0003${last.created_at ?? ""}`;
|
||||
}
|
||||
}
|
||||
return base;
|
||||
}
|
||||
|
||||
function updateBoardUnreadFingerprints() {
|
||||
for (const t of state.tickets) {
|
||||
const tn = String(t.ticket_number);
|
||||
const fp = effectiveBoardFingerprint(t);
|
||||
const seenKey = tn;
|
||||
if (!Object.prototype.hasOwnProperty.call(state.ticketSeenFingerprint, seenKey)) {
|
||||
state.ticketSeenFingerprint[seenKey] = fp;
|
||||
t._f7_board_unread = false;
|
||||
continue;
|
||||
}
|
||||
if (state.chatModalOpen && state.currentTicket != null && String(state.currentTicket) === tn) {
|
||||
state.ticketSeenFingerprint[seenKey] = fp;
|
||||
t._f7_board_unread = false;
|
||||
continue;
|
||||
}
|
||||
t._f7_board_unread = fp !== state.ticketSeenFingerprint[seenKey];
|
||||
}
|
||||
}
|
||||
|
||||
function ticketHasUnread(ticket) {
|
||||
const v = ticket?.has_unread;
|
||||
return v === true || v === 1 || v === "1" || v === "true";
|
||||
if (ticket && ticket._f7_board_unread) return true;
|
||||
const raw =
|
||||
ticket?.has_unread ??
|
||||
ticket?.hasUnread ??
|
||||
ticket?.unread ??
|
||||
ticket?.is_unread ??
|
||||
ticket?.unread_count;
|
||||
if (typeof raw === "number" && raw > 0) return true;
|
||||
return raw === true || raw === 1 || raw === "1" || raw === "true" || raw === "yes";
|
||||
}
|
||||
|
||||
function scheduleTicketsBoardPolling() {
|
||||
@@ -792,6 +840,11 @@
|
||||
}
|
||||
|
||||
function closeChatModal() {
|
||||
const closingTn = state.currentTicket;
|
||||
if (closingTn != null) {
|
||||
const row = state.tickets.find((x) => String(x.ticket_number) === String(closingTn));
|
||||
if (row) state.ticketSeenFingerprint[String(closingTn)] = ticketBoardFingerprint(row);
|
||||
}
|
||||
disconnectTicketSocket();
|
||||
revokeAttachmentUrls();
|
||||
state.chatModalOpen = false;
|
||||
@@ -816,6 +869,7 @@
|
||||
});
|
||||
if (!response.ok) throw new Error("Не удалось загрузить список обращений");
|
||||
state.tickets = await response.json();
|
||||
updateBoardUnreadFingerprints();
|
||||
renderTickets();
|
||||
}
|
||||
|
||||
@@ -886,6 +940,7 @@
|
||||
openChatModal(ticket);
|
||||
try {
|
||||
await fetchMessages();
|
||||
await fetchTickets();
|
||||
const row = state.tickets.find((x) => x.ticket_number === ticketNumber);
|
||||
if (row) row.has_unread = false;
|
||||
renderTickets();
|
||||
|
||||
Reference in New Issue
Block a user