Skip to content

Commit

Permalink
[Release-blocker] composite audio context updates (#4539)
Browse files Browse the repository at this point in the history
* move audio context to react context to minimize hook usage

* fix build

* Change files

* Fix test
  • Loading branch information
dmceachernmsft authored Apr 26, 2024
1 parent 27edcdb commit c688200
Show file tree
Hide file tree
Showing 9 changed files with 68 additions and 16 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"type": "patch",
"area": "fix",
"workstream": "Calling Sounds",
"comment": "Move internal CallComposite AudioContext to a react context to avoid over creation of audio contexts",
"packageName": "@azure/communication-react",
"email": "[email protected]",
"dependentChangeType": "patch"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"type": "patch",
"area": "fix",
"workstream": "Calling Sounds",
"comment": "Move internal CallComposite AudioContext to a react context to avoid over creation of audio contexts",
"packageName": "@azure/communication-react",
"email": "[email protected]",
"dependentChangeType": "patch"
}
4 changes: 3 additions & 1 deletion common/config/jest/jestSetup.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ console.error = (...args) => {
console.warning = (...args) => {
throw args;
};

window.AudioContext = jest.fn().mockImplementation(() => {
return {};
});
// Add `ResizeObserver` to globals. Without this GridLayout jest tests fail with "ReferenceError: ResizeObserver is not defined"
global.ResizeObserver = require('resize-observer-polyfill');
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,6 @@ const onSendDtmfTone = (dtmfTone: DtmfTone): Promise<void> => {
return Promise.resolve();
};

window.AudioContext = jest.fn().mockImplementation(() => {
return {};
});

describe('Dialpad tests', () => {
beforeAll(() => {
registerIcons({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,6 @@ import { _MockCallAdapter } from './MockCallAdapter';
import { CallComposite } from './CallComposite';
import { render } from '@testing-library/react';

window.AudioContext = jest.fn().mockImplementation(() => {
return {};
});

describe('CallComposite device permission test for different roles', () => {
let audioDevicePermissionRequests = 0;
let videoDevicePermissionRequests = 0;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ import { useTrackedCapabilityChangedNotifications } from './utils/TrackCapabilit
import { useEndedCallConsoleErrors } from './utils/useConsoleErrors';
/* @conditional-compile-remove(end-of-call-survey) */
import { SurveyPage } from './pages/SurveyPage';
import { useAudio } from '../common/AudioProvider';

/**
* Props for {@link CallComposite}.
Expand Down Expand Up @@ -411,7 +412,7 @@ const MainScreen = (props: MainScreenProps): JSX.Element => {
};
}, [adapter]);

const compositeAudioContext = useRef<AudioContext>(new AudioContext());
const compositeAudioContext = useAudio();

const capabilitiesChangedInfoAndRole = useSelector(capabilitiesChangedInfoAndRoleSelector);

Expand Down Expand Up @@ -593,7 +594,7 @@ const MainScreen = (props: MainScreenProps): JSX.Element => {
capabilitiesChangedNotificationBarProps={capabilitiesChangedNotificationBarProps}
pinnedParticipants={pinnedParticipants}
setPinnedParticipants={setPinnedParticipants}
compositeAudioContext={compositeAudioContext.current}
compositeAudioContext={compositeAudioContext}
/>
);
break;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,6 @@ import { CallWithChatComposite } from './CallWithChatComposite';
import { CallWithChatAdapterState } from './state/CallWithChatAdapterState';
import { registerIcons } from '@fluentui/react';

window.AudioContext = jest.fn().mockImplementation(() => {
return {};
});

function createMockCallWithChatAdapter(): CallWithChatAdapter {
const callWithChatAdapter = {} as CallWithChatAdapter;
callWithChatAdapter.onStateChange = jest.fn();
Expand Down
36 changes: 36 additions & 0 deletions packages/react-composites/src/composites/common/AudioProvider.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

import React, { useContext, createContext } from 'react';

/**
* @private
*/
export interface ACSAudioProviderProps {
audioContext: AudioContext;
children: JSX.Element;
}

/**
*
* @param props
* @returns
*/
export const ACSAudioProvider = (props: ACSAudioProviderProps): JSX.Element => {
const { audioContext, children } = props;
const alreadyWrapped = useAudio();
if (alreadyWrapped) {
return <>{children}</>;
}
return <ACSAudioContext.Provider value={audioContext}>{props.children}</ACSAudioContext.Provider>;
};

/**
* @private
*/
const ACSAudioContext = createContext<AudioContext>(new AudioContext());

/**
* @private
*/
export const useAudio = (): AudioContext => useContext(ACSAudioContext);
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import { AvatarPersonaDataCallback } from './AvatarPersona';
import { CallCompositeIcons, CallWithChatCompositeIcons, ChatCompositeIcons, DEFAULT_COMPOSITE_ICONS } from './icons';
import { globalLayerHostStyle } from './styles/GlobalHostLayer.styles';
import { useId } from '@fluentui/react-hooks';
import { ACSAudioProvider } from './AudioProvider';
/**
* Properties common to all composites exported from this library.
*
Expand Down Expand Up @@ -106,13 +107,19 @@ export const BaseProvider = (
*/
registerIcons({ icons: { ...iconsToRegister, ...props.icons } });

/**
* We need to create one context for the AudioProvider to ensure that we only have one instance of the AudioContext.
*/
const compositeAudioContext = new AudioContext();
// we use Customizer to override default LayerHost injected to <body />
// which stop polluting global dom tree and increase compatibility with react-full-screen
const CompositeElement = (
<FluentThemeProvider fluentTheme={fluentTheme} rtl={rtl}>
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=0" />
<Customizer scopedSettings={{ Layer: { hostId: globalLayerHostId } }}>
<WithBackgroundColor>{props.children}</WithBackgroundColor>
<ACSAudioProvider audioContext={compositeAudioContext}>
<WithBackgroundColor>{props.children}</WithBackgroundColor>
</ACSAudioProvider>
</Customizer>
<LayerHost id={globalLayerHostId} className={mergeStyles(globalLayerHostStyle)} />
</FluentThemeProvider>
Expand Down

0 comments on commit c688200

Please sign in to comment.