Чат: аватары-фото сотрудников и ботов

- chatApplyAvatar(el, userId): подгружает фото из Mattermost
  /users/{id}/image (работает и для сотрудников, и для ботов) и накладывает
  на кружок-аватар; до загрузки/при ошибке остаются инициалы. Кэш по userId.
- Применено в сообщениях (chatBuildMessageEl) и в пикере коллег
  (chatRenderDmPickerList)
- Переиспользует существующий chatApiFetchBlob (Bearer-токен)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-07-24 17:10:09 +03:00
parent 53c852160c
commit 479fe3e0d7
+28
View File
@@ -92,6 +92,28 @@ function chatApiFetchBlob(path) {
});
}
var chatAvatarCache = {}; // userId -> objectURL профільного фото (юзери і боти)
// Підвантажує аватар користувача/бота з Mattermost (/users/{id}/image) і
// накладає його на елемент-кружок. Поки фото не завантажилось (або якщо
// сталася помилка) лишаються ініціали, вже намальовані на елементі.
function chatApplyAvatar(el, userId) {
if (!el || !userId) return;
el.style.position = 'relative';
el.style.overflow = 'hidden';
var img = document.createElement('img');
img.alt = '';
img.style.cssText = 'position:absolute; inset:0; width:100%; height:100%; object-fit:cover; opacity:0; transition:opacity 150ms;';
img.onload = function () { img.style.opacity = '1'; };
el.appendChild(img);
if (chatAvatarCache[userId]) { img.src = chatAvatarCache[userId]; return; }
chatApiFetchBlob('/users/' + userId + '/image').then(function (blob) {
var url = URL.createObjectURL(blob);
chatAvatarCache[userId] = url;
img.src = url;
}).catch(function () { /* лишаємо ініціали */ });
}
function chatResolveUserIds(ids) {
var missing = ids.filter(function (id) { return id && !chatUserNames[id]; });
missing = missing.filter(function (id, i) { return missing.indexOf(id) === i; });
@@ -314,6 +336,7 @@ function chatBuildMessageEl(post, context) {
avatar.style.cssText = 'width:2rem; height:2rem; 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.625rem; font-weight:700; flex-shrink:0;';
var senderName = chatUserNames[post.user_id] || post.user_id;
avatar.textContent = senderName.slice(0, 2).toUpperCase();
chatApplyAvatar(avatar, post.user_id);
wrap.appendChild(avatar);
var body = document.createElement('div');
@@ -709,6 +732,11 @@ function chatRenderDmPickerList() {
}
};
row.appendChild(checkbox);
var av = document.createElement('div');
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 = c.name.slice(0, 2).toUpperCase();
chatApplyAvatar(av, c.mattermostId);
row.appendChild(av);
var name = document.createElement('span');
name.textContent = c.name;
row.appendChild(name);