diff --git a/static/assets/js/chat.js b/static/assets/js/chat.js index 61136a7..92b92d1 100644 --- a/static/assets/js/chat.js +++ b/static/assets/js/chat.js @@ -23,6 +23,8 @@ var chatUploadedFileNames = {}; var chatFileBlobCache = {}; var chatDmPickerSelected = {}; var chatReactionPickerEl = null; +var chatCurrentTeamId = null; // обрана «кімната» (команда Mattermost) +var chatDmOtherUserId = {}; // channelId -> id співрозмовника (для аватара ЛС) function chatPanelOpen(id) { var panel = document.getElementById(id); @@ -71,6 +73,8 @@ function chatInit() { chatUploadedFileNames = {}; chatFileBlobCache = {}; chatDmPickerSelected = {}; + chatCurrentTeamId = null; + chatDmOtherUserId = {}; chatLoadTeams().then(chatLoadChannels); chatConnectSocket(); @@ -140,9 +144,43 @@ function chatResolveUsernames(posts) { function chatLoadTeams() { return chatApiFetch('/users/me/teams').then(function (teams) { teams.forEach(function (team) { chatTeamsById[team.id] = team; }); + var sorted = teams.slice().sort(function (a, b) { return a.display_name.localeCompare(b.display_name); }); + if (!chatCurrentTeamId && sorted.length) chatCurrentTeamId = sorted[0].id; + chatRenderTeamSwitcher(); }).catch(function (e) { console.error(e); }); } +// Верхній перемикач «кімнат» — команди Mattermost (Офіс / Аптеки / Склад). +function chatRenderTeamSwitcher() { + var el = document.getElementById('chat-team-switcher'); + if (!el) return; + var teams = Object.keys(chatTeamsById).map(function (id) { return chatTeamsById[id]; }) + .sort(function (a, b) { return a.display_name.localeCompare(b.display_name); }); + if (teams.length <= 1) { el.innerHTML = ''; return; } // перемикати нема що + + el.innerHTML = ''; + var group = document.createElement('div'); + group.style.cssText = 'display:inline-flex; gap:0.25rem; background:var(--color-muted); border-radius:var(--radius); padding:0.25rem;'; + teams.forEach(function (t) { + var active = t.id === chatCurrentTeamId; + var btn = document.createElement('button'); + btn.type = 'button'; + btn.textContent = t.display_name; + btn.style.cssText = 'padding:0.3125rem 0.875rem; font-size:0.8125rem; font-weight:500; border:none; cursor:pointer; border-radius:calc(var(--radius) - 2px); transition:all 150ms;' + + (active ? ' background:var(--color-card); color:var(--color-card-foreground); box-shadow:0 1px 2px rgb(0 0 0/0.08);' + : ' background:transparent; color:var(--color-muted-foreground);'); + btn.onclick = function () { chatSelectTeam(t.id); }; + group.appendChild(btn); + }); + el.appendChild(group); +} + +function chatSelectTeam(teamId) { + chatCurrentTeamId = teamId; + chatRenderTeamSwitcher(); + chatRenderChannelList(); +} + function chatLoadChannels() { return chatApiFetch('/users/me/channels').then(function (channels) { chatChannels = channels @@ -161,8 +199,10 @@ function chatLoadChannels() { function chatResolveDmLabel(channel) { var promise; + var otherId = null; if (channel.type === 'D') { - var otherId = channel.name.split('__').filter(function (id) { return id !== chatConfig.currentUserId; })[0]; + otherId = channel.name.split('__').filter(function (id) { return id !== chatConfig.currentUserId; })[0]; + chatDmOtherUserId[channel.id] = otherId; promise = chatResolveUserIds([otherId]).then(function () { return chatUserNames[otherId] || otherId; }); @@ -178,6 +218,11 @@ function chatResolveDmLabel(channel) { chatDmLabels[channel.id] = label; var labelEl = document.getElementById('chat-dm-label-' + channel.id); if (labelEl) labelEl.textContent = label; + var avEl = document.getElementById('chat-dm-avatar-' + channel.id); + if (avEl) { + avEl.textContent = (label || '').slice(0, 2).toUpperCase(); + if (otherId) chatApplyAvatar(avEl, otherId); // фото — лише для особистих (не групових) + } if (chatCurrentChannelId === channel.id) chatUpdateHeader(channel); }).catch(function (e) { console.error(e); }); } @@ -186,15 +231,19 @@ function chatRenderChannelList() { var el = document.getElementById('chat-channel-list'); if (!el) return; - if (!chatChannels.length) { + var list = chatChannels.filter(function (ch) { + return !chatCurrentTeamId || ch.team_id === chatCurrentTeamId; + }); + + if (!list.length) { el.innerHTML = '
Немає каналів
'; return; } el.innerHTML = ''; - chatChannels.forEach(function (ch) { - var team = chatTeamsById[ch.team_id]; - var row = chatBuildChannelRow(ch.id, '#' + ch.display_name, team ? team.display_name : ''); + list.forEach(function (ch) { + // Команда обрана перемикачем зверху — назву команди у рядок не дублюємо. + var row = chatBuildChannelRow(ch.id, '#' + ch.display_name, ''); el.appendChild(row); }); } @@ -210,33 +259,52 @@ function chatRenderDmList() { el.innerHTML = ''; chatDmChannels.forEach(function (ch) { - var row = chatBuildChannelRow(ch.id, chatDmLabels[ch.id] || '…', ch.type === 'G' ? 'Групова розмова' : ''); + var row = chatBuildChannelRow(ch.id, chatDmLabels[ch.id] || '…', + ch.type === 'G' ? 'Групова розмова' : '', {avatar: true}); el.appendChild(row); + // Якщо співрозмовник ЛС уже відомий — одразу показати його аватар. + if (ch.type === 'D' && chatDmOtherUserId[ch.id]) { + chatApplyAvatar(document.getElementById('chat-dm-avatar-' + ch.id), chatDmOtherUserId[ch.id]); + } }); } -function chatBuildChannelRow(channelId, label, subLabel) { +function chatBuildChannelRow(channelId, label, subLabel, opts) { + opts = opts || {}; var row = document.createElement('div'); row.id = 'chat-channel-row-' + channelId; - row.style.cssText = 'padding:0.5rem 0.625rem; border-bottom:1px solid var(--color-border); cursor:pointer;' + + row.style.cssText = 'display:flex; align-items:center; gap:0.5rem; padding:0.5rem 0.625rem; border-bottom:1px solid var(--color-border); cursor:pointer;' + (channelId === chatCurrentChannelId ? ' background:hsl(0 72% 44% / 0.06);' : ''); row.onmouseover = function () { if (channelId !== chatCurrentChannelId) row.style.background = 'var(--color-muted)'; }; row.onmouseout = function () { if (channelId !== chatCurrentChannelId) row.style.background = ''; }; row.onclick = function () { chatSelectChannel(channelId); }; + if (opts.avatar) { + // Кружок-аватар співрозмовника ЛС; фото підвантажиться в chatResolveDmLabel. + var av = document.createElement('div'); + av.id = 'chat-dm-avatar-' + channelId; + av.style.cssText = 'width:1.75rem; height:1.75rem; border-radius:50%; background:hsl(205 80% 55% / 0.15); color:hsl(205 80% 45%); display:flex; align-items:center; justify-content:center; font-size:0.5625rem; font-weight:700; flex-shrink:0;'; + av.textContent = (label && label !== '…') ? label.slice(0, 2).toUpperCase() : ''; + row.appendChild(av); + } + + var textCol = document.createElement('div'); + textCol.style.cssText = 'min-width:0; flex:1;'; + var name = document.createElement('div'); name.id = 'chat-dm-label-' + channelId; - name.style.cssText = 'font-size:0.8125rem; font-weight:500; color:var(--color-foreground);'; + name.style.cssText = 'font-size:0.8125rem; font-weight:500; color:var(--color-foreground); overflow:hidden; text-overflow:ellipsis; white-space:nowrap;'; name.textContent = label; - row.appendChild(name); + textCol.appendChild(name); if (subLabel) { var sub = document.createElement('div'); sub.style.cssText = 'font-size:0.6875rem; color:var(--color-muted-foreground);'; sub.textContent = subLabel; - row.appendChild(sub); + textCol.appendChild(sub); } + row.appendChild(textCol); return row; } diff --git a/templates/pages/chat.html b/templates/pages/chat.html index 4579ccc..069e57f 100644 --- a/templates/pages/chat.html +++ b/templates/pages/chat.html @@ -8,7 +8,9 @@ {% if chat_state == 'ready' %} {{ chat_config|json_script:"chat-config-data" }} {{ chat_colleagues|json_script:"chat-colleagues-data" }} -
+ +
+