SHA256
Доработать UI и счётчики
This commit is contained in:
@@ -20,16 +20,68 @@ function iconHtml(item) {
|
||||
: `<span>${item.icon}</span>`;
|
||||
}
|
||||
|
||||
function getTotalUnreadMessages() {
|
||||
const chats = Object.values(state.chats || {});
|
||||
let total = 0;
|
||||
chats.forEach((messages) => {
|
||||
if (!Array.isArray(messages)) return;
|
||||
messages.forEach((msg) => {
|
||||
if (msg?.from === 'in' && msg?.unread) total += 1;
|
||||
});
|
||||
function normalizeCounters(payload = {}) {
|
||||
const notifications = payload?.notifications || {};
|
||||
const notificationTotal = Math.max(0, Number(payload?.notificationsUnreadCount ?? (
|
||||
Number(notifications.replies || 0) + Number(notifications.connections || 0) + Number(notifications.events || 0)
|
||||
)) || 0);
|
||||
return {
|
||||
dmUnreadCount: Math.max(0, Number(payload?.dmUnreadCount || 0) || 0),
|
||||
channelsUnreadCount: Math.max(0, Number(payload?.channelsUnreadCount || 0) || 0),
|
||||
notificationsUnreadCount: notificationTotal,
|
||||
notifications: {
|
||||
replies: Math.max(0, Number(notifications.replies ?? payload?.notificationRepliesUnreadCount ?? 0) || 0),
|
||||
connections: Math.max(0, Number(notifications.connections ?? payload?.notificationConnectionsUnreadCount ?? 0) || 0),
|
||||
events: Math.max(0, Number(notifications.events ?? payload?.notificationEventsUnreadCount ?? 0) || 0),
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
function setCounterState(payload = {}) {
|
||||
state.userCounters = normalizeCounters(payload);
|
||||
state.notificationUnreadTotal = state.userCounters.notificationsUnreadCount;
|
||||
}
|
||||
|
||||
function renderBadge(btn, count, ariaLabel, extraClass = '') {
|
||||
if (!btn) return;
|
||||
let badge = btn.querySelector('.toolbar-unread-badge');
|
||||
if (count <= 0) { badge?.remove(); return; }
|
||||
if (!badge) {
|
||||
badge = document.createElement('span');
|
||||
badge.className = `toolbar-unread-badge${extraClass ? ` ${extraClass}` : ''}`;
|
||||
btn.append(badge);
|
||||
}
|
||||
badge.textContent = count > 99 ? '99+' : String(count);
|
||||
badge.setAttribute('aria-label', `${ariaLabel}: ${count}`);
|
||||
}
|
||||
|
||||
function applyCountersToMountedToolbars() {
|
||||
const c = normalizeCounters(state.userCounters);
|
||||
document.querySelectorAll('.toolbar').forEach((toolbar) => {
|
||||
renderBadge(toolbar.querySelector('[data-toolbar-page="messages-list"]'), c.dmUnreadCount, 'Непрочитанных личных сообщений');
|
||||
renderBadge(toolbar.querySelector('[data-toolbar-page="channels-list"]'), c.channelsUnreadCount, 'Непрочитанных сообщений в каналах');
|
||||
renderBadge(toolbar.querySelector('[data-toolbar-page="notifications-view"]'), c.notificationsUnreadCount, 'Новых уведомлений', 'notification-toolbar-badge');
|
||||
});
|
||||
return total;
|
||||
}
|
||||
|
||||
let countersPushBound = false;
|
||||
function ensureCountersPushBound() {
|
||||
if (countersPushBound) return;
|
||||
countersPushBound = true;
|
||||
authService.onEvent('UserCountersChanged', (event) => {
|
||||
setCounterState(event?.payload || {});
|
||||
applyCountersToMountedToolbars();
|
||||
});
|
||||
}
|
||||
|
||||
async function refreshUserCounters() {
|
||||
if (!state.session.isAuthorized) return;
|
||||
try {
|
||||
setCounterState(await authService.getUserCounters());
|
||||
applyCountersToMountedToolbars();
|
||||
} catch {
|
||||
// Keep the last known counters; realtime push or the next refresh can recover.
|
||||
}
|
||||
}
|
||||
|
||||
function navigateWithGuestRules(pageId, navigate) {
|
||||
@@ -65,7 +117,8 @@ export function renderToolbar(currentPageId, navigate) {
|
||||
const root = document.createElement('nav');
|
||||
root.className = 'toolbar';
|
||||
const active = resolveToolbarActive(currentPageId);
|
||||
const unreadTotal = getTotalUnreadMessages();
|
||||
ensureCountersPushBound();
|
||||
const counters = normalizeCounters(state.userCounters);
|
||||
|
||||
ITEMS.forEach((item) => {
|
||||
const btn = document.createElement('button');
|
||||
@@ -92,21 +145,9 @@ export function renderToolbar(currentPageId, navigate) {
|
||||
} else {
|
||||
btn.innerHTML = `${iconHtml(item)}<span>${item.label}</span>`;
|
||||
}
|
||||
if (isMessages && unreadTotal > 0) {
|
||||
const badge = document.createElement('span');
|
||||
badge.className = 'toolbar-unread-badge';
|
||||
badge.textContent = unreadTotal > 99 ? '99+' : String(unreadTotal);
|
||||
badge.setAttribute('aria-label', `Непрочитанных сообщений: ${badge.textContent}`);
|
||||
btn.append(badge);
|
||||
}
|
||||
if (isNotifications && Number(state.notificationUnreadTotal || 0) > 0) {
|
||||
const badge = document.createElement('span');
|
||||
badge.className = 'toolbar-unread-badge notification-toolbar-badge';
|
||||
const n = Number(state.notificationUnreadTotal || 0);
|
||||
badge.textContent = n > 99 ? '99+' : String(n);
|
||||
badge.setAttribute('aria-label', `Новых уведомлений: ${n}`);
|
||||
btn.append(badge);
|
||||
}
|
||||
if (isMessages) renderBadge(btn, counters.dmUnreadCount, 'Непрочитанных личных сообщений');
|
||||
if (item.pageId === 'channels-list') renderBadge(btn, counters.channelsUnreadCount, 'Непрочитанных сообщений в каналах');
|
||||
if (isNotifications) renderBadge(btn, counters.notificationsUnreadCount, 'Новых уведомлений', 'notification-toolbar-badge');
|
||||
if (item.pageId === 'channels-list') {
|
||||
btn.addEventListener('click', () => navigate('channels-list'));
|
||||
} else {
|
||||
@@ -115,19 +156,7 @@ export function renderToolbar(currentPageId, navigate) {
|
||||
root.append(btn);
|
||||
});
|
||||
|
||||
if (state.session.isAuthorized && currentPageId !== 'notifications-view') {
|
||||
void authService.getNotifications(true).then((payload) => {
|
||||
const total = Number(payload?.repliesUnseenCount || 0) + Number(payload?.connectionsUnseenCount || 0) + Number(payload?.eventsUnseenCount || 0);
|
||||
state.notificationUnreadTotal = total;
|
||||
const btn = root.querySelector('[data-toolbar-page="notifications-view"]');
|
||||
if (!btn) return;
|
||||
let badge = btn.querySelector('.notification-toolbar-badge');
|
||||
if (total <= 0) { badge?.remove(); return; }
|
||||
if (!badge) { badge = document.createElement('span'); badge.className = 'toolbar-unread-badge notification-toolbar-badge'; btn.append(badge); }
|
||||
badge.textContent = total > 99 ? '99+' : String(total);
|
||||
badge.setAttribute('aria-label', `Новых уведомлений: ${total}`);
|
||||
}).catch(() => {});
|
||||
}
|
||||
if (state.session.isAuthorized) void refreshUserCounters();
|
||||
|
||||
return root;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user