Channels UI surgical cleanup and create description save fix
This commit is contained in:
parent
126b4ba3a1
commit
7bdb3118ae
@ -132,8 +132,8 @@ export function render({ navigate }) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
const baseMessage = `Канал "${normalizeChannelDisplayName(check.name)}" создан.`;
|
const baseMessage = `Канал "${normalizeChannelDisplayName(check.name)}" создан.`;
|
||||||
const successMessage = created?.usedLegacyDescriptionFallback
|
const successMessage = created?.usedLegacyDescriptionFallback && created?.savedDescriptionViaUserParam
|
||||||
? `${baseMessage} Описание не сохранено: на текущем сервере включен legacy-формат create-channel.`
|
? `${baseMessage} Описание сохранено через блок параметра.`
|
||||||
: baseMessage;
|
: baseMessage;
|
||||||
persistCreateSuccessFlash(successMessage);
|
persistCreateSuccessFlash(successMessage);
|
||||||
navigate('channels-list');
|
navigate('channels-list');
|
||||||
|
|||||||
@ -363,6 +363,8 @@ export function render({ navigate, route }) {
|
|||||||
|
|
||||||
const screen = document.createElement('section');
|
const screen = document.createElement('section');
|
||||||
screen.className = 'stack channels-screen channels-screen--thread';
|
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');
|
const userIndicator = document.createElement('div');
|
||||||
userIndicator.className = 'card channels-user-chip';
|
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;
|
return screen;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -605,21 +605,12 @@ function renderBody(screen, navigate, routeKey, channelData, handlers) {
|
|||||||
title.className = 'channel-head-title';
|
title.className = 'channel-head-title';
|
||||||
title.textContent = channelData.channel.displayName || channelData.channel.name;
|
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');
|
const owner = document.createElement('p');
|
||||||
owner.className = 'channel-head-meta';
|
owner.className = 'channel-head-meta';
|
||||||
owner.textContent = `Владелец: ${channelData.channel.ownerName}`;
|
owner.textContent = `Владелец: ${channelData.channel.ownerName}`;
|
||||||
|
|
||||||
const headActions = document.createElement('div');
|
const headActions = document.createElement('div');
|
||||||
headActions.className = 'row';
|
headActions.className = 'channel-head-actions';
|
||||||
const aboutButton = document.createElement('button');
|
const aboutButton = document.createElement('button');
|
||||||
aboutButton.type = 'button';
|
aboutButton.type = 'button';
|
||||||
aboutButton.className = 'secondary-btn small-btn';
|
aboutButton.className = 'secondary-btn small-btn';
|
||||||
@ -646,20 +637,7 @@ function renderBody(screen, navigate, routeKey, channelData, handlers) {
|
|||||||
headActions.append(editButton);
|
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);
|
head.append(title);
|
||||||
if (hasDescription || channelData.isOwnChannel) head.append(description);
|
|
||||||
head.append(owner, headActions);
|
head.append(owner, headActions);
|
||||||
|
|
||||||
const actionButton = document.createElement('button');
|
const actionButton = document.createElement('button');
|
||||||
@ -723,16 +701,8 @@ export function render({ navigate, route }) {
|
|||||||
|
|
||||||
const screen = document.createElement('section');
|
const screen = document.createElement('section');
|
||||||
screen.className = 'stack channels-screen channels-screen--channel';
|
screen.className = 'stack channels-screen channels-screen--channel';
|
||||||
|
const appScreen = document.getElementById('app-screen');
|
||||||
const titleFromIndex = state.channelsIndex[channelId]?.channel?.channelName;
|
appScreen?.classList.add('channels-scroll-clean');
|
||||||
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 statusBox = document.createElement('div');
|
const statusBox = document.createElement('div');
|
||||||
statusBox.className = 'card status-line is-unavailable channels-status';
|
statusBox.className = 'card status-line is-unavailable channels-status';
|
||||||
@ -846,11 +816,11 @@ export function render({ navigate, route }) {
|
|||||||
|
|
||||||
screen.append(
|
screen.append(
|
||||||
renderHeader({
|
renderHeader({
|
||||||
title: headerTitle,
|
title: '',
|
||||||
leftAction: { label: '<', onClick: () => navigate('channels-list') },
|
leftAction: { label: '<', onClick: () => navigate('channels-list') },
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
screen.append(userIndicator, statusBox);
|
screen.append(statusBox);
|
||||||
|
|
||||||
const skeleton = renderSkeleton(screen);
|
const skeleton = renderSkeleton(screen);
|
||||||
|
|
||||||
@ -924,5 +894,9 @@ export function render({ navigate, route }) {
|
|||||||
}
|
}
|
||||||
})();
|
})();
|
||||||
|
|
||||||
|
screen.cleanup = () => {
|
||||||
|
appScreen?.classList.remove('channels-scroll-clean');
|
||||||
|
};
|
||||||
|
|
||||||
return screen;
|
return screen;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -39,20 +39,6 @@ export function render({ navigate }) {
|
|||||||
const card = document.createElement('div');
|
const card = document.createElement('div');
|
||||||
card.className = 'card stack';
|
card.className = 'card stack';
|
||||||
|
|
||||||
const nextStepCard = document.createElement('div');
|
|
||||||
nextStepCard.className = 'card stack';
|
|
||||||
nextStepCard.innerHTML = `
|
|
||||||
<strong>Вы вошли как @${login}</strong>
|
|
||||||
<p class="meta-muted">Следующий шаг для ручной проверки: откройте вкладку «Каналы» в нижнем меню.</p>
|
|
||||||
`;
|
|
||||||
|
|
||||||
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');
|
const topRow = document.createElement('div');
|
||||||
topRow.className = 'row';
|
topRow.className = 'row';
|
||||||
topRow.innerHTML = `
|
topRow.innerHTML = `
|
||||||
@ -215,7 +201,7 @@ export function render({ navigate }) {
|
|||||||
shineBtn.addEventListener('click', () => onToggleClick('shine'));
|
shineBtn.addEventListener('click', () => onToggleClick('shine'));
|
||||||
|
|
||||||
card.append(topRow, badgesRow, status, listWrap);
|
card.append(topRow, badgesRow, status, listWrap);
|
||||||
screen.append(nextStepCard, card);
|
screen.append(card);
|
||||||
|
|
||||||
refreshProfileSnapshot();
|
refreshProfileSnapshot();
|
||||||
|
|
||||||
|
|||||||
@ -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() {
|
function makeClientInfo() {
|
||||||
const ua = navigator.userAgent || 'unknown';
|
const ua = navigator.userAgent || 'unknown';
|
||||||
return ua.slice(0, 50);
|
return ua.slice(0, 50);
|
||||||
@ -891,6 +901,7 @@ export class AuthService {
|
|||||||
const check = validateChannelDisplayName(channelName);
|
const check = validateChannelDisplayName(channelName);
|
||||||
if (!check.ok) throw new Error(channelNameErrorText(check.code));
|
if (!check.ok) throw new Error(channelNameErrorText(check.code));
|
||||||
const cleanChannelName = normalizeChannelDisplayName(check.normalized);
|
const cleanChannelName = normalizeChannelDisplayName(check.normalized);
|
||||||
|
const cleanChannelDescription = normalizeChannelDescription(channelDescription);
|
||||||
const channelSlug = toCanonicalChannelSlug(cleanChannelName);
|
const channelSlug = toCanonicalChannelSlug(cleanChannelName);
|
||||||
|
|
||||||
const key = `create-channel:${cleanLogin}:${channelSlug || cleanChannelName.toLowerCase()}`;
|
const key = `create-channel:${cleanLogin}:${channelSlug || cleanChannelName.toLowerCase()}`;
|
||||||
@ -930,7 +941,7 @@ export class AuthService {
|
|||||||
prevLineHashHex,
|
prevLineHashHex,
|
||||||
thisLineNumber,
|
thisLineNumber,
|
||||||
channelName: cleanChannelName,
|
channelName: cleanChannelName,
|
||||||
channelDescription,
|
channelDescription: cleanChannelDescription,
|
||||||
})
|
})
|
||||||
: makeCreateChannelBodyBytes({
|
: makeCreateChannelBodyBytes({
|
||||||
lineCode: 0,
|
lineCode: 0,
|
||||||
@ -952,6 +963,7 @@ export class AuthService {
|
|||||||
|
|
||||||
let payload;
|
let payload;
|
||||||
let usedLegacyDescriptionFallback = false;
|
let usedLegacyDescriptionFallback = false;
|
||||||
|
let savedDescriptionViaUserParam = false;
|
||||||
try {
|
try {
|
||||||
payload = await submitCreate(true);
|
payload = await submitCreate(true);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@ -960,13 +972,32 @@ export class AuthService {
|
|||||||
usedLegacyDescriptionFallback = true;
|
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 {
|
return {
|
||||||
...payload,
|
...payload,
|
||||||
usedLegacyDescriptionFallback,
|
usedLegacyDescriptionFallback,
|
||||||
|
savedDescriptionViaUserParam,
|
||||||
channel: {
|
channel: {
|
||||||
ownerBlockchainName: blockchainName,
|
...selector,
|
||||||
channelRootBlockNumber: Number(payload?.serverLastGlobalNumber),
|
|
||||||
channelRootBlockHash: normalizeHex32(payload?.serverLastGlobalHash, ZERO64),
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|||||||
@ -1293,6 +1293,14 @@ textarea.input {
|
|||||||
gap: 6px;
|
gap: 6px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.channel-head-actions {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: flex-start;
|
||||||
|
gap: 8px;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
|
||||||
.channel-head-title {
|
.channel-head-title {
|
||||||
font-size: 24px;
|
font-size: 24px;
|
||||||
color: #f0d089;
|
color: #f0d089;
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user