SHA256
Добавил гостевой режим, единые shine-ссылки и пометку о нестабильности мнений
This commit is contained in:
@@ -12,11 +12,66 @@ import {
|
||||
} from '../services/channels-ux.js';
|
||||
import { openSpeechInputModal } from '../components/speech-input-modal.js';
|
||||
import { navigateBack } from '../router.js';
|
||||
import { renderUserAvatar } from '../components/avatar-image.js';
|
||||
import { loadProfileSnapshot } from '../services/user-profile-params.js';
|
||||
import { extractLoginFromBlockchainName, makeProfileRoute, makeShineChannelRoute, makeShineMessageRoute } from '../services/shine-routes.js';
|
||||
|
||||
export const pageMeta = { id: 'channel-thread-view', title: 'Тред' };
|
||||
|
||||
const pendingReactionActions = new Set();
|
||||
const pendingThreadScroll = new Map();
|
||||
const threadAvatarSnapshotCache = new Map();
|
||||
const threadAvatarPendingByLogin = new Map();
|
||||
|
||||
async function loadThreadAvatarSnapshot(login) {
|
||||
const cleanLogin = String(login || '').trim();
|
||||
if (!cleanLogin) return null;
|
||||
const key = cleanLogin.toLowerCase();
|
||||
if (threadAvatarSnapshotCache.has(key)) return threadAvatarSnapshotCache.get(key);
|
||||
if (threadAvatarPendingByLogin.has(key)) return threadAvatarPendingByLogin.get(key);
|
||||
const pending = loadProfileSnapshot(cleanLogin)
|
||||
.then((snapshot) => {
|
||||
threadAvatarSnapshotCache.set(key, snapshot || null);
|
||||
threadAvatarPendingByLogin.delete(key);
|
||||
return snapshot || null;
|
||||
})
|
||||
.catch(() => {
|
||||
threadAvatarSnapshotCache.set(key, null);
|
||||
threadAvatarPendingByLogin.delete(key);
|
||||
return null;
|
||||
});
|
||||
threadAvatarPendingByLogin.set(key, pending);
|
||||
return pending;
|
||||
}
|
||||
|
||||
function createThreadAvatar(login) {
|
||||
const cleanLogin = String(login || '').trim();
|
||||
const title = cleanLogin ? `Профиль ${cleanLogin}` : '';
|
||||
const avatarEl = renderUserAvatar({
|
||||
login: cleanLogin || 'unknown',
|
||||
size: 'small',
|
||||
className: 'channel-message-avatar',
|
||||
title,
|
||||
});
|
||||
if (!cleanLogin) return avatarEl;
|
||||
void loadThreadAvatarSnapshot(cleanLogin).then((snapshot) => {
|
||||
if (!avatarEl.isConnected) return;
|
||||
const upgraded = renderUserAvatar({
|
||||
login: cleanLogin,
|
||||
avatar: snapshot?.avatar?.txId
|
||||
? {
|
||||
ar: String(snapshot.avatar.txId || '').trim(),
|
||||
sha256Hex: String(snapshot?.avatar?.sha256Hex || '').trim().toLowerCase(),
|
||||
}
|
||||
: null,
|
||||
size: 'small',
|
||||
className: 'channel-message-avatar',
|
||||
title,
|
||||
});
|
||||
avatarEl.replaceWith(upgraded);
|
||||
});
|
||||
return avatarEl;
|
||||
}
|
||||
|
||||
function logThreadRuntimeError(stage, error, context = {}) {
|
||||
const message = String(error?.message || error || 'thread runtime error');
|
||||
@@ -55,13 +110,6 @@ function looksLikeBlockchainName(value) {
|
||||
return /^[^-]+-\d+$/.test(raw);
|
||||
}
|
||||
|
||||
function extractLoginFromBlockchainName(value) {
|
||||
const raw = String(value || '').trim();
|
||||
const match = raw.match(/^(.+)-\d+$/);
|
||||
if (!match) return '';
|
||||
return String(match[1] || '').trim();
|
||||
}
|
||||
|
||||
function makeReactionActionKey(messageRef) {
|
||||
const login = String(state.session.login || '').trim().toLowerCase();
|
||||
const blockchainName = String(messageRef?.blockchainName || '').trim();
|
||||
@@ -200,32 +248,31 @@ async function resolveChannelDisplayNameFromServer(channelSelector) {
|
||||
|
||||
function buildThreadRouteFromTarget(target, selector) {
|
||||
if (!target) return '';
|
||||
return [
|
||||
'm',
|
||||
encodeRoutePart(target.blockchainName),
|
||||
target.blockNumber,
|
||||
].join('/');
|
||||
const ownerBch = String(selector?.short?.ownerBlockchainName || selector?.channel?.ownerBlockchainName || '').trim();
|
||||
return makeShineMessageRoute({
|
||||
ownerLogin: extractLoginFromBlockchainName(ownerBch) || extractLoginFromBlockchainName(target.blockchainName),
|
||||
messageBlockchainName: target.blockchainName,
|
||||
messageBlockNumber: target.blockNumber,
|
||||
});
|
||||
}
|
||||
|
||||
function buildChannelRouteFromThread(selector, resolvedChannelLabel = '') {
|
||||
const ownerBch = String(selector?.short?.ownerBlockchainName || selector?.channel?.ownerBlockchainName || '').trim();
|
||||
if (selector?.short?.ownerBlockchainName && selector?.short?.channelName) {
|
||||
return [
|
||||
'channel',
|
||||
encodeRoutePart(selector.short.ownerBlockchainName),
|
||||
encodeRoutePart(selector.short.channelName),
|
||||
].join('/');
|
||||
return makeShineChannelRoute({
|
||||
ownerLogin: extractLoginFromBlockchainName(ownerBch),
|
||||
ownerBlockchainName: ownerBch,
|
||||
channelName: selector.short.channelName,
|
||||
});
|
||||
}
|
||||
|
||||
const ownerBch = String(selector?.channel?.ownerBlockchainName || '').trim();
|
||||
const label = String(resolvedChannelLabel || '').trim();
|
||||
const slashIndex = label.indexOf('/');
|
||||
const channelName = slashIndex >= 0 ? label.slice(slashIndex + 1).trim() : '';
|
||||
if (!ownerBch || !channelName) return '';
|
||||
return [
|
||||
'channel',
|
||||
encodeRoutePart(ownerBch),
|
||||
encodeRoutePart(channelName),
|
||||
].join('/');
|
||||
return makeShineChannelRoute({
|
||||
ownerLogin: extractLoginFromBlockchainName(ownerBch),
|
||||
ownerBlockchainName: ownerBch,
|
||||
channelName,
|
||||
});
|
||||
}
|
||||
|
||||
function buildTargetFromNode(node) {
|
||||
@@ -443,9 +490,7 @@ function renderNodeCard(node, heading, handlers, localNumber) {
|
||||
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 avatar = createThreadAvatar(author);
|
||||
|
||||
const authorBlock = document.createElement('div');
|
||||
authorBlock.className = 'channel-message-author';
|
||||
@@ -591,7 +636,7 @@ function renderNodeCard(node, heading, handlers, localNumber) {
|
||||
event.stopPropagation();
|
||||
const login = String(node?.authorLogin || '').trim();
|
||||
if (!login) return;
|
||||
handlers.navigate(`user/${encodeRoutePart(login)}`);
|
||||
handlers.navigate(makeProfileRoute(login));
|
||||
});
|
||||
card.addEventListener('click', () => {
|
||||
handlers.onOpenThread(target);
|
||||
|
||||
Reference in New Issue
Block a user