SHA256
Зафиксировали текущие изменения UI и документации
This commit is contained in:
@@ -1,7 +1,6 @@
|
||||
import { renderHeader } from '../components/header.js';
|
||||
import { renderUserAvatar } from '../components/avatar-image.js';
|
||||
import { createOverflowDots } from '../components/overflow-dots.js';
|
||||
import { createShineConnectionsLogo } from '../components/shine-logo.js';
|
||||
import { directMessages } from '../mock-data.js';
|
||||
import {
|
||||
addAppLogEntry,
|
||||
@@ -36,6 +35,72 @@ import { makeProfileLinksRoute, makeProfileRoute } from '../services/shine-route
|
||||
export const pageMeta = { id: 'chat-view', title: 'Чат' };
|
||||
const CONVERSATION_CLEAR_NOTICE_TEXT = 'История переписки очищена с этого места';
|
||||
|
||||
function menuIconSvg(name) {
|
||||
const paths = {
|
||||
reply: '<path d="M9 7 4 12l5 5"/><path d="M5 12h8a6 6 0 0 1 6 6"/>',
|
||||
copy: '<rect x="8" y="8" width="10" height="10" rx="2"/><path d="M6 15H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h8a2 2 0 0 1 2 2v1"/>',
|
||||
read: '<path d="M5 9v6h4l5 4V5L9 9H5Z"/><path d="M17 9a4 4 0 0 1 0 6"/>',
|
||||
edit: '<path d="m4 20 4.4-1 9.9-9.9a2.1 2.1 0 0 0-3-3L5.4 16 4 20Z"/><path d="m13.8 7.2 3 3"/>',
|
||||
delete: '<path d="M4 7h16"/><path d="M9 7V4h6v3"/><path d="m7 7 1 13h8l1-13"/>',
|
||||
call: '<path d="M7.6 10.8a15 15 0 0 0 5.6 5.6l1.9-1.9a1 1 0 0 1 1-.2c1.1.4 2.3.6 3.5.6a1 1 0 0 1 1 1V20a1 1 0 0 1-1 1C10.6 21 3 13.4 3 4a1 1 0 0 1 1-1h3.4a1 1 0 0 1 1 1c0 1.2.2 2.4.6 3.5a1 1 0 0 1-.3 1l-1.1 1.3Z"/>',
|
||||
video: '<rect x="3" y="6" width="13" height="12" rx="2"/><path d="m16 10 5-3v10l-5-3"/>',
|
||||
clear: '<path d="M4 7h16"/><path d="M9 7V4h6v3"/><path d="m7 7 1 13h8l1-13"/>',
|
||||
};
|
||||
return `<svg class="dm-menu-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">${paths[name] || ''}</svg>`;
|
||||
}
|
||||
|
||||
function openUserIdentityMenu({ anchorEl, login, navigate }) {
|
||||
const root = document.getElementById('modal-root');
|
||||
if (!root || !anchorEl) return;
|
||||
const cleanLogin = String(login || '').trim();
|
||||
root.innerHTML = `
|
||||
<div class="dm-floating-menu-layer dm-user-menu-layer" id="chat-user-menu-layer">
|
||||
<div class="dm-head-menu dm-head-menu--portal dm-user-identity-menu" role="menu">
|
||||
<button type="button" class="dm-head-menu-item" role="menuitem" data-user-action="connections">
|
||||
<img class="dm-menu-image-icon" src="/assets/SHiNE_connections_blue.svg" alt="" aria-hidden="true" />
|
||||
<span>Связи</span>
|
||||
</button>
|
||||
<button type="button" class="dm-head-menu-item" role="menuitem" data-user-action="profile">
|
||||
<img class="dm-menu-image-icon" src="/assets/profile-icon-profile.svg" alt="" aria-hidden="true" />
|
||||
<span>Профиль</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
const layer = root.querySelector('#chat-user-menu-layer');
|
||||
const menu = root.querySelector('.dm-user-identity-menu');
|
||||
const close = () => {
|
||||
document.removeEventListener('keydown', onKeydown);
|
||||
root.innerHTML = '';
|
||||
};
|
||||
const onKeydown = (event) => {
|
||||
if (event.key === 'Escape') close();
|
||||
};
|
||||
const rect = anchorEl.getBoundingClientRect();
|
||||
|
||||
window.requestAnimationFrame(() => {
|
||||
if (!menu) return;
|
||||
const width = menu.offsetWidth || 190;
|
||||
const left = Math.max(10, Math.min(window.innerWidth - width - 10, rect.left));
|
||||
menu.style.left = `${Math.round(left)}px`;
|
||||
menu.style.top = `${Math.round(rect.bottom + 7)}px`;
|
||||
});
|
||||
|
||||
layer?.addEventListener('pointerdown', (event) => {
|
||||
if (event.target === layer) close();
|
||||
});
|
||||
document.addEventListener('keydown', onKeydown);
|
||||
root.querySelector('[data-user-action="connections"]')?.addEventListener('click', () => {
|
||||
close();
|
||||
navigate(makeProfileLinksRoute(cleanLogin));
|
||||
});
|
||||
root.querySelector('[data-user-action="profile"]')?.addEventListener('click', () => {
|
||||
close();
|
||||
navigate(makeProfileRoute(cleanLogin));
|
||||
});
|
||||
}
|
||||
|
||||
function createChatHeaderParts(login, navigate) {
|
||||
const cleanLogin = String(login || '').trim() || 'unknown';
|
||||
|
||||
@@ -52,12 +117,13 @@ function createChatHeaderParts(login, navigate) {
|
||||
const avatarButton = document.createElement('button');
|
||||
avatarButton.type = 'button';
|
||||
avatarButton.className = 'chat-header-avatar-btn';
|
||||
avatarButton.title = `Профиль ${cleanLogin}`;
|
||||
avatarButton.setAttribute('aria-label', `Открыть профиль ${cleanLogin}`);
|
||||
avatarButton.title = `Меню ${cleanLogin}`;
|
||||
avatarButton.setAttribute('aria-label', `Открыть меню пользователя ${cleanLogin}`);
|
||||
avatarButton.append(avatarSlot);
|
||||
|
||||
const loginEl = document.createElement('span');
|
||||
loginEl.className = 'chat-header-login';
|
||||
const loginEl = document.createElement('button');
|
||||
loginEl.type = 'button';
|
||||
loginEl.className = 'chat-header-login chat-header-login-btn';
|
||||
loginEl.setAttribute('role', 'heading');
|
||||
loginEl.setAttribute('aria-level', '1');
|
||||
loginEl.setAttribute('aria-label', `Чат с ${cleanLogin}`);
|
||||
@@ -84,23 +150,14 @@ function createChatHeaderParts(login, navigate) {
|
||||
})
|
||||
.catch(() => {});
|
||||
|
||||
const connectionsButton = document.createElement('button');
|
||||
connectionsButton.type = 'button';
|
||||
connectionsButton.className = 'icon-btn chat-header-icon-btn chat-header-connections-btn';
|
||||
connectionsButton.title = `Связи ${cleanLogin}`;
|
||||
connectionsButton.setAttribute('aria-label', `Открыть связи ${cleanLogin}`);
|
||||
connectionsButton.append(createShineConnectionsLogo({ className: 'chat-header-connections-logo' }));
|
||||
connectionsButton.addEventListener('click', () => {
|
||||
const openMenu = (event) => {
|
||||
if (!cleanLogin || cleanLogin === 'unknown') return;
|
||||
navigate(makeProfileLinksRoute(cleanLogin));
|
||||
});
|
||||
openUserIdentityMenu({ anchorEl: event.currentTarget, login: cleanLogin, navigate });
|
||||
};
|
||||
avatarButton.addEventListener('click', openMenu);
|
||||
loginEl.addEventListener('click', openMenu);
|
||||
|
||||
avatarButton.addEventListener('click', () => {
|
||||
if (!cleanLogin || cleanLogin === 'unknown') return;
|
||||
navigate(makeProfileRoute(cleanLogin));
|
||||
});
|
||||
|
||||
return { centerNode: loginEl, avatarButton, connectionsButton };
|
||||
return { centerNode: loginEl, avatarButton };
|
||||
}
|
||||
|
||||
function truncatePreviewText(value, maxLen = 72) {
|
||||
@@ -232,11 +289,11 @@ function openMessageActionsMenu({
|
||||
root.innerHTML = `
|
||||
<div class="dm-floating-menu-layer" id="chat-message-actions-layer">
|
||||
<div class="modal-card stack dm-message-actions-menu dm-message-actions-popover" id="${menuId}">
|
||||
${canReply ? '<button class="secondary-btn dm-message-action-btn" type="button" id="msg-action-reply">Ответить</button>' : ''}
|
||||
<button class="secondary-btn dm-message-action-btn" type="button" id="msg-action-copy">Скопировать как текст</button>
|
||||
${showReadAloud ? '<button class="secondary-btn dm-message-action-btn" type="button" id="msg-action-read">Прочесть</button>' : ''}
|
||||
${canEdit ? '<button class="secondary-btn dm-message-action-btn" type="button" id="msg-action-edit">Изменить</button>' : ''}
|
||||
${canDelete ? '<button class="secondary-btn dm-message-action-btn dm-message-action-btn--danger" type="button" id="msg-action-delete">Удалить</button>' : ''}
|
||||
${canReply ? `<button class="secondary-btn dm-message-action-btn" type="button" id="msg-action-reply">${menuIconSvg('reply')}<span>Ответить</span></button>` : ''}
|
||||
<button class="secondary-btn dm-message-action-btn" type="button" id="msg-action-copy">${menuIconSvg('copy')}<span>Скопировать как текст</span></button>
|
||||
${showReadAloud ? `<button class="secondary-btn dm-message-action-btn" type="button" id="msg-action-read">${menuIconSvg('read')}<span>Прочесть</span></button>` : ''}
|
||||
${canEdit ? `<button class="secondary-btn dm-message-action-btn" type="button" id="msg-action-edit">${menuIconSvg('edit')}<span>Изменить</span></button>` : ''}
|
||||
${canDelete ? `<button class="secondary-btn dm-message-action-btn dm-message-action-btn--danger" type="button" id="msg-action-delete">${menuIconSvg('delete')}<span>Удалить</span></button>` : ''}
|
||||
${String(infoText || '').trim() ? `<div class="meta-muted">${String(infoText || '').trim()}</div>` : ''}
|
||||
</div>
|
||||
</div>
|
||||
@@ -328,10 +385,10 @@ function openChatActionsMenu({
|
||||
root.innerHTML = `
|
||||
<div class="dm-floating-menu-layer" id="chat-header-actions-layer">
|
||||
<div class="modal-card stack dm-message-actions-menu dm-message-actions-popover" id="${menuId}">
|
||||
<button class="secondary-btn dm-message-action-btn" type="button" id="chat-menu-call">Звонок</button>
|
||||
<button class="secondary-btn dm-message-action-btn" type="button" id="chat-menu-video-call">Видеозвонок</button>
|
||||
<button class="secondary-btn dm-message-action-btn" type="button" id="chat-menu-clear-history">Очистить историю</button>
|
||||
<button class="secondary-btn dm-message-action-btn dm-message-action-btn--danger" type="button" id="chat-menu-delete-chat">Удалить чат</button>
|
||||
<button class="secondary-btn dm-message-action-btn" type="button" id="chat-menu-call">${menuIconSvg('call')}<span>Звонок</span></button>
|
||||
<button class="secondary-btn dm-message-action-btn" type="button" id="chat-menu-video-call">${menuIconSvg('video')}<span>Видеозвонок</span></button>
|
||||
<button class="secondary-btn dm-message-action-btn" type="button" id="chat-menu-clear-history">${menuIconSvg('clear')}<span>Очистить историю</span></button>
|
||||
<button class="secondary-btn dm-message-action-btn dm-message-action-btn--danger" type="button" id="chat-menu-delete-chat">${menuIconSvg('delete')}<span>Удалить чат</span></button>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
@@ -1083,7 +1140,7 @@ export function render({ navigate, route, chrome }) {
|
||||
],
|
||||
});
|
||||
const chatHeaderLeft = chatHeader.querySelector('.header-left');
|
||||
chatHeaderLeft?.append(chatHeaderParts.connectionsButton, chatHeaderParts.avatarButton);
|
||||
chatHeaderLeft?.append(chatHeaderParts.avatarButton);
|
||||
chrome?.setTopbar(chatHeader);
|
||||
|
||||
if (!isKnownContact) {
|
||||
|
||||
Reference in New Issue
Block a user