Аватары: общий компонент, кэш txId и avatar.ar в графе связей

This commit is contained in:
AidarKC
2026-04-26 02:27:41 +03:00
parent 667c5310bf
commit 4c1aeeeac8
13 changed files with 392 additions and 103 deletions
+15 -56
View File
@@ -11,8 +11,8 @@ import {
saveProfileToggle,
} from '../services/user-profile-params.js';
import { buildIdentityLines, loadUserProfileCard } from '../services/user-connections.js';
import { buildArweaveDataUrl } from '../services/arweave-file-service.js';
import { openAvatarWizard } from '../components/avatar-wizard.js';
import { renderUserAvatar } from '../components/avatar-image.js';
export const pageMeta = { id: 'profile-view', title: 'Профиль' };
@@ -105,10 +105,7 @@ export function render({ navigate }) {
topRow.innerHTML = `
<div class="row" style="gap:12px; align-items:center;">
<div class="profile-avatar-block">
<div class="avatar large profile-avatar" data-profile-avatar="true">
<span data-avatar-fallback="true">${profile.avatarInitials}</span>
<img src="" alt="Аватар" data-avatar-image="true" hidden />
</div>
<div data-profile-avatar-slot="true"></div>
<button class="ghost-btn profile-avatar-change-btn" type="button" data-change-avatar="true">Сменить аватар</button>
</div>
<div class="profile-identity-lines" data-profile-identity="true">
@@ -154,8 +151,7 @@ export function render({ navigate }) {
let currentGender = PROFILE_GENDER_UNKNOWN;
let currentAvatar = { value: '', source: '', txId: '', timeMs: 0 };
const identityEl = topRow.querySelector('[data-profile-identity="true"]');
const avatarImageEl = topRow.querySelector('[data-avatar-image="true"]');
const avatarFallbackEl = topRow.querySelector('[data-avatar-fallback="true"]');
const avatarSlotEl = topRow.querySelector('[data-profile-avatar-slot="true"]');
function openGenderPickerModal(initialGender) {
const root = document.getElementById('modal-root');
@@ -219,56 +215,19 @@ export function render({ navigate }) {
)).join('');
}
function buildAvatarFallbackText() {
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();
const fromNames = `${firstName.charAt(0)}${lastName.charAt(0)}`.toUpperCase();
if (fromNames.trim()) return fromNames;
const loginFirst = String(login || '').trim().charAt(0).toUpperCase();
if (loginFirst) return loginFirst;
return String(profile.avatarInitials || '??').trim().slice(0, 2).toUpperCase();
}
function showAvatarFallback() {
if (avatarImageEl instanceof HTMLImageElement) {
avatarImageEl.hidden = true;
avatarImageEl.removeAttribute('src');
}
if (avatarFallbackEl instanceof HTMLElement) {
avatarFallbackEl.hidden = false;
avatarFallbackEl.textContent = buildAvatarFallbackText();
}
}
function updateAvatarUi() {
if (!(avatarImageEl instanceof HTMLImageElement)) return;
const txId = String(currentAvatar?.txId || '').trim();
if (!txId) {
showAvatarFallback();
return;
}
let avatarUrl = '';
try {
avatarUrl = buildArweaveDataUrl({
gateway: state.entrySettings.arweaveServer,
txId,
});
} catch {
showAvatarFallback();
return;
}
avatarImageEl.onload = () => {
if (avatarFallbackEl instanceof HTMLElement) avatarFallbackEl.hidden = true;
avatarImageEl.hidden = false;
};
avatarImageEl.onerror = () => {
showAvatarFallback();
};
avatarImageEl.src = avatarUrl;
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) {
@@ -846,7 +805,7 @@ export function render({ navigate }) {
card.append(topRow, badgesRow, status, listWrap, relativesCard);
screen.append(card);
showAvatarFallback();
updateAvatarUi();
refreshProfileSnapshot();
return screen;