diff --git a/Dev_Docs/Pending_Features/2026-05-19_1359_карточка-автора-в-канале-и-назад-по-истории.md b/Dev_Docs/Pending_Features/2026-05-19_1359_карточка-автора-в-канале-и-назад-по-истории.md new file mode 100644 index 0000000..294cec8 --- /dev/null +++ b/Dev_Docs/Pending_Features/2026-05-19_1359_карточка-автора-в-канале-и-назад-по-истории.md @@ -0,0 +1,25 @@ +# Карточка автора в сообщении канала и стрелка «назад» по истории + +- Краткое описание: + - В `channel-view` в карточке сообщения добавлена вложенная плитка автора (аватар, логин, номер сообщения, дата/время). + - Клик по плитке автора открывает профиль пользователя. + - Клик по области сообщения (вне плитки автора и вне action-кнопок) открывает тред, как кнопка `Тред`. + - Стрелка `назад` в `channel-view`, `channel-thread-view` и профиле переведена на реальную навигацию `history.back()`. + - Маршрут профиля переименован с `user-profile-view` на `user`. + +- Что проверять: + - В канале у каждого сообщения сверху есть вложенная плитка автора. + - Клик по вложенной плитке открывает профиль автора. + - Клик по тексту/телу сообщения открывает тред. + - Кнопки `Лайк`, `Ответить`, `Тред`, `Отправить` работают отдельно и не конфликтуют с кликом по карточке. + - Стрелка `назад` возвращает на предыдущий экран по реальной истории переходов. + - При отсутствии истории стрелка `назад` не делает переход. + - Переходы на профиль работают по новому маршруту `user/{login}/...`. + +- Ожидаемый результат: + - Навигация в каналах и тредах соответствует ожидаемому UX. + - Переходы в профиль и назад по истории работают стабильно. + - Старый маршрут `user-profile-view` больше не используется. + +- Статус: + - `pending` diff --git a/VERSION.properties b/VERSION.properties index e1f1883..5373409 100644 --- a/VERSION.properties +++ b/VERSION.properties @@ -1,2 +1,2 @@ -client.version=1.2.66 -server.version=1.2.60 +client.version=1.2.67 +server.version=1.2.61 diff --git a/shine-UI/js/app.js b/shine-UI/js/app.js index fbc6dd3..1d523a3 100644 --- a/shine-UI/js/app.js +++ b/shine-UI/js/app.js @@ -101,7 +101,7 @@ const routes = { 'messages-list': messagesList, 'contact-search-view': contactSearchView, 'chat-view': chatView, - 'user-profile-view': userProfileView, + user: userProfileView, 'channels-list': channelsList, 'channel-view': channelView, 'channel-thread-view': channelThreadView, diff --git a/shine-UI/js/pages/channel-thread-view.js b/shine-UI/js/pages/channel-thread-view.js index 685ec82..211f586 100644 --- a/shine-UI/js/pages/channel-thread-view.js +++ b/shine-UI/js/pages/channel-thread-view.js @@ -11,6 +11,7 @@ import { softHaptic, } from '../services/channels-ux.js'; import { openSpeechInputModal } from '../components/speech-input-modal.js'; +import { navigateBack } from '../router.js'; export const pageMeta = { id: 'channel-thread-view', title: 'Тред' }; @@ -197,17 +198,6 @@ async function resolveChannelDisplayNameFromServer(channelSelector) { } } -function buildBackRoute(selector) { - if (selector?.short?.ownerBlockchainName && selector?.short?.channelName) { - return [ - 'channel', - encodeRoutePart(selector.short.ownerBlockchainName), - encodeRoutePart(selector.short.channelName), - ].join('/'); - } - return 'channels-list'; -} - function buildThreadRouteFromTarget(target, selector) { if (!target) return ''; return [ @@ -501,7 +491,6 @@ function renderSkeleton(screen) { export function render({ navigate, route }) { const selector = parseThreadSelector(route); - const backRoute = buildBackRoute(selector); const channelDisplayName = resolveChannelDisplayName(selector?.channel); const routeKey = `${selector?.message?.blockchainName || ''}:${selector?.message?.blockNumber || ''}:${selector?.message?.blockHash || ''}`; @@ -624,7 +613,7 @@ export function render({ navigate, route }) { screen.append( renderHeader({ title: 'Тред', - leftAction: { label: '<', onClick: () => navigate(backRoute) }, + leftAction: { label: '<', onClick: () => navigateBack() }, }), ); screen.append(channelIndicator, statusBox); diff --git a/shine-UI/js/pages/channel-view.js b/shine-UI/js/pages/channel-view.js index 7cae989..6150e38 100644 --- a/shine-UI/js/pages/channel-view.js +++ b/shine-UI/js/pages/channel-view.js @@ -17,6 +17,7 @@ import { softHaptic, } from '../services/channels-ux.js'; import { openSpeechInputModal } from '../components/speech-input-modal.js'; +import { navigateBack } from '../router.js'; export const pageMeta = { id: 'channel-view', title: 'Канал' }; const CHANNEL_TYPE_PERSONAL = 100; @@ -628,8 +629,9 @@ function renderPostCard(post, { const card = document.createElement('article'); card.className = 'card stack channel-message-card'; - const topRow = document.createElement('div'); - topRow.className = 'channel-message-top'; + const authorTile = document.createElement('button'); + authorTile.type = 'button'; + authorTile.className = 'channel-message-author-tile'; const avatar = document.createElement('div'); avatar.className = 'channel-message-avatar'; @@ -654,13 +656,19 @@ function renderPostCard(post, { title.append(loginEl, numberEl); authorBlock.append(title, timestamp); - topRow.append(avatar, authorBlock); + authorTile.append(avatar, authorBlock); + authorTile.addEventListener('click', (event) => { + event.stopPropagation(); + const cleanLogin = String(post.authorLogin || '').trim(); + if (!cleanLogin) return; + navigate(`user/${encodeRoutePart(cleanLogin)}/channel-view`); + }); const body = document.createElement('p'); body.className = 'channel-message-body'; body.textContent = post.body; - card.append(topRow, body); + card.append(authorTile, body); const refKey = messageRefKey(post.messageRef); if (refKey) { @@ -688,6 +696,7 @@ function renderPostCard(post, { `; likeButton.disabled = isPending; likeButton.addEventListener('click', async (event) => { + event.stopPropagation(); animatePress(event.currentTarget); if (isPending) return; if (!isLiked) { @@ -710,6 +719,7 @@ function renderPostCard(post, { ${post.repliesCount || 0} `; replyButton.addEventListener('click', (event) => { + event.stopPropagation(); animatePress(event.currentTarget); openReplyModal({ navigate, @@ -726,6 +736,7 @@ function renderPostCard(post, { Тред `; openThreadButton.addEventListener('click', (event) => { + event.stopPropagation(); animatePress(event.currentTarget); const route = buildThreadRoute(post.messageRef, selector); if (route) navigate(route); @@ -747,6 +758,10 @@ function renderPostCard(post, { actions.append(openThreadButton, shareButton); card.append(actions); + card.addEventListener('click', () => { + const route = buildThreadRoute(post.messageRef, selector); + if (route) navigate(route); + }); return card; } @@ -978,7 +993,7 @@ export function render({ navigate, route }) { screen.append( renderHeader({ title: '', - leftAction: { label: '<', onClick: () => navigate('channels-list') }, + leftAction: { label: '<', onClick: () => navigateBack() }, }), ); screen.append(statusBox); diff --git a/shine-UI/js/pages/contact-search-view.js b/shine-UI/js/pages/contact-search-view.js index ea9a862..77cb8c8 100644 --- a/shine-UI/js/pages/contact-search-view.js +++ b/shine-UI/js/pages/contact-search-view.js @@ -53,7 +53,7 @@ export function render({ navigate }) {
`; row.addEventListener('click', () => { - navigate(`user-profile-view/${encodeURIComponent(login)}/contact-search-view`); + navigate(`user/${encodeURIComponent(login)}/contact-search-view`); }); resultsList.append(row); }); diff --git a/shine-UI/js/pages/network-view.js b/shine-UI/js/pages/network-view.js index e09054c..833f0bb 100644 --- a/shine-UI/js/pages/network-view.js +++ b/shine-UI/js/pages/network-view.js @@ -533,7 +533,7 @@ export function render({ navigate, route }) { const cleanLogin = normalizeLogin(login); if (!cleanLogin) return ''; if (normKey(cleanLogin) === normKey(state.session.login)) return 'profile-view'; - return `user-profile-view/${encodeURIComponent(cleanLogin)}/${encodeURIComponent('network-view/keep-history')}`; + return `user/${encodeURIComponent(cleanLogin)}/${encodeURIComponent('network-view/keep-history')}`; } function helpText() { diff --git a/shine-UI/js/pages/user-profile-view.js b/shine-UI/js/pages/user-profile-view.js index 9f20444..a68b1b8 100644 --- a/shine-UI/js/pages/user-profile-view.js +++ b/shine-UI/js/pages/user-profile-view.js @@ -7,7 +7,9 @@ import { } from '../services/user-connections.js'; import { renderUserAvatar } from '../components/avatar-image.js'; -export const pageMeta = { id: 'user-profile-view', title: 'Чужой профиль' }; +import { navigateBack } from '../router.js'; + +export const pageMeta = { id: 'user', title: 'Чужой профиль' }; function escapeHtml(text) { return String(text || '') @@ -147,7 +149,6 @@ function renderReadOnlyParams(card) { export function render({ navigate, route }) { const requestedLogin = String(route.params.login || '').trim(); - const fromPage = String(route.params.fromPage || 'messages-list').trim() || 'messages-list'; const sessionLogin = String(state.session.login || '').trim(); const screen = document.createElement('section'); @@ -163,7 +164,7 @@ export function render({ navigate, route }) { screen.append( renderHeader({ title: 'Профиль пользователя', - leftAction: { label: '←', onClick: () => navigate(fromPage) }, + leftAction: { label: '←', onClick: () => navigateBack() }, rightActions: [{ label: 'Обновить', onClick: () => refresh() }], }), status, diff --git a/shine-UI/js/router.js b/shine-UI/js/router.js index 71b7962..f653ffd 100644 --- a/shine-UI/js/router.js +++ b/shine-UI/js/router.js @@ -122,7 +122,7 @@ export function getRoute() { return { pageId, params: { sessionId: dynamicId ? decodeURIComponent(dynamicId) : '' } }; } - if (pageId === 'user-profile-view') { + if (pageId === 'user') { return { pageId, params: { @@ -162,6 +162,11 @@ export function navigate(path) { window.dispatchEvent(new PopStateEvent('popstate')); } +export function navigateBack() { + if (window.history.length <= 1) return; + window.history.back(); +} + export function resolveToolbarActive(pageId) { if (ROOT_PAGES.includes(pageId)) return pageId; if ( @@ -185,6 +190,6 @@ export function resolveToolbarActive(pageId) { } if (pageId === 'chat-view' || pageId === 'contact-search-view') return 'messages-list'; if (pageId === 'channel-view' || pageId === 'channel-thread-view' || pageId === 'add-channel-view' || pageId === 'add-personal-public-chat-view') return 'channels-list'; - if (pageId === 'user-profile-view') return 'messages-list'; + if (pageId === 'user') return 'messages-list'; return 'profile-view'; } diff --git a/shine-UI/styles/components.css b/shine-UI/styles/components.css index 4307b2a..dd96847 100644 --- a/shine-UI/styles/components.css +++ b/shine-UI/styles/components.css @@ -2171,6 +2171,23 @@ textarea.input { gap: 12px; } +.channel-message-author-tile { + display: flex; + align-items: center; + gap: 12px; + width: 100%; + padding: 10px 12px; + border-radius: 8px; + border: 1px solid rgba(255, 255, 255, 0.12); + background: rgba(255, 255, 255, 0.04); + text-align: left; + cursor: pointer; +} + +.channel-message-author-tile:hover { + background: rgba(255, 255, 255, 0.08); +} + .channel-message-avatar { width: 44px; height: 44px;