92 lines
3.0 KiB
TypeScript
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>
|
||
|
|
)}
|
||
|
|
</>
|
||
|
|
);
|
||
|
|
}
|