Добавить просмотр лайков сообщений канала

This commit is contained in:
AidarKC
2026-09-09 17:01:13 +03:00
parent 9f77c54955
commit 2d059e9ff5
11 changed files with 595 additions and 20 deletions
+165 -9
View File
@@ -1790,6 +1790,169 @@ function renderChannelMetaEventCard(event) {
return card;
}
function likeCategoryCounts(post) {
const total = Math.max(0, Number(post?.likesCount || 0));
const primary = Math.max(0, Math.min(total, Number(post?.primaryLikesCount || 0)));
const shining = Math.max(0, Math.min(primary, Number(post?.shiningLikesCount || 0)));
return {
shining,
official: Math.max(0, primary - shining),
others: Math.max(0, total - primary),
total,
};
}
function openMessageLikesListModal({ navigate, messageRef, initialTab = 'shining' }) {
const overlay = document.createElement('div');
overlay.className = 'channel-likes-modal-overlay';
overlay.innerHTML = `
<section class="channel-likes-modal" role="dialog" aria-modal="true" aria-label="Кто поставил лайк">
<header class="channel-likes-modal__header">
<h3>Лайки</h3>
<button type="button" class="ui-button channel-likes-modal__close" aria-label="Закрыть">×</button>
</header>
<div class="channel-likes-tabs" role="tablist">
<button type="button" class="ui-button" data-like-tab="shining">Сияющие <span data-like-tab-count="shining"></span></button>
<button type="button" class="ui-button" data-like-tab="official">Официальные <span data-like-tab-count="official"></span></button>
<button type="button" class="ui-button" data-like-tab="others">Остальные <span data-like-tab-count="others"></span></button>
</div>
<div class="channel-likes-modal__status">Загрузка...</div>
<div class="channel-likes-user-list"></div>
</section>
`;
document.body.append(overlay);
const modal = overlay.querySelector('.channel-likes-modal');
const status = overlay.querySelector('.channel-likes-modal__status');
const list = overlay.querySelector('.channel-likes-user-list');
const close = () => overlay.remove();
overlay.querySelector('.channel-likes-modal__close')?.addEventListener('click', close);
overlay.addEventListener('click', (event) => { if (event.target === overlay) close(); });
modal?.addEventListener('click', (event) => event.stopPropagation());
let activeTab = ['shining', 'official', 'others'].includes(initialTab) ? initialTab : 'shining';
let payload = null;
const renderTab = () => {
overlay.querySelectorAll('[data-like-tab]').forEach((button) => {
const isActive = button.dataset.likeTab === activeTab;
button.classList.toggle('is-active', isActive);
button.setAttribute('aria-selected', isActive ? 'true' : 'false');
});
if (!payload) return;
const rows = Array.isArray(payload?.[activeTab]) ? payload[activeTab] : [];
list.innerHTML = '';
rows.forEach((row) => {
const userButton = document.createElement('button');
userButton.type = 'button';
userButton.className = 'ui-button card row profile-list-row channel-like-user-row';
const avatarRaw = String(row?.avatarAr || '').trim();
userButton.append(renderUserAvatar({
login: String(row?.login || '').trim() || 'unknown',
firstName: String(row?.firstName || ''),
lastName: String(row?.lastName || ''),
avatar: avatarRaw ? { ar: avatarRaw } : null,
size: 'md',
}));
const firstName = String(row?.firstName || '').trim();
const lastName = String(row?.lastName || '').trim();
const login = String(row?.login || '').trim();
const name = [firstName, lastName].filter(Boolean).join(' ') || login;
const text = document.createElement('div');
text.className = 'profile-list-row-text';
text.innerHTML = `<b>${escapeHtml(name)}</b><small>${escapeHtml(login)}</small>`;
userButton.append(text);
userButton.addEventListener('click', () => {
close();
navigate(makeProfileRoute(login));
});
list.append(userButton);
});
status.textContent = rows.length ? (payload?.truncated ? 'Показаны первые 1000 лайков.' : '') : 'В этом списке пока никого нет.';
};
overlay.querySelectorAll('[data-like-tab]').forEach((button) => {
button.addEventListener('click', () => {
activeTab = button.dataset.likeTab;
renderTab();
});
});
renderTab();
(async () => {
try {
payload = await authService.getMessageLikes(messageRef, 1000);
if (!overlay.isConnected) return;
['shining', 'official', 'others'].forEach((key) => {
const countEl = overlay.querySelector(`[data-like-tab-count="${key}"]`);
if (countEl) countEl.textContent = String(Array.isArray(payload?.[key]) ? payload[key].length : 0);
});
renderTab();
} catch (error) {
if (!overlay.isConnected) return;
status.className = 'channel-likes-modal__status is-error';
status.textContent = toUserMessage(error, 'Не удалось загрузить список лайков.');
list.innerHTML = '';
}
})();
}
function openMessageLikePopup({ anchor, post, navigate, onToggleLike }) {
document.querySelectorAll('.channel-like-popup-layer').forEach((el) => el.remove());
const counts = likeCategoryCounts(post);
const layer = document.createElement('div');
layer.className = 'channel-like-popup-layer';
layer.innerHTML = `
<section class="channel-like-popup" role="dialog" aria-label="Лайки сообщения">
<div class="channel-like-popup__title">Лайки</div>
<div class="channel-like-popup__counts">
<button type="button" class="ui-button channel-like-count" data-like-list="shining"><b>${counts.shining}</b><span>Сияющие</span></button>
<button type="button" class="ui-button channel-like-count" data-like-list="official"><b>${counts.official}</b><span>Официальные</span></button>
<button type="button" class="ui-button channel-like-count" data-like-list="others"><b>${counts.others}</b><span>Остальные</span></button>
</div>
<div class="channel-like-popup__total">Всего лайков: <b>${counts.total}</b></div>
<button type="button" class="ui-button channel-like-popup__action">${post.reactionState === 'liked' ? 'Убрать свой лайк' : 'Добавить свой лайк'}</button>
<button type="button" class="ui-button channel-like-popup__close">Закрыть</button>
</section>
`;
document.body.append(layer);
const popup = layer.querySelector('.channel-like-popup');
const rect = anchor.getBoundingClientRect();
const width = Math.min(330, Math.max(270, window.innerWidth - 24));
const left = Math.max(12, Math.min(window.innerWidth - width - 12, rect.left + rect.width / 2 - width / 2));
const estimatedHeight = 255;
const top = rect.bottom + estimatedHeight < window.innerHeight - 8
? rect.bottom + 8
: Math.max(8, rect.top - estimatedHeight - 8);
popup.style.width = `${width}px`;
popup.style.left = `${left}px`;
popup.style.top = `${top}px`;
const close = () => layer.remove();
layer.addEventListener('click', (event) => { if (event.target === layer) close(); });
popup.addEventListener('click', (event) => event.stopPropagation());
layer.querySelector('.channel-like-popup__close')?.addEventListener('click', close);
layer.querySelectorAll('[data-like-list]').forEach((button) => {
button.addEventListener('click', () => {
const initialTab = button.dataset.likeList;
close();
openMessageLikesListModal({ navigate, messageRef: post.messageRef, initialTab });
});
});
layer.querySelector('.channel-like-popup__action')?.addEventListener('click', async (event) => {
const button = event.currentTarget;
button.disabled = true;
try {
await onToggleLike(post.messageRef, post.reactionState === 'liked' ? 'unlike' : 'like');
close();
} catch (error) {
button.disabled = false;
showToast(toUserMessage(error, 'Не удалось изменить лайк.'));
}
});
}
function renderPostCard(post, {
navigate,
selector,
@@ -1976,18 +2139,11 @@ function renderPostCard(post, {
`;
setActionTitle(likeButton, isPending ? 'Лайк...' : 'Лайк');
likeButton.disabled = isPending;
likeButton.addEventListener('click', async (event) => {
likeButton.addEventListener('click', (event) => {
event.stopPropagation();
animatePress(event.currentTarget);
if (isPending) return;
if (!isLiked) {
const ok = window.confirm('Поставить лайк?');
if (!ok) return;
}
await longPressFeel(event.currentTarget, 130);
likeButton.disabled = true;
setActionTitle(likeButton, 'Лайк...');
await onToggleLike(post.messageRef, isLiked ? 'unlike' : 'like', { likeButton });
openMessageLikePopup({ anchor: event.currentTarget, post, navigate, onToggleLike });
});
const replyButton = document.createElement('button');