@azure/communication-react
Version:
React library for building modern communication user experiences utilizing Azure Communication Services
166 lines • 10.7 kB
JavaScript
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
import { Dropdown, Label, mergeStyles, Stack } from '@fluentui/react';
import { DefaultButton } from '@fluentui/react';
import { useEffect } from 'react';
import { useTheme } from "../../../../../react-components/src";
import React from 'react';
import { CallCompositeIcon } from '../../common/icons';
import { useLocale } from '../../localization';
import { deviceSelectionContainerStyles, dropDownStyles, dropDownTitleIconStyles, mainStackTokens, optionIconStyles, soundStackTokens } from '../styles/LocalDeviceSettings.styles';
import { useAdapter } from '../adapter/CallAdapterProvider';
import { ConfigurationPageCameraDropdown } from './ConfigurationPageCameraDropdown';
import { ConfigurationPageMicDropdown } from './ConfigurationPageMicDropdown';
import { cameraAndVideoEffectsContainerStyleDesktop } from '../styles/CallConfiguration.styles';
import { effectsButtonStyles } from '../styles/CallConfiguration.styles';
import { useSelector } from '../hooks/useSelector';
import { getRole, getVideoEffectsDependency } from '../selectors/baseSelectors';
import { getEnvironmentInfo } from '../selectors/baseSelectors';
import { _isSafari } from '../utils';
import { useId } from '@fluentui/react-hooks';
const getDropDownList = (list) => {
// Remove duplicates
const noDuplicates = new Map();
for (const item of list) {
noDuplicates.set(item.id, item);
}
const dropdownList = [];
for (const item of noDuplicates.values()) {
dropdownList.push({
key: item.id,
text: item.name === '' ? item.deviceType : item.name
});
}
return dropdownList;
};
const getOptionIcon = (type) => {
if (type === 'Camera') {
return React.createElement(CallCompositeIcon, { iconName: "LocalDeviceSettingsCamera", className: optionIconStyles });
}
else if (type === 'Microphone') {
return React.createElement(CallCompositeIcon, { iconName: "LocalDeviceSettingsMic", className: optionIconStyles });
}
else if (type === 'Speaker') {
return React.createElement(CallCompositeIcon, { iconName: "LocalDeviceSettingsSpeaker", className: optionIconStyles });
}
else {
return undefined;
}
};
const onRenderTitle = (iconType, props) => {
var _a;
return props ? React.createElement("div", { className: dropDownTitleIconStyles },
getOptionIcon(iconType),
React.createElement("span", null, (_a = props[0]) === null || _a === void 0 ? void 0 : _a.text)) : React.createElement(React.Fragment, null);
};
const localVideoViewOptions = {
scalingMode: 'Crop',
isMirrored: true
};
/**
* @private
*/
export const LocalDeviceSettings = (props) => {
var _a;
const theme = useTheme();
const locale = useLocale();
const adapter = useAdapter();
const onResolveVideoEffectDependency = useSelector(getVideoEffectsDependency);
const defaultPlaceHolder = locale.strings.call.defaultPlaceHolder;
const cameraLabel = locale.strings.call.cameraLabel;
const soundLabel = locale.strings.call.soundLabel;
const noSpeakersLabel = locale.strings.call.noSpeakersLabel;
const noCameraLabel = locale.strings.call.noCamerasLabel;
const noMicLabel = locale.strings.call.noMicrophonesLabel;
const role = useSelector(getRole);
const cameraPermissionGranted = props.cameraPermissionGranted;
const micPermissionGranted = props.microphonePermissionGranted;
const roleCanUseCamera = role !== 'Consumer';
const roleCanUseMic = role !== 'Consumer';
// TODO: speaker permission is tied to microphone permission (when you request 'audio' permission using the SDK) its
// actually granting access to query both microphone and speaker. However the browser popup asks you explicity for
// 'microphone'. This needs investigation on how we want to handle this and maybe needs follow up with SDK team.
useEffect(() => {
if (cameraPermissionGranted) {
adapter.queryCameras();
}
if (micPermissionGranted) {
adapter.queryMicrophones();
}
adapter.querySpeakers();
}, [adapter, cameraPermissionGranted, micPermissionGranted]);
const hasCameras = props.cameras.length > 0;
const hasMicrophones = props.microphones.length > 0;
const hasSpeakers = props.speakers.length > 0;
const environmentInfo = useSelector(getEnvironmentInfo);
const isSafariWithNoSpeakers = _isSafari(environmentInfo) && !hasSpeakers;
const cameraLabelId = useId('camera-label');
const soundLabelId = useId('sound-label');
const cameraGrantedDropdown = React.createElement(Dropdown, { "data-ui-id": "call-composite-local-camera-settings", "aria-labelledby": cameraLabelId, placeholder: hasCameras ? defaultPlaceHolder : noCameraLabel, options: cameraPermissionGranted ? getDropDownList(props.cameras) : [{
key: 'deniedOrUnknown',
text: ''
}], styles: dropDownStyles(theme), disabled: !cameraPermissionGranted || !hasCameras, errorMessage: props.cameraPermissionGranted === undefined || props.cameraPermissionGranted ? undefined : locale.strings.call.cameraPermissionDenied, defaultSelectedKey: micPermissionGranted ? props.selectedCamera ? props.selectedCamera.id : props.cameras ? (_a = props.cameras[0]) === null || _a === void 0 ? void 0 : _a.id : '' : 'deniedOrUnknown', onChange: (event, option, index) => __awaiter(void 0, void 0, void 0, function* () {
const camera = props.cameras[index !== null && index !== void 0 ? index : 0];
if (camera) {
yield props.onSelectCamera(camera, localVideoViewOptions);
}
else {
console.error('No cameras available');
}
}), onRenderTitle: (props) => onRenderTitle('Camera', props) });
const micGrantedDropdown = React.createElement(React.Fragment, null, roleCanUseMic && React.createElement(Dropdown, { "aria-labelledby": soundLabelId, placeholder: hasMicrophones ? defaultPlaceHolder : noMicLabel, styles: dropDownStyles(theme), disabled: !micPermissionGranted || !hasMicrophones, errorMessage: props.microphonePermissionGranted === undefined || props.microphonePermissionGranted ? undefined : locale.strings.call.microphonePermissionDenied, options: micPermissionGranted ? getDropDownList(props.microphones) : [{
key: 'deniedOrUnknown',
text: ''
}], defaultSelectedKey: micPermissionGranted ? props.selectedMicrophone ? props.selectedMicrophone.id : defaultDeviceId(props.microphones) : 'deniedOrUnknown', onChange: (event, option, index) => {
const microphone = props.microphones[index !== null && index !== void 0 ? index : 0];
if (microphone) {
props.onSelectMicrophone(microphone);
}
else {
console.error('No microphones available');
}
}, onRenderTitle: (props) => onRenderTitle('Microphone', props) }));
const speakerDropdown = React.createElement(Dropdown, { "aria-labelledby": soundLabelId, placeholder: hasSpeakers ? defaultPlaceHolder : noSpeakersLabel, styles: dropDownStyles(theme), disabled: props.speakers.length === 0, options: getDropDownList(props.speakers), defaultSelectedKey: props.selectedSpeaker ? props.selectedSpeaker.id : defaultDeviceId(props.speakers), onChange: (event, option, index) => {
const speaker = props.speakers[index !== null && index !== void 0 ? index : 0];
if (speaker) {
props.onSelectSpeaker(speaker);
}
else {
console.error('No speakers available');
}
}, onRenderTitle: (props) => onRenderTitle('Speaker', props) });
return React.createElement(Stack, { "data-ui-id": "call-composite-device-settings", tokens: mainStackTokens, styles: deviceSelectionContainerStyles },
roleCanUseCamera && React.createElement(Stack, null,
React.createElement(Stack, { horizontal: true, horizontalAlign: "space-between", styles: cameraAndVideoEffectsContainerStyleDesktop },
React.createElement(Label, { id: cameraLabelId, className: mergeStyles(dropDownStyles(theme).label) }, cameraLabel),
onResolveVideoEffectDependency && React.createElement(DefaultButton, { iconProps: {
iconName: 'ConfigurationScreenVideoEffectsButton'
}, styles: effectsButtonStyles(theme, !cameraPermissionGranted), onClick: props.onClickVideoEffects, disabled: !cameraPermissionGranted, "data-ui-id": 'call-config-video-effects-button' }, locale.strings.call.configurationPageVideoEffectsButtonLabel)),
React.createElement(ConfigurationPageCameraDropdown, { cameraGrantedDropdown: cameraGrantedDropdown, cameraPermissionGranted: cameraPermissionGranted !== null && cameraPermissionGranted !== void 0 ? cameraPermissionGranted : false, ariaLabelledby: cameraLabelId })),
React.createElement(Stack, null,
React.createElement(Label, { id: soundLabelId, className: mergeStyles(dropDownStyles(theme).label) }, soundLabel),
React.createElement(Stack, { "data-ui-id": "call-composite-sound-settings", tokens: soundStackTokens },
React.createElement(ConfigurationPageMicDropdown, { micGrantedDropdown: micGrantedDropdown, micPermissionGranted: micPermissionGranted !== null && micPermissionGranted !== void 0 ? micPermissionGranted : false, ariaLabelledby: soundLabelId }),
isSafariWithNoSpeakers ? React.createElement(React.Fragment, null) : speakerDropdown)));
};
const defaultDeviceId = (devices) => {
var _a;
if (devices.length === 0) {
return undefined;
}
const defaultDevice = devices.find(device => device.isSystemDefault);
if (defaultDevice) {
return defaultDevice.id;
}
return (_a = devices[0]) === null || _a === void 0 ? void 0 : _a.id;
};
//# sourceMappingURL=LocalDeviceSettings.js.map