SHA256
Добавить SHiNE теги в DM и безопасный рендер превью
This commit is contained in:
@@ -20,6 +20,7 @@ import { startOutgoingCall } from '../services/call-service.js';
|
||||
import { openSpeechInputModal } from '../components/speech-input-modal.js';
|
||||
import { isSpeechToTextConfigured, isTextToSpeechConfigured, speakTextBySettings } from '../services/speech-tools-service.js';
|
||||
import { showToast } from '../services/channels-ux.js';
|
||||
import { parseDmTechBlocks, sanitizeUserDmTextForSend } from '../services/dm-tech-blocks.js';
|
||||
|
||||
export const pageMeta = { id: 'chat-view', title: 'Чат' };
|
||||
const CONVERSATION_CLEAR_NOTICE_TEXT = 'История переписки очищена с этого места';
|
||||
@@ -483,12 +484,14 @@ function renderLog(list, chatId, { onOpenActions, markAsRead = true, scrollMode
|
||||
}
|
||||
|
||||
const bubble = document.createElement('div');
|
||||
const bubbleKind = String(msg?.kind || '').trim();
|
||||
const parsedText = parseDmTechBlocks(String(msg?.text || ''));
|
||||
const autoKind = parsedText.callSummary ? 'call-tech' : '';
|
||||
const bubbleKind = String(msg?.kind || autoKind || '').trim();
|
||||
bubble.className = `bubble ${msg.from}${bubbleKind ? ` ${bubbleKind}` : ''}`;
|
||||
|
||||
const textNode = document.createElement('div');
|
||||
textNode.className = 'bubble-text';
|
||||
textNode.textContent = msg.text || '';
|
||||
textNode.textContent = parsedText.displayText || '';
|
||||
bubble.append(textNode);
|
||||
|
||||
const metaNode = document.createElement('div');
|
||||
@@ -582,7 +585,8 @@ export function render({ navigate, route }) {
|
||||
showTtsMissingConfigDialog(navigate);
|
||||
return;
|
||||
}
|
||||
await speakTextBySettings(String(msg?.text || ''), state.entrySettings);
|
||||
const parsedText = parseDmTechBlocks(String(msg?.text || ''));
|
||||
await speakTextBySettings(String(parsedText.displayText || ''), state.entrySettings);
|
||||
};
|
||||
|
||||
const handleStartCall = async () => {
|
||||
@@ -888,7 +892,7 @@ export function render({ navigate, route }) {
|
||||
};
|
||||
|
||||
const sendTextMessage = async (rawText) => {
|
||||
const text = String(rawText || '').trim();
|
||||
const text = sanitizeUserDmTextForSend(String(rawText || '')).trim();
|
||||
if (!text) return;
|
||||
const editing = activeEdit;
|
||||
const tempId = editing ? '' : addOutgoingPendingMessage(chatId, text);
|
||||
|
||||
@@ -10,6 +10,7 @@ import {
|
||||
import { loadCurrentRelations } from '../services/user-connections.js';
|
||||
import { renderUserAvatar } from '../components/avatar-image.js';
|
||||
import { loadProfileSnapshot } from '../services/user-profile-params.js';
|
||||
import { parseDmTechBlocks } from '../services/dm-tech-blocks.js';
|
||||
|
||||
export const pageMeta = { id: 'messages-list', title: 'Личные сообщения' };
|
||||
const dmAvatarSnapshotCache = new Map();
|
||||
@@ -64,6 +65,12 @@ function createDmAvatar(login) {
|
||||
return avatarEl;
|
||||
}
|
||||
|
||||
function resolveLastMessagePreview(text = '') {
|
||||
const parsed = parseDmTechBlocks(String(text || ''));
|
||||
const display = String(parsed.displayText || '').trim();
|
||||
return display || '';
|
||||
}
|
||||
|
||||
function formatChatRowTime(ts) {
|
||||
const value = Number(ts || 0);
|
||||
if (!Number.isFinite(value) || value <= 0) return '';
|
||||
@@ -88,12 +95,14 @@ export function render({ navigate }) {
|
||||
<div class="dm-head-brand">
|
||||
<div class="dm-head-hex">${(login[0] || 'A').toUpperCase()}</div>
|
||||
<div class="dm-head-id">
|
||||
<span class="dm-head-name">${login}</span>
|
||||
<span class="dm-head-name"></span>
|
||||
</div>
|
||||
</div>
|
||||
<h1 class="dm-head-title">Контакты</h1>
|
||||
<button type="button" class="dm-head-plus" aria-label="Новый диалог">+</button>
|
||||
`;
|
||||
const headName = head.querySelector('.dm-head-name');
|
||||
if (headName) headName.textContent = login;
|
||||
head.querySelector('.dm-head-plus')?.addEventListener('click', () => navigate('contact-search-view'));
|
||||
|
||||
const divider = document.createElement('div');
|
||||
@@ -113,19 +122,25 @@ export function render({ navigate }) {
|
||||
row.innerHTML = `
|
||||
<div class="dm-row-main">
|
||||
<div class="dm-row-titleline dm-row-titlewrap">
|
||||
<strong class="dm-row-title">${item.name}</strong>
|
||||
<strong class="dm-row-title"></strong>
|
||||
${item.notInContacts ? '<span class="dm-contact-note">не в контактах</span>' : ''}
|
||||
</div>
|
||||
<p class="dm-row-last-message">${item.lastMessage}</p>
|
||||
<p class="dm-row-last-message"></p>
|
||||
</div>
|
||||
<div class="dm-row-meta-col">
|
||||
${item.unread ? `<span class="dm-unread-badge">${item.unread > 99 ? '99+' : item.unread}</span>` : '<span class="dm-row-meta-spacer" aria-hidden="true"></span>'}
|
||||
<div class="dm-row-meta-line">
|
||||
${item.time ? `<span class="dm-row-time">${item.time}</span>` : '<span class="dm-row-time dm-row-time--empty"></span>'}
|
||||
${item.time ? '<span class="dm-row-time"></span>' : '<span class="dm-row-time dm-row-time--empty"></span>'}
|
||||
<span class="dm-chevron">${SVG_CHEVRON}</span>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
const titleEl = row.querySelector('.dm-row-title');
|
||||
const previewEl = row.querySelector('.dm-row-last-message');
|
||||
const timeEl = row.querySelector('.dm-row-time');
|
||||
if (titleEl) titleEl.textContent = String(item.name || '');
|
||||
if (previewEl) previewEl.textContent = resolveLastMessagePreview(item.lastMessage) || 'Диалог пока пуст.';
|
||||
if (timeEl) timeEl.textContent = String(item.time || '');
|
||||
row.prepend(avatarWrap);
|
||||
row.addEventListener('click', () => navigate(`chat-view/${encodeURIComponent(normalizeDmChatId(item.id))}`));
|
||||
return row;
|
||||
|
||||
Reference in New Issue
Block a user