SHA256
Add channels IT coverage, live UI loading, and runbook
This commit is contained in:
@@ -1,46 +1,119 @@
|
||||
import { renderHeader } from '../components/header.js?v=20260330001044';
|
||||
import { channelPosts, channels } from '../mock-data.js?v=20260330001044';
|
||||
import { authService, state } from '../state.js?v=20260330001044';
|
||||
|
||||
export const pageMeta = { id: 'channel-view', title: 'Канал' };
|
||||
|
||||
export function render({ navigate, route }) {
|
||||
const channelId = route.params.channelId || 'ch1';
|
||||
function findMockChannel(channelId) {
|
||||
const channel = channels.find((c) => c.id === channelId) || channels[0];
|
||||
const posts = channelPosts[channelId] || [];
|
||||
const isOwnChannel = channel.ownerLogin === '@shine.alex';
|
||||
return {
|
||||
channel,
|
||||
posts: (channelPosts[channel.id] || []).map((post) => ({ title: post.title, body: post.body })),
|
||||
isOwnChannel: channel.ownerLogin === '@shine.alex',
|
||||
};
|
||||
}
|
||||
|
||||
const screen = document.createElement('section');
|
||||
screen.className = 'stack';
|
||||
|
||||
screen.append(
|
||||
renderHeader({
|
||||
title: `Канал: ${channel.name}`,
|
||||
leftAction: { label: '←', onClick: () => navigate('channels-list') },
|
||||
})
|
||||
);
|
||||
function mapApiMessageToPost(message) {
|
||||
return {
|
||||
title: `${message.authorLogin || 'author'} • #${message.messageRef?.blockNumber ?? '?'}`,
|
||||
body: message.text || '(пусто)',
|
||||
};
|
||||
}
|
||||
|
||||
function renderBody(screen, navigate, channelData) {
|
||||
const head = document.createElement('div');
|
||||
head.className = 'card';
|
||||
head.innerHTML = `
|
||||
<strong># ${channel.name}</strong>
|
||||
<p class="meta-muted" style="margin-top:4px;">${channel.description}</p>
|
||||
<p class="meta-muted" style="margin-top:8px;">Владелец: ${channel.ownerName}</p>
|
||||
<strong># ${channelData.channel.name}</strong>
|
||||
<p class="meta-muted" style="margin-top:4px;">${channelData.channel.description}</p>
|
||||
<p class="meta-muted" style="margin-top:8px;">Владелец: ${channelData.channel.ownerName}</p>
|
||||
`;
|
||||
|
||||
const actionButton = document.createElement('button');
|
||||
actionButton.className = isOwnChannel ? 'primary-btn' : 'secondary-btn';
|
||||
actionButton.textContent = isOwnChannel ? 'Добавить сообщение в канал' : 'Отписаться от канала';
|
||||
actionButton.className = channelData.isOwnChannel ? 'primary-btn' : 'secondary-btn';
|
||||
actionButton.textContent = channelData.isOwnChannel ? 'Добавить сообщение в канал' : 'Отписаться от канала';
|
||||
|
||||
const feed = document.createElement('div');
|
||||
feed.className = 'stack';
|
||||
|
||||
posts.forEach((post) => {
|
||||
channelData.posts.forEach((post) => {
|
||||
const card = document.createElement('article');
|
||||
card.className = 'card stack';
|
||||
card.innerHTML = `<strong>${post.title}</strong><p class="meta-muted">${post.body}</p>`;
|
||||
feed.append(card);
|
||||
});
|
||||
|
||||
screen.append(head, actionButton, feed);
|
||||
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);
|
||||
|
||||
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, apiData);
|
||||
return;
|
||||
}
|
||||
} catch {
|
||||
// fallback to mock below
|
||||
}
|
||||
|
||||
loading.remove();
|
||||
renderBody(screen, navigate, findMockChannel(channelId));
|
||||
})();
|
||||
|
||||
return screen;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user