import { resolveToolbarActive } from '../router.js'; import { state } from '../state.js'; const ITEMS = [ { pageId: 'messages-list', label: 'Личные сообщения', icon: '💬' }, { pageId: 'channels-list', label: 'Каналы', icon: '📢' }, { pageId: 'network-view', label: 'Связи', icon: '🕸' }, { pageId: 'notifications-view', label: 'Уведомления', icon: '🔔' }, { pageId: 'profile-view', label: 'Профиль', icon: '👤' }, ]; const CHANNEL_HOLD_MS = 260; const PROFILE_HOLD_MS = 320; const CHANNEL_MODES = Object.freeze([ { key: 'feed', label: 'Каналы' }, { key: 'dialogs', label: 'Чаты' }, { key: 'my', label: 'Мои' }, ]); 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'; const isMessages = item.pageId === 'messages-list'; const isNetwork = item.pageId === 'network-view'; btn.className = `toolbar-btn${item.pageId === active ? ' active' : ''}${isProfile ? ' toolbar-btn-profile' : ''}${isMessages ? ' toolbar-btn-messages' : ''}${isNetwork ? ' toolbar-btn-network' : ''}`; if (isProfile) { btn.innerHTML = ` ${item.icon} ${item.label} connected `; } else { btn.innerHTML = `${item.icon}${item.label}`; } 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 (item.pageId === 'channels-list') { installChannelsHoldSwitcher(btn, navigate); } else if (item.pageId === 'profile-view') { installProfileHoldMenu(btn, navigate); } else { btn.addEventListener('click', () => navigate(item.pageId)); } root.append(btn); }); return root; } function installProfileHoldMenu(button, navigate) { let holdTimer = 0; let pressed = false; let holdActive = false; let overlay = null; const clearTimer = () => { if (holdTimer) { window.clearTimeout(holdTimer); holdTimer = 0; } }; const closeOverlay = () => { if (overlay) overlay.remove(); overlay = null; holdActive = false; }; const openOverlay = () => { const rect = button.getBoundingClientRect(); overlay = document.createElement('div'); overlay.className = 'toolbar-channels-hold-overlay'; overlay.style.minWidth = '190px'; overlay.style.left = `${Math.round(rect.left + rect.width / 2)}px`; overlay.style.top = `${Math.round(rect.top - 12)}px`; overlay.innerHTML = ``; overlay.querySelector('[data-action="switch-profile"]')?.addEventListener('click', (event) => { event.preventDefault(); event.stopPropagation(); closeOverlay(); navigate('account-switcher-view'); }); document.body.append(overlay); holdActive = true; }; button.addEventListener('pointerdown', () => { pressed = true; holdActive = false; clearTimer(); holdTimer = window.setTimeout(() => { if (!pressed) return; openOverlay(); }, PROFILE_HOLD_MS); }); button.addEventListener('pointerup', () => { clearTimer(); const wasHold = holdActive; pressed = false; if (wasHold) { window.setTimeout(closeOverlay, 80); return; } navigate('profile-view'); }); button.addEventListener('pointercancel', () => { clearTimer(); pressed = false; closeOverlay(); }); button.addEventListener('contextmenu', (event) => event.preventDefault()); } function installChannelsHoldSwitcher(button, navigate) { let holdTimer = 0; let pressed = false; let holdActive = false; let overlay = null; let selectedMode = 'dialogs'; const clearTimer = () => { if (holdTimer) { window.clearTimeout(holdTimer); holdTimer = 0; } }; const closeOverlay = () => { if (overlay) overlay.remove(); overlay = null; holdActive = false; }; const setSelectedModeByX = (clientX) => { if (!overlay) return; const rect = overlay.getBoundingClientRect(); const part = rect.width / 3; const localX = Math.max(0, Math.min(rect.width - 1, clientX - rect.left)); const index = Math.max(0, Math.min(2, Math.floor(localX / Math.max(1, part)))); selectedMode = CHANNEL_MODES[index].key; const buttons = overlay.querySelectorAll('.toolbar-channels-hold-item'); buttons.forEach((el, idx) => { el.classList.toggle('is-active', idx === index); }); }; const openOverlay = () => { const rect = button.getBoundingClientRect(); overlay = document.createElement('div'); overlay.className = 'toolbar-channels-hold-overlay'; overlay.innerHTML = CHANNEL_MODES.map((mode) => ( `` )).join(''); overlay.style.left = `${Math.round(rect.left + rect.width / 2)}px`; overlay.style.top = `${Math.round(rect.top - 12)}px`; document.body.append(overlay); holdActive = true; }; button.addEventListener('pointerdown', (event) => { pressed = true; holdActive = false; selectedMode = 'dialogs'; clearTimer(); holdTimer = window.setTimeout(() => { if (!pressed) return; openOverlay(); setSelectedModeByX(event.clientX); }, CHANNEL_HOLD_MS); }); button.addEventListener('pointermove', (event) => { if (holdActive) setSelectedModeByX(event.clientX); }); button.addEventListener('pointerup', () => { clearTimer(); const wasHold = holdActive; const mode = selectedMode; pressed = false; closeOverlay(); if (wasHold) { navigate(`channels-list/${mode}`); return; } navigate('channels-list/dialogs'); }); button.addEventListener('pointercancel', () => { clearTimer(); pressed = false; closeOverlay(); }); button.addEventListener('contextmenu', (event) => { event.preventDefault(); }); }