restreamer-ui-v2/src/views/Edit/SourceSelect.js
2022-06-03 17:14:06 +02:00

195 lines
3.9 KiB
JavaScript

import React from 'react';
import SemverSatisfies from 'semver/functions/satisfies';
import Grid from '@mui/material/Grid';
import Button from '@mui/material/Button';
import Typography from '@mui/material/Typography';
import { Trans } from '@lingui/macro';
import Sources from './Sources';
function initConfig(initialConfig) {
let config = {};
for (let s of Sources.List()) {
config[s.id] = {};
}
config = {
...config,
...initialConfig,
};
return config;
}
function init(source) {
const settings = {};
for (let id of Sources.IDs()) {
settings[id] = {};
}
settings[source.type] = source.settings;
return settings;
}
function reducer(settings, data) {
const newSettings = {
...settings,
...data,
};
return newSettings;
}
export default function SourceSelect(props) {
// $source holds the currently selected device. It is initialized with the
// last stored source.
const [$source, setSource] = React.useState(props.source.type);
// $settings is for storing the settings of the different devices, such that if
// the user switches between them, they can be restored. It takes the last
// stored source settings as initial value.
const [$settings, setSettings] = React.useReducer(reducer, props.source, init);
const config = initConfig(props.config);
const handleSource = (source) => {
props.onChange(props.type);
setSource(source);
props.onSelect(props.type, source);
};
const handleRefresh = async () => {
await props.onRefresh();
};
const handleProbe = async (settings, inputs) => {
await props.onProbe(props.type, $source, settings, inputs);
};
const handleChange = (source) => (settings) => {
setSettings({
...$settings,
[source]: settings,
});
props.onChange(props.type, source, settings);
};
let sourceControl = null;
const s = Sources.Get($source);
if (s !== null) {
const Component = s.component;
if (SemverSatisfies(props.skills.ffmpeg.version, s.ffversion)) {
sourceControl = (
<Component
knownDevices={props.skills.sources[$source]}
skills={props.skills}
config={config[$source]}
settings={$settings[$source]}
onChange={handleChange($source)}
onProbe={handleProbe}
onRefresh={handleRefresh}
/>
);
}
}
return (
<Grid container spacing={1}>
<Grid item xs={12}>
<Select
type={props.type}
selected={$source}
ffversion={props.skills.ffmpeg.version}
availableSources={props.skills.sources}
onSelect={handleSource}
/>
</Grid>
<Grid item xs={12}>
{sourceControl}
</Grid>
</Grid>
);
}
SourceSelect.defaultProps = {
type: '',
skills: {},
source: {},
config: {},
onProbe: function (type, device, settings, inputs) {},
onSelect: function (type, device) {},
onChange: function (type, device, settings) {},
onRefresh: function () {},
};
function Select(props) {
const handleSource = (source) => () => {
props.onSelect(source);
};
let availableSources = [];
for (let s of Sources.List()) {
if (!(s.id in props.availableSources)) {
continue;
}
if (!s.capabilities.includes(props.type)) {
continue;
}
if (!SemverSatisfies(props.ffversion, s.ffversion)) {
continue;
}
const variant = s.id === props.selected ? 'bigSelected' : 'big';
const Icon = s.icon;
availableSources.push(
<Grid item xs={6} md={4} align="center" key={s.id}>
<Button variant={variant} onClick={handleSource(s.id)}>
<div>
<Icon />
<Typography>{s.name}</Typography>
</div>
</Button>
</Grid>
);
}
if (availableSources.length === 0) {
return (
<Grid container spacing={1}>
<Grid item xs={12}>
<Typography variant="body1">
<Trans>No sources available</Trans>
</Typography>
</Grid>
</Grid>
);
}
return (
<Grid container spacing={1}>
{availableSources}
</Grid>
);
}
Select.defaultProps = {
type: '',
selected: '',
ffversion: '0.0.0',
availableSources: {},
onSelect: function (source) {},
};