import { renderHeader } from '../components/header.js?v=20260405171816';
import { channelPosts, channels } from '../mock-data.js?v=20260405171816';
import { addLocalChannelPost, authService, getLocalChannelPosts, state } from '../state.js?v=20260405171816';
export const pageMeta = { id: 'channel-view', title: 'Канал' };
function findMockChannel(channelId) {
const channel = channels.find((c) => c.id === channelId) || channels[0];
return {
channel,
posts: [
...(channelPosts[channel.id] || []).map((post) => ({ title: post.title, body: post.body })),
...getLocalChannelPosts(channelId),
],
isOwnChannel: channel.ownerLogin === '@shine.alex',
};
}
function mapApiMessageToPost(message) {
return {
title: `${message.authorLogin || 'author'} • #${message.messageRef?.blockNumber ?? '?'}`,
body: message.text || '(пусто)',
};
}
function renderPostCard(post) {
const card = document.createElement('article');
card.className = 'card stack';
card.innerHTML = `${post.title}
${post.body}
`;
return card;
}
function openAddMessageModal({ channelId, channelName, onSubmit }) {
const root = document.getElementById('modal-root');
root.innerHTML = `
Новое сообщение в канал
# ${channelName}
`;
const textEl = root.querySelector('#channel-message-text');
const errorEl = root.querySelector('#channel-message-error');
const close = () => {
root.innerHTML = '';
};
root.querySelector('#channel-message-cancel').addEventListener('click', close);
root.querySelector('#channel-message-submit').addEventListener('click', () => {
const body = textEl.value.trim();
if (!body) {
errorEl.textContent = 'Введите текст сообщения.';
return;
}
onSubmit({
title: `${state.session.login || 'Вы'} • сейчас`,
body,
});
close();
});
if (textEl) textEl.focus();
}
function renderBody(screen, navigate, channelId, channelData) {
const head = document.createElement('div');
head.className = 'card';
head.innerHTML = `
# ${channelData.channel.name}
${channelData.channel.description}
Владелец: ${channelData.channel.ownerName}
`;
const actionButton = document.createElement('button');
actionButton.className = channelData.isOwnChannel ? 'primary-btn' : 'secondary-btn';
actionButton.textContent = channelData.isOwnChannel ? 'Добавить сообщение в канал' : 'Отписаться от канала';
const feed = document.createElement('div');
feed.className = 'stack';
channelData.posts.forEach((post) => {
feed.append(renderPostCard(post));
});
if (channelData.isOwnChannel) {
actionButton.addEventListener('click', () => {
openAddMessageModal({
channelId,
channelName: channelData.channel.name,
onSubmit: (post) => {
addLocalChannelPost(channelId, post);
channelData.posts.push(post);
feed.append(renderPostCard(post));
},
});
});
}
const backButton = document.createElement('button');
backButton.className = 'secondary-btn';
backButton.textContent = 'Назад к списку';
backButton.addEventListener('click', () => navigate('channels-list'));
screen.append(head, actionButton, feed, backButton);
}
async function loadFromApi(channelId) {
const summary = state.channelsIndex[channelId];
if (!summary) return null;
const selector = {
ownerBlockchainName: summary.channel?.ownerBlockchainName,
channelRootBlockNumber: summary.channel?.channelRoot?.blockNumber,
channelRootBlockHash: summary.channel?.channelRoot?.blockHash,
};
if (!selector.ownerBlockchainName || selector.channelRootBlockNumber == null) return null;
const payload = await authService.getChannelMessages(selector, 200, 'asc');
const posts = [
...(payload.messages || []).map(mapApiMessageToPost),
...getLocalChannelPosts(channelId),
];
return {
channel: {
name: payload.channel?.channelName || summary.channel?.channelName || 'unknown',
description: `bch=${payload.channel?.ownerBlockchainName || selector.ownerBlockchainName}`,
ownerName: payload.channel?.ownerLogin || summary.channel?.ownerLogin || 'unknown',
},
posts,
isOwnChannel: (payload.channel?.ownerLogin || '').toLowerCase() === (state.session.login || '').toLowerCase(),
};
}
export function render({ navigate, route }) {
const channelId = route.params.channelId || 'ch1';
const screen = document.createElement('section');
screen.className = 'stack';
const headerTitle = state.channelsIndex[channelId]?.channel?.channelName
? `Канал: ${state.channelsIndex[channelId].channel.channelName}`
: `Канал: ${(channels.find((c) => c.id === channelId) || channels[0]).name}`;
screen.append(
renderHeader({
title: headerTitle,
leftAction: { label: '←', onClick: () => navigate('channels-list') },
})
);
const loading = document.createElement('div');
loading.className = 'card meta-muted';
loading.textContent = 'Загрузка канала...';
screen.append(loading);
(async () => {
try {
const apiData = await loadFromApi(channelId);
loading.remove();
if (apiData) {
renderBody(screen, navigate, channelId, apiData);
return;
}
} catch {
// fallback to mock below
}
loading.remove();
renderBody(screen, navigate, channelId, findMockChannel(channelId));
})();
return screen;
}