Files

160 lines
4.7 KiB
JavaScript

import { state } from '../state.js';
import { buildArweaveDataUrl, validateArweaveTxId, validateSha256Hex } from '../services/arweave-file-service.js';
import { getCachedAvatarObjectUrl } from '../services/arweave-avatar-cache-service.js';
function normalizeLogin(value) {
return String(value || '').trim();
}
function pickSizeClass(size) {
const raw = String(size || '').trim().toLowerCase();
if (raw === 'node') return 'node-dot';
if (raw === 'xs' || raw === 'xsmall' || raw === 'tiny') return 'avatar-xs';
if (raw === 'sm' || raw === 'small') return 'avatar-sm';
if (raw === 'md' || raw === 'medium' || raw === '') return 'avatar-md';
if (raw === 'lg' || raw === 'big') return 'avatar-lg';
if (raw === 'xl' || raw === 'xlarge' || raw === 'large' || raw === 'very-large') return 'avatar-xl';
return raw || '';
}
export function buildAvatarInitials({ login, firstName = '', lastName = '' } = {}) {
const first = String(firstName || '').trim();
const last = String(lastName || '').trim();
if (first || last) {
const initials = `${(first[0] || '').toUpperCase()}${(last[0] || '').toUpperCase()}`.trim();
if (initials) return initials;
}
const cleanLogin = normalizeLogin(login);
return (cleanLogin[0] || '?').toUpperCase();
}
export function renderAvatar({
initials = '?',
avatar = null,
size = 'xl',
className = '',
title = '',
alt = 'Аватар',
glow = false,
} = {}) {
const wrap = document.createElement('div');
const classes = new Set(['avatar', 'avatar-image', 'avatar-framed']);
const sizeClass = pickSizeClass(size);
if (sizeClass) classes.add(sizeClass);
const extraClass = String(className || '').trim();
if (extraClass) extraClass.split(/\s+/g).filter(Boolean).forEach((value) => classes.add(value));
if (glow) classes.add('avatar-glow');
wrap.className = Array.from(classes).join(' ');
if (title) wrap.title = String(title);
const fallback = document.createElement('span');
fallback.className = 'avatar-fallback';
fallback.textContent = String(initials || '?').trim().slice(0, 2).toUpperCase() || '?';
wrap.append(fallback);
const txId = String(avatar?.ar || '').trim();
if (!validateArweaveTxId(txId)) {
return wrap;
}
const sha256Hex = String(avatar?.sha256Hex || avatar?.sha256 || '').trim().toLowerCase();
const expectedSha256Hex = validateSha256Hex(sha256Hex) ? sha256Hex : '';
const img = document.createElement('img');
img.className = 'avatar-photo';
img.alt = String(alt || 'Аватар');
img.loading = 'lazy';
img.decoding = 'async';
wrap.append(img);
const setLoadedState = (loaded) => {
wrap.classList.toggle('has-image', Boolean(loaded));
};
setLoadedState(false);
const gateway = state?.entrySettings?.arweaveServer;
void getCachedAvatarObjectUrl({ gateway, txId, expectedSha256Hex })
.then((objectUrl) => {
const directUrl = buildArweaveDataUrl({ gateway, txId });
let triedDirectUrl = false;
let objectUrlReleased = false;
const releaseObjectUrl = () => {
if (objectUrlReleased) return;
objectUrlReleased = true;
if (objectUrl.startsWith('blob:')) {
URL.revokeObjectURL(objectUrl);
}
};
img.onload = () => {
setLoadedState(true);
releaseObjectUrl();
};
img.onerror = () => {
if (!triedDirectUrl) {
if (expectedSha256Hex) {
releaseObjectUrl();
img.removeAttribute('src');
setLoadedState(false);
return;
}
triedDirectUrl = true;
releaseObjectUrl();
img.src = directUrl;
return;
}
releaseObjectUrl();
img.removeAttribute('src');
setLoadedState(false);
};
img.src = objectUrl;
})
.catch(() => {
if (expectedSha256Hex) {
img.removeAttribute('src');
setLoadedState(false);
return;
}
let directUrl = '';
try {
directUrl = buildArweaveDataUrl({ gateway, txId });
} catch {
directUrl = '';
}
if (!directUrl) {
img.removeAttribute('src');
setLoadedState(false);
return;
}
img.onload = () => {
setLoadedState(true);
};
img.onerror = () => {
img.removeAttribute('src');
setLoadedState(false);
};
img.src = directUrl;
});
return wrap;
}
export function renderUserAvatar({
login,
firstName = '',
lastName = '',
avatar = null,
size = 'xl',
className = '',
title = '',
glow = false,
} = {}) {
return renderAvatar({
initials: buildAvatarInitials({ login, firstName, lastName }),
avatar,
size,
className,
title,
alt: 'Аватар',
glow,
});
}