import { profile } from '../mock-data.js'; import { state } from '../state.js'; import { PROFILE_GENDER_FEMALE, PROFILE_GENDER_MALE, loadProfileSnapshot, } from '../services/user-profile-params.js'; import { buildIdentityLines } from '../services/user-connections.js'; import { renderUserAvatar } from '../components/avatar-image.js'; import { makeProfileLinksRoute } from '../services/shine-routes.js'; export const pageMeta = { id: 'profile-view', title: 'Профиль' }; function toggleText(enabled) { return enabled ? 'Yes' : 'No'; } function genderLabel(value) { if (value === PROFILE_GENDER_MALE) return 'Мужской'; if (value === PROFILE_GENDER_FEMALE) return 'Женский'; return 'Не указан'; } function escapeHtml(text) { return String(text || '') .replaceAll('&', '&') .replaceAll('<', '<') .replaceAll('>', '>') .replaceAll('"', '"') .replaceAll("'", '''); } function openProfileInfoModal({ title, text }) { const root = document.getElementById('modal-root'); if (!root) return; root.innerHTML = ` `; const close = () => { root.innerHTML = ''; }; root.querySelector('#profile-info-close')?.addEventListener('click', close); root.querySelector('#profile-info-modal')?.addEventListener('click', (event) => { if (event.target?.id === 'profile-info-modal') close(); }); } function officialInfoText() { return 'Можно создавать несколько альтернативных или анонимных каналов. ' + 'Но для корректного учёта голосов на одного реального человека используется только один официальный канал.'; } function shineInfoText() { return 'Сияющие — это те, от кого идёт внутреннее сияние на тонком плане.\n\n' + 'Пять принципов сияющих:\n' + '1) сияющие не обманывают;\n' + '2) сияющие чувствуют, что человек — это не только физическое тело, а нечто большее;\n' + '3) сияющие развиваются и в духовной, и в материальной плоскости;\n' + '4) у сияющих есть близкие друзья, с которыми им по-настоящему хорошо;\n' + '5) сияющие заботятся о мире: о людях, гармонии и общем благе.'; } export function render({ navigate }) { const login = state.session.login || profile.login; const screen = document.createElement('section'); screen.className = 'stack profile-screen'; const topActions = document.createElement('div'); topActions.className = 'profile-top-actions'; topActions.innerHTML = ` `; topActions.querySelector('[data-top-action="edit"]')?.addEventListener('click', () => navigate('profile-edit-view')); topActions.querySelector('[data-top-action="settings"]')?.addEventListener('click', () => navigate('settings-view')); screen.append(topActions); const bottomActions = document.createElement('div'); bottomActions.className = 'profile-bottom-actions'; bottomActions.innerHTML = ` `; bottomActions.querySelector('[data-bottom-action="wallet"]')?.addEventListener('click', () => navigate('wallet-view')); bottomActions.querySelector('[data-bottom-action="links"]')?.addEventListener('click', () => navigate(makeProfileLinksRoute(login))); screen.append(bottomActions); const card = document.createElement('div'); card.className = 'card stack profile-main-card'; const topRow = document.createElement('div'); topRow.className = 'row'; topRow.innerHTML = `
`; const badgesRow = document.createElement('div'); badgesRow.className = 'row'; badgesRow.innerHTML = ` `; const listWrap = document.createElement('div'); listWrap.className = 'stack profile-param-list'; const officialBtn = badgesRow.querySelector('[data-toggle="official"]'); const shineBtn = badgesRow.querySelector('[data-toggle="shine"]'); const identityEl = topRow.querySelector('[data-profile-identity="true"]'); const avatarSlotEl = topRow.querySelector('[data-profile-avatar-slot="true"]'); let currentFields = []; let currentToggles = []; let currentGender = 'unknown'; let currentAvatar = { value: '', source: '', txId: '', sha256Hex: '', timeMs: 0 }; function syncIdentity() { if (!identityEl) return; const firstName = currentFields.find((field) => field.key === 'first_name')?.value || ''; const lastName = currentFields.find((field) => field.key === 'last_name')?.value || ''; const lines = buildIdentityLines({ login, firstName, lastName }); identityEl.innerHTML = lines.map((line, idx) => ( `
${escapeHtml(line)}
` )).join(''); } function updateAvatarUi() { if (!(avatarSlotEl instanceof HTMLElement)) return; const firstName = String(currentFields.find((field) => field.key === 'first_name')?.value || '').trim(); const lastName = String(currentFields.find((field) => field.key === 'last_name')?.value || '').trim(); avatarSlotEl.innerHTML = ''; avatarSlotEl.append(renderUserAvatar({ login, firstName, lastName, avatar: currentAvatar?.txId ? { ar: currentAvatar.txId, sha256Hex: String(currentAvatar?.sha256Hex || '').trim().toLowerCase() } : null, size: 'large', className: 'profile-avatar', })); } function updateToggleButton(button, prefix, enabled) { button.textContent = `${prefix}: ${toggleText(enabled)}`; button.classList.remove('is-no', 'is-yes-official', 'is-yes-shine'); if (!enabled) { button.classList.add('is-no'); return; } if (prefix === 'Официальный') button.classList.add('is-yes-official'); else button.classList.add('is-yes-shine'); } function updateTogglesUi() { const official = currentToggles.find((item) => item.key === 'official') || { enabled: false }; const shine = currentToggles.find((item) => item.key === 'shine') || { enabled: false }; updateToggleButton(officialBtn, 'Официальный', official.enabled); updateToggleButton(shineBtn, 'Сияющий', shine.enabled); } officialBtn?.classList.add('profile-badge-trigger'); shineBtn?.classList.add('profile-badge-trigger'); officialBtn?.addEventListener('click', () => { openProfileInfoModal({ title: 'Официальный канал', text: officialInfoText(), }); }); shineBtn?.addEventListener('click', () => { openProfileInfoModal({ title: 'Справка о сияющих', text: shineInfoText(), }); }); function renderFields(fields) { listWrap.innerHTML = ''; fields.forEach((field) => { const row = document.createElement('div'); row.className = 'card profile-param-item row'; const value = String(field.value || '').trim() || 'не заполнено'; row.innerHTML = `
${field.label}: ${escapeHtml(value)}
`; listWrap.append(row); if (field.key === 'last_name') { const genderRow = document.createElement('div'); genderRow.className = 'card profile-param-item row'; genderRow.innerHTML = `
Пол: ${escapeHtml(genderLabel(currentGender))}
`; listWrap.append(genderRow); } }); } async function refreshProfileSnapshot() { try { const snapshot = await loadProfileSnapshot(login); currentFields = Array.isArray(snapshot.fields) ? snapshot.fields : []; currentToggles = Array.isArray(snapshot.toggles) ? snapshot.toggles : []; currentGender = snapshot.gender || 'unknown'; currentAvatar = snapshot.avatar || { value: '', source: '', txId: '', sha256Hex: '', timeMs: 0 }; syncIdentity(); updateAvatarUi(); updateTogglesUi(); renderFields(currentFields); } catch (error) { // ignore status row in profile-view } } card.append(topRow, badgesRow, listWrap); screen.append(card); updateAvatarUi(); refreshProfileSnapshot(); return screen; }