SHA256
143 lines
6.5 KiB
JavaScript
143 lines
6.5 KiB
JavaScript
import { createTopBar } from '../components/topbar.js';
|
|
import { renderUserAvatar } from '../components/avatar-image.js';
|
|
import { authService } from '../state.js';
|
|
import { navigateBack } from '../router.js';
|
|
import { userDisplayName } from '../services/user-display.js';
|
|
|
|
export const pageMeta = { id: 'user-profile-list-view', title: 'Список' };
|
|
|
|
function parseAvatar(raw) {
|
|
const value = String(raw || '');
|
|
const m = value.match(/(?:^|,)\s*AR:([A-Za-z0-9_-]{43})(?:,|$)/);
|
|
return m ? { ar: m[1] } : null;
|
|
}
|
|
|
|
const TITLES = {
|
|
friends: 'Друзья', close_friends: 'Друзья', primary_received: 'Подтвердили аккаунт',
|
|
primary_given: 'Подтверждённые аккаунты', shine_received: 'Подтвердили сияние', shine_given: 'Подтверждённые сияющие',
|
|
channels_owned: 'Каналы', channels_following: 'Подписки на каналы',
|
|
};
|
|
|
|
function friendTabsHtml(activeKind) {
|
|
return `
|
|
<div class="profile-list-tabs" role="tablist" aria-label="Тип друзей">
|
|
<button type="button" class="profile-list-tab${activeKind === 'friends' ? ' is-active' : ''}" data-friend-kind="friends" role="tab" aria-selected="${activeKind === 'friends'}">Друзья</button>
|
|
<button type="button" class="profile-list-tab${activeKind === 'close_friends' ? ' is-active' : ''}" data-friend-kind="close_friends" role="tab" aria-selected="${activeKind === 'close_friends'}">Близкие друзья</button>
|
|
</div>`;
|
|
}
|
|
|
|
export function render({ navigate, route, chrome }) {
|
|
const login = String(route?.params?.login || '').trim();
|
|
const initialKind = String(route?.params?.kind || '').trim();
|
|
let activeKind = initialKind;
|
|
const isFriendsScreen = initialKind === 'friends' || initialKind === 'close_friends';
|
|
|
|
const screen = document.createElement('section');
|
|
screen.className = 'stack';
|
|
const body = document.createElement('div');
|
|
body.className = 'stack';
|
|
const status = document.createElement('div');
|
|
status.className = 'status-line';
|
|
status.textContent = 'Загрузка...';
|
|
|
|
chrome?.setTopbar(createTopBar({
|
|
title: TITLES[initialKind] || 'Список',
|
|
back: { label: '←', onClick: () => navigateBack() },
|
|
}));
|
|
|
|
if (isFriendsScreen) {
|
|
const tabs = document.createElement('div');
|
|
tabs.innerHTML = friendTabsHtml(activeKind);
|
|
screen.append(tabs.firstElementChild);
|
|
}
|
|
screen.append(status, body);
|
|
|
|
let loadGeneration = 0;
|
|
|
|
function renderRelationRows(rows) {
|
|
rows.forEach((row) => {
|
|
const el = document.createElement('button');
|
|
el.type = 'button';
|
|
el.className = 'ui-button card row profile-list-row';
|
|
el.append(renderUserAvatar({
|
|
login: row.login,
|
|
firstName: row.firstName,
|
|
lastName: row.lastName,
|
|
avatar: parseAvatar(row.avatarAr),
|
|
size: 'md',
|
|
}));
|
|
const fullName = userDisplayName(row);
|
|
const t = document.createElement('div');
|
|
t.className = 'profile-list-row-text';
|
|
const marks = [
|
|
row.relationType && row.relationType !== 'none' ? ({ contact: 'контакт', friend: 'друг', close_friend: 'близкий друг' }[row.relationType] || row.relationType) : '',
|
|
row.accountRole === 'primary' ? 'основной' : row.accountRole === 'non_voting' ? 'голос не учитывается' : '',
|
|
row.shineStatus === 'shining' ? 'сияющий' : row.shineStatus === 'not_interested' ? 'сияние неинтересно' : '',
|
|
row.primaryConfirmed ? 'основной подтверждён ✓' : '',
|
|
row.shineConfirmed ? 'сияющий подтверждён ✓' : '',
|
|
].filter(Boolean).join(' · ');
|
|
t.innerHTML = `<b>${fullName}</b><small>@${String(row.login || '')}${marks ? ` · ${marks}` : ''}</small>`;
|
|
el.append(t);
|
|
el.addEventListener('click', () => navigate(`SHiNE/${encodeURIComponent(row.login)}`));
|
|
body.append(el);
|
|
});
|
|
}
|
|
|
|
async function load(kind) {
|
|
const generation = ++loadGeneration;
|
|
status.className = 'status-line';
|
|
status.textContent = 'Загрузка...';
|
|
body.replaceChildren();
|
|
try {
|
|
if (kind === 'channels_owned' || kind === 'channels_following') {
|
|
const payload = await authService.listUserProfileChannels(login, kind === 'channels_owned' ? 'owned' : 'following', 200, 0);
|
|
if (generation !== loadGeneration) return;
|
|
const rows = Array.isArray(payload?.channels) ? payload.channels : [];
|
|
rows.forEach((row) => {
|
|
const el = document.createElement('button');
|
|
el.type = 'button';
|
|
el.className = 'ui-button card row profile-list-row';
|
|
el.append(renderUserAvatar({ login: row.ownerLogin, firstName: row.displayName, lastName: '', avatar: parseAvatar(row.avatarAr), size: 'md' }));
|
|
const t = document.createElement('div');
|
|
t.className = 'profile-list-row-text';
|
|
t.innerHTML = `<b>${String(row.displayName || row.slug || '')}</b><small>${String(row.ownerLogin || '')} / ${String(row.slug || '')}</small>`;
|
|
el.append(t);
|
|
el.addEventListener('click', () => navigate(`channel/${encodeURIComponent(row.ownerBlockchainName)}/${Number(row.rootBlockNumber || 0)}/${encodeURIComponent(row.rootBlockHashHex || '')}/about`));
|
|
body.append(el);
|
|
});
|
|
status.textContent = rows.length ? '' : 'Список пуст.';
|
|
return;
|
|
}
|
|
|
|
const payload = await authService.listUserProfileRelations(login, kind, 200, 0);
|
|
if (generation !== loadGeneration) return;
|
|
const rows = Array.isArray(payload?.users) ? payload.users : [];
|
|
renderRelationRows(rows);
|
|
status.textContent = rows.length ? '' : 'Список пуст.';
|
|
} catch (e) {
|
|
if (generation !== loadGeneration) return;
|
|
status.className = 'status-line is-unavailable';
|
|
status.textContent = `Ошибка: ${e.message || 'unknown'}`;
|
|
}
|
|
}
|
|
|
|
if (isFriendsScreen) {
|
|
screen.addEventListener('click', (event) => {
|
|
const tab = event.target.closest('[data-friend-kind]');
|
|
if (!tab) return;
|
|
const nextKind = String(tab.dataset.friendKind || '');
|
|
if (!nextKind || nextKind === activeKind) return;
|
|
activeKind = nextKind;
|
|
screen.querySelectorAll('[data-friend-kind]').forEach((button) => {
|
|
const selected = button.dataset.friendKind === activeKind;
|
|
button.classList.toggle('is-active', selected);
|
|
button.setAttribute('aria-selected', selected ? 'true' : 'false');
|
|
});
|
|
void load(activeKind);
|
|
});
|
|
}
|
|
|
|
void load(activeKind);
|
|
return screen;
|
|
}
|