Feat : Preview on streamers
This commit is contained in:
@@ -3,7 +3,13 @@ import type { AgentView, PreemptionVerdict, WatchTarget } from '@stream-control/
|
||||
import { DEFAULT_PRIORITY, priorityLabel } from '@stream-control/shared';
|
||||
import { api } from '../api';
|
||||
import { formatDateTime, formatDuration, formatRelative } from '../format';
|
||||
import { preemption, recordingContext, stateLabel, type RecordingContext } from '../streamers';
|
||||
import {
|
||||
preemption,
|
||||
previewSrc,
|
||||
recordingContext,
|
||||
stateLabel,
|
||||
type RecordingContext,
|
||||
} from '../streamers';
|
||||
import { StreamerDetail } from './StreamerDetail';
|
||||
|
||||
interface Props {
|
||||
@@ -172,12 +178,18 @@ function StreamerCard({
|
||||
}) {
|
||||
const [busy, setBusy] = useState(false);
|
||||
const [avatarBroken, setAvatarBroken] = useState(false);
|
||||
// La valeur en échec, pas un simple booléen : `preview` change de contenu à
|
||||
// chaque sonde (nouveau `snapshotAt`) sans changer d'identité de composant,
|
||||
// un booléen figé resterait donc bloqué après le premier accroc réseau.
|
||||
const [brokenPreview, setBrokenPreview] = useState<string | null>(null);
|
||||
|
||||
const state = stateLabel(target);
|
||||
const agent = agents.find((candidate) => candidate.id === target.agentId) ?? null;
|
||||
const context = recordingContext(target, agent, targets);
|
||||
const verdict = preemption(target, context);
|
||||
const isLive = target.state === 'public';
|
||||
const preview = isLive ? previewSrc(target) : null;
|
||||
const showPreview = preview !== null && preview !== brokenPreview;
|
||||
|
||||
const liveFor = isLive && target.statusChangedAt ? Date.now() - target.statusChangedAt : null;
|
||||
const lastDuration =
|
||||
@@ -218,6 +230,22 @@ function StreamerCard({
|
||||
tabIndex={0}
|
||||
title="Ouvrir la fiche"
|
||||
>
|
||||
{showPreview && (
|
||||
// Un `<img>`, pas un flux : le serveur n'a que l'API REST de la
|
||||
// plateforme, ce n'est pas l'agent qui pilote un navigateur ici. Le
|
||||
// clic n'est pas intercepté : même agrandie, l'image reste celle de
|
||||
// cette fiche, et l'ouvrir en cliquant dessus reste cohérent.
|
||||
<div className="streamer-preview">
|
||||
<img
|
||||
src={preview}
|
||||
alt=""
|
||||
loading="lazy"
|
||||
referrerPolicy="no-referrer"
|
||||
onError={() => setBrokenPreview(preview)}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<header className="streamer-head">
|
||||
{target.avatarUrl && !avatarBroken ? (
|
||||
<img
|
||||
|
||||
Reference in New Issue
Block a user