SHiNE-server/shine-UI/js/pages/profile-view.js

209 lines
8.7 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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';
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('<', '&lt;')
.replaceAll('>', '&gt;')
.replaceAll('"', '&quot;')
.replaceAll("'", '&#39;');
}
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 = `
<button class="ghost-btn profile-top-action-btn" type="button" data-top-action="edit">Изменить профиль</button>
<button class="ghost-btn profile-top-action-btn" type="button" data-top-action="wallet">Кошелёк</button>
<button class="ghost-btn profile-top-action-btn" type="button" data-top-action="settings">Настройки</button>
`;
topActions.querySelector('[data-top-action="edit"]')?.addEventListener('click', () => navigate('profile-edit-view'));
topActions.querySelector('[data-top-action="wallet"]')?.addEventListener('click', () => navigate('wallet-view'));
topActions.querySelector('[data-top-action="settings"]')?.addEventListener('click', () => navigate('settings-view'));
screen.append(topActions);
const card = document.createElement('div');
card.className = 'card stack profile-main-card';
const topRow = document.createElement('div');
topRow.className = 'row';
topRow.innerHTML = `
<div class="row" style="gap:12px; align-items:center;">
<div data-profile-avatar-slot="true"></div>
<div class="profile-identity-lines" data-profile-identity="true">
<div class="profile-identity-line profile-identity-login">${String(login || '').trim() || 'unknown'}</div>
</div>
</div>
`;
const statusRow = document.createElement('div');
statusRow.className = 'row profile-status-row';
statusRow.innerHTML = `
<div class="status-line" data-profile-status-line="true">Загрузка параметров...</div>
<button class="ghost-btn profile-refresh-btn" type="button" data-reload="true">Обновить</button>
`;
const badgesRow = document.createElement('div');
badgesRow.className = 'row';
badgesRow.innerHTML = `
<button class="badge profile-toggle-btn is-no" type="button" data-toggle="official">Официальный: No</button>
<button class="badge profile-toggle-btn is-no" type="button" data-toggle="shine">Сияющий: No</button>
`;
const listWrap = document.createElement('div');
listWrap.className = 'stack profile-param-list';
const reloadBtn = statusRow.querySelector('[data-reload="true"]');
const statusLineEl = statusRow.querySelector('[data-profile-status-line="true"]');
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: '', 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) => (
`<div class="profile-identity-line${idx === lines.length - 1 ? ' profile-identity-login' : ''}">${escapeHtml(line)}</div>`
)).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 } : 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);
}
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 = `<div class="profile-param-value"><b>${field.label}</b>: ${escapeHtml(value)}</div>`;
listWrap.append(row);
if (field.key === 'last_name') {
const genderRow = document.createElement('div');
genderRow.className = 'card profile-param-item row';
genderRow.innerHTML = `<div class="profile-param-value"><b>Пол</b>: ${escapeHtml(genderLabel(currentGender))}</div>`;
listWrap.append(genderRow);
}
});
}
async function refreshProfileSnapshot() {
try {
if (statusLineEl instanceof HTMLElement) {
statusLineEl.className = 'status-line';
statusLineEl.textContent = 'Загрузка параметров...';
}
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: '', timeMs: 0 };
syncIdentity();
updateAvatarUi();
updateTogglesUi();
renderFields(currentFields);
if (statusLineEl instanceof HTMLElement) {
statusLineEl.className = 'status-line is-available';
statusLineEl.textContent = 'Профиль обновлён.';
}
} catch (error) {
if (statusLineEl instanceof HTMLElement) {
statusLineEl.className = 'status-line is-unavailable';
statusLineEl.textContent = `Ошибка загрузки профиля: ${error?.message || 'unknown'}`;
}
}
}
const showToggleInfo = (toggleKey) => {
const item = currentToggles.find((entry) => entry.key === toggleKey);
const isEnabled = Boolean(item?.enabled);
if (toggleKey === 'official') {
if (statusLineEl instanceof HTMLElement) statusLineEl.className = 'status-line is-available';
if (statusLineEl instanceof HTMLElement) statusLineEl.textContent = isEnabled
? 'Аккаунт является официальным.'
: 'Аккаунт не является официальным.';
return;
}
if (statusLineEl instanceof HTMLElement) statusLineEl.className = 'status-line is-available';
if (statusLineEl instanceof HTMLElement) statusLineEl.textContent = isEnabled
? 'Аккаунт является сияющим.'
: 'Аккаунт не является сияющим.';
};
reloadBtn?.addEventListener('click', refreshProfileSnapshot);
officialBtn?.addEventListener('click', () => showToggleInfo('official'));
shineBtn?.addEventListener('click', () => showToggleInfo('shine'));
card.append(topRow, badgesRow, statusRow, listWrap);
screen.append(card);
updateAvatarUi();
refreshProfileSnapshot();
return screen;
}