Каналы: создавать профиль одним блоком

This commit is contained in:
AidarKC
2026-08-01 02:19:29 +04:00
parent 8125d1a124
commit 8303682bd7
11 changed files with 201 additions and 118 deletions
+6 -14
View File
@@ -36,7 +36,7 @@ function renderAvatarPreview(slot, avatar, title) {
slot.innerHTML = '';
const wrap = document.createElement('div');
wrap.className = 'channel-profile-avatar';
wrap.style.setProperty('--channel-avatar-size', '82px');
wrap.style.setProperty('--channel-avatar-size', '104px');
if (avatar?.ar) {
const img = document.createElement('img');
img.alt = 'Аватар канала';
@@ -180,25 +180,17 @@ export function render({ navigate }) {
errorEl.textContent = '';
try {
const createResult = await authService.addBlockCreateChannel({
await authService.addBlockCreateChannel({
login,
storagePwd,
channelName: normalizeChannelDisplayName(check.name),
channelDescription: '',
channelDescription: check.description,
channelProfileTitle: check.title,
channelAvatar: selectedAvatar,
channelType: CHANNEL_TYPE_PUBLIC,
channelTypeVersion: 1,
});
if (check.title || check.description || selectedAvatar?.ar) {
await authService.addBlockChannelMeta({
login,
storagePwd,
channel: createResult.channel,
title: check.title,
description: check.description,
avatar: selectedAvatar,
});
if (selectedAvatar?.ar) markArweaveAttachmentPlaced(login, selectedAvatar);
}
if (selectedAvatar?.ar) markArweaveAttachmentPlaced(login, selectedAvatar);
persistCreateSuccessFlash(`Канал "${normalizeChannelDisplayName(check.name)}" создан.`);
navigate('channels-list');
+32 -76
View File
@@ -65,6 +65,7 @@ const CHANNEL_TYPE_PUBLIC = 1;
const CHANNEL_TYPE_PERSONAL = 100;
const CHANNEL_TYPE_GROUP = 200;
const CHANNEL_TYPE_VERSION_DEFAULT = 1;
const CREATE_CHANNEL_DESCRIPTION_MAX_BYTES = 2048;
const SESSION_TYPE_CLIENT = 1;
const SESSION_TYPE_WALLET = 50;
const SESSION_TYPE_HOMESERVER = 100;
@@ -691,10 +692,10 @@ function makeConnectionBodyBytes({
}
function normalizeChannelDescription(value) {
const text = String(value == null ? '' : value).trim().replace(/\s+/g, ' ');
const text = String(value == null ? '' : value).trim().replace(/\r\n/g, '\n').replace(/\r/g, '\n');
const bytes = utf8Bytes(text);
if (bytes.length > 200) {
throw new Error('Описание канала слишком длинное: максимум 200 символов.');
if (bytes.length > CREATE_CHANNEL_DESCRIPTION_MAX_BYTES) {
throw new Error('Описание канала слишком длинное: максимум 2048 байт.');
}
return text;
}
@@ -771,8 +772,8 @@ function makeCreateChannelBodyBytes({
}
const descriptionBytes = utf8Bytes(cleanDescription);
if (descriptionBytes.length > 200) {
throw new Error('Описание канала слишком длинное: максимум 200 символов.');
if (descriptionBytes.length > CREATE_CHANNEL_DESCRIPTION_MAX_BYTES) {
throw new Error('Описание канала слишком длинное: максимум 2048 байт.');
}
const typeVer = Number(channelTypeVersion);
if (!Number.isFinite(typeCode) || typeCode < 0 || typeCode > 65535) {
@@ -796,31 +797,6 @@ function makeCreateChannelBodyBytes({
);
}
function makeCreateChannelBodyBytesLegacy({
lineCode,
prevLineNumber,
prevLineHashHex,
thisLineNumber,
channelName,
}) {
const check = validatePersonalChannelName(channelName);
if (!check.ok) throw new Error(check.error);
const cleanName = check.normalized;
const nameBytes = utf8Bytes(cleanName);
if (nameBytes.length < 1 || nameBytes.length > 255) {
throw new Error('Channel name must be 1..255 bytes');
}
return concatBytes(
int32Bytes(lineCode),
int32Bytes(prevLineNumber),
hexToBytes(normalizeHex32(prevLineHashHex)),
int32Bytes(thisLineNumber),
int8Byte(nameBytes.length),
nameBytes,
);
}
function makeTextPostBodyBytes({ lineCode, prevLineNumber, prevLineHashHex, thisLineNumber, text }) {
const message = String(text || '').trim();
if (!message) throw new Error('Message text is required');
@@ -2084,6 +2060,8 @@ export class AuthService {
login,
channelName,
channelDescription = '',
channelProfileTitle = '',
channelAvatar = null,
channelType = CHANNEL_TYPE_PUBLIC,
channelTypeVersion = CHANNEL_TYPE_VERSION_DEFAULT,
storagePwd,
@@ -2099,7 +2077,13 @@ export class AuthService {
throw new Error(typeCode === CHANNEL_TYPE_PERSONAL ? nameCheck.error : channelNameErrorText(nameCheck.code));
}
const cleanChannelName = normalizeChannelDisplayName(nameCheck.normalized);
const cleanChannelDescription = normalizeChannelDescription(channelDescription);
const cleanChannelDescription = typeCode === CHANNEL_TYPE_PUBLIC
? composeChannelMetaText({
title: channelProfileTitle,
description: channelDescription,
avatar: channelAvatar,
})
: normalizeChannelDescription(channelDescription);
const channelSlug = toCanonicalChannelSlug(cleanChannelName);
const typeVersion = Number(channelTypeVersion);
const key = `create-channel:${cleanLogin}:${typeCode}:${channelSlug || cleanChannelName.toLowerCase()}`;
@@ -2131,51 +2115,23 @@ export class AuthService {
thisLineNumber = createdChannels.length + 1;
}
let payload;
try {
payload = await this.addBlockSigned({
login: cleanLogin,
storagePwd,
msgType: MSG_TYPE_TECH,
msgSubType: MSG_SUBTYPE_TECH_CREATE_CHANNEL,
msgVersion: CREATE_CHANNEL_BODY_VERSION,
bodyBytes: makeCreateChannelBodyBytes({
lineCode: 0,
prevLineNumber,
prevLineHashHex,
thisLineNumber,
channelName: cleanChannelName,
channelDescription: cleanChannelDescription,
channelType: typeCode,
channelTypeVersion: typeVersion,
}),
});
} catch (error) {
const rawCode = String(error?.code || '').toUpperCase();
const rawText = String(error?.message || '').toLowerCase();
const isLegacyFormatMismatch = (
rawCode === 'BAD_BLOCK_FORMAT' ||
rawText.includes('bad_block_format') ||
rawText.includes('некорректный формат блока')
);
if (!isLegacyFormatMismatch) throw error;
// Совместимость со старыми серверами, где CreateChannel body без description/type.
payload = await this.addBlockSigned({
login: cleanLogin,
storagePwd,
msgType: MSG_TYPE_TECH,
msgSubType: MSG_SUBTYPE_TECH_CREATE_CHANNEL,
msgVersion: 1,
bodyBytes: makeCreateChannelBodyBytesLegacy({
lineCode: 0,
prevLineNumber,
prevLineHashHex,
thisLineNumber,
channelName: cleanChannelName,
}),
});
}
const payload = await this.addBlockSigned({
login: cleanLogin,
storagePwd,
msgType: MSG_TYPE_TECH,
msgSubType: MSG_SUBTYPE_TECH_CREATE_CHANNEL,
msgVersion: CREATE_CHANNEL_BODY_VERSION,
bodyBytes: makeCreateChannelBodyBytes({
lineCode: 0,
prevLineNumber,
prevLineHashHex,
thisLineNumber,
channelName: cleanChannelName,
channelDescription: cleanChannelDescription,
channelType: typeCode,
channelTypeVersion: typeVersion,
}),
});
const selector = {
ownerBlockchainName: blockchainName,