SHA256
UI: шапка channel owner/name и унификация карточек треда
This commit is contained in:
@@ -314,14 +314,12 @@ function openReplyModal({ onSubmit, navigate }) {
|
||||
|
||||
function renderNodeCard(node, heading, handlers, localNumber) {
|
||||
const card = document.createElement('article');
|
||||
card.className = 'card stack thread-node-card';
|
||||
card.className = 'card stack thread-node-card channel-message-card';
|
||||
|
||||
const author = node?.authorLogin || 'автор';
|
||||
const text = resolveNodeText(node) || '(пусто)';
|
||||
const likes = Number(node?.likesCount || 0);
|
||||
const replies = Number(node?.repliesCount || 0);
|
||||
const versions = Number(node?.versionsTotal || 1);
|
||||
const changes = Math.max(0, versions - 1);
|
||||
|
||||
const headingText = String(heading || '').trim();
|
||||
if (headingText) {
|
||||
@@ -331,18 +329,36 @@ function renderNodeCard(node, heading, handlers, localNumber) {
|
||||
card.append(headingEl);
|
||||
}
|
||||
|
||||
const meta = document.createElement('p');
|
||||
meta.className = 'thread-node-meta';
|
||||
meta.innerHTML = `
|
||||
<span class="author-line-login">${author}</span>
|
||||
<span class="author-line-num">· #${localNumber}</span>
|
||||
`;
|
||||
const authorTile = document.createElement('button');
|
||||
authorTile.type = 'button';
|
||||
authorTile.className = 'channel-message-author-tile';
|
||||
|
||||
const avatar = document.createElement('div');
|
||||
avatar.className = 'channel-message-avatar';
|
||||
avatar.textContent = String(author || 'A').trim().charAt(0).toUpperCase() || 'A';
|
||||
|
||||
const authorBlock = document.createElement('div');
|
||||
authorBlock.className = 'channel-message-author';
|
||||
const title = document.createElement('div');
|
||||
title.className = 'channel-message-title author-line';
|
||||
const loginEl = document.createElement('span');
|
||||
loginEl.className = 'author-line-login';
|
||||
loginEl.textContent = author;
|
||||
const numberEl = document.createElement('span');
|
||||
numberEl.className = 'author-line-num';
|
||||
numberEl.textContent = `· #${localNumber}`;
|
||||
const timestamp = document.createElement('div');
|
||||
timestamp.className = 'channel-message-time';
|
||||
timestamp.textContent = node?.createdAtMs ? new Date(node.createdAtMs).toLocaleString() : '—';
|
||||
title.append(loginEl, numberEl);
|
||||
authorBlock.append(title, timestamp);
|
||||
authorTile.append(avatar, authorBlock);
|
||||
|
||||
const body = document.createElement('p');
|
||||
body.className = 'thread-node-body';
|
||||
body.className = 'channel-message-body';
|
||||
body.textContent = text;
|
||||
|
||||
card.append(meta, body);
|
||||
card.append(authorTile, body);
|
||||
|
||||
const target = buildTargetFromNode(node);
|
||||
const refKey = messageRefKey(target);
|
||||
@@ -358,15 +374,20 @@ function renderNodeCard(node, heading, handlers, localNumber) {
|
||||
const isLiked = getMessageReactionState(target) === 'liked';
|
||||
|
||||
const actions = document.createElement('div');
|
||||
actions.className = 'thread-node-actions';
|
||||
actions.className = 'thread-node-actions channel-message-actions';
|
||||
|
||||
const likeButton = document.createElement('button');
|
||||
likeButton.type = 'button';
|
||||
likeButton.className = 'secondary-btn thread-like-btn';
|
||||
likeButton.className = 'channel-action-item thread-like-btn';
|
||||
if (isLiked) likeButton.classList.add('is-liked');
|
||||
likeButton.textContent = isPending ? `❤️ ${likes}...` : `${isLiked ? '❤️' : '🤍'} ${likes}`;
|
||||
likeButton.innerHTML = `
|
||||
<span class="channel-action-icon" aria-hidden="true">${isLiked ? '❤️' : '🤍'}</span>
|
||||
<span class="channel-action-label">${isPending ? 'Лайк...' : 'Лайк'}</span>
|
||||
<span class="channel-action-counter">${likes}</span>
|
||||
`;
|
||||
likeButton.disabled = isPending;
|
||||
likeButton.addEventListener('click', async (event) => {
|
||||
event.stopPropagation();
|
||||
animatePress(event.currentTarget);
|
||||
if (isPending) return;
|
||||
if (!isLiked) {
|
||||
@@ -375,7 +396,6 @@ function renderNodeCard(node, heading, handlers, localNumber) {
|
||||
}
|
||||
await longPressFeel(event.currentTarget, 130);
|
||||
likeButton.disabled = true;
|
||||
likeButton.textContent = `❤️ ${likes}...`;
|
||||
try {
|
||||
await handlers.onToggleLike(target, isLiked ? 'unlike' : 'like');
|
||||
} catch (error) {
|
||||
@@ -390,9 +410,14 @@ function renderNodeCard(node, heading, handlers, localNumber) {
|
||||
|
||||
const replyButton = document.createElement('button');
|
||||
replyButton.type = 'button';
|
||||
replyButton.className = 'secondary-btn thread-reply-btn';
|
||||
replyButton.textContent = `💬 ${replies}`;
|
||||
replyButton.className = 'channel-action-item thread-reply-btn';
|
||||
replyButton.innerHTML = `
|
||||
<span class="channel-action-icon" aria-hidden="true">💬</span>
|
||||
<span class="channel-action-label">Ответить</span>
|
||||
<span class="channel-action-counter">${replies}</span>
|
||||
`;
|
||||
replyButton.addEventListener('click', (event) => {
|
||||
event.stopPropagation();
|
||||
animatePress(event.currentTarget);
|
||||
openReplyModal({
|
||||
navigate: handlers.navigate,
|
||||
@@ -400,17 +425,13 @@ function renderNodeCard(node, heading, handlers, localNumber) {
|
||||
});
|
||||
});
|
||||
|
||||
const changedButton = document.createElement('button');
|
||||
changedButton.type = 'button';
|
||||
changedButton.className = 'secondary-btn thread-version-btn';
|
||||
changedButton.textContent = `✏️ ${changes}`;
|
||||
changedButton.disabled = true;
|
||||
changedButton.style.display = changes > 0 ? '' : 'none';
|
||||
|
||||
const shareButton = document.createElement('button');
|
||||
shareButton.type = 'button';
|
||||
shareButton.className = 'secondary-btn thread-share-btn';
|
||||
shareButton.textContent = '↗ Отправить';
|
||||
shareButton.className = 'channel-action-item thread-share-btn';
|
||||
shareButton.innerHTML = `
|
||||
<span class="channel-action-icon" aria-hidden="true">↗</span>
|
||||
<span class="channel-action-label">Отправить</span>
|
||||
`;
|
||||
shareButton.addEventListener('click', async (event) => {
|
||||
event.stopPropagation();
|
||||
animatePress(event.currentTarget);
|
||||
@@ -419,16 +440,28 @@ function renderNodeCard(node, heading, handlers, localNumber) {
|
||||
|
||||
const openThreadButton = document.createElement('button');
|
||||
openThreadButton.type = 'button';
|
||||
openThreadButton.className = 'secondary-btn thread-open-btn';
|
||||
openThreadButton.textContent = '🧵 В тред';
|
||||
openThreadButton.className = 'channel-action-item thread-open-btn';
|
||||
openThreadButton.innerHTML = `
|
||||
<span class="channel-action-icon" aria-hidden="true">#</span>
|
||||
<span class="channel-action-label">Тред</span>
|
||||
`;
|
||||
openThreadButton.addEventListener('click', (event) => {
|
||||
event.stopPropagation();
|
||||
animatePress(event.currentTarget);
|
||||
handlers.onOpenThread(target);
|
||||
});
|
||||
|
||||
actions.append(likeButton, replyButton, changedButton, shareButton, openThreadButton);
|
||||
actions.append(likeButton, replyButton, openThreadButton, shareButton);
|
||||
card.append(actions);
|
||||
authorTile.addEventListener('click', (event) => {
|
||||
event.stopPropagation();
|
||||
const login = String(node?.authorLogin || '').trim();
|
||||
if (!login) return;
|
||||
handlers.navigate(`user/${encodeRoutePart(login)}`);
|
||||
});
|
||||
card.addEventListener('click', () => {
|
||||
handlers.onOpenThread(target);
|
||||
});
|
||||
return card;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user