142 lines
4.4 KiB
TypeScript
142 lines
4.4 KiB
TypeScript
import { useEffect, useMemo, useState } from 'react';
|
|
|
|
import { Channel, InputChannel, OutputChannel } from '../../types';
|
|
import Button from '../../../../components/Button/Button';
|
|
import Card from '../../../../components/Card/Card';
|
|
import ChoiceSelect from '../../../../components/ChoiceSelect/ChoiceSelect';
|
|
|
|
import './CopyChannelModal.css';
|
|
|
|
const INPUT_CHANNELS: readonly string[] = Object.values(InputChannel);
|
|
const OUTPUT_CHANNELS: readonly string[] = Object.values(OutputChannel);
|
|
const ALL_CHANNELS: readonly string[] = [...INPUT_CHANNELS, ...OUTPUT_CHANNELS];
|
|
|
|
const isInputChannel = (raw: string) => INPUT_CHANNELS.includes(raw);
|
|
|
|
/** Wraps a raw enum string back into the { Input } / { Output } shape. */
|
|
const wrapChannel = (raw: string): Channel =>
|
|
isInputChannel(raw) ? { Input: raw as InputChannel } : { Output: raw as OutputChannel };
|
|
|
|
/** The channels a given source can be copied onto: same type, minus itself. */
|
|
const destinationsFor = (source: string): string[] =>
|
|
(isInputChannel(source) ? INPUT_CHANNELS : OUTPUT_CHANNELS).filter(
|
|
(channel) => channel !== source
|
|
);
|
|
|
|
function CopyChannelModal({
|
|
onClose,
|
|
copyChannel,
|
|
}: {
|
|
onClose: () => void;
|
|
copyChannel: (channel1: Channel, channel2: Channel) => Promise<boolean>;
|
|
}) {
|
|
const [source, setSource] = useState<string>(ALL_CHANNELS[0]);
|
|
const [destination, setDestination] = useState<string>(() => destinationsFor(ALL_CHANNELS[0])[0]);
|
|
const [isCopying, setIsCopying] = useState(false);
|
|
|
|
const destinationOptions = useMemo(() => destinationsFor(source), [source]);
|
|
|
|
useEffect(() => {
|
|
const handleKeyDown = (e: KeyboardEvent) => {
|
|
if (e.key === 'Escape') {
|
|
onClose();
|
|
}
|
|
};
|
|
|
|
window.addEventListener('keydown', handleKeyDown);
|
|
return () => window.removeEventListener('keydown', handleKeyDown);
|
|
}, [onClose]);
|
|
|
|
const handleSourceChange = (nextSource: string) => {
|
|
setSource(nextSource);
|
|
|
|
// Picking a source of the other type (or picking the current
|
|
// destination as source) invalidates the destination.
|
|
const nextDestinations = destinationsFor(nextSource);
|
|
|
|
if (!nextDestinations.includes(destination)) {
|
|
setDestination(nextDestinations[0]);
|
|
}
|
|
};
|
|
|
|
const handleCopy = async () => {
|
|
setIsCopying(true);
|
|
|
|
try {
|
|
await copyChannel(wrapChannel(source), wrapChannel(destination));
|
|
onClose();
|
|
} finally {
|
|
setIsCopying(false);
|
|
}
|
|
};
|
|
|
|
return (
|
|
<div
|
|
className="copy-channel-overlay"
|
|
onMouseDown={(e) => {
|
|
if (e.target === e.currentTarget) {
|
|
onClose();
|
|
}
|
|
}}
|
|
>
|
|
<Card className="copy-channel-modal">
|
|
<div className="copy-channel-modal__header">
|
|
<h2>Copy Channel</h2>
|
|
<p>
|
|
Copy every setting of one channel onto another. Channels can only be copied onto
|
|
channels of the same type.
|
|
</p>
|
|
</div>
|
|
|
|
<div className="copy-channel-modal__transfer">
|
|
<div className="copy-channel-modal__field">
|
|
<label htmlFor="copy-channel-source">From</label>
|
|
|
|
<ChoiceSelect<string>
|
|
id="copy-channel-source"
|
|
value={source}
|
|
options={ALL_CHANNELS}
|
|
onChange={handleSourceChange}
|
|
/>
|
|
</div>
|
|
|
|
<div className="copy-channel-modal__arrow" aria-hidden="true">
|
|
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.5">
|
|
<path d="M4 12h15M13 6l6 6-6 6" strokeLinecap="round" strokeLinejoin="round" />
|
|
</svg>
|
|
</div>
|
|
|
|
<div className="copy-channel-modal__field">
|
|
<label htmlFor="copy-channel-destination">To</label>
|
|
|
|
<ChoiceSelect<string>
|
|
id="copy-channel-destination"
|
|
value={destination}
|
|
options={destinationOptions}
|
|
onChange={setDestination}
|
|
/>
|
|
</div>
|
|
</div>
|
|
|
|
<p className="copy-channel-modal__summary">
|
|
{destination} will take the settings of {source}.
|
|
<br />
|
|
This overwrites {destination}.
|
|
</p>
|
|
|
|
<div className="copy-channel-modal__actions">
|
|
<Button variant="outline" onClick={onClose} disabled={isCopying}>
|
|
Cancel
|
|
</Button>
|
|
|
|
<Button variant="outline" onClick={handleCopy} disabled={isCopying}>
|
|
{isCopying ? 'Copying...' : 'Copy'}
|
|
</Button>
|
|
</div>
|
|
</Card>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
export default CopyChannelModal;
|