openvibers/openvibe.live Commits
Arena: a live cam whose stream frame is not there yet (a restart, a new stream) or fails shows the streamer's portrait, not a broken image (#155)
Changes
@@@ -249,6 +249,9 @@ a.arena-tag:hover { color: var(--accent-light); }.arena-cam.is-quiet { opacity: 0.75; }.arena-cam-thumb img.arena-live-thumb { width: 100%; height: 100%; object-fit: cover; display: block; }.arena-cam-thumb .arena-portrait { width: 100%; height: 100%; }++/* The stream's frame when it loads; the portrait when there is none yet (a restart, a new stream) or it fails. */++.arena-cam-thumb img.arena-live-thumb[hidden] { display: none; }++.arena-cam-thumb img.arena-live-thumb:not([hidden]) ~ .arena-portrait { display: none; }.arena-cam-lock { display: inline-flex; align-items: center; gap: 6px; padding: 3px 9px; border-radius: 999px; font-size: 0.74rem; font-weight: 700; color: #fecaca; background: color-mix(in srgb, var(--live-red) 18%, transparent); border: 1px solid color-mix(in srgb, var(--live-red) 45%, transparent); width: fit-content; }.arena-cam-lock i { color: var(--live-red); }.arena-cam-last q { display: block; font-size: 0.9rem; color: var(--text-primary); line-height: 1.35; quotes: '“' '”'; }@@@ -176,7 +176,7 @@ function _aRenderCams(live) {<div class="arena-cam-grid">${live.map(f => {const lock = f.ears && f.ears.focus;return `<div class="arena-cam ${f.transcribed ? '' : 'is-quiet'} ${lock ? 'is-locked' : ''}" style="--fc:${_aEsc(f.user.profile_color || '#8b5cf6')}">-- ${_aA(_aChannelLink(f.user, f.stream.slug || f.stream.managed_stream_id), `${f.thumbnail_url ? `<img class="arena-live-thumb" src="${_aEsc(f.thumbnail_url)}" alt="">` : _aPortrait(f, 'md')}<span class="arena-cam-rec"><span class="arena-live-dot arena-live-dot-sm"></span> LIVE · <i class="fa-solid fa-eye"></i> ${_aNum(f.stream.viewer_count)}</span>`, 'arena-cam-thumb')}++ ${_aA(_aChannelLink(f.user, f.stream.slug || f.stream.managed_stream_id), `${f.thumbnail_url ? `<img class="arena-live-thumb" src="${_aEsc(f.thumbnail_url)}" alt="" onerror="this.hidden=true">` : ''}${_aPortrait(f, 'md')}<span class="arena-cam-rec"><span class="arena-live-dot arena-live-dot-sm"></span> LIVE · <i class="fa-solid fa-eye"></i> ${_aNum(f.stream.viewer_count)}</span>`, 'arena-cam-thumb')}<div class="arena-cam-body"><div class="arena-mic-head">${_aA(_aFighterLink(f.user), `<strong>${_aEsc(f.user.display_name || f.user.username)}</strong>`)}
@@ -249,6 +249,9 @@ a.arena-tag:hover { color: var(--accent-light); }
@@ -249,6 +249,9 @@ a.arena-tag:hover { color: var(--accent-light); }
.arena-cam.is-quiet { opacity: 0.75; }
.arena-cam.is-quiet { opacity: 0.75; }
.arena-cam-thumb img.arena-live-thumb { width: 100%; height: 100%; object-fit: cover; display: block; }
.arena-cam-thumb img.arena-live-thumb { width: 100%; height: 100%; object-fit: cover; display: block; }
.arena-cam-thumb .arena-portrait { width: 100%; height: 100%; }
.arena-cam-thumb .arena-portrait { width: 100%; height: 100%; }
+/* The stream's frame when it loads; the portrait when there is none yet (a restart, a new stream) or it fails. */
+.arena-cam-thumb img.arena-live-thumb[hidden] { display: none; }
+.arena-cam-thumb img.arena-live-thumb:not([hidden]) ~ .arena-portrait { display: none; }
.arena-cam-lock { display: inline-flex; align-items: center; gap: 6px; padding: 3px 9px; border-radius: 999px; font-size: 0.74rem; font-weight: 700; color: #fecaca; background: color-mix(in srgb, var(--live-red) 18%, transparent); border: 1px solid color-mix(in srgb, var(--live-red) 45%, transparent); width: fit-content; }
.arena-cam-lock { display: inline-flex; align-items: center; gap: 6px; padding: 3px 9px; border-radius: 999px; font-size: 0.74rem; font-weight: 700; color: #fecaca; background: color-mix(in srgb, var(--live-red) 18%, transparent); border: 1px solid color-mix(in srgb, var(--live-red) 45%, transparent); width: fit-content; }
.arena-cam-lock i { color: var(--live-red); }
.arena-cam-lock i { color: var(--live-red); }
.arena-cam-last q { display: block; font-size: 0.9rem; color: var(--text-primary); line-height: 1.35; quotes: '“' '”'; }
.arena-cam-last q { display: block; font-size: 0.9rem; color: var(--text-primary); line-height: 1.35; quotes: '“' '”'; }
@@ -176,7 +176,7 @@ function _aRenderCams(live) {
@@ -176,7 +176,7 @@ function _aRenderCams(live) {
<div class="arena-cam-grid">${live.map(f => {
<div class="arena-cam-grid">${live.map(f => {
const lock = f.ears && f.ears.focus;
const lock = f.ears && f.ears.focus;
return `<div class="arena-cam ${f.transcribed ? '' : 'is-quiet'} ${lock ? 'is-locked' : ''}" style="--fc:${_aEsc(f.user.profile_color || '#8b5cf6')}">
return `<div class="arena-cam ${f.transcribed ? '' : 'is-quiet'} ${lock ? 'is-locked' : ''}" style="--fc:${_aEsc(f.user.profile_color || '#8b5cf6')}">
- ${_aA(_aChannelLink(f.user, f.stream.slug || f.stream.managed_stream_id), `${f.thumbnail_url ? `<img class="arena-live-thumb" src="${_aEsc(f.thumbnail_url)}" alt="">` : _aPortrait(f, 'md')}<span class="arena-cam-rec"><span class="arena-live-dot arena-live-dot-sm"></span> LIVE · <i class="fa-solid fa-eye"></i> ${_aNum(f.stream.viewer_count)}</span>`, 'arena-cam-thumb')}
+ ${_aA(_aChannelLink(f.user, f.stream.slug || f.stream.managed_stream_id), `${f.thumbnail_url ? `<img class="arena-live-thumb" src="${_aEsc(f.thumbnail_url)}" alt="" onerror="this.hidden=true">` : ''}${_aPortrait(f, 'md')}<span class="arena-cam-rec"><span class="arena-live-dot arena-live-dot-sm"></span> LIVE · <i class="fa-solid fa-eye"></i> ${_aNum(f.stream.viewer_count)}</span>`, 'arena-cam-thumb')}
<div class="arena-cam-body">
<div class="arena-cam-body">
<div class="arena-mic-head">
<div class="arena-mic-head">
${_aA(_aFighterLink(f.user), `<strong>${_aEsc(f.user.display_name || f.user.username)}</strong>`)}
${_aA(_aFighterLink(f.user), `<strong>${_aEsc(f.user.display_name || f.user.username)}</strong>`)}