Files
memby/admin-ui/src/pages/Playback.tsx
T
2026-08-14 09:40:03 +12:00

92 lines
3.0 KiB
TypeScript

import { useEffect, useState } from 'react';
import { api } from '../api/client';
import { useAction } from '../lib/hooks';
import { useGateway } from '../lib/gateway';
import { useToast } from '../lib/toast';
import { Banner, Button, Card, Field, Loading, PageHead, Tag, Toggle } from '../components/ui';
export function PlaybackPage() {
const { status, error, loading, reload } = useGateway();
const { wrap, show } = useToast();
const { busy, run } = useAction();
const [enabled, setEnabled] = useState(true);
const [seconds, setSeconds] = useState('6.5');
const [touched, setTouched] = useState(false);
useEffect(() => {
if (touched || !status) return;
setEnabled(status.playbackPolicy?.prerollEnabled !== false);
setSeconds(String((status.playbackPolicy?.prerollDurationMs ?? 6500) / 1000));
}, [status, touched]);
const save = () =>
run('save', async () => {
const value = Number(seconds);
// Checked here as well as on the server because the server clamps rather than
// refusing, and a value silently corrected to 30 would look like the field not
// having saved.
if (!Number.isFinite(value) || value < 1 || value > 30) {
show('The preroll duration must be between 1 and 30 seconds.', 'bad');
return;
}
await wrap(
() =>
api.post('/admin/api/playback-policy', {
prerollEnabled: enabled,
prerollDurationMs: Math.round(value * 1000),
}),
'Playback policy saved.',
);
setTouched(false);
await reload();
});
return (
<>
<PageHead title="Playback" intro="Presentation policy sent with every playback launch." />
<Banner message={error} />
{loading ? (
<Loading rows={1} />
) : (
<Card
title="Upcoming-show preroll"
intro="Sent with every playback launch. A change applies to the next title opened on every gateway-connected television; no app release is required."
icon="play"
tone="info"
actions={enabled ? <Tag tone="ok">on · {seconds}s</Tag> : <Tag>off</Tag>}
footer={
<Button variant="primary" busy={busy === 'save'} onClick={() => void save()}>
Save playback policy
</Button>
}
>
<Toggle
label="Show the preroll before a title starts"
checked={enabled}
onChange={(next) => {
setEnabled(next);
setTouched(true);
}}
/>
<div className="fields">
<Field label="Duration" hint="Between 1 and 30 seconds. The stream is already playing behind it.">
<input
type="number"
min={1}
max={30}
step={0.5}
value={seconds}
onChange={(event) => {
setSeconds(event.target.value);
setTouched(true);
}}
/>
</Field>
</div>
</Card>
)}
</>
);
}