From 8eb2ef438c42c2e2ef91da48b1254a4f7ab6da9714e992714b26febe0e291eac Mon Sep 17 00:00:00 2001 From: peradas Date: Wed, 2 Sep 2026 14:53:11 +0300 Subject: [PATCH] =?UTF-8?q?=D0=92=D0=BE=D1=81=D1=81=D1=82=D0=B0=D0=BD?= =?UTF-8?q?=D0=BE=D0=B2=D0=B8=D1=82=D1=8C=20=D0=BF=D1=80=D0=BE=D1=84=D0=B8?= =?UTF-8?q?=D0=BB=D1=8C=20=D0=BF=D0=BE=D0=BB=D1=8C=D0=B7=D0=BE=D0=B2=D0=B0?= =?UTF-8?q?=D1=82=D0=B5=D0=BB=D1=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- shine-UI/js/pages/user-profile-view.js | 159 +++++- shine-UI/styles/components.css | 690 +++++++++++++++++++++++++ 2 files changed, 827 insertions(+), 22 deletions(-) diff --git a/shine-UI/js/pages/user-profile-view.js b/shine-UI/js/pages/user-profile-view.js index 837a329a..c3bf2b2c 100644 --- a/shine-UI/js/pages/user-profile-view.js +++ b/shine-UI/js/pages/user-profile-view.js @@ -1,8 +1,8 @@ import { renderHeader } from '../components/header.js'; import { SHINE_CONNECTIONS_LOGO_SRC } from '../components/shine-logo.js'; import { renderUserAvatar } from '../components/avatar-image.js'; -import { state } from '../state.js'; -import { loadUserProfileCard } from '../services/user-connections.js'; +import { authService, state } from '../state.js'; +import { loadRelationsForPair, loadUserProfileCard } from '../services/user-connections.js'; import { makeProfileLinksRoute } from '../services/shine-routes.js'; import { navigateBack } from '../router.js'; @@ -17,6 +17,13 @@ function escapeHtml(text) { .replaceAll("'", '''); } +function effectiveSocial(flags = {}) { + if (flags.outCloseFriend) return 'close_friend'; + if (flags.outFriend) return 'friend'; + if (flags.outContact) return 'contact'; + return 'none'; +} + function metricHtml({ kind, label, value, glow = false, positionClass = '' }) { const numericValue = Number(value || 0); return ` @@ -51,8 +58,8 @@ function openProfileSheet(title, html) { }); } -function spiritualPathSheetHtml(card) { - return `
${escapeHtml(card?.spiritualPath || 'Не заполнено')}
`; +function aboutSheetHtml(card) { + return `
${escapeHtml(card?.about || 'Не заполнено')}
`; } function contactsSheetHtml(card) { @@ -79,6 +86,20 @@ function addIconHtml() { `; } +function relationMenuHtml(flags = {}) { + const current = effectiveSocial(flags); + const rows = [ + ['friend', 'Друг'], + ['close_friend', 'Близкий друг'], + ['contact', 'Контакт'], + ]; + return rows.map(([kind, label]) => ` + `).join(''); +} + export function render({ navigate, route, chrome }) { const requestedLogin = String(route?.params?.login || '').trim(); const selfLogin = String(state.session.login || '').trim(); @@ -101,6 +122,66 @@ export function render({ navigate, route, chrome }) { screen.append(status, body); let card = null; + let relationFlags = null; + let relationLoadPromise = null; + let addMenu = null; + let addActionButton = null; + + function updateRelationUi() { + if (!addMenu) return; + addMenu.innerHTML = relationMenuHtml(relationFlags || {}); + const social = effectiveSocial(relationFlags || {}); + addActionButton?.classList.toggle('is-active', social !== 'none'); + addActionButton?.setAttribute('aria-label', social === 'none' ? 'Добавить' : 'Изменить связь'); + } + + async function ensureRelationFlags() { + if (relationFlags) return relationFlags; + if (relationLoadPromise) return relationLoadPromise; + if (!selfLogin || !card?.login) return {}; + relationLoadPromise = loadRelationsForPair({ currentLogin: selfLogin, targetLogin: card.login }) + .then((flags) => { + relationFlags = flags || {}; + updateRelationUi(); + return relationFlags; + }) + .finally(() => { + relationLoadPromise = null; + }); + return relationLoadPromise; + } + + async function setRelationKind(kind, enabled) { + await authService.setUserRelation({ + login: selfLogin, + toLogin: card.login, + kind, + enabled, + storagePwd: state.session.storagePwdInMemory, + }); + } + + async function changeSocial(next) { + const flags = await ensureRelationFlags(); + const current = effectiveSocial(flags); + if (current === next) return; + + if (next === 'contact') { + if (flags.outCloseFriend) await setRelationKind('close_friend', false); + if (flags.outFriend) await setRelationKind('friend', false); + if (!flags.outContact) await setRelationKind('contact', true); + } + if (next === 'friend') { + if (flags.outCloseFriend) await setRelationKind('close_friend', false); + if (!flags.outFriend) await setRelationKind('friend', true); + } + if (next === 'close_friend' && !flags.outCloseFriend) { + await setRelationKind('close_friend', true); + } + + relationFlags = await loadRelationsForPair({ currentLogin: selfLogin, targetLogin: card.login }); + updateRelationUi(); + } function renderProfile() { if (!card) return; @@ -108,18 +189,11 @@ export function render({ navigate, route, chrome }) { 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 = header.querySelector('.page-title'); if (title) title.textContent = card.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' })} @@ -128,17 +202,16 @@ export function render({ navigate, route, chrome }) { ${metricHtml({ kind: 'close_friends', label: 'Близкие друзья', value: stats.closeFriendsCount, positionClass: 'is-bottom-right' })}
- ${about ? `
${escapeHtml(about)}
` : ''} - -
+ ${!isSelf ? `