diff --git a/Dev_Docs/Pending_Features/2026-05-19_1618_dm-список-и-поведение-enter-в-чате.md b/Dev_Docs/Pending_Features/2026-05-19_1618_dm-список-и-поведение-enter-в-чате.md new file mode 100644 index 0000000..71b50fe --- /dev/null +++ b/Dev_Docs/Pending_Features/2026-05-19_1618_dm-список-и-поведение-enter-в-чате.md @@ -0,0 +1,24 @@ +# Личные сообщения: правая мета-колонка и Enter/Ctrl+Enter + +- Краткое описание: + - В списке `Личные сообщения` обновлена правая колонка карточки диалога: + - сверху отображается бейдж количества непрочитанных (если есть); + - снизу маленьким шрифтом отображается дата/время последнего сообщения; + - если сообщений нет, вместо времени отображается `-`. + - В экране чата нижний блок ввода закреплён (sticky) и остаётся на месте при прокрутке. + - В поле ввода чата изменено поведение клавиш: + - `Enter` отправляет сообщение; + - `Ctrl+Enter` добавляет перенос строки и не отправляет сообщение. + +- Что проверять: + - В карточках диалогов справа корректно показываются непрочитанные/время/прочерк. + - В чате нижний блок ввода не уезжает при прокрутке истории. + - `Enter` отправляет сообщение из textarea. + - `Ctrl+Enter` вставляет новую строку в textarea. + +- Ожидаемый результат: + - Список диалогов показывает полезную мета-информацию в стабильном формате. + - Ввод сообщений в чате работает в привычной схеме Enter/многострочность. + +- Статус: + - `pending` diff --git a/VERSION.properties b/VERSION.properties index c6613f1..f6a9b74 100644 --- a/VERSION.properties +++ b/VERSION.properties @@ -1,2 +1,2 @@ -client.version=1.2.71 -server.version=1.2.65 +client.version=1.2.72 +server.version=1.2.66 diff --git a/shine-UI/js/pages/chat-view.js b/shine-UI/js/pages/chat-view.js index e5bc3a8..640c042 100644 --- a/shine-UI/js/pages/chat-view.js +++ b/shine-UI/js/pages/chat-view.js @@ -416,6 +416,16 @@ export function render({ navigate, route }) { const input = form.elements.message; autoResizeComposer(input); input?.addEventListener('input', () => autoResizeComposer(input)); + input?.addEventListener('keydown', async (event) => { + if (event.key !== 'Enter') return; + if (event.ctrlKey) return; + event.preventDefault(); + const text = String(input.value || '').trim(); + if (!text) return; + input.value = ''; + autoResizeComposer(input); + await sendTextMessage(text); + }); form.querySelector('#chat-voice-input')?.addEventListener('click', async () => { await openSpeechInputModal({ diff --git a/shine-UI/js/pages/messages-list.js b/shine-UI/js/pages/messages-list.js index b5b9391..814bd5c 100644 --- a/shine-UI/js/pages/messages-list.js +++ b/shine-UI/js/pages/messages-list.js @@ -11,6 +11,17 @@ import { loadCurrentRelations } from '../services/user-connections.js'; export const pageMeta = { id: 'messages-list', title: 'Личные сообщения' }; +function formatChatRowTime(ts) { + const value = Number(ts || 0); + if (!Number.isFinite(value) || value <= 0) return '-'; + return new Intl.DateTimeFormat('ru-RU', { + day: '2-digit', + month: '2-digit', + hour: '2-digit', + minute: '2-digit', + }).format(new Date(value)); +} + export function render({ navigate }) { const screen = document.createElement('section'); screen.className = 'stack dm-screen dm-list-screen'; @@ -38,9 +49,9 @@ export function render({ navigate }) {

${item.lastMessage}

-
- ${item.time} +
${item.unread ? `${item.unread}` : ''} + ${item.time}
`; row.addEventListener('click', () => navigate(`chat-view/${encodeURIComponent(item.id)}`)); @@ -59,12 +70,13 @@ export function render({ navigate }) { const chat = getChatMessages(login); const lastChat = chat[chat.length - 1]; const unread = chat.filter((m) => m?.from === 'in' && m?.unread).length; + const lastTimeMs = Number(lastChat?.createdAtMs || 0); return { id: login, initials: (login[0] || '?').toUpperCase(), name: preview?.name || login, lastMessage: lastChat?.text || preview?.lastMessage || 'Диалог пока пуст.', - time: preview?.time || '—', + time: formatChatRowTime(lastTimeMs), unread, notInContacts: false, }; @@ -81,12 +93,13 @@ export function render({ navigate }) { const chat = getChatMessages(login); const lastChat = chat[chat.length - 1]; const unread = chat.filter((m) => m?.from === 'in' && m?.unread).length; + const lastTimeMs = Number(lastChat?.createdAtMs || 0); return { id: login, initials: (login[0] || '?').toUpperCase(), name: login, lastMessage: lastChat?.text || 'Диалог пока пуст.', - time: 'сейчас', + time: formatChatRowTime(lastTimeMs), unread, notInContacts: true, }; diff --git a/shine-UI/styles/components.css b/shine-UI/styles/components.css index 13b8467..ed6a707 100644 --- a/shine-UI/styles/components.css +++ b/shine-UI/styles/components.css @@ -3427,6 +3427,19 @@ textarea.input { box-shadow: 0 4px 16px rgba(0, 0, 0, 0.32); } +.dm-row-meta-col { + display: grid; + justify-items: end; + align-content: start; + gap: 6px; + min-width: 64px; +} + +.dm-row-time { + font-size: 11px; + line-height: 1.2; +} + .dm-chat-wrap { gap: 12px; } @@ -3465,6 +3478,14 @@ textarea.input { gap: 10px; grid-template-columns: 1fr auto; align-items: end; + position: sticky; + bottom: 0; + z-index: 10; + padding: 10px; + border-top: 1px solid rgba(212, 175, 55, 0.22); + background: rgba(8, 12, 20, 0.9); + backdrop-filter: blur(12px); + -webkit-backdrop-filter: blur(12px); } .dm-voice-btn {