diff --git a/shine-UI/js/pages/add-channel-view.js b/shine-UI/js/pages/add-channel-view.js index 0023b12..3cd4e7b 100644 --- a/shine-UI/js/pages/add-channel-view.js +++ b/shine-UI/js/pages/add-channel-view.js @@ -132,8 +132,8 @@ export function render({ navigate }) { }); const baseMessage = `Канал "${normalizeChannelDisplayName(check.name)}" создан.`; - const successMessage = created?.usedLegacyDescriptionFallback - ? `${baseMessage} Описание не сохранено: на текущем сервере включен legacy-формат create-channel.` + const successMessage = created?.usedLegacyDescriptionFallback && created?.savedDescriptionViaUserParam + ? `${baseMessage} Описание сохранено через блок параметра.` : baseMessage; persistCreateSuccessFlash(successMessage); navigate('channels-list'); diff --git a/shine-UI/js/pages/channel-thread-view.js b/shine-UI/js/pages/channel-thread-view.js index 638083a..01869a6 100644 --- a/shine-UI/js/pages/channel-thread-view.js +++ b/shine-UI/js/pages/channel-thread-view.js @@ -363,6 +363,8 @@ export function render({ navigate, route }) { const screen = document.createElement('section'); screen.className = 'stack channels-screen channels-screen--thread'; + const appScreen = document.getElementById('app-screen'); + appScreen?.classList.add('channels-scroll-clean'); const userIndicator = document.createElement('div'); userIndicator.className = 'card channels-user-chip'; @@ -538,5 +540,9 @@ export function render({ navigate, route }) { } })(); + screen.cleanup = () => { + appScreen?.classList.remove('channels-scroll-clean'); + }; + return screen; } diff --git a/shine-UI/js/pages/channel-view.js b/shine-UI/js/pages/channel-view.js index b3c4613..abd739a 100644 --- a/shine-UI/js/pages/channel-view.js +++ b/shine-UI/js/pages/channel-view.js @@ -605,21 +605,12 @@ function renderBody(screen, navigate, routeKey, channelData, handlers) { title.className = 'channel-head-title'; title.textContent = channelData.channel.displayName || channelData.channel.name; - const description = document.createElement('p'); - description.className = 'channel-head-description'; - const hasDescription = !!String(channelData.channel.description || '').trim(); - if (hasDescription) { - description.textContent = channelData.channel.description; - } else if (channelData.isOwnChannel) { - description.textContent = 'Описание пока не задано.'; - } - const owner = document.createElement('p'); owner.className = 'channel-head-meta'; owner.textContent = `Владелец: ${channelData.channel.ownerName}`; const headActions = document.createElement('div'); - headActions.className = 'row'; + headActions.className = 'channel-head-actions'; const aboutButton = document.createElement('button'); aboutButton.type = 'button'; aboutButton.className = 'secondary-btn small-btn'; @@ -646,20 +637,7 @@ function renderBody(screen, navigate, routeKey, channelData, handlers) { headActions.append(editButton); } - if (hasDescription) { - const moreButton = document.createElement('button'); - moreButton.type = 'button'; - moreButton.className = 'channel-head-more'; - moreButton.textContent = 'ещё'; - moreButton.addEventListener('click', () => { - description.classList.toggle('is-expanded'); - moreButton.textContent = description.classList.contains('is-expanded') ? 'скрыть' : 'ещё'; - }); - headActions.append(moreButton); - } - head.append(title); - if (hasDescription || channelData.isOwnChannel) head.append(description); head.append(owner, headActions); const actionButton = document.createElement('button'); @@ -723,16 +701,8 @@ export function render({ navigate, route }) { const screen = document.createElement('section'); screen.className = 'stack channels-screen channels-screen--channel'; - - const titleFromIndex = state.channelsIndex[channelId]?.channel?.channelName; - const ownerFromIndex = state.channelsIndex[channelId]?.channel?.ownerLogin; - const titleFromIndexDisplay = (ownerFromIndex && titleFromIndex) ? `${ownerFromIndex}/${titleFromIndex}` : titleFromIndex; - const titleFromRoute = route.params.ownerBlockchainName ? String(route.params.ownerBlockchainName) : ''; - const headerTitle = `Канал: ${titleFromIndexDisplay || titleFromRoute || 'Канал'}`; - - const userIndicator = document.createElement('div'); - userIndicator.className = 'card channels-user-chip'; - userIndicator.textContent = `Вы вошли как @${state.session.login || 'неизвестно'}`; + const appScreen = document.getElementById('app-screen'); + appScreen?.classList.add('channels-scroll-clean'); const statusBox = document.createElement('div'); statusBox.className = 'card status-line is-unavailable channels-status'; @@ -846,11 +816,11 @@ export function render({ navigate, route }) { screen.append( renderHeader({ - title: headerTitle, + title: '', leftAction: { label: '<', onClick: () => navigate('channels-list') }, }), ); - screen.append(userIndicator, statusBox); + screen.append(statusBox); const skeleton = renderSkeleton(screen); @@ -924,5 +894,9 @@ export function render({ navigate, route }) { } })(); + screen.cleanup = () => { + appScreen?.classList.remove('channels-scroll-clean'); + }; + return screen; } diff --git a/shine-UI/js/pages/profile-view.js b/shine-UI/js/pages/profile-view.js index 56e0546..d9fa079 100644 --- a/shine-UI/js/pages/profile-view.js +++ b/shine-UI/js/pages/profile-view.js @@ -39,20 +39,6 @@ export function render({ navigate }) { const card = document.createElement('div'); card.className = 'card stack'; - const nextStepCard = document.createElement('div'); - nextStepCard.className = 'card stack'; - nextStepCard.innerHTML = ` - Вы вошли как @${login} -

Следующий шаг для ручной проверки: откройте вкладку «Каналы» в нижнем меню.

- `; - - const openChannelsButton = document.createElement('button'); - openChannelsButton.className = 'primary-btn'; - openChannelsButton.type = 'button'; - openChannelsButton.textContent = 'Открыть каналы'; - openChannelsButton.addEventListener('click', () => navigate('channels-list')); - nextStepCard.append(openChannelsButton); - const topRow = document.createElement('div'); topRow.className = 'row'; topRow.innerHTML = ` @@ -215,7 +201,7 @@ export function render({ navigate }) { shineBtn.addEventListener('click', () => onToggleClick('shine')); card.append(topRow, badgesRow, status, listWrap); - screen.append(nextStepCard, card); + screen.append(card); refreshProfileSnapshot(); diff --git a/shine-UI/js/services/auth-service.js b/shine-UI/js/services/auth-service.js index f14e05c..33f7da7 100644 --- a/shine-UI/js/services/auth-service.js +++ b/shine-UI/js/services/auth-service.js @@ -89,6 +89,16 @@ function isLegacyCreateChannelFormatError(error) { ); } +function channelDescriptionParamKeyFromSelector(selector) { + const owner = String(selector?.ownerBlockchainName || '').trim(); + const rootNo = Number(selector?.channelRootBlockNumber); + const rootHash = String(selector?.channelRootBlockHash || '').trim().toLowerCase(); + if (!owner || !Number.isFinite(rootNo) || rootNo < 0 || !/^[0-9a-f]{64}$/.test(rootHash)) { + return ''; + } + return `channel_desc:${owner}:${rootNo}:${rootHash}`; +} + function makeClientInfo() { const ua = navigator.userAgent || 'unknown'; return ua.slice(0, 50); @@ -891,6 +901,7 @@ export class AuthService { const check = validateChannelDisplayName(channelName); if (!check.ok) throw new Error(channelNameErrorText(check.code)); const cleanChannelName = normalizeChannelDisplayName(check.normalized); + const cleanChannelDescription = normalizeChannelDescription(channelDescription); const channelSlug = toCanonicalChannelSlug(cleanChannelName); const key = `create-channel:${cleanLogin}:${channelSlug || cleanChannelName.toLowerCase()}`; @@ -930,7 +941,7 @@ export class AuthService { prevLineHashHex, thisLineNumber, channelName: cleanChannelName, - channelDescription, + channelDescription: cleanChannelDescription, }) : makeCreateChannelBodyBytes({ lineCode: 0, @@ -952,6 +963,7 @@ export class AuthService { let payload; let usedLegacyDescriptionFallback = false; + let savedDescriptionViaUserParam = false; try { payload = await submitCreate(true); } catch (error) { @@ -960,13 +972,32 @@ export class AuthService { usedLegacyDescriptionFallback = true; } + const selector = { + ownerBlockchainName: blockchainName, + channelRootBlockNumber: Number(payload?.serverLastGlobalNumber), + channelRootBlockHash: normalizeHex32(payload?.serverLastGlobalHash, ZERO64), + }; + + if (usedLegacyDescriptionFallback && cleanChannelDescription) { + const param = channelDescriptionParamKeyFromSelector(selector); + if (!param) { + throw new Error('Не удалось сохранить описание канала: некорректный идентификатор канала.'); + } + await this.addBlockUserParam({ + login: cleanLogin, + storagePwd, + param, + value: JSON.stringify({ v: cleanChannelDescription }), + }); + savedDescriptionViaUserParam = true; + } + return { ...payload, usedLegacyDescriptionFallback, + savedDescriptionViaUserParam, channel: { - ownerBlockchainName: blockchainName, - channelRootBlockNumber: Number(payload?.serverLastGlobalNumber), - channelRootBlockHash: normalizeHex32(payload?.serverLastGlobalHash, ZERO64), + ...selector, }, }; }); diff --git a/shine-UI/styles/components.css b/shine-UI/styles/components.css index 1bf280b..80be719 100644 --- a/shine-UI/styles/components.css +++ b/shine-UI/styles/components.css @@ -1293,6 +1293,14 @@ textarea.input { gap: 6px; } +.channel-head-actions { + display: flex; + align-items: center; + justify-content: flex-start; + gap: 8px; + flex-wrap: wrap; +} + .channel-head-title { font-size: 24px; color: #f0d089;