signal-desktop/ts/components/CallingDeviceSelection.tsx

195 lines
5.1 KiB
TypeScript

// Copyright 2020 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
import * as React from 'react';
import type { AudioDevice } from '@signalapp/ringrtc';
import type { Option } from './Select';
import { Modal } from './Modal';
import { Select } from './Select';
import type { LocalizerType } from '../types/Util';
import type {
ChangeIODevicePayloadType,
MediaDeviceSettings,
} from '../types/Calling';
import { CallingDeviceType } from '../types/Calling';
import { Theme } from '../util/theme';
export type Props = MediaDeviceSettings & {
changeIODevice: (payload: ChangeIODevicePayloadType) => void;
i18n: LocalizerType;
toggleSettings: () => void;
};
function localizeDefault(i18n: LocalizerType, deviceLabel: string): string {
return deviceLabel.toLowerCase().startsWith('default')
? deviceLabel.replace(
/default/i,
i18n('icu:callingDeviceSelection__select--default')
)
: deviceLabel;
}
function renderAudioOptions(
devices: Array<AudioDevice>,
i18n: LocalizerType
): Array<Option> {
if (!devices.length) {
return [
{
text: i18n('icu:callingDeviceSelection__select--no-device'),
value: '',
},
];
}
return devices.map(device => {
return {
text: localizeDefault(i18n, device.name),
value: device.index,
};
});
}
function renderVideoOptions(
devices: Array<MediaDeviceInfo>,
i18n: LocalizerType
): Array<Option> {
if (!devices.length) {
return [
{
text: i18n('icu:callingDeviceSelection__select--no-device'),
value: '',
},
];
}
return devices.map((device: MediaDeviceInfo) => {
return {
text: localizeDefault(i18n, device.label),
value: device.deviceId,
};
});
}
function createAudioChangeHandler(
devices: Array<AudioDevice>,
changeIODevice: (payload: ChangeIODevicePayloadType) => void,
type: CallingDeviceType.SPEAKER | CallingDeviceType.MICROPHONE
) {
return (value: string): void => {
changeIODevice({
type,
selectedDevice: devices[Number(value)],
});
};
}
function createCameraChangeHandler(
changeIODevice: (payload: ChangeIODevicePayloadType) => void
) {
return (value: string): void => {
changeIODevice({
type: CallingDeviceType.CAMERA,
selectedDevice: value,
});
};
}
export function CallingDeviceSelection({
availableCameras,
availableMicrophones,
availableSpeakers,
changeIODevice,
i18n,
selectedCamera,
selectedMicrophone,
selectedSpeaker,
toggleSettings,
}: Props): JSX.Element {
const selectedMicrophoneIndex = selectedMicrophone
? selectedMicrophone.index
: undefined;
const selectedSpeakerIndex = selectedSpeaker
? selectedSpeaker.index
: undefined;
return (
<Modal
modalName="CallingDeviceSelection"
i18n={i18n}
theme={Theme.Dark}
onClose={toggleSettings}
>
<div className="module-calling-device-selection">
<button
type="button"
className="module-calling-device-selection__close-button"
onClick={toggleSettings}
tabIndex={0}
aria-label={i18n('icu:close')}
/>
</div>
<h1 className="module-calling-device-selection__title">
{i18n('icu:callingDeviceSelection__settings')}
</h1>
<label htmlFor="video" className="module-calling-device-selection__label">
{i18n('icu:callingDeviceSelection__label--video')}
</label>
<div className="module-calling-device-selection__select">
<Select
disabled={!availableCameras.length}
id="camera"
name="camera"
onChange={createCameraChangeHandler(changeIODevice)}
options={renderVideoOptions(availableCameras, i18n)}
value={selectedCamera}
/>
</div>
<label
htmlFor="audio-input"
className="module-calling-device-selection__label"
>
{i18n('icu:callingDeviceSelection__label--audio-input')}
</label>
<div className="module-calling-device-selection__select">
<Select
disabled={!availableMicrophones.length}
id="audio-input"
name="audio-input"
onChange={createAudioChangeHandler(
availableMicrophones,
changeIODevice,
CallingDeviceType.MICROPHONE
)}
options={renderAudioOptions(availableMicrophones, i18n)}
value={selectedMicrophoneIndex}
/>
</div>
<label
htmlFor="audio-output"
className="module-calling-device-selection__label"
>
{i18n('icu:callingDeviceSelection__label--audio-output')}
</label>
<div className="module-calling-device-selection__select">
<Select
disabled={!availableSpeakers.length}
id="audio-output"
name="audio-output"
onChange={createAudioChangeHandler(
availableSpeakers,
changeIODevice,
CallingDeviceType.SPEAKER
)}
options={renderAudioOptions(availableSpeakers, i18n)}
value={selectedSpeakerIndex}
/>
</div>
</Modal>
);
}