diff --git a/DOC/Формат Блокцейнов.md b/DOC/Формат Блокцейнов.md index 518e3c3..a12008f 100644 --- a/DOC/Формат Блокцейнов.md +++ b/DOC/Формат Блокцейнов.md @@ -1,4 +1,4 @@ -1) Общий формат записи блока (BchBlockEntry) +нет это неправильно откатись к1) Общий формат записи блока (BchBlockEntry) Блок хранится как FULL = RAW + TAIL, где RAW участвует в хэшировании/подписи, а TAIL хранит подпись и итоговый хэш. diff --git a/shine-UI/js/app.js b/shine-UI/js/app.js index d1795f1..301f18f 100644 --- a/shine-UI/js/app.js +++ b/shine-UI/js/app.js @@ -637,11 +637,10 @@ async function init() { return; } - const myLogin = String(state.session.login || '').trim().toLowerCase(); const fromLogin = parsed.fromLogin || ''; const toLogin = parsed.toLogin || ''; - const chatId = String(fromLogin || '').toLowerCase() === myLogin ? toLogin : fromLogin; const messageType = Number(parsed.messageType || 0); + const chatId = messageType === 2 ? toLogin : fromLogin; const text = (messageType === 1 || messageType === 2) ? new TextDecoder().decode(parsed.payloadBytes || new Uint8Array(0)) : ''; diff --git a/shine-UI/js/pages/chat-view.js b/shine-UI/js/pages/chat-view.js index 5456654..34e104e 100644 --- a/shine-UI/js/pages/chat-view.js +++ b/shine-UI/js/pages/chat-view.js @@ -27,6 +27,47 @@ function parseBaseKey(baseKey) { return { fromLogin, toLogin, timeMs, nonce }; } +function resolveMessageTimeMs(msg) { + const base = parseBaseKey(msg?.baseKey); + if (base?.timeMs && Number.isFinite(base.timeMs) && base.timeMs > 0) return base.timeMs; + + const messageKey = String(msg?.messageKey || '').trim(); + if (messageKey) { + const parts = messageKey.split('|'); + const timeMs = Number(parts[2] || 0); + if (parts.length >= 4 && Number.isFinite(timeMs) && timeMs > 0) return timeMs; + } + + const tempId = String(msg?.tempId || '').trim(); + if (tempId.startsWith('tmp-')) { + const ts = Number(tempId.split('-')[1] || 0); + if (Number.isFinite(ts) && ts > 0) return ts; + } + + const fallback = Number(msg?.createdAtMs || 0); + if (Number.isFinite(fallback) && fallback > 0) return fallback; + return 0; +} + +function formatMessageTime(valueMs) { + const timeMs = Number(valueMs || 0); + if (!Number.isFinite(timeMs) || timeMs <= 0) return ''; + return new Intl.DateTimeFormat('ru-RU', { + day: '2-digit', + month: '2-digit', + year: '2-digit', + hour: '2-digit', + minute: '2-digit', + }).format(new Date(timeMs)); +} + +function resolveDeliveryStatus(msg) { + if (msg?.from !== 'out') return ''; + if (msg?.secondTick) return '✓✓'; + if (msg?.firstTick) return '✓'; + return '…'; +} + function renderLog(list, chatId) { list.innerHTML = ''; const messages = getChatMessages(chatId); @@ -45,13 +86,28 @@ function renderLog(list, chatId) { const bubble = document.createElement('div'); const bubbleKind = String(msg?.kind || '').trim(); bubble.className = `bubble ${msg.from}${bubbleKind ? ` ${bubbleKind}` : ''}`; - let text = msg.text || ''; - if (msg.from === 'out') { - if (msg.secondTick) text += ' ✓✓'; - else if (msg.firstTick) text += ' ✓'; - else text += ' …'; + + const textNode = document.createElement('div'); + textNode.className = 'bubble-text'; + textNode.textContent = msg.text || ''; + + const metaNode = document.createElement('div'); + metaNode.className = 'bubble-meta'; + + const timeNode = document.createElement('span'); + timeNode.className = 'bubble-time'; + timeNode.textContent = formatMessageTime(resolveMessageTimeMs(msg)); + metaNode.append(timeNode); + + const status = resolveDeliveryStatus(msg); + if (status) { + const statusNode = document.createElement('span'); + statusNode.className = 'bubble-status'; + statusNode.textContent = status; + metaNode.append(statusNode); } - bubble.textContent = text; + + bubble.append(textNode, metaNode); list.append(bubble); }); list.scrollTop = list.scrollHeight; diff --git a/shine-UI/styles/components.css b/shine-UI/styles/components.css index e4dca4f..e3575d0 100644 --- a/shine-UI/styles/components.css +++ b/shine-UI/styles/components.css @@ -796,6 +796,32 @@ line-height: 1.35; } +.bubble-text { + white-space: pre-wrap; + word-break: break-word; +} + +.bubble-meta { + margin-top: 4px; + display: flex; + justify-content: flex-end; + align-items: center; + gap: 5px; + font-size: 9px; + line-height: 1.1; + color: rgba(198, 214, 247, 0.78); +} + +.bubble-time { + letter-spacing: 0.01em; +} + +.bubble-status { + min-width: 14px; + text-align: right; + color: rgba(215, 230, 255, 0.92); +} + .bubble.in { justify-self: start; background: #1f2c46;