import { renderHeader } from '../components/header.js'; import { authService, state } from '../state.js'; import { navigateBack } from '../router.js'; import { makeShineChannelRootRoute, makeShineChannelShortRoute } from '../services/shine-routes.js'; import { showToast } from '../services/channels-ux.js'; import { toUserMessage } from '../services/ui-error-texts.js'; export const pageMeta = { id: 'channel-about-view', title: 'О канале' }; function escapeHtml(text) { return String(text || '') .replaceAll('&', '&') .replaceAll('<', '<') .replaceAll('>', '>') .replaceAll('"', '"') .replaceAll("'", '''); } function normalizeHash(hash) { const normalized = String(hash || '').trim().toLowerCase(); return normalized || '0'; } function toSafeInt(value) { const parsed = Number(value); return Number.isFinite(parsed) ? parsed : null; } function findCachedChannel(ownerBlockchainName, channelRootBlockNumber, channelRootBlockHash) { const ownerBch = String(ownerBlockchainName || '').trim().toLowerCase(); const rootNo = Number(channelRootBlockNumber); const rootHash = normalizeHash(channelRootBlockHash); const rows = Object.values(state.channelsIndex || {}); return rows.find((row) => ( String(row?.channel?.ownerBlockchainName || '').trim().toLowerCase() === ownerBch && Number(row?.channel?.channelRoot?.blockNumber) === rootNo && normalizeHash(row?.channel?.channelRoot?.blockHash) === rootHash )) || null; } function buildChannelLink(route) { if (!route) return ''; const url = new URL(window.location.href); url.pathname = `/${String(route).replace(/^\/+/, '')}`; url.hash = ''; return url.toString(); } function statsText(value) { return Number.isFinite(Number(value)) ? String(Math.max(0, Number(value))) : '0'; } export function render({ navigate, route, chrome }) { const ownerBlockchainName = String(route?.params?.ownerBlockchainName || '').trim(); const channelRootBlockNumber = toSafeInt(route?.params?.channelRootBlockNumber); const channelRootBlockHash = normalizeHash(route?.params?.channelRootBlockHash); const channelRoute = makeShineChannelRootRoute({ ownerBlockchainName, channelRootBlockNumber, channelRootBlockHash, }); const screen = document.createElement('section'); screen.className = 'stack channels-screen channels-screen--channel-about'; const topbar = renderHeader({ title: 'О канале', leftAction: { label: '←', onClick: () => { if (window.history.length > 1) { navigateBack(); return; } if (channelRoute) navigate(channelRoute); }, ariaLabel: 'Назад', title: 'Назад', }, }); chrome?.setTopbar(topbar); const card = document.createElement('div'); card.className = 'card stack channel-about-card'; card.innerHTML = `
Загрузка данных канала…
`; const footer = document.createElement('div'); footer.className = 'meta-muted screen-footer'; footer.textContent = 'О канале (channel-about-view)'; screen.append(card, footer); const renderContent = (channel) => { const cleanName = String(channel?.displayTitle || channel?.displayName || channel?.channelName || 'Канал').trim(); const ownerName = String(channel?.ownerLogin || channel?.ownerName || 'автор').trim(); const description = String(channel?.channelDescription || channel?.description || '').trim(); const subscribersCount = Number(channel?.subscribersCount || 0); const aboutRoute = makeShineChannelRootRoute({ ownerBlockchainName: channel?.ownerBlockchainName || ownerBlockchainName, channelRootBlockNumber: channel?.channelRoot?.blockNumber ?? channelRootBlockNumber, channelRootBlockHash: channel?.channelRoot?.blockHash ?? channelRootBlockHash, }); const channelLinkRoute = makeShineChannelShortRoute({ ownerBlockchainName: channel?.ownerBlockchainName || ownerBlockchainName, channelName: channel?.channelName || '', }); const channelLink = buildChannelLink(channelLinkRoute); const changedAtMs = Number(channel?.metaUpdatedAtMs || 0); const changedAtLabel = changedAtMs ? new Date(changedAtMs).toLocaleString('ru-RU') : '—'; const avatarState = String(channel?.avaAr || '').trim() ? 'Установлен' : 'Не установлен'; const content = card.querySelector('#channel-about-content'); if (!content) return; content.innerHTML = `
Дата${escapeHtml(changedAtLabel)} Владелец${escapeHtml(ownerName)} Подписчиков${escapeHtml(statsText(subscribersCount))} Системное имя${escapeHtml(String(channel?.channelName || '').trim() || 'channel')} Название${escapeHtml(cleanName)} Описание${escapeHtml(description || 'Описание не задано.')} Аватар${escapeHtml(avatarState)} Ссылка${escapeHtml(channelLink)}
`; content.querySelector('#channel-about-open')?.addEventListener('click', () => { if (!channelLinkRoute) return; navigate(channelLinkRoute); }); content.querySelector('#channel-about-copy')?.addEventListener('click', async () => { if (!channelLink) return; try { await navigator.clipboard.writeText(channelLink); showToast('Ссылка скопирована'); } catch (error) { showToast(toUserMessage(error, 'Не удалось скопировать ссылку'), { kind: 'error' }); } }); }; const cached = findCachedChannel(ownerBlockchainName, channelRootBlockNumber, channelRootBlockHash); if (cached?.channel) { renderContent({ ...cached.channel, subscribersCount: cached.channel.subscribersCount ?? cached.subscribersCount ?? 0, }); return screen; } void (async () => { try { const payload = await authService.getChannelMessages({ ownerBlockchainName, channelRootBlockNumber, channelRootBlockHash, }, 1, 'asc', String(state.session.login || '').trim()); renderContent(payload?.channel || {}); } catch (error) { const content = card.querySelector('#channel-about-content'); if (content) { content.innerHTML = `
Не удалось загрузить данные канала.
${escapeHtml(toUserMessage(error, 'Проверьте подключение к серверу и повторите попытку.'))}
`; } } })(); return screen; }