157 lines
5.8 KiB
JavaScript
157 lines
5.8 KiB
JavaScript
import { renderHeader } from '../components/header.js';
|
||
import { directMessages } from '../mock-data.js';
|
||
import {
|
||
getChatMessages,
|
||
isSessionInvalidError,
|
||
setContacts,
|
||
state,
|
||
terminateCurrentSession,
|
||
} from '../state.js';
|
||
import { loadCurrentRelations } from '../services/user-connections.js';
|
||
|
||
export const pageMeta = { id: 'messages-list', title: 'Личные сообщения' };
|
||
|
||
export function render({ navigate }) {
|
||
const screen = document.createElement('section');
|
||
screen.className = 'stack';
|
||
|
||
screen.append(
|
||
renderHeader({
|
||
title: 'Личные сообщения',
|
||
leftLabel: String(state.session.login || '').trim(),
|
||
rightActions: [{ label: '+', onClick: () => navigate('contact-search-view') }],
|
||
}),
|
||
);
|
||
|
||
const list = document.createElement('div');
|
||
list.className = 'stack';
|
||
const status = document.createElement('div');
|
||
status.className = 'status-line';
|
||
status.textContent = 'Загрузка списка сообщений...';
|
||
|
||
function renderRow(item) {
|
||
const row = document.createElement('article');
|
||
row.className = 'list-item';
|
||
row.innerHTML = `
|
||
<div class="avatar">${item.initials}</div>
|
||
<div>
|
||
<div class="row" style="justify-content:flex-start; gap:8px;">
|
||
<strong>${item.name}</strong>
|
||
${item.notInContacts ? '<span class="meta-muted">не в контактах</span>' : ''}
|
||
</div>
|
||
<p class="meta-muted" style="margin-top:4px;">${item.lastMessage}</p>
|
||
</div>
|
||
<div style="display:grid; justify-items:end; gap:6px;">
|
||
<span class="meta-muted">${item.time}</span>
|
||
${item.unread ? `<span class="unread">${item.unread}</span>` : '<span></span>'}
|
||
</div>
|
||
`;
|
||
row.addEventListener('click', () => navigate(`chat-view/${encodeURIComponent(item.id)}`));
|
||
return row;
|
||
}
|
||
|
||
async function loadList() {
|
||
try {
|
||
const relations = await loadCurrentRelations();
|
||
const contacts = relations.outContacts || [];
|
||
setContacts(contacts);
|
||
list.innerHTML = '';
|
||
const contactRows = contacts.map((login) => {
|
||
const preview = directMessages.find((item) => item.id.toLowerCase() === login.toLowerCase());
|
||
const chat = getChatMessages(login);
|
||
const lastChat = chat[chat.length - 1];
|
||
const unread = chat.filter((m) => m?.from === 'in' && m?.unread).length;
|
||
return {
|
||
id: login,
|
||
initials: (login[0] || '?').toUpperCase(),
|
||
name: preview?.name || login,
|
||
lastMessage: lastChat?.text || preview?.lastMessage || 'Диалог пока пуст.',
|
||
time: preview?.time || '—',
|
||
unread,
|
||
notInContacts: false,
|
||
};
|
||
});
|
||
|
||
const allChatIds = Object.keys(state.chats || {})
|
||
.filter((id) => id && id.toLowerCase() !== String(state.session.login || '').toLowerCase())
|
||
.filter((id) => (getChatMessages(id) || []).length > 0);
|
||
|
||
const contactKeys = new Set(contacts.map((x) => String(x || '').toLowerCase()));
|
||
const extraRows = allChatIds
|
||
.filter((login) => !contactKeys.has(String(login || '').toLowerCase()))
|
||
.map((login) => {
|
||
const chat = getChatMessages(login);
|
||
const lastChat = chat[chat.length - 1];
|
||
const unread = chat.filter((m) => m?.from === 'in' && m?.unread).length;
|
||
return {
|
||
id: login,
|
||
initials: (login[0] || '?').toUpperCase(),
|
||
name: login,
|
||
lastMessage: lastChat?.text || 'Диалог пока пуст.',
|
||
time: 'сейчас',
|
||
unread,
|
||
notInContacts: true,
|
||
};
|
||
});
|
||
|
||
const rows = [...contactRows, ...extraRows];
|
||
if (!rows.length) {
|
||
const empty = document.createElement('div');
|
||
empty.className = 'card meta-muted';
|
||
empty.textContent = 'Пока нет ни контактов, ни сообщений';
|
||
list.append(empty);
|
||
status.className = 'status-line is-available';
|
||
status.textContent = 'Нет диалогов.';
|
||
return;
|
||
}
|
||
|
||
rows.forEach((item) => list.append(renderRow(item)));
|
||
status.className = 'status-line is-available';
|
||
status.textContent = `Загружено диалогов: ${rows.length}`;
|
||
} catch (error) {
|
||
if (isSessionInvalidError(error)) {
|
||
list.innerHTML = '';
|
||
status.className = 'status-line is-unavailable';
|
||
status.textContent = 'Сессия устарела.';
|
||
|
||
const card = document.createElement('div');
|
||
card.className = 'card stack';
|
||
|
||
const title = document.createElement('strong');
|
||
title.textContent = 'Сессия устарела';
|
||
|
||
const details = document.createElement('p');
|
||
details.className = 'meta-muted';
|
||
details.textContent = 'Ваша сессия больше не действует. Авторизуйтесь заново.';
|
||
|
||
const okBtn = document.createElement('button');
|
||
okBtn.type = 'button';
|
||
okBtn.className = 'primary-btn';
|
||
okBtn.textContent = 'ОК';
|
||
okBtn.addEventListener('click', async () => {
|
||
await terminateCurrentSession({
|
||
infoMessage: 'Ваша сессия устарела. Выполните вход заново.',
|
||
});
|
||
navigate('start-view');
|
||
});
|
||
|
||
card.append(title, details, okBtn);
|
||
list.append(card);
|
||
return;
|
||
}
|
||
|
||
list.innerHTML = '';
|
||
const fail = document.createElement('div');
|
||
fail.className = 'card meta-muted';
|
||
fail.textContent = `Не удалось загрузить сообщения: ${error.message || 'unknown'}`;
|
||
list.append(fail);
|
||
status.className = 'status-line is-unavailable';
|
||
status.textContent = 'Список недоступен.';
|
||
}
|
||
}
|
||
|
||
screen.append(status, list);
|
||
loadList();
|
||
return screen;
|
||
}
|