From 53cd55a2a036f4c624b080bf52945b661dcb6c88f75e838e79e110eb116913e0 Mon Sep 17 00:00:00 2001 From: AidarKC Date: Wed, 2 Sep 2026 14:23:42 +0400 Subject: [PATCH] Fix DM name fallbacks and chat header --- shine-UI/js/pages/chat-view.js | 97 ++++++++++++--------- shine-UI/js/pages/messages-list.js | 2 +- shine-UI/js/services/user-profile-params.js | 5 ++ shine-UI/styles/components.css | 22 +++++ 4 files changed, 85 insertions(+), 41 deletions(-) diff --git a/shine-UI/js/pages/chat-view.js b/shine-UI/js/pages/chat-view.js index 71dd9512..ef940ba6 100644 --- a/shine-UI/js/pages/chat-view.js +++ b/shine-UI/js/pages/chat-view.js @@ -128,7 +128,7 @@ function createChatHeaderParts(login, navigate) { loginEl.setAttribute('role', 'heading'); loginEl.setAttribute('aria-level', '1'); loginEl.setAttribute('aria-label', `Чат с ${cleanLogin}`); - loginEl.innerHTML = `${cleanLogin}${cleanLogin}`; + loginEl.innerHTML = `${cleanLogin}`; void loadProfileSnapshot(cleanLogin) .then((snapshot) => { @@ -153,7 +153,10 @@ function createChatHeaderParts(login, navigate) { const nameNode = loginEl.querySelector('.chat-header-display-name'); const loginNode = loginEl.querySelector('.chat-header-user-login'); if (nameNode) nameNode.textContent = display; - if (loginNode) loginNode.textContent = cleanLogin; + if (loginNode) { + loginNode.textContent = cleanLogin; + loginNode.hidden = display === cleanLogin; + } } }) .catch(() => {}); @@ -967,7 +970,6 @@ export function render({ navigate, route, chrome }) { const screen = document.createElement('section'); screen.className = 'stack dm-screen dm-chat-screen'; const isTextToSpeechReady = isTextToSpeechConfigured(state.entrySettings); - const isKnownContact = (state.contacts || []).some((x) => String(x || '').toLowerCase() === String(chatId || '').toLowerCase()); const hasUnreadIncoming = getChatMessages(chatId).some((msg) => msg?.from === 'in' && msg?.unread); const UNREAD_SEPARATOR_AUTO_HIDE_MS = 5000; let historyHasMore = true; @@ -1151,44 +1153,59 @@ export function render({ navigate, route, chrome }) { chatHeaderLeft?.append(chatHeaderParts.avatarButton); chrome?.setTopbar(chatHeader); - if (!isKnownContact) { - const card = document.createElement('div'); - card.className = 'card'; - const btn = document.createElement('button'); - btn.className = 'secondary-btn'; - btn.type = 'button'; - btn.textContent = 'Добавить собеседника в контакты'; - btn.addEventListener('click', async () => { - try { - const approved = await openConfirmContactModal(chatId); - if (!approved) return; - await authService.setUserRelation({ - login: state.session.login, - toLogin: chatId, - kind: 'contact', - enabled: true, - storagePwd: state.session.storagePwdInMemory, - }); - const contactsPayload = await authService.listContacts(); - setContacts(contactsPayload?.contacts || []); - addAppLogEntry({ - level: 'info', - source: 'contacts', - message: `Пользователь ${chatId} добавлен в контакты`, - }); - card.remove(); - } catch (e) { - addAppLogEntry({ - level: 'warn', - source: 'contacts', - message: 'Не удалось добавить пользователя в контакты', - details: { login: chatId, error: e?.message || 'unknown' }, - }); - } + const addContactCard = document.createElement('div'); + addContactCard.className = 'card dm-add-contact-card'; + addContactCard.hidden = true; + const addContactBtn = document.createElement('button'); + addContactBtn.className = 'secondary-btn'; + addContactBtn.type = 'button'; + addContactBtn.textContent = 'Добавить собеседника в контакты'; + addContactBtn.addEventListener('click', async () => { + try { + const approved = await openConfirmContactModal(chatId); + if (!approved) return; + await authService.setUserRelation({ + login: state.session.login, + toLogin: chatId, + kind: 'contact', + enabled: true, + storagePwd: state.session.storagePwdInMemory, + }); + const contactsPayload = await authService.listContacts(); + setContacts(contactsPayload?.contacts || []); + addAppLogEntry({ + level: 'info', + source: 'contacts', + message: `Пользователь ${chatId} добавлен в контакты`, + }); + addContactCard.remove(); + } catch (e) { + addAppLogEntry({ + level: 'warn', + source: 'contacts', + message: 'Не удалось добавить пользователя в контакты', + details: { login: chatId, error: e?.message || 'unknown' }, + }); + } + }); + addContactCard.append(addContactBtn); + log.prepend(addContactCard); + + // The button is valid only when there is no social relation at all. + // listContacts already returns relationFlag for every dialog/peer, so no extra server API is needed. + void authService.listContacts() + .then((contactsPayload) => { + setContacts(contactsPayload?.contacts || []); + const peerKey = String(chatId || '').trim().toLowerCase(); + const peerDialog = (Array.isArray(contactsPayload?.dialogs) ? contactsPayload.dialogs : []) + .find((dialog) => String(dialog?.peerLogin || '').trim().toLowerCase() === peerKey); + const relationFlag = String(peerDialog?.relationFlag || 'none').trim().toLowerCase(); + addContactCard.hidden = relationFlag !== 'none'; + }) + .catch(() => { + // On an API failure do not offer a potentially invalid relation action. + addContactCard.hidden = true; }); - card.append(btn); - screen.append(card); - } const form = document.createElement('form'); form.className = 'chat-input dm-chat-input'; diff --git a/shine-UI/js/pages/messages-list.js b/shine-UI/js/pages/messages-list.js index d92fb8d1..d9fcce62 100644 --- a/shine-UI/js/pages/messages-list.js +++ b/shine-UI/js/pages/messages-list.js @@ -319,7 +319,7 @@ function renderRow(item) { 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 = userDisplayName(item); + if (titleEl) titleEl.textContent = userDisplayName({ login: item.peerLogin, firstName: item.firstName, lastName: item.lastName }); if (previewEl) previewEl.textContent = 'Загрузка…'; if (timeEl) timeEl.textContent = formatChatRowTime(item.lastMessageTimeMs); row.prepend(avatarWrap); diff --git a/shine-UI/js/services/user-profile-params.js b/shine-UI/js/services/user-profile-params.js index 33dd57b1..58408d2b 100644 --- a/shine-UI/js/services/user-profile-params.js +++ b/shine-UI/js/services/user-profile-params.js @@ -116,6 +116,9 @@ export async function loadProfileSnapshot(login) { }); } + const firstName = String(fields.find((field) => field.key === 'first_name')?.value || '').trim(); + const lastName = String(fields.find((field) => field.key === 'last_name')?.value || '').trim(); + const latestAccountRole = loadLatestByAliasesFromItems(items, ['account_role']); const rawAccountRole = String(latestAccountRole?.value || '').trim().toLowerCase(); const accountRole = rawAccountRole === PROFILE_ACCOUNT_ROLE_PRIMARY || rawAccountRole === PROFILE_ACCOUNT_ROLE_NON_VOTING @@ -157,6 +160,8 @@ export async function loadProfileSnapshot(login) { return { fields, + firstName, + lastName, toggles, accountRole, accountRoleTimeMs: latestAccountRole?.timeMs || 0, diff --git a/shine-UI/styles/components.css b/shine-UI/styles/components.css index 4e4d3f7c..81f456f2 100644 --- a/shine-UI/styles/components.css +++ b/shine-UI/styles/components.css @@ -6468,6 +6468,14 @@ html, body { overflow-x: hidden; } gap: 10px; } +.dm-add-contact-card { + margin: 0 14px 2px; +} + +.dm-add-contact-card .secondary-btn { + width: 100%; +} + .dm-history-loader { position: sticky; top: 0; @@ -11378,3 +11386,17 @@ body.chat-topbar-overlay .composer-slot { font-size: 9px; } } + +/* ===== Channel list preview/time flex sizing fix (2026-09-02) ===== + * The global .channel-row-time rule still has width:100%, which makes the + * timestamp consume the entire flex row and collapses the message preview. + * Keep the timestamp content-sized and reserve a little more room at the + * right edge so the final digit is never clipped by the widened list tile. */ +.channels-screen--list .channel-row { + padding-right: 14px !important; +} + +.channels-screen--list .channel-row-preview-line .channel-row-time { + width: auto !important; + max-width: none !important; +}