import { profile } from '../mock-data.js'; import { state } from '../state.js'; import { renderUserAvatar } from '../components/avatar-image.js'; import { createOverflowDots } from '../components/overflow-dots.js'; import { createTopBar } from '../components/topbar.js'; import { loadUserProfileCard } from '../services/user-connections.js'; export const pageMeta = { id: 'profile-view', title: 'Профиль' }; function escapeHtml(text) { return String(text || '') .replaceAll('&', '&') .replaceAll('<', '<') .replaceAll('>', '>') .replaceAll('"', '"') .replaceAll("'", '''); } function metricHtml({ kind, label, value, glow = false, positionClass = '' }) { const numericValue = Number(value || 0); return ` `; } function spiritualPathDetailHtml(card) { const value = String(card?.spiritualPath || '').trim(); return `
${escapeHtml(value || 'Не заполнено')}
`; } function contactsDetailHtml(card) { const rows = [ ['Ссылки', card?.web], ['Телефон', card?.phone], ['Адрес', card?.address], ].filter(([, value]) => String(value || '').trim()); if (!rows.length) { return '
Не заполнено
'; } return rows.map(([label, value]) => `
${escapeHtml(label)} ${escapeHtml(value)}
`).join(''); } export function render({ navigate, chrome }) { const login = String(state.session.login || profile.login || '').trim(); const screen = document.createElement('section'); screen.className = 'stack user-profile-screen'; const topbar = createTopBar({ title: login || 'Профиль', className: 'topbar--profile user-profile-header', actions: [ { iconNode: createOverflowDots(), title: 'Меню профиля', ariaLabel: 'Меню профиля', className: 'profile-head-menu-btn', menu: { className: 'profile-head-menu', minWidth: 250, items: [ { label: 'Редактировать профиль', iconSrc: '/assets/profile-icon-profile.svg', action: () => navigate('profile-edit-view') }, { label: 'Кошелёк', iconSrc: '/assets/profile-icon-wallet.svg', action: () => navigate('wallet-view') }, { label: 'Настройки', iconSrc: '/assets/profile-icon-settings.svg', action: () => navigate('settings-view') }, { label: 'Подтверждённые аккаунты', action: () => navigate(`SHiNE/${encodeURIComponent(login)}/list/primary_given`) }, { label: 'Подтверждённые сияющие', action: () => navigate(`SHiNE/${encodeURIComponent(login)}/list/shine_given`) }, { label: 'Сменить профиль', action: () => navigate('profiles-view') }, ], }, }, ], }); chrome?.setTopbar(topbar); const status = document.createElement('div'); status.className = 'status-line user-profile-status'; status.textContent = 'Загрузка профиля...'; const body = document.createElement('div'); body.className = 'user-profile-body'; screen.append(status, body); let card = null; function renderProfile() { if (!card) return; const stats = card.stats || {}; const official = card.accountRole === 'primary'; const shining = card.shineStatus === 'shining'; const fullName = [card.firstName, card.lastName] .map((value) => String(value || '').trim()) .filter(Boolean) .join(' '); const about = String(card.about || '').trim(); const title = topbar.querySelector('.topbar__title'); if (title) title.textContent = card.login || login || 'Профиль'; body.innerHTML = ` ${fullName ? `
${escapeHtml(fullName)}
` : ''}
${metricHtml({ kind: 'primary_received', label: 'Официальный', value: stats.primaryReceivedCount, glow: official, positionClass: 'is-top-left' })} ${metricHtml({ kind: 'shine_received', label: 'Сияющий', value: stats.shineReceivedCount, glow: shining, positionClass: 'is-top-right' })}
${metricHtml({ kind: 'friends', label: 'Друзья', value: stats.friendsCount, positionClass: 'is-bottom-left' })} ${metricHtml({ kind: 'close_friends', label: 'Близкие друзья', value: stats.closeFriendsCount, positionClass: 'is-bottom-right' })}
${about ? `
${escapeHtml(about)}
` : ''}
${metricHtml({ kind: 'channels_following', label: 'Подписки', value: stats.followingChannelsCount, positionClass: 'is-channel-metric' })} ${metricHtml({ kind: 'channels_owned', label: 'Каналы', value: stats.ownedPublicChannelsCount, positionClass: 'is-channel-metric' })}
`; const avatarSlot = body.querySelector('.user-profile-avatar-slot'); avatarSlot?.append(renderUserAvatar({ login: card.login, firstName: card.firstName, lastName: card.lastName, avatar: card.avatar, size: 'xl', className: 'user-profile-hero-avatar', glow: shining, })); status.textContent = ''; } body.addEventListener('click', (event) => { if (!card) return; const listButton = event.target.closest('[data-profile-list]'); if (listButton) { const kind = listButton.dataset.profileList; if (kind) navigate(`SHiNE/${encodeURIComponent(card.login)}/list/${encodeURIComponent(kind)}`); return; } const actionButton = event.target.closest('[data-self-profile-action]'); if (actionButton) { const action = actionButton.dataset.selfProfileAction; if (action === 'edit') navigate('profile-edit-view'); if (action === 'wallet') navigate('wallet-view'); if (action === 'settings') navigate('settings-view'); return; } const detailButton = event.target.closest('[data-profile-detail]'); if (!detailButton) return; const detailKind = detailButton.dataset.profileDetail; const detailPanel = body.querySelector('#profile-view-detail-panel'); if (!detailPanel) return; body.querySelectorAll('[data-profile-detail]').forEach((button) => { const active = button === detailButton; button.classList.toggle('is-active', active); button.setAttribute('aria-pressed', active ? 'true' : 'false'); }); if (detailKind === 'spiritual-path') { detailPanel.innerHTML = spiritualPathDetailHtml(card); } else if (detailKind === 'contacts') { detailPanel.innerHTML = contactsDetailHtml(card); } else { return; } detailPanel.hidden = false; detailPanel.dataset.activeDetail = detailKind; }); async function refresh() { if (state.session.isLocalDemo) { status.textContent = 'Локальный тестовый режим.'; return; } card = await loadUserProfileCard(login); renderProfile(); } refresh().catch((error) => { status.className = 'status-line user-profile-status is-unavailable'; status.textContent = `Ошибка: ${error?.message || 'unknown'}`; }); return screen; }