Feat : Interface scalable size
This commit is contained in:
@@ -4,12 +4,16 @@ import { DEFAULT_PRIORITY, priorityLabel } from '@stream-control/shared';
|
||||
import { api } from '../api';
|
||||
import { formatDateTime, formatDuration, formatRelative } from '../format';
|
||||
import {
|
||||
PREVIEW_SIZES,
|
||||
loadPreviewSize,
|
||||
preemption,
|
||||
previewSrc,
|
||||
recordingContext,
|
||||
savePreviewSize,
|
||||
stateLabel,
|
||||
tagStyle,
|
||||
usedTags,
|
||||
type PreviewSize,
|
||||
type RecordingContext,
|
||||
} from '../streamers';
|
||||
import { StreamerDetail } from './StreamerDetail';
|
||||
@@ -44,6 +48,14 @@ export function StreamersPage({
|
||||
const [openId, setOpenId] = useState<string | null>(null);
|
||||
const [selectedTags, setSelectedTags] = useState<Set<string>>(new Set());
|
||||
const [managingTags, setManagingTags] = useState(false);
|
||||
const [previewSize, setPreviewSize] = useState<PreviewSize>(loadPreviewSize);
|
||||
|
||||
function changePreviewSize(size: PreviewSize) {
|
||||
setPreviewSize(size);
|
||||
// Mémorisé côté navigateur : c'est un confort d'affichage propre à l'écran
|
||||
// qu'on a sous les yeux, pas un réglage à imposer aux autres postes.
|
||||
savePreviewSize(size);
|
||||
}
|
||||
|
||||
// Tirées des profils plutôt que du serveur : la liste arrive déjà entière par
|
||||
// le flux temps réel, donc toujours à jour, et une étiquette que personne ne
|
||||
@@ -142,6 +154,21 @@ export function StreamersPage({
|
||||
</button>
|
||||
))}
|
||||
<div className="spacer" />
|
||||
<span className="muted small">Aperçus</span>
|
||||
{PREVIEW_SIZES.map((size) => (
|
||||
<button
|
||||
key={size.id}
|
||||
className={previewSize === size.id ? 'chip active' : 'chip'}
|
||||
onClick={() => changePreviewSize(size.id)}
|
||||
title={
|
||||
size.id === 'off'
|
||||
? "N'afficher aucune image du flux — et ne pas la charger"
|
||||
: `Aperçu ${size.label.toLowerCase()}`
|
||||
}
|
||||
>
|
||||
{size.label}
|
||||
</button>
|
||||
))}
|
||||
<span className="muted small">{visible.length} profil(s)</span>
|
||||
</div>
|
||||
)}
|
||||
@@ -188,7 +215,7 @@ export function StreamersPage({
|
||||
) : visible.length === 0 ? (
|
||||
<p className="muted small">Aucun profil pour ce filtre.</p>
|
||||
) : (
|
||||
<div className="streamer-grid">
|
||||
<div className={`streamer-grid preview-${previewSize}`}>
|
||||
{visible.map((target) => (
|
||||
<StreamerCard
|
||||
key={target.id}
|
||||
@@ -196,6 +223,7 @@ export function StreamersPage({
|
||||
targets={targets}
|
||||
agents={agents}
|
||||
notify={notify}
|
||||
previewSize={previewSize}
|
||||
onOpen={() => setOpenId(target.id)}
|
||||
/>
|
||||
))}
|
||||
@@ -235,12 +263,14 @@ function StreamerCard({
|
||||
targets,
|
||||
agents,
|
||||
notify,
|
||||
previewSize,
|
||||
onOpen,
|
||||
}: {
|
||||
target: WatchTarget;
|
||||
targets: WatchTarget[];
|
||||
agents: AgentView[];
|
||||
notify: (message: string, tone?: 'info' | 'error') => void;
|
||||
previewSize: PreviewSize;
|
||||
onOpen: () => void;
|
||||
}) {
|
||||
const [busy, setBusy] = useState(false);
|
||||
@@ -255,7 +285,9 @@ function StreamerCard({
|
||||
const context = recordingContext(target, agent, targets);
|
||||
const verdict = preemption(target, context);
|
||||
const isLive = target.state === 'public';
|
||||
const preview = isLive ? previewSrc(target) : null;
|
||||
// Rien n'est calculé — ni chargé — quand les aperçus sont coupés : ce sont des
|
||||
// images distantes, autant ne pas les demander si personne ne les regarde.
|
||||
const preview = isLive && previewSize !== 'off' ? previewSrc(target) : null;
|
||||
const showPreview = preview !== null && preview !== brokenPreview;
|
||||
|
||||
const liveFor = isLive && target.statusChangedAt ? Date.now() - target.statusChangedAt : null;
|
||||
|
||||
Reference in New Issue
Block a user