195 lines
5.1 KiB
TypeScript
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>
|
|
);
|
|
}
|