feat: show round profile pictures in the streamer sidebar
Windows CI / verify (push) Canceled after 0s
Windows CI / verify (push) Canceled after 0s
This commit is contained in:
@@ -222,7 +222,10 @@ async function fetchStreamerProfile(login: string, forceRefresh = false): Promis
|
||||
const pending = streamerProfileLoads.get(key);
|
||||
if (pending) return pending;
|
||||
const task = window.api.getStreamerProfile(login, forceRefresh).then((profile) => {
|
||||
if (profile) streamerProfileCache.set(key, profile);
|
||||
if (profile) {
|
||||
streamerProfileCache.set(key, profile);
|
||||
updateStreamerAvatars(login);
|
||||
}
|
||||
return profile;
|
||||
});
|
||||
streamerProfileLoads.set(key, task);
|
||||
|
||||
@@ -590,6 +590,32 @@ function showStreamerContextMenu(event: MouseEvent, streamer: string): void {
|
||||
menu.querySelector<HTMLButtonElement>('button')?.focus();
|
||||
}
|
||||
|
||||
function createStreamerAvatar(streamer: string): HTMLElement {
|
||||
const fallback = document.createElement('span');
|
||||
fallback.className = 'streamer-avatar-fallback';
|
||||
fallback.textContent = Array.from(getStreamerDisplayName(streamer))[0]?.toUpperCase() || '?';
|
||||
fallback.setAttribute('aria-hidden', 'true');
|
||||
const profile = streamerProfileCache.get(streamer.trim().toLowerCase());
|
||||
if (!profile?.avatarUrl) return fallback;
|
||||
const image = document.createElement('img');
|
||||
image.className = 'streamer-avatar';
|
||||
image.alt = '';
|
||||
image.draggable = false;
|
||||
image.referrerPolicy = 'no-referrer';
|
||||
image.decoding = 'async';
|
||||
image.addEventListener('error', () => image.replaceWith(fallback), { once: true });
|
||||
image.src = profile.avatarUrl;
|
||||
return image;
|
||||
}
|
||||
|
||||
function updateStreamerAvatars(login: string): void {
|
||||
const key = login.trim().toLowerCase();
|
||||
for (const item of Array.from(document.querySelectorAll<HTMLElement>('#streamerList .streamer-item'))) {
|
||||
if (item.dataset.streamerName?.trim().toLowerCase() !== key) continue;
|
||||
item.querySelector('.streamer-avatar, .streamer-avatar-fallback')?.replaceWith(createStreamerAvatar(login));
|
||||
}
|
||||
}
|
||||
|
||||
function renderStreamers(): void {
|
||||
const list = byId('streamerList');
|
||||
list.replaceChildren();
|
||||
@@ -639,7 +665,8 @@ function renderStreamers(): void {
|
||||
// first, then AUTO / VOD / REC / remove in order.
|
||||
item.setAttribute('role', 'button');
|
||||
item.setAttribute('tabindex', '0');
|
||||
item.setAttribute('aria-label', streamer);
|
||||
item.setAttribute('aria-label', getStreamerDisplayName(streamer));
|
||||
item.title = getStreamerDisplayName(streamer);
|
||||
if (currentStreamer === streamer) item.setAttribute('aria-current', 'true');
|
||||
|
||||
// Live-dot — red pulsing dot when this streamer is currently
|
||||
@@ -677,7 +704,7 @@ function renderStreamers(): void {
|
||||
void removeStreamer(streamer);
|
||||
}
|
||||
});
|
||||
item.append(nameSpan, removeSpan);
|
||||
item.append(createStreamerAvatar(streamer), nameSpan, removeSpan);
|
||||
|
||||
item.addEventListener('contextmenu', (contextEvent) => {
|
||||
showStreamerContextMenu(contextEvent, streamer);
|
||||
|
||||
@@ -20,7 +20,7 @@ describe('production style modules', () => {
|
||||
.replace(/\r\n/g, '\n'));
|
||||
const digest = createHash('sha256').update(content).digest('hex');
|
||||
|
||||
expect(digest).toBe('89aee0dd2015cbf2890d9acfb4e5661f8520777f43fe48a86a89abc821982f4d');
|
||||
expect(digest).toBe('ece1a9e9f064bc42b0797cd15fcdcdc6367530a67f7c8f6b72c9e10253d73f9a');
|
||||
});
|
||||
|
||||
test('derives the Windows hot-development executable version from package metadata', () => {
|
||||
|
||||
@@ -859,6 +859,23 @@ textarea:disabled,
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
flex: 0 0 24px;
|
||||
display: grid;
|
||||
place-items: center;
|
||||
border-radius: 50%;
|
||||
object-fit: cover;
|
||||
overflow: hidden;
|
||||
background: var(--workspace-control);
|
||||
color: var(--workspace-text-muted);
|
||||
font-size: 12px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.context-sidebar .streamer-name {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.context-sidebar .queue-section {
|
||||
|
||||
Reference in New Issue
Block a user