SHA256
Каналы: новый роутинг, поиск, вход-возврат, удаление просмотров и документация
This commit is contained in:
@@ -98,6 +98,24 @@ function buildAbsoluteRouteUrl(routePath = '') {
|
||||
|
||||
function parseThreadSelector(route) {
|
||||
const params = route?.params || {};
|
||||
if (params.ownerLogin && params.channelName && params.messageBlockNumber && params.messageBlockHash) {
|
||||
return {
|
||||
short: {
|
||||
ownerLogin: String(params.ownerLogin || '').trim(),
|
||||
channelName: String(params.channelName || '').trim(),
|
||||
},
|
||||
message: {
|
||||
blockchainName: '',
|
||||
blockNumber: toSafeInt(params.messageBlockNumber),
|
||||
blockHash: normalizeRouteHash(params.messageBlockHash),
|
||||
},
|
||||
channel: {
|
||||
ownerBlockchainName: '',
|
||||
rootBlockNumber: null,
|
||||
rootBlockHash: '0',
|
||||
},
|
||||
};
|
||||
}
|
||||
const blockNumber = toSafeInt(params.messageBlockNumber);
|
||||
if (!params.messageBlockchainName || blockNumber == null) return null;
|
||||
|
||||
@@ -153,7 +171,17 @@ function buildBackRoute(selector) {
|
||||
}
|
||||
|
||||
function buildThreadRouteFromTarget(target, selector) {
|
||||
if (!target || !selector?.channel?.ownerBlockchainName || selector.channel.rootBlockNumber == null) return '';
|
||||
if (!target) return '';
|
||||
if (selector?.short?.ownerLogin && selector?.short?.channelName) {
|
||||
return [
|
||||
'channel',
|
||||
encodeRoutePart(selector.short.ownerLogin),
|
||||
encodeRoutePart(selector.short.channelName),
|
||||
target.blockNumber,
|
||||
normalizeRouteHash(target.blockHash),
|
||||
].join('/');
|
||||
}
|
||||
if (!selector?.channel?.ownerBlockchainName || selector.channel.rootBlockNumber == null) return '';
|
||||
return [
|
||||
'channel-thread-view',
|
||||
encodeRoutePart(target.blockchainName),
|
||||
@@ -293,13 +321,6 @@ function renderNodeCard(node, heading, handlers, localNumber, routeKey, options
|
||||
|
||||
card.append(meta, body, stats);
|
||||
|
||||
if (options.showViews === true) {
|
||||
const views = document.createElement('p');
|
||||
views.className = 'thread-node-views';
|
||||
views.textContent = `Просмотры: ${Number(node?.viewCount || 0)}`;
|
||||
card.append(views);
|
||||
}
|
||||
|
||||
const target = buildTargetFromNode(node);
|
||||
const refKey = messageRefKey(target);
|
||||
const countersVisible = refKey ? isCounterVisible(routeKey, refKey) : true;
|
||||
@@ -337,15 +358,19 @@ function renderNodeCard(node, heading, handlers, localNumber, routeKey, options
|
||||
likeButton.type = 'button';
|
||||
likeButton.className = 'secondary-btn thread-like-btn';
|
||||
if (isLiked) likeButton.classList.add('is-liked');
|
||||
likeButton.textContent = isPending ? '✦ Сияние...' : '✦ Сияние';
|
||||
likeButton.textContent = isPending ? '❤️ Лайк...' : (isLiked ? '❤️ Лайк' : '🤍 Лайк');
|
||||
likeButton.disabled = isPending;
|
||||
likeButton.addEventListener('click', async (event) => {
|
||||
animatePress(event.currentTarget);
|
||||
if (isPending) return;
|
||||
if (!isLiked) {
|
||||
const ok = window.confirm('Поставить лайк?');
|
||||
if (!ok) return;
|
||||
}
|
||||
revealCounters();
|
||||
await longPressFeel(event.currentTarget, 130);
|
||||
likeButton.disabled = true;
|
||||
likeButton.textContent = 'Сияние...';
|
||||
likeButton.textContent = 'Лайк...';
|
||||
try {
|
||||
await handlers.onToggleLike(target, isLiked ? 'unlike' : 'like');
|
||||
} catch (error) {
|
||||
@@ -361,7 +386,7 @@ function renderNodeCard(node, heading, handlers, localNumber, routeKey, options
|
||||
const replyButton = document.createElement('button');
|
||||
replyButton.type = 'button';
|
||||
replyButton.className = 'secondary-btn thread-reply-btn';
|
||||
replyButton.textContent = '⟳ Отразить';
|
||||
replyButton.textContent = '💬 Ответить';
|
||||
replyButton.addEventListener('click', (event) => {
|
||||
animatePress(event.currentTarget);
|
||||
revealCounters();
|
||||
@@ -373,7 +398,7 @@ function renderNodeCard(node, heading, handlers, localNumber, routeKey, options
|
||||
const shareButton = document.createElement('button');
|
||||
shareButton.type = 'button';
|
||||
shareButton.className = 'secondary-btn thread-share-btn';
|
||||
shareButton.textContent = '↗ Транслировать';
|
||||
shareButton.textContent = '↗ Отправить';
|
||||
shareButton.addEventListener('click', async (event) => {
|
||||
event.stopPropagation();
|
||||
animatePress(event.currentTarget);
|
||||
@@ -454,10 +479,6 @@ export function render({ navigate, route }) {
|
||||
const appScreen = document.getElementById('app-screen');
|
||||
appScreen?.classList.add('channels-scroll-clean');
|
||||
|
||||
const userIndicator = document.createElement('div');
|
||||
userIndicator.className = 'card channels-user-chip';
|
||||
userIndicator.textContent = `Вы вошли как @${state.session.login || 'неизвестно'}`;
|
||||
|
||||
const channelIndicator = document.createElement('div');
|
||||
channelIndicator.className = 'card channels-user-chip';
|
||||
channelIndicator.textContent = `Канал: ${channelDisplayName || selector?.channel?.ownerBlockchainName || 'неизвестно'}`;
|
||||
@@ -490,7 +511,11 @@ export function render({ navigate, route }) {
|
||||
const requireSigningSession = () => {
|
||||
const login = state.session.login;
|
||||
const storagePwd = state.session.storagePwdInMemory;
|
||||
if (!login || !storagePwd) throw new Error('Сессия недействительна. Выполните вход заново.');
|
||||
if (!login || !storagePwd) {
|
||||
state.authReturnHash = window.location.hash || '#/channels-list';
|
||||
navigate('login-view');
|
||||
throw new Error('Для этого действия нужно войти');
|
||||
}
|
||||
return { login, storagePwd };
|
||||
};
|
||||
|
||||
@@ -562,7 +587,7 @@ export function render({ navigate, route }) {
|
||||
leftAction: { label: '<', onClick: () => navigate(backRoute) },
|
||||
}),
|
||||
);
|
||||
screen.append(userIndicator, channelIndicator, statusBox);
|
||||
screen.append(channelIndicator, statusBox);
|
||||
|
||||
if (!selector) {
|
||||
const invalid = document.createElement('div');
|
||||
@@ -576,7 +601,25 @@ export function render({ navigate, route }) {
|
||||
|
||||
(async () => {
|
||||
try {
|
||||
const payload = await authService.getMessageThread(selector.message, 20, 2, 50, state.session.login);
|
||||
let resolvedMessage = selector.message;
|
||||
if (selector.short?.ownerLogin && selector.short?.channelName) {
|
||||
const ownerFeed = await authService.listSubscriptionsFeed(selector.short.ownerLogin, 1000);
|
||||
const ownChannels = Array.isArray(ownerFeed?.ownedChannels) ? ownerFeed.ownedChannels : [];
|
||||
const channel = ownChannels.find((item) => (
|
||||
String(item?.channel?.channelName || '').trim().toLowerCase() === selector.short.channelName.toLowerCase()
|
||||
));
|
||||
const ownerBch = String(channel?.channel?.ownerBlockchainName || '').trim();
|
||||
if (!ownerBch || !Number.isFinite(resolvedMessage?.blockNumber)) {
|
||||
throw new Error('Канал или сообщение не найдено.');
|
||||
}
|
||||
resolvedMessage = {
|
||||
blockchainName: ownerBch,
|
||||
blockNumber: resolvedMessage.blockNumber,
|
||||
blockHash: normalizeRouteHash(resolvedMessage.blockHash),
|
||||
};
|
||||
}
|
||||
|
||||
const payload = await authService.getMessageThread(resolvedMessage, 20, 2, 50, state.session.login);
|
||||
skeleton.remove();
|
||||
|
||||
const ancestors = Array.isArray(payload?.ancestors) ? payload.ancestors : [];
|
||||
@@ -605,7 +648,7 @@ export function render({ navigate, route }) {
|
||||
if (focus) {
|
||||
const focusWrap = document.createElement('div');
|
||||
focusWrap.className = 'stack thread-block thread-block--focus';
|
||||
focusWrap.append(renderNodeCard(focus, '', handlers, nextNumber(), routeKey, { showViews: true }));
|
||||
focusWrap.append(renderNodeCard(focus, '', handlers, nextNumber(), routeKey, { showViews: false }));
|
||||
screen.append(focusWrap);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user