From ab31ccf6d8e373e2c19cee6d215d43ccb21e3baf5cfc410c25220c446ea9e9ff Mon Sep 17 00:00:00 2001 From: AidarKC Date: Thu, 14 May 2026 17:58:16 +0300 Subject: [PATCH] =?UTF-8?q?UI:=20=D0=BA=D0=B0=D0=BD=D0=B0=D0=BB=D1=8B=201.?= =?UTF-8?q?.32,=20=D0=BF=D1=83=D0=B1=D0=BB=D0=B8=D1=87=D0=BD=D1=8B=D0=B9?= =?UTF-8?q?=20type=3D1=20=D0=B8=20=D0=B0=D0=BA=D1=82=D1=83=D0=B0=D0=BB?= =?UTF-8?q?=D1=8C=D0=BD=D1=8B=D0=B9=20prevLine=20=D0=BF=D0=B5=D1=80=D0=B5?= =?UTF-8?q?=D0=B4=20=D0=B7=D0=B0=D0=BF=D0=B8=D1=81=D1=8C=D1=8E?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...ΠΎΠ½Π°Π»ΡŒΠ½Ρ‹ΠΉ-Ρ‡Π°Ρ‚-Ρ„ΠΎΡ€ΠΌΠ°Ρ‚-Π±Π»ΠΎΠΊΠ°-ΠΈ-ΠΎΠ±Ρ€Π°Ρ‚Π½Ρ‹ΠΉ-ΠΊΠ°Π½Π°Π».md | 3 ++ VERSION.properties | 4 +- shine-UI/js/pages/add-channel-view.js | 45 ++-------------- shine-UI/js/services/auth-service.js | 54 +++++++++++++++---- shine-UI/js/services/channel-name-rules.js | 4 +- shine-UI/js/services/ui-error-texts.js | 3 +- 6 files changed, 55 insertions(+), 58 deletions(-) diff --git a/Dev_Docs/Pending_Features/2026-05-14_1945_ΠΏΠ΅Ρ€ΡΠΎΠ½Π°Π»ΡŒΠ½Ρ‹ΠΉ-Ρ‡Π°Ρ‚-Ρ„ΠΎΡ€ΠΌΠ°Ρ‚-Π±Π»ΠΎΠΊΠ°-ΠΈ-ΠΎΠ±Ρ€Π°Ρ‚Π½Ρ‹ΠΉ-ΠΊΠ°Π½Π°Π».md b/Dev_Docs/Pending_Features/2026-05-14_1945_ΠΏΠ΅Ρ€ΡΠΎΠ½Π°Π»ΡŒΠ½Ρ‹ΠΉ-Ρ‡Π°Ρ‚-Ρ„ΠΎΡ€ΠΌΠ°Ρ‚-Π±Π»ΠΎΠΊΠ°-ΠΈ-ΠΎΠ±Ρ€Π°Ρ‚Π½Ρ‹ΠΉ-ΠΊΠ°Π½Π°Π».md index d19c392..af03aea 100644 --- a/Dev_Docs/Pending_Features/2026-05-14_1945_ΠΏΠ΅Ρ€ΡΠΎΠ½Π°Π»ΡŒΠ½Ρ‹ΠΉ-Ρ‡Π°Ρ‚-Ρ„ΠΎΡ€ΠΌΠ°Ρ‚-Π±Π»ΠΎΠΊΠ°-ΠΈ-ΠΎΠ±Ρ€Π°Ρ‚Π½Ρ‹ΠΉ-ΠΊΠ°Π½Π°Π».md +++ b/Dev_Docs/Pending_Features/2026-05-14_1945_ΠΏΠ΅Ρ€ΡΠΎΠ½Π°Π»ΡŒΠ½Ρ‹ΠΉ-Ρ‡Π°Ρ‚-Ρ„ΠΎΡ€ΠΌΠ°Ρ‚-Π±Π»ΠΎΠΊΠ°-ΠΈ-ΠΎΠ±Ρ€Π°Ρ‚Π½Ρ‹ΠΉ-ΠΊΠ°Π½Π°Π».md @@ -14,6 +14,9 @@ - fallback ΠΏΠΎ `ownerLogin + channelName`; - Π΄ΠΎΠΏΠΎΠ»Π½ΠΈΡ‚Π΅Π»ΡŒΠ½Ρ‹ΠΉ fallback Ρ‡Π΅Ρ€Π΅Π· `GetUser(owner)` с сопоставлСниСм `blockchainName`. Π­Ρ‚ΠΎ сниТаСт число Π»ΠΎΠΆΠ½Ρ‹Ρ… `Канал Π½Π΅ Π½Π°ΠΉΠ΄Π΅Π½` ΠΏΡ€ΠΈ ΠΎΡ‚ΠΊΡ€Ρ‹Ρ‚ΠΈΠΈ сторис/ΠΊΠ°Π½Π°Π»ΠΎΠ² Π΄Ρ€ΡƒΠ³ΠΈΡ… ΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚Π΅Π»Π΅ΠΉ. + - Π’ Ρ„ΠΎΡ€ΠΌΠ΅ Β«Π‘ΠΎΠ·Π΄Π°Ρ‚ΡŒ ΠΊΠ°Π½Π°Π»Β» (Π²ΠΊΠ»Π°Π΄ΠΊΠ° «Мои») ΡƒΠ΄Π°Π»Ρ‘Π½ Π²Ρ‹Π±ΠΎΡ€ Ρ‚ΠΈΠΏΠ° ΠΊΠ°Π½Π°Π»Π°: создаётся Ρ‚ΠΎΠ»ΡŒΠΊΠΎ ΠΏΡƒΠ±Π»ΠΈΡ‡Π½Ρ‹ΠΉ ΠΊΠ°Π½Π°Π» `type=1` с полями Β«Π½Π°Π·Π²Π°Π½ΠΈΠ΅ + описаниС». + - Минимальная Π΄Π»ΠΈΠ½Π° названия ΠΊΠ°Π½Π°Π»Π° ΠΈΠ·ΠΌΠ΅Π½Π΅Π½Π° с `3` Π½Π° `1` (Π½ΠΎΠ²Ρ‹ΠΉ Π΄ΠΈΠ°ΠΏΠ°Π·ΠΎΠ½: `1..32`). + - ΠŸΠ΅Ρ€Π΅Π΄ записью сообщСния Π² ΠΊΠ°Π½Π°Π» UI Ρ‚Π΅ΠΏΠ΅Ρ€ΡŒ ΠΏΠΎΠ»ΡƒΡ‡Π°Π΅Ρ‚ Π°ΠΊΡ‚ΡƒΠ°Π»ΡŒΠ½ΠΎΠ΅ состояниС Π»ΠΈΠ½ΠΈΠΈ ΠΊΠ°Π½Π°Π»Π° (послСдний Π±Π»ΠΎΠΊ Π² Π»ΠΈΠ½ΠΈΠΈ) ΠΈ строит `TEXT_POST` ΠΎΡ‚ свСТСго `prevLine`, Ρ‡Ρ‚ΠΎ ΡƒΠ±ΠΈΡ€Π°Π΅Ρ‚ постоянныС ΠΊΠΎΠ½Ρ„Π»ΠΈΠΊΡ‚Ρ‹ состояния (`bad_prev_line_hash` / `line_err_prev_hash_mismatch`) ΠΏΡ€ΠΈ Π΄ΠΎΠ±Π°Π²Π»Π΅Π½ΠΈΠΈ Π² свои сторис/ΠΊΠ°Π½Π°Π»Ρ‹. - Ρ‡Ρ‚ΠΎ ΠΈΠΌΠ΅Π½Π½ΠΎ ΠΏΡ€ΠΎΠ²Π΅Ρ€ΡΡ‚ΡŒ: - Π‘ΠΎΠ·Π΄Π°Ρ‚ΡŒ ΠΏΠ΅Ρ€ΡΠΎΠ½Π°Π»ΡŒΠ½Ρ‹ΠΉ ΠΏΡƒΠ±Π»ΠΈΡ‡Π½Ρ‹ΠΉ Ρ‡Π°Ρ‚ Ρ‡Π΅Ρ€Π΅Π· UI (`ΠšΠ°Π½Π°Π»Ρ‹ -> Π§Π°Ρ‚Ρ‹ -> Новый ΠΏΠ΅Ρ€ΡΠΎΠ½Π°Π»ΡŒΠ½Ρ‹ΠΉ ΠΏΡƒΠ±Π»ΠΈΡ‡Π½Ρ‹ΠΉ Ρ‡Π°Ρ‚`) ΠΈ ΡƒΠ±Π΅Π΄ΠΈΡ‚ΡŒΡΡ, Ρ‡Ρ‚ΠΎ ошибка `BAD_BLOCK_FORMAT` большС Π½Π΅ появляСтся. diff --git a/VERSION.properties b/VERSION.properties index b137940..cb23813 100644 --- a/VERSION.properties +++ b/VERSION.properties @@ -1,2 +1,2 @@ -client.version=1.2.56 -server.version=1.2.50 +client.version=1.2.57 +server.version=1.2.51 diff --git a/shine-UI/js/pages/add-channel-view.js b/shine-UI/js/pages/add-channel-view.js index f773ab5..d4b3c70 100644 --- a/shine-UI/js/pages/add-channel-view.js +++ b/shine-UI/js/pages/add-channel-view.js @@ -12,8 +12,6 @@ export const pageMeta = { id: 'add-channel-view', title: 'Π‘ΠΎΠ·Π΄Π°Ρ‚ΡŒ ΠΊΠ°Π½ const CREATE_CHANNEL_FLASH_KEY = 'shine-channels-create-success'; const CHANNEL_TYPE_PUBLIC = 1; -const CHANNEL_TYPE_PERSONAL = 100; -const CHANNEL_TYPE_GROUP = 200; function persistCreateSuccessFlash(message) { try { @@ -48,15 +46,8 @@ export function render({ navigate }) { form.innerHTML = ` Π‘ΠΎΠ·Π΄Π°Π½ΠΈΠ΅ ΠΊΠ°Π½Π°Π»Π°

МоТно ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚ΡŒ Ρ‚ΠΎΠ»ΡŒΠΊΠΎ Π»Π°Ρ‚ΠΈΠ½ΠΈΡ†Ρƒ, Ρ†ΠΈΡ„Ρ€Ρ‹, _ ΠΈ -.

-

Π”Π»ΠΈΠ½Π° названия: ΠΎΡ‚ 3 Π΄ΠΎ 32 символов.

- - - -
ΠŸΡƒΠ±Π»ΠΈΡ‡Π½Ρ‹ΠΉ ΠΊΠ°Π½Π°Π» видят всС. ΠŸΠΈΡΠ°Ρ‚ΡŒ ΠΌΠΎΠΆΠ΅Ρ‚ Ρ‚ΠΎΠ»ΡŒΠΊΠΎ Π²Π»Π°Π΄Π΅Π»Π΅Ρ†.
+

Π”Π»ΠΈΠ½Π° названия: ΠΎΡ‚ 1 Π΄ΠΎ 32 символов.

+
Π’ΠΈΠΏ ΠΊΠ°Π½Π°Π»Π° фиксирован: ΠΏΡƒΠ±Π»ΠΈΡ‡Π½Ρ‹ΠΉ (1).
@@ -75,8 +66,6 @@ export function render({ navigate }) { `; const nameEl = form.querySelector('#channel-name'); - const typeEl = form.querySelector('#channel-type'); - const typeHintEl = form.querySelector('#channel-type-hint'); const descriptionEl = form.querySelector('#channel-description'); const nameErrorEl = form.querySelector('#channel-name-error'); const descriptionErrorEl = form.querySelector('#channel-description-error'); @@ -92,27 +81,10 @@ export function render({ navigate }) { submitEl.disabled = submitInFlight; cancelEl.disabled = submitInFlight; nameEl.disabled = submitInFlight; - typeEl.disabled = submitInFlight; descriptionEl.disabled = submitInFlight; submitEl.textContent = submitInFlight ? 'Π‘ΠΎΠ·Π΄Π°Ρ‘ΠΌ...' : 'Π‘ΠΎΠ·Π΄Π°Ρ‚ΡŒ'; }; - const updateTypeHint = () => { - const typeCode = Number(typeEl.value || CHANNEL_TYPE_PUBLIC); - if (typeCode === CHANNEL_TYPE_PERSONAL) { - typeHintEl.textContent = 'Для ΠΏΠ΅Ρ€ΡΠΎΠ½Π°Π»ΡŒΠ½ΠΎΠ³ΠΎ ΠΊΠ°Π½Π°Π»Π° Π½Π°Π·Π²Π°Π½ΠΈΠ΅ Π΄ΠΎΠ»ΠΆΠ½ΠΎ Π±Ρ‹Ρ‚ΡŒ login собСсСдника.'; - nameEl.placeholder = 'НапримСр: aidar'; - return; - } - if (typeCode === CHANNEL_TYPE_GROUP) { - typeHintEl.textContent = 'Для Π³Ρ€ΡƒΠΏΠΏΠΎΠ²ΠΎΠ³ΠΎ ΠΊΠ°Π½Π°Π»Π° участников Π΄ΠΎΠ±Π°Π²Π»ΡΡŽΡ‚ ΠΊΠΎΠΌΠ°Π½Π΄Π°ΠΌΠΈ /.add ΠΈ /.remove.'; - nameEl.placeholder = 'НапримСр: team_room'; - return; - } - typeHintEl.textContent = 'ΠŸΡƒΠ±Π»ΠΈΡ‡Π½Ρ‹ΠΉ ΠΊΠ°Π½Π°Π» видят всС. ΠŸΠΈΡΠ°Ρ‚ΡŒ ΠΌΠΎΠΆΠ΅Ρ‚ Ρ‚ΠΎΠ»ΡŒΠΊΠΎ Π²Π»Π°Π΄Π΅Π»Π΅Ρ†.'; - nameEl.placeholder = 'НапримСр: my_channel-1'; - }; - const updateValidation = () => { const nameCheck = validateChannelDisplayName(nameEl.value); const descriptionCheck = validateDescription(descriptionEl.value); @@ -154,21 +126,12 @@ export function render({ navigate }) { errorEl.textContent = ''; try { - const channelType = Number(typeEl.value || CHANNEL_TYPE_PUBLIC); - if (channelType === CHANNEL_TYPE_PERSONAL) { - const targetLogin = normalizeChannelDisplayName(check.name); - const foundUser = await authService.getUser(targetLogin); - if (!foundUser?.exists) { - throw new Error('Π›ΠΎΠ³ΠΈΠ½ для ΠΏΠ΅Ρ€ΡΠΎΠ½Π°Π»ΡŒΠ½ΠΎΠ³ΠΎ ΠΊΠ°Π½Π°Π»Π° Π½Π΅ Π½Π°ΠΉΠ΄Π΅Π½.'); - } - } - await authService.addBlockCreateChannel({ login, storagePwd, channelName: normalizeChannelDisplayName(check.name), channelDescription: normalizeChannelDescription(check.description), - channelType, + channelType: CHANNEL_TYPE_PUBLIC, channelTypeVersion: 1, }); @@ -182,11 +145,9 @@ export function render({ navigate }) { }); cancelEl.addEventListener('click', () => navigate('channels-list')); - typeEl.addEventListener('change', updateTypeHint); screen.append(form); nameEl.focus(); - updateTypeHint(); updateValidation(); return screen; } diff --git a/shine-UI/js/services/auth-service.js b/shine-UI/js/services/auth-service.js index 5c95f3d..39df3e0 100644 --- a/shine-UI/js/services/auth-service.js +++ b/shine-UI/js/services/auth-service.js @@ -791,14 +791,21 @@ export class AuthService { if (!cleanLogin) throw new Error('Missing login for AddBlock'); if (!storagePwd) throw new Error('Missing storagePwd for AddBlock signing'); - const user = await this.getUser(cleanLogin); - const blockchainName = String(user?.blockchainName || `${cleanLogin}-${BCH_SUFFIX}`).trim(); - const freshNum = Number(user?.serverLastGlobalNumber); - const freshHash = normalizeHex32(user?.serverLastGlobalHash, ZERO64); - const freshCursor = { - serverLastGlobalNumber: Number.isFinite(freshNum) ? freshNum : -1, - serverLastGlobalHash: freshHash, + const resolveFreshCursor = async () => { + const user = await this.getUser(cleanLogin); + const blockchainName = String(user?.blockchainName || `${cleanLogin}-${BCH_SUFFIX}`).trim(); + const freshNum = Number(user?.serverLastGlobalNumber); + const freshHash = normalizeHex32(user?.serverLastGlobalHash, ZERO64); + return { + blockchainName, + cursor: { + serverLastGlobalNumber: Number.isFinite(freshNum) ? freshNum : -1, + serverLastGlobalHash: freshHash, + }, + }; }; + const freshState = await resolveFreshCursor(); + const blockchainName = freshState.blockchainName; const savedKeys = await loadEncryptedUserSecrets(cleanLogin, storagePwd); const blockchainPrivatePkcs8 = savedKeys?.blockchainKey; @@ -832,7 +839,7 @@ export class AuthService { }); }; - let cursor = freshCursor; + let cursor = freshState.cursor; let response = await tryAdd(cursor); if (response.status !== 200) { const knownNum = Number(response?.payload?.serverLastGlobalNumber); @@ -840,6 +847,10 @@ export class AuthService { if (Number.isFinite(knownNum) && /^[0-9a-fA-F]{64}$/.test(knownHash)) { cursor = { serverLastGlobalNumber: knownNum, serverLastGlobalHash: knownHash.toLowerCase() }; response = await tryAdd(cursor); + } else { + const refreshed = await resolveFreshCursor(); + cursor = refreshed.cursor; + response = await tryAdd(cursor); } } @@ -1211,11 +1222,32 @@ export class AuthService { rootHashHex = normalizeHex32(rootChannel.rootBlockHash, ZERO64); } + let prevLineNumber = lineCode; + let prevLineHashHex = rootHashHex; + let thisLineNumber = 0; + try { + const latestPayload = await this.getChannelMessages({ + ownerBlockchainName, + channelRootBlockNumber: lineCode, + channelRootBlockHash: rootHashHex, + }, 1, 'desc', cleanLogin); + const latestMessage = Array.isArray(latestPayload?.messages) ? latestPayload.messages[0] : null; + const latestBlockNumber = Number(latestMessage?.messageRef?.blockNumber); + const latestBlockHash = normalizeHex32(latestMessage?.messageRef?.blockHash, ''); + if (Number.isFinite(latestBlockNumber) && latestBlockNumber >= 0 && latestBlockHash) { + prevLineNumber = latestBlockNumber; + prevLineHashHex = latestBlockHash; + thisLineNumber = latestBlockNumber + 1; + } + } catch { + // fallback to root anchor + } + const bodyBytes = makeTextPostBodyBytes({ lineCode, - prevLineNumber: lineCode, - prevLineHashHex: rootHashHex, - thisLineNumber: 0, + prevLineNumber, + prevLineHashHex, + thisLineNumber, text: cleanText, }); diff --git a/shine-UI/js/services/channel-name-rules.js b/shine-UI/js/services/channel-name-rules.js index 80ba197..e5d7abe 100644 --- a/shine-UI/js/services/channel-name-rules.js +++ b/shine-UI/js/services/channel-name-rules.js @@ -1,4 +1,4 @@ -const MIN_LEN = 3; +const MIN_LEN = 1; const MAX_LEN = 32; const ALLOWED_CHARS_RE = /^[A-Za-z0-9_-]+$/; @@ -54,7 +54,7 @@ export function channelNameErrorText(code) { case 'blank': return 'Π’Π²Π΅Π΄ΠΈΡ‚Π΅ Π½Π°Π·Π²Π°Π½ΠΈΠ΅ ΠΊΠ°Π½Π°Π»Π°.'; case 'too_short': - return 'НазваниС слишком ΠΊΠΎΡ€ΠΎΡ‚ΠΊΠΎΠ΅: ΠΌΠΈΠ½ΠΈΠΌΡƒΠΌ 3 символа.'; + return 'НазваниС слишком ΠΊΠΎΡ€ΠΎΡ‚ΠΊΠΎΠ΅: ΠΌΠΈΠ½ΠΈΠΌΡƒΠΌ 1 символ.'; case 'too_long': return 'НазваниС слишком Π΄Π»ΠΈΠ½Π½ΠΎΠ΅: максимум 32 символа.'; case 'bad_chars': diff --git a/shine-UI/js/services/ui-error-texts.js b/shine-UI/js/services/ui-error-texts.js index d526903..8cc22f3 100644 --- a/shine-UI/js/services/ui-error-texts.js +++ b/shine-UI/js/services/ui-error-texts.js @@ -55,9 +55,10 @@ export function toUserMessage(error, fallback = 'ДСйствиС Π½Π΅ Π²Ρ‹ΠΏΠΎ text.includes('channel name must match') || text.includes('channelname contains unsupported') || text.includes('channelname length must be 3..32') || + text.includes('channelname length must be 1..32') || text.includes('bad_channel_name') ) { - return 'НСкоррСктноС Π½Π°Π·Π²Π°Π½ΠΈΠ΅ ΠΊΠ°Π½Π°Π»Π°. Π Π°Π·Ρ€Π΅ΡˆΠ΅Π½Ρ‹ ΠΊΠΈΡ€ΠΈΠ»Π»ΠΈΡ†Π°, Π»Π°Ρ‚ΠΈΠ½ΠΈΡ†Π°, Ρ†ΠΈΡ„Ρ€Ρ‹, ΠΏΡ€ΠΎΠ±Π΅Π», _ ΠΈ - (3..32 символа).'; + return 'НСкоррСктноС Π½Π°Π·Π²Π°Π½ΠΈΠ΅ ΠΊΠ°Π½Π°Π»Π°. Π Π°Π·Ρ€Π΅ΡˆΠ΅Π½Ρ‹ ΠΊΠΈΡ€ΠΈΠ»Π»ΠΈΡ†Π°, Π»Π°Ρ‚ΠΈΠ½ΠΈΡ†Π°, Ρ†ΠΈΡ„Ρ€Ρ‹, ΠΏΡ€ΠΎΠ±Π΅Π», _ ΠΈ - (1..32 символа).'; } if (text.includes('channel name is required') || text.includes('Π²Π²Π΅Π΄ΠΈΡ‚Π΅ имя ΠΊΠ°Π½Π°Π»Π°')) {