feat(ui): старт с личных сообщений и бейдж непрочитанных

This commit is contained in:
AidarKC 2026-04-22 17:23:20 +03:00
parent 58bbf063ca
commit 97a2bee81a
3 changed files with 57 additions and 5 deletions

View File

@ -448,7 +448,7 @@ function renderPageFailureFallback(pageId, error) {
function renderApp() {
const route = getRoute();
const pageId = route.pageId || (state.session.isAuthorized ? 'profile-view' : 'start-view');
const pageId = route.pageId || (state.session.isAuthorized ? 'messages-list' : 'start-view');
if (!state.session.isAuthorized && !PRE_AUTH_PAGES.includes(pageId)) {
navigate('start-view');
@ -456,7 +456,7 @@ function renderApp() {
}
if (state.session.isAuthorized && PRE_AUTH_PAGES.includes(pageId)) {
navigate('profile-view');
navigate('messages-list');
return;
}
@ -629,6 +629,8 @@ async function init() {
? new TextDecoder().decode(parsed.payloadBytes || new Uint8Array(0))
: '';
let shouldRefreshToolbarUnread = false;
if (messageType === 1 || messageType === 2) {
const isIncomingForCurrent = messageType === 1;
const added = addSignedMessageToChat({
@ -651,6 +653,9 @@ async function init() {
details: { messageKey, baseKey: parsed.baseKey, messageType },
});
}
if (added && isIncomingForCurrent) {
shouldRefreshToolbarUnread = true;
}
if (added && isIncomingForCurrent && Notification.permission === 'granted' && !payload.backlog) {
try {
new Notification(`Сообщение от ${fromLogin}`, { body: text || '' });
@ -691,7 +696,7 @@ async function init() {
}
const pageId = getRoute().pageId || '';
if (pageId === 'chat-view' || pageId === 'messages-list') {
if (pageId === 'chat-view' || pageId === 'messages-list' || shouldRefreshToolbarUnread) {
renderApp();
}
});
@ -773,7 +778,7 @@ async function init() {
await ensureSessionRuntimeStarted();
if (!window.location.hash) {
navigate(state.session.isAuthorized ? 'profile-view' : 'start-view');
navigate(state.session.isAuthorized ? 'messages-list' : 'start-view');
} else {
renderApp();
}

View File

@ -1,4 +1,5 @@
import { resolveToolbarActive } from '../router.js';
import { state } from '../state.js';
const ITEMS = [
{ pageId: 'messages-list', label: 'Личные сообщения', icon: '💬' },
@ -8,15 +9,29 @@ const ITEMS = [
{ pageId: 'profile-view', label: 'Профиль', icon: '👤' },
];
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;
});
});
return total;
}
export function renderToolbar(currentPageId, navigate) {
const root = document.createElement('nav');
root.className = 'toolbar';
const active = resolveToolbarActive(currentPageId);
const unreadTotal = getTotalUnreadMessages();
ITEMS.forEach((item) => {
const btn = document.createElement('button');
const isProfile = item.pageId === 'profile-view';
btn.className = `toolbar-btn${item.pageId === active ? ' active' : ''}${isProfile ? ' toolbar-btn-profile' : ''}`;
const isMessages = item.pageId === 'messages-list';
btn.className = `toolbar-btn${item.pageId === active ? ' active' : ''}${isProfile ? ' toolbar-btn-profile' : ''}${isMessages ? ' toolbar-btn-messages' : ''}`;
if (isProfile) {
btn.innerHTML = `
<span>${item.icon}</span>
@ -31,6 +46,13 @@ export function renderToolbar(currentPageId, navigate) {
} else {
btn.innerHTML = `<span>${item.icon}</span><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);
}
btn.addEventListener('click', () => navigate(item.pageId));
root.append(btn);
});

View File

@ -621,6 +621,31 @@
position: relative;
}
.toolbar-btn-messages {
position: relative;
}
.toolbar-unread-badge {
position: absolute;
top: 2px;
right: 6px;
min-width: 16px;
height: 16px;
border-radius: 999px;
padding: 0 5px;
display: inline-flex;
align-items: center;
justify-content: center;
background: #f07f8a;
color: #fff2f4;
border: 1px solid rgba(255, 222, 227, 0.55);
box-shadow: 0 4px 10px rgba(152, 36, 52, 0.35);
font-size: 10px;
font-weight: 700;
line-height: 1;
pointer-events: none;
}
.toolbar-btn-profile .toolbar-label-wrap {
padding-bottom: 10px;
}