Чат: автооткрытие свежего диалога и поиск сообщений
- Списки каналов и ЛС сортируются по last_post_at (свежие сверху)
- При открытии чата автоматически выбирается самая свежая розмова — сразу
видны последние сообщения, а не пустой экран
- Поиск сообщений через Mattermost /teams/{id}/posts/search: строка поиска
над лентой, результаты списком (кто/канал/время/текст), клик открывает канал
- Выход из поиска при открытии канала и смене команды
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
+129
-2
@@ -176,20 +176,30 @@ function chatRenderTeamSwitcher() {
|
||||
}
|
||||
|
||||
function chatSelectTeam(teamId) {
|
||||
chatCloseSearch(); // результати пошуку прив'язані до команди
|
||||
chatCurrentTeamId = teamId;
|
||||
chatRenderTeamSwitcher();
|
||||
chatRenderChannelList();
|
||||
}
|
||||
|
||||
function chatByRecent(a, b) {
|
||||
// Спершу найсвіжіші за останнім повідомленням, потім за назвою.
|
||||
var diff = (b.last_post_at || 0) - (a.last_post_at || 0);
|
||||
return diff !== 0 ? diff : (a.display_name || '').localeCompare(b.display_name || '');
|
||||
}
|
||||
|
||||
function chatLoadChannels() {
|
||||
return chatApiFetch('/users/me/channels').then(function (channels) {
|
||||
chatChannels = channels
|
||||
.filter(function (ch) { return ch.type === 'O' || ch.type === 'P'; })
|
||||
.sort(function (a, b) { return a.display_name.localeCompare(b.display_name); });
|
||||
chatDmChannels = channels.filter(function (ch) { return ch.type === 'D' || ch.type === 'G'; });
|
||||
.sort(chatByRecent);
|
||||
chatDmChannels = channels
|
||||
.filter(function (ch) { return ch.type === 'D' || ch.type === 'G'; })
|
||||
.sort(chatByRecent);
|
||||
chatRenderChannelList();
|
||||
chatRenderDmList();
|
||||
chatDmChannels.forEach(chatResolveDmLabel);
|
||||
chatAutoSelectRecent();
|
||||
}).catch(function (e) {
|
||||
console.error(e);
|
||||
var el = document.getElementById('chat-channel-list');
|
||||
@@ -197,6 +207,20 @@ function chatLoadChannels() {
|
||||
});
|
||||
}
|
||||
|
||||
function chatAutoSelectRecent() {
|
||||
// Відкриваємо найсвіжішу розмову за замовчуванням, щоб одразу бачити останні
|
||||
// повідомлення (а не порожній екран). Лише якщо користувач ще нічого не обрав.
|
||||
if (chatCurrentChannelId) return;
|
||||
var candidates = chatChannels.filter(function (ch) {
|
||||
return !chatCurrentTeamId || ch.team_id === chatCurrentTeamId;
|
||||
}).concat(chatDmChannels);
|
||||
if (!candidates.length) return;
|
||||
var best = candidates.reduce(function (a, b) {
|
||||
return (b.last_post_at || 0) > (a.last_post_at || 0) ? b : a;
|
||||
});
|
||||
if (best) chatSelectChannel(best.id);
|
||||
}
|
||||
|
||||
function chatResolveDmLabel(channel) {
|
||||
var promise;
|
||||
var otherId = null;
|
||||
@@ -324,6 +348,7 @@ function chatUpdateHeader(ch) {
|
||||
}
|
||||
|
||||
function chatSelectChannel(channelId) {
|
||||
chatCloseSearch(); // вихід із режиму пошуку при відкритті каналу
|
||||
var prevRow = chatCurrentChannelId ? document.getElementById('chat-channel-row-' + chatCurrentChannelId) : null;
|
||||
if (prevRow) prevRow.style.background = '';
|
||||
|
||||
@@ -394,6 +419,108 @@ function chatRenderThreadMessages(posts, replace) {
|
||||
chatAppendPosts(el, posts, chatThreadRenderedPostIds, 'thread');
|
||||
}
|
||||
|
||||
// ── Пошук повідомлень (Mattermost /teams/{id}/posts/search) ──────────────────
|
||||
|
||||
function chatCloseSearch() {
|
||||
var resultsEl = document.getElementById('chat-search-results');
|
||||
var messagesEl = document.getElementById('chat-messages');
|
||||
if (resultsEl) { resultsEl.style.display = 'none'; resultsEl.innerHTML = ''; }
|
||||
if (messagesEl) messagesEl.style.display = 'flex';
|
||||
var input = document.getElementById('chat-search-input');
|
||||
if (input) input.value = '';
|
||||
}
|
||||
|
||||
function chatSearchChannelLabel(channelId) {
|
||||
var ch = chatFindChannel(channelId);
|
||||
if (!ch) return '';
|
||||
if (ch.type === 'O' || ch.type === 'P') return '#' + ch.display_name;
|
||||
return chatDmLabels[ch.id] || 'Особисте';
|
||||
}
|
||||
|
||||
function chatSearchMessages(terms) {
|
||||
terms = (terms || '').trim();
|
||||
var resultsEl = document.getElementById('chat-search-results');
|
||||
var messagesEl = document.getElementById('chat-messages');
|
||||
if (!resultsEl) return;
|
||||
if (!terms) { chatCloseSearch(); return; }
|
||||
if (!chatCurrentTeamId) return;
|
||||
|
||||
resultsEl.style.display = 'flex';
|
||||
if (messagesEl) messagesEl.style.display = 'none';
|
||||
resultsEl.innerHTML = '<div style="text-align:center; font-size:0.8125rem; color:var(--color-muted-foreground); padding:1rem;">Пошук…</div>';
|
||||
|
||||
chatApiFetch('/teams/' + chatCurrentTeamId + '/posts/search', {
|
||||
method: 'POST',
|
||||
headers: {'Content-Type': 'application/json'},
|
||||
body: JSON.stringify({terms: terms, is_or_search: false, per_page: 30}),
|
||||
}).then(function (data) {
|
||||
var ids = data.order || [];
|
||||
var posts = ids.map(function (id) { return data.posts[id]; }).filter(Boolean);
|
||||
return chatResolveUsernames(posts).then(function () {
|
||||
chatRenderSearchResults(posts);
|
||||
});
|
||||
}).catch(function (e) {
|
||||
console.error(e);
|
||||
resultsEl.innerHTML = '<div style="text-align:center; font-size:0.8125rem; color:var(--color-destructive); padding:1rem;">Не вдалося виконати пошук</div>';
|
||||
});
|
||||
}
|
||||
|
||||
function chatRenderSearchResults(posts) {
|
||||
var el = document.getElementById('chat-search-results');
|
||||
if (!el) return;
|
||||
el.innerHTML = '';
|
||||
|
||||
var head = document.createElement('div');
|
||||
head.style.cssText = 'display:flex; align-items:center; justify-content:space-between; margin-bottom:0.25rem;';
|
||||
var title = document.createElement('span');
|
||||
title.style.cssText = 'font-size:0.75rem; color:var(--color-muted-foreground);';
|
||||
title.textContent = 'Знайдено: ' + posts.length;
|
||||
var closeBtn = document.createElement('button');
|
||||
closeBtn.type = 'button';
|
||||
closeBtn.textContent = 'Закрити пошук';
|
||||
closeBtn.style.cssText = 'background:none; border:none; cursor:pointer; font-size:0.75rem; color:var(--color-primary);';
|
||||
closeBtn.onclick = chatCloseSearch;
|
||||
head.appendChild(title);
|
||||
head.appendChild(closeBtn);
|
||||
el.appendChild(head);
|
||||
|
||||
if (!posts.length) {
|
||||
var none = document.createElement('div');
|
||||
none.style.cssText = 'text-align:center; font-size:0.8125rem; color:var(--color-muted-foreground); padding:1rem;';
|
||||
none.textContent = 'Нічого не знайдено';
|
||||
el.appendChild(none);
|
||||
return;
|
||||
}
|
||||
|
||||
posts.sort(function (a, b) { return (b.create_at || 0) - (a.create_at || 0); });
|
||||
posts.forEach(function (post) {
|
||||
var row = document.createElement('div');
|
||||
row.style.cssText = 'padding:0.625rem 0.75rem; border:1px solid var(--color-border); border-radius:0.5rem; cursor:pointer;';
|
||||
row.onmouseover = function () { row.style.background = 'var(--color-muted)'; };
|
||||
row.onmouseout = function () { row.style.background = ''; };
|
||||
row.onclick = function () { chatSelectChannel(post.channel_id); };
|
||||
|
||||
var meta = document.createElement('div');
|
||||
meta.style.cssText = 'display:flex; justify-content:space-between; gap:0.5rem; margin-bottom:0.25rem;';
|
||||
var who = document.createElement('span');
|
||||
who.style.cssText = 'font-size:0.75rem; font-weight:600; color:var(--color-foreground); overflow:hidden; text-overflow:ellipsis; white-space:nowrap;';
|
||||
who.textContent = (chatUserNames[post.user_id] || '') + ' · ' + chatSearchChannelLabel(post.channel_id);
|
||||
var when = document.createElement('span');
|
||||
when.style.cssText = 'font-size:0.6875rem; color:var(--color-muted-foreground); flex-shrink:0;';
|
||||
when.textContent = post.create_at ? new Date(post.create_at).toLocaleString() : '';
|
||||
meta.appendChild(who);
|
||||
meta.appendChild(when);
|
||||
|
||||
var msg = document.createElement('div');
|
||||
msg.style.cssText = 'font-size:0.8125rem; color:var(--color-foreground); white-space:pre-wrap; word-break:break-word;';
|
||||
msg.textContent = post.message || '';
|
||||
|
||||
row.appendChild(meta);
|
||||
row.appendChild(msg);
|
||||
el.appendChild(row);
|
||||
});
|
||||
}
|
||||
|
||||
function chatBuildMessageEl(post, context) {
|
||||
var isMine = post.user_id === chatConfig.currentUserId;
|
||||
var wrap = document.createElement('div');
|
||||
|
||||
Reference in New Issue
Block a user