UNPKG

@azure/communication-react

Version:

React library for building modern communication user experiences utilizing Azure Communication Services

150 lines • 12.1 kB
// 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 React, { useCallback, useMemo, useRef, useState } from 'react'; import { useAdaptedSelector } from '../hooks/useAdaptedSelector'; import { useHandlers } from '../hooks/useHandlers'; import { LocalDeviceSettings } from '../components/LocalDeviceSettings'; import { StartCallButton } from '../components/StartCallButton'; import { devicePermissionSelector } from '../selectors/devicePermissionSelector'; import { useSelector } from '../hooks/useSelector'; import { CameraButton, DevicesButton, ErrorBar, _useContainerWidth, useTheme } from "../../../../../react-components/src"; import { getCallingSelector } from "../../../../../calling-component-bindings/src"; import { Image, mergeStyles, Panel, PanelType, Stack } from '@fluentui/react'; import { callDetailsContainerStyles, configurationCenteredContent, configurationSectionStyle, deviceConfigurationStackTokens, fillWidth, logoStyles, panelFocusProps, panelStyles, startCallButtonStyleDesktop } from '../styles/CallConfiguration.styles'; import { LocalPreview } from '../components/LocalPreview'; import { callDetailsStyleDesktop, callDetailsStyleMobile, configurationStackTokensDesktop, configurationStackTokensMobile, configurationContainerStyle, selectionContainerStyle, startCallButtonContainerStyleDesktop, startCallButtonContainerStyleMobile, startCallButtonStyleMobile, titleContainerStyleDesktop, titleContainerStyleMobile } from '../styles/CallConfiguration.styles'; import { useLocale } from '../../localization'; import { bannerNotificationStyles } from '../styles/CallPage.styles'; import { usePropsFor } from '../hooks/usePropsFor'; import { ConfigurationPageErrorBar } from '../components/ConfigurationPageErrorBar'; import { _isSafari } from '../utils'; import { VIDEO_EFFECTS_SIDE_PANE_WIDTH_REM, useVideoEffectsPane } from '../components/SidePane/useVideoEffectsPane'; import { SidePane } from '../components/SidePane/SidePane'; import { useIsParticularSidePaneOpen } from '../components/SidePane/SidePaneProvider'; import { localVideoSelector } from '../../CallComposite/selectors/localVideoStreamSelector'; import { SvgWithWordWrapping } from '../components/SvgWithWordWrapping'; import { getMicrophones, getRole } from '../selectors/baseSelectors'; import { getEnvironmentInfo } from '../selectors/baseSelectors'; /** * @private */ export const ConfigurationPage = (props) => { var _a; const { startCallHandler, mobileView, modalLayerHostId, joinCallOptions = { microphoneCheck: 'requireMicrophoneAvailable' } } = props; const theme = useTheme(); const options = useAdaptedSelector(getCallingSelector(DevicesButton)); const localDeviceSettingsHandlers = useHandlers(LocalDeviceSettings); const { video: cameraPermissionGranted, audio: microphonePermissionGranted } = useSelector(devicePermissionSelector); const environmentInfo = useSelector(getEnvironmentInfo); const configContainerRef = useRef(null); const configWidth = _useContainerWidth(configContainerRef); /** * We want to stack the two sections (preview and devices) when the container is less than 450 wide. * We lose size calculation when the container is less than 450 wide, so we stack the two sections. */ const stackConfig = !!configWidth && configWidth < 450; const errorBarProps = usePropsFor(ErrorBar); const microphones = useSelector(getMicrophones); let disableStartCallButton = (!microphonePermissionGranted || (microphones === null || microphones === void 0 ? void 0 : microphones.length) === 0) && joinCallOptions.microphoneCheck === 'requireMicrophoneAvailable'; const role = useSelector(getRole); const isCameraOn = useSelector(localVideoSelector).isAvailable; const [cameraLoading, setCameraLoading] = useState(false); const switchCamera = useCallback((device, options) => __awaiter(void 0, void 0, void 0, function* () { // Only set camera to be loading if we are switching source while the camera is on setCameraLoading(isCameraOn); try { yield localDeviceSettingsHandlers.onSelectCamera(device, options); } finally { setCameraLoading(false); } }), [localDeviceSettingsHandlers, isCameraOn]); const { onToggleCamera } = usePropsFor(CameraButton); const toggleCamera = useCallback((options) => __awaiter(void 0, void 0, void 0, function* () { // Only set camera to loading if we are turning on the camera (i.e. the camera was off) setCameraLoading(!isCameraOn); try { yield onToggleCamera(options); } finally { setCameraLoading(false); } }), [isCameraOn, onToggleCamera]); let filteredLatestErrors = props.latestErrors; // TODO: move this logic to the error bar selector once role is plumbed from the headless SDK if (role !== 'Consumer') { filteredLatestErrors = filteredLatestErrors.filter(e => e.type !== 'callCameraAccessDenied' && e.type !== 'callCameraAccessDeniedSafari'); } if ((useIsParticularSidePaneOpen('videoeffects') || !isCameraOn) && errorBarProps) { filteredLatestErrors = filteredLatestErrors.filter(e => e.type !== 'unableToStartVideoEffect'); } if (role === 'Consumer') { // If user's role permissions do not allow access to the microphone button then DO NOT disable the start call button // because microphone device permission is not needed for the user's role disableStartCallButton = false; } const locale = useLocale(); const title = locale.strings.call.configurationPageTitle.length > 0 ? React.createElement(Stack.Item, { className: mobileView ? titleContainerStyleMobile(theme) : titleContainerStyleDesktop(theme) }, React.createElement(SvgWithWordWrapping, { width: mobileView ? 325 : 445, lineHeightPx: 16 * 1.5, bufferHeightPx: 16, text: locale.strings.call.configurationPageTitle, role: "heading" })) : React.createElement(React.Fragment, null); const callDescription = locale.strings.call.configurationPageCallDetails && React.createElement(Stack.Item, { className: mobileView ? callDetailsStyleMobile(theme) : callDetailsStyleDesktop(theme) }, locale.strings.call.configurationPageCallDetails); const mobileWithPreview = mobileView && role !== 'Consumer'; // When permission API is not available, we want to show screen saying checking for access (disappears on its own) // then based on permission setting, we show permission denied or nothing const { toggleVideoEffectsPane, closeVideoEffectsPane, isVideoEffectsPaneOpen } = useVideoEffectsPane(props.updateSidePaneRenderer, mobileView, props.latestErrors, props.onDismissError); const startCall = useCallback(() => __awaiter(void 0, void 0, void 0, function* () { closeVideoEffectsPane(); startCallHandler(); }), [startCallHandler, closeVideoEffectsPane]); const panelLayerProps = useMemo(() => ({ hostId: modalLayerHostId }), [modalLayerHostId]); const filteredErrorBarProps = useMemo(() => (Object.assign(Object.assign({}, errorBarProps), { activeErrorMessages: filteredLatestErrors })), [errorBarProps, filteredLatestErrors]); const containerStyles = useMemo(() => { var _a; return configurationContainerStyle(!mobileView, (_a = props.backgroundImage) === null || _a === void 0 ? void 0 : _a.url); }, [mobileView, (_a = props.backgroundImage) === null || _a === void 0 ? void 0 : _a.url]); return React.createElement("div", { ref: configContainerRef, className: mergeStyles(containerStyles) }, React.createElement(Stack, { styles: bannerNotificationStyles }, React.createElement(ConfigurationPageErrorBar, { errorBarProps: filteredErrorBarProps, onDismissError: props.onDismissError })), React.createElement(Stack, { verticalFill: true, grow: true, horizontal: true, className: fillWidth }, React.createElement(Stack, { className: configurationCenteredContent(mobileWithPreview, !!props.logo), verticalAlign: stackConfig && !mobileView ? undefined : 'center', verticalFill: mobileWithPreview, tokens: mobileWithPreview ? configurationStackTokensMobile : configurationStackTokensDesktop }, React.createElement(Stack.Item, { styles: callDetailsContainerStyles }, React.createElement(Logo, { logo: props.logo }), title, callDescription), React.createElement(Stack, { horizontal: configHorizontal(mobileWithPreview, mobileView, stackConfig), horizontalAlign: mobileWithPreview ? 'stretch' : 'center', verticalFill: mobileWithPreview, tokens: deviceConfigurationStackTokens }, role !== 'Consumer' && React.createElement(LocalPreview, { mobileView: mobileWithPreview, showDevicesButton: mobileView, onToggleCamera: toggleCamera, cameraLoading: cameraLoading && !isCameraOn }), React.createElement(Stack, { styles: mobileView ? undefined : configurationSectionStyle }, !mobileWithPreview && React.createElement(Stack, { className: mobileView ? undefined : selectionContainerStyle(theme, _isSafari(environmentInfo)) }, React.createElement(LocalDeviceSettings, Object.assign({}, options, localDeviceSettingsHandlers, { onSelectCamera: switchCamera, cameraPermissionGranted: cameraPermissionGrantedTrampoline(cameraPermissionGranted), microphonePermissionGranted: micPermissionGrantedTrampoline(microphonePermissionGranted), onClickVideoEffects: toggleVideoEffectsPane }))), React.createElement(Stack, { styles: mobileWithPreview ? startCallButtonContainerStyleMobile : startCallButtonContainerStyleDesktop, horizontalAlign: mobileWithPreview ? 'stretch' : 'end' }, React.createElement(StartCallButton, { className: mobileWithPreview ? startCallButtonStyleMobile : startCallButtonStyleDesktop, onClick: startCall, disabled: disableStartCallButton, hideIcon: true }))))), React.createElement(Panel, { isOpen: isVideoEffectsPaneOpen, hasCloseButton: false, isBlocking: false, isHiddenOnDismiss: false, styles: panelStyles, focusTrapZoneProps: panelFocusProps, layerProps: panelLayerProps, type: PanelType.custom, customWidth: `${VIDEO_EFFECTS_SIDE_PANE_WIDTH_REM}rem` }, React.createElement(SidePane, { ariaLabel: isVideoEffectsPaneOpen ? locale.strings.call.videoEffectsPaneAriaLabel : undefined, mobileView: props.mobileView, updateSidePaneRenderer: props.updateSidePaneRenderer, maxWidth: `${VIDEO_EFFECTS_SIDE_PANE_WIDTH_REM}rem`, minWidth: `${VIDEO_EFFECTS_SIDE_PANE_WIDTH_REM}rem` })))); }; const cameraPermissionGrantedTrampoline = (cameraPermissionGranted, videoState) => { return cameraPermissionGranted; }; const micPermissionGrantedTrampoline = (microphonePermissionGranted, audioState) => { return microphonePermissionGranted; }; const Logo = (props) => { if (!props.logo) { return React.createElement(React.Fragment, null); } return React.createElement(Image, { styles: logoStyles(props.logo.shape), src: props.logo.url, alt: props.logo.alt }); }; const configHorizontal = (mobileWithPreview, isMobile, configTooNarrow) => { if (configTooNarrow && !isMobile) { return false; } return !mobileWithPreview; }; //# sourceMappingURL=ConfigurationPage.js.map