From 67b52ca346f81f7433e870eaacb7fc6b517c4946 Mon Sep 17 00:00:00 2001 From: James Burnside <2684369+JamesBurnside@users.noreply.github.com> Date: Thu, 17 Oct 2024 03:59:11 +0000 Subject: [PATCH 1/5] Add option to adapter dispose to ignore the callClient. Update adapter.dispose to be async --- .../review/beta/communication-react.api.md | 10 ++++++++-- .../review/stable/communication-react.api.md | 10 ++++++++-- .../src/composites/CallComposite/MockCallAdapter.ts | 2 +- .../adapter/AzureCommunicationCallAdapter.ts | 10 ++++++---- .../composites/CallComposite/adapter/CallAdapter.ts | 7 +++++-- .../adapter/AzureCommunicationCallWithChatAdapter.ts | 8 ++++---- .../adapter/CallWithChatAdapter.ts | 10 +++++++--- .../adapter/CallWithChatBackedCallAdapter.ts | 3 ++- .../adapter/CallWithChatBackedChatAdapter.ts | 2 +- packages/react-composites/tests/app/call/LiveApp.tsx | 4 +++- 10 files changed, 45 insertions(+), 21 deletions(-) diff --git a/packages/communication-react/review/beta/communication-react.api.md b/packages/communication-react/review/beta/communication-react.api.md index bcb631a7207..fb9904d5011 100644 --- a/packages/communication-react/review/beta/communication-react.api.md +++ b/packages/communication-react/review/beta/communication-react.api.md @@ -1183,7 +1183,10 @@ export interface CallSurveyImprovementSuggestions { } // @public -export interface CallWithChatAdapter extends CallWithChatAdapterManagement, AdapterState, Disposable_2, CallWithChatAdapterSubscriptions { +export interface CallWithChatAdapter extends CallWithChatAdapterManagement, AdapterState, CallWithChatAdapterSubscriptions { + dispose(options?: { + doNotDisposeCallAgent?: boolean; + }): Promise; } // @public @@ -2087,7 +2090,10 @@ export type ClientState = CallClientState & ChatClientState; export type Common = Pick>; // @public -export interface CommonCallAdapter extends AdapterState, Disposable_2, CallAdapterCallOperations, CallAdapterDeviceManagement, CallAdapterSubscribers { +export interface CommonCallAdapter extends AdapterState, CallAdapterCallOperations, CallAdapterDeviceManagement, CallAdapterSubscribers { + dispose(options?: { + doNotDisposeCallAgent?: boolean; + }): Promise; // @deprecated joinCall(microphoneOn?: boolean): void; joinCall(options?: JoinCallOptions): void; diff --git a/packages/communication-react/review/stable/communication-react.api.md b/packages/communication-react/review/stable/communication-react.api.md index e93382feba5..bffb02055c3 100644 --- a/packages/communication-react/review/stable/communication-react.api.md +++ b/packages/communication-react/review/stable/communication-react.api.md @@ -967,7 +967,10 @@ export interface CallSurveyImprovementSuggestions { } // @public -export interface CallWithChatAdapter extends CallWithChatAdapterManagement, AdapterState, Disposable_2, CallWithChatAdapterSubscriptions { +export interface CallWithChatAdapter extends CallWithChatAdapterManagement, AdapterState, CallWithChatAdapterSubscriptions { + dispose(options?: { + doNotDisposeCallAgent?: boolean; + }): Promise; } // @public @@ -1801,7 +1804,10 @@ export type ClientState = CallClientState & ChatClientState; export type Common = Pick>; // @public -export interface CommonCallAdapter extends AdapterState, Disposable_2, CallAdapterCallOperations, CallAdapterDeviceManagement, CallAdapterSubscribers { +export interface CommonCallAdapter extends AdapterState, CallAdapterCallOperations, CallAdapterDeviceManagement, CallAdapterSubscribers { + dispose(options?: { + doNotDisposeCallAgent?: boolean; + }): Promise; // @deprecated joinCall(microphoneOn?: boolean): void; joinCall(options?: JoinCallOptions): void; diff --git a/packages/react-composites/src/composites/CallComposite/MockCallAdapter.ts b/packages/react-composites/src/composites/CallComposite/MockCallAdapter.ts index 0a1d8e40947..0985a7bc27a 100644 --- a/packages/react-composites/src/composites/CallComposite/MockCallAdapter.ts +++ b/packages/react-composites/src/composites/CallComposite/MockCallAdapter.ts @@ -60,7 +60,7 @@ export class _MockCallAdapter implements CallAdapter { getState(): CallAdapterState { return this.state; } - dispose(): void { + dispose(): Promise { throw Error('dispose not implemented'); } joinCall(): Call | undefined { diff --git a/packages/react-composites/src/composites/CallComposite/adapter/AzureCommunicationCallAdapter.ts b/packages/react-composites/src/composites/CallComposite/adapter/AzureCommunicationCallAdapter.ts index 305c67f31f7..e57c0f1b50e 100644 --- a/packages/react-composites/src/composites/CallComposite/adapter/AzureCommunicationCallAdapter.ts +++ b/packages/react-composites/src/composites/CallComposite/adapter/AzureCommunicationCallAdapter.ts @@ -648,10 +648,12 @@ export class AzureCommunicationCallAdapter { this.resetDiagnosticsForwarder(); this.callClient.offStateChange(this.onClientStateChange); - this.callAgent.dispose(); + if (!options?.doNotDisposeCallAgent) { + await this.callAgent.dispose(); + } } public async queryCameras(): Promise { @@ -1955,7 +1957,7 @@ function useAzureCommunicationCallAdapterGeneric< if (beforeDisposeRef.current) { await beforeDisposeRef.current(adapterRef.current); } - adapterRef.current.dispose(); + await adapterRef.current.dispose(); adapterRef.current = undefined; } let newAdapter: Adapter | undefined = undefined; @@ -2050,7 +2052,7 @@ function useAzureCommunicationCallAdapterGeneric< if (beforeDisposeRef.current) { await beforeDisposeRef.current(adapterRef.current); } - adapterRef.current.dispose(); + await adapterRef.current.dispose(); adapterRef.current = undefined; } })(); diff --git a/packages/react-composites/src/composites/CallComposite/adapter/CallAdapter.ts b/packages/react-composites/src/composites/CallComposite/adapter/CallAdapter.ts index a361f28d073..9218309b902 100644 --- a/packages/react-composites/src/composites/CallComposite/adapter/CallAdapter.ts +++ b/packages/react-composites/src/composites/CallComposite/adapter/CallAdapter.ts @@ -38,7 +38,7 @@ import { AddPhoneNumberOptions } from '@azure/communication-calling'; import { DtmfTone } from '@azure/communication-calling'; import { CommunicationIdentifier } from '@azure/communication-common'; import type { CommunicationUserIdentifier, PhoneNumberIdentifier } from '@azure/communication-common'; -import type { AdapterState, Disposable, AdapterError, AdapterErrors } from '../../common/adapters'; +import type { AdapterState, AdapterError, AdapterErrors } from '../../common/adapters'; /* @conditional-compile-remove(breakout-rooms) */ import type { AdapterNotifications } from '../../common/adapters'; @@ -1120,10 +1120,13 @@ export interface CallAdapterCallManagement extends CallAdapterCallOperations { */ export interface CommonCallAdapter extends AdapterState, - Disposable, CallAdapterCallOperations, CallAdapterDeviceManagement, CallAdapterSubscribers { + /** + * Dispose of the adapter. This performs cleanup of resources. + */ + dispose(options?: { doNotDisposeCallAgent?: boolean }): Promise; /** * Join the call with microphone initially on/off. * @deprecated Use joinCall(options?:JoinCallOptions) instead. diff --git a/packages/react-composites/src/composites/CallWithChatComposite/adapter/AzureCommunicationCallWithChatAdapter.ts b/packages/react-composites/src/composites/CallWithChatComposite/adapter/AzureCommunicationCallWithChatAdapter.ts index 6951073af22..b564cfa8a21 100644 --- a/packages/react-composites/src/composites/CallWithChatComposite/adapter/AzureCommunicationCallWithChatAdapter.ts +++ b/packages/react-composites/src/composites/CallWithChatComposite/adapter/AzureCommunicationCallWithChatAdapter.ts @@ -425,14 +425,14 @@ export class AzureCommunicationCallWithChatAdapter implements CallWithChatAdapte return this.context.getState(); } /** Dispose of the current CallWithChatAdapter. */ - public dispose(): void { + public async dispose(options?: { doNotDisposeCallAgent?: boolean }): Promise { this.isAdapterDisposed = true; if (this.chatAdapter) { this.chatAdapter.offStateChange(this.onChatStateChange); this.chatAdapter.dispose(); } this.callAdapter.offStateChange(this.onCallStateChange); - this.callAdapter.dispose(); + await this.callAdapter.dispose(options); } /** Remove a participant from the Call only. */ public async removeParticipant(userId: string | CommunicationIdentifier): Promise { @@ -1299,7 +1299,7 @@ export const useAzureCommunicationCallWithChatAdapter = ( if (beforeDisposeRef.current) { await beforeDisposeRef.current(adapterRef.current); } - adapterRef.current.dispose(); + await adapterRef.current.dispose(); adapterRef.current = undefined; } if (creatingAdapterRef.current) { @@ -1349,7 +1349,7 @@ export const useAzureCommunicationCallWithChatAdapter = ( if (beforeDisposeRef.current) { await beforeDisposeRef.current(adapterRef.current); } - adapterRef.current.dispose(); + await adapterRef.current.dispose(); adapterRef.current = undefined; } })(); diff --git a/packages/react-composites/src/composites/CallWithChatComposite/adapter/CallWithChatAdapter.ts b/packages/react-composites/src/composites/CallWithChatComposite/adapter/CallWithChatAdapter.ts index 61bd48bb1fe..ccaf4e8a33c 100644 --- a/packages/react-composites/src/composites/CallWithChatComposite/adapter/CallWithChatAdapter.ts +++ b/packages/react-composites/src/composites/CallWithChatComposite/adapter/CallWithChatAdapter.ts @@ -25,7 +25,7 @@ import { } from '../../ChatComposite'; import { ResourceDetails } from '../../ChatComposite'; import { CallWithChatAdapterState } from '../state/CallWithChatAdapterState'; -import type { AdapterError, AdapterState, Disposable } from '../../common/adapters'; +import type { AdapterError, AdapterState } from '../../common/adapters'; import { AudioDeviceInfo, Call, @@ -603,8 +603,12 @@ export type ChatInitializedListener = (event: { adapter: CallWithChatAdapter }) export interface CallWithChatAdapter extends CallWithChatAdapterManagement, AdapterState, - Disposable, - CallWithChatAdapterSubscriptions {} + CallWithChatAdapterSubscriptions { + /** + * Dispose of the adapter. This performs cleanup of resources. + */ + dispose(options?: { doNotDisposeCallAgent?: boolean }): Promise; +} /** * Events fired off by the {@link CallWithChatAdapter}. diff --git a/packages/react-composites/src/composites/CallWithChatComposite/adapter/CallWithChatBackedCallAdapter.ts b/packages/react-composites/src/composites/CallWithChatComposite/adapter/CallWithChatBackedCallAdapter.ts index 03a56c15887..ab0d50541af 100644 --- a/packages/react-composites/src/composites/CallWithChatComposite/adapter/CallWithChatBackedCallAdapter.ts +++ b/packages/react-composites/src/composites/CallWithChatComposite/adapter/CallWithChatBackedCallAdapter.ts @@ -85,7 +85,8 @@ export class CallWithChatBackedCallAdapter implements CallAdapter { }; public getState = (): CallAdapterState => callAdapterStateFromCallWithChatAdapterState(this.callWithChatAdapter.getState()); - public dispose = (): void => this.callWithChatAdapter.dispose(); + public dispose = (options?: { doNotDisposeCallAgent?: boolean }): Promise => + this.callWithChatAdapter.dispose(options); public joinCall = (options?: boolean | JoinCallOptions): Call | undefined => { if (typeof options === 'boolean') { return this.callWithChatAdapter.joinCall(options); diff --git a/packages/react-composites/src/composites/CallWithChatComposite/adapter/CallWithChatBackedChatAdapter.ts b/packages/react-composites/src/composites/CallWithChatComposite/adapter/CallWithChatBackedChatAdapter.ts index 25c21e638f7..cd4744d0aae 100644 --- a/packages/react-composites/src/composites/CallWithChatComposite/adapter/CallWithChatBackedChatAdapter.ts +++ b/packages/react-composites/src/composites/CallWithChatComposite/adapter/CallWithChatBackedChatAdapter.ts @@ -67,7 +67,7 @@ export class CallWithChatBackedChatAdapter implements ChatAdapter { await this.callWithChatAdapter.removeParticipant(userId); public loadPreviousChatMessages = async (messagesToLoad: number): Promise => await this.callWithChatAdapter.loadPreviousChatMessages(messagesToLoad); - public dispose = (): void => this.callWithChatAdapter.dispose(); + public dispose = (): Promise => this.callWithChatAdapter.dispose(); public onStateChange = (handler: (state: ChatAdapterState) => void): void => { const convertedHandler = (state: CallWithChatAdapterState): void => { diff --git a/packages/react-composites/tests/app/call/LiveApp.tsx b/packages/react-composites/tests/app/call/LiveApp.tsx index 5f80bb15823..9bc235e8b72 100644 --- a/packages/react-composites/tests/app/call/LiveApp.tsx +++ b/packages/react-composites/tests/app/call/LiveApp.tsx @@ -21,7 +21,9 @@ export function LiveApp(props: { queryArgs: QueryArgs }): JSX.Element { setCallAdapter(wrapAdapterForTests(await createCallAdapterWithCredentials(queryArgs))); })(); - return () => callAdapter && callAdapter.dispose(); + return () => { + callAdapter && callAdapter.dispose(); + }; // eslint-disable-next-line react-hooks/exhaustive-deps }, [queryArgs]); From 10f428c99d9a0dba3e87f5c306a5ff3c66921984 Mon Sep 17 00:00:00 2001 From: James Burnside <2684369+JamesBurnside@users.noreply.github.com> Date: Thu, 17 Oct 2024 03:59:47 +0000 Subject: [PATCH 2/5] Change files --- ...ation-react-7c38ebea-7a13-4fee-8d1d-0cfca4f07642.json | 9 +++++++++ ...ation-react-7c38ebea-7a13-4fee-8d1d-0cfca4f07642.json | 9 +++++++++ 2 files changed, 18 insertions(+) create mode 100644 change-beta/@azure-communication-react-7c38ebea-7a13-4fee-8d1d-0cfca4f07642.json create mode 100644 change/@azure-communication-react-7c38ebea-7a13-4fee-8d1d-0cfca4f07642.json diff --git a/change-beta/@azure-communication-react-7c38ebea-7a13-4fee-8d1d-0cfca4f07642.json b/change-beta/@azure-communication-react-7c38ebea-7a13-4fee-8d1d-0cfca4f07642.json new file mode 100644 index 00000000000..2bb31d5e26b --- /dev/null +++ b/change-beta/@azure-communication-react-7c38ebea-7a13-4fee-8d1d-0cfca4f07642.json @@ -0,0 +1,9 @@ +{ + "type": "minor", + "area": "fix", + "workstream": "", + "comment": "Update callAdapter.dispose and callWithChatAdapter.dispose to be async to match underlying behavior", + "packageName": "@azure/communication-react", + "email": "2684369+JamesBurnside@users.noreply.github.com", + "dependentChangeType": "patch" +} diff --git a/change/@azure-communication-react-7c38ebea-7a13-4fee-8d1d-0cfca4f07642.json b/change/@azure-communication-react-7c38ebea-7a13-4fee-8d1d-0cfca4f07642.json new file mode 100644 index 00000000000..2bb31d5e26b --- /dev/null +++ b/change/@azure-communication-react-7c38ebea-7a13-4fee-8d1d-0cfca4f07642.json @@ -0,0 +1,9 @@ +{ + "type": "minor", + "area": "fix", + "workstream": "", + "comment": "Update callAdapter.dispose and callWithChatAdapter.dispose to be async to match underlying behavior", + "packageName": "@azure/communication-react", + "email": "2684369+JamesBurnside@users.noreply.github.com", + "dependentChangeType": "patch" +} From 6bf945114b0f6c4d0d66cf172db7a122ca8edaa8 Mon Sep 17 00:00:00 2001 From: James Burnside <2684369+JamesBurnside@users.noreply.github.com> Date: Thu, 17 Oct 2024 04:00:18 +0000 Subject: [PATCH 3/5] Change files --- ...ation-react-0f94f856-0d7e-4f92-9e16-2c58149052ac.json | 9 +++++++++ ...ation-react-0f94f856-0d7e-4f92-9e16-2c58149052ac.json | 9 +++++++++ 2 files changed, 18 insertions(+) create mode 100644 change-beta/@azure-communication-react-0f94f856-0d7e-4f92-9e16-2c58149052ac.json create mode 100644 change/@azure-communication-react-0f94f856-0d7e-4f92-9e16-2c58149052ac.json diff --git a/change-beta/@azure-communication-react-0f94f856-0d7e-4f92-9e16-2c58149052ac.json b/change-beta/@azure-communication-react-0f94f856-0d7e-4f92-9e16-2c58149052ac.json new file mode 100644 index 00000000000..8f869a12865 --- /dev/null +++ b/change-beta/@azure-communication-react-0f94f856-0d7e-4f92-9e16-2c58149052ac.json @@ -0,0 +1,9 @@ +{ + "type": "patch", + "area": "fix", + "workstream": "", + "comment": "Add option to callAdapter and callWithChatAdapter dispose methods to not dispose of the callAgent", + "packageName": "@azure/communication-react", + "email": "2684369+JamesBurnside@users.noreply.github.com", + "dependentChangeType": "patch" +} diff --git a/change/@azure-communication-react-0f94f856-0d7e-4f92-9e16-2c58149052ac.json b/change/@azure-communication-react-0f94f856-0d7e-4f92-9e16-2c58149052ac.json new file mode 100644 index 00000000000..8f869a12865 --- /dev/null +++ b/change/@azure-communication-react-0f94f856-0d7e-4f92-9e16-2c58149052ac.json @@ -0,0 +1,9 @@ +{ + "type": "patch", + "area": "fix", + "workstream": "", + "comment": "Add option to callAdapter and callWithChatAdapter dispose methods to not dispose of the callAgent", + "packageName": "@azure/communication-react", + "email": "2684369+JamesBurnside@users.noreply.github.com", + "dependentChangeType": "patch" +} From f33377ddd2fd2af25c145570be3e483ac5193e77 Mon Sep 17 00:00:00 2001 From: James Burnside <2684369+JamesBurnside@users.noreply.github.com> Date: Thu, 17 Oct 2024 04:35:22 +0000 Subject: [PATCH 4/5] fix typing in mocks --- packages/react-composites/tests/app/lib/MockCallAdapter.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/react-composites/tests/app/lib/MockCallAdapter.ts b/packages/react-composites/tests/app/lib/MockCallAdapter.ts index 9fac5630a6e..add8fe3ec30 100644 --- a/packages/react-composites/tests/app/lib/MockCallAdapter.ts +++ b/packages/react-composites/tests/app/lib/MockCallAdapter.ts @@ -46,7 +46,7 @@ export class MockCallAdapter implements CallAdapter { allowUnsupportedBrowserVersion(): void { throw Error('allowWithUnsupportedBrowserVersion not implemented'); } - dispose(): void { + dispose(): Promise { throw Error('dispose not implemented'); } joinCall(): Call | undefined { From dbeede01a570c4c1e57179abad22ff1e07cd5cae Mon Sep 17 00:00:00 2001 From: James Burnside <2684369+JamesBurnside@users.noreply.github.com> Date: Thu, 17 Oct 2024 04:39:38 +0000 Subject: [PATCH 5/5] fixup samples --- samples/CallWithChat/src/app/views/CallScreen.tsx | 2 +- samples/Calling/src/app/views/CallCompositeContainer.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/samples/CallWithChat/src/app/views/CallScreen.tsx b/samples/CallWithChat/src/app/views/CallScreen.tsx index 1780a9c95d5..226b20ac9a4 100644 --- a/samples/CallWithChat/src/app/views/CallScreen.tsx +++ b/samples/CallWithChat/src/app/views/CallScreen.tsx @@ -223,7 +223,7 @@ export const CallScreen = (props: CallScreenProps): JSX.Element => { // This ensures the service knows the user intentionally left the call if the user // closed the browser tab during an active call. useEffect(() => { - const disposeAdapter = (): void => adapter?.dispose(); + const disposeAdapter = async (): Promise => await adapter?.dispose(); window.addEventListener('beforeunload', disposeAdapter); return () => window.removeEventListener('beforeunload', disposeAdapter); }, [adapter]); diff --git a/samples/Calling/src/app/views/CallCompositeContainer.tsx b/samples/Calling/src/app/views/CallCompositeContainer.tsx index 9135ee00a72..760865de1de 100644 --- a/samples/Calling/src/app/views/CallCompositeContainer.tsx +++ b/samples/Calling/src/app/views/CallCompositeContainer.tsx @@ -55,7 +55,7 @@ export const CallCompositeContainer = (props: CallCompositeContainerProps): JSX. // This ensures the service knows the user intentionally left the call if the user // closed the browser tab during an active call. useEffect(() => { - const disposeAdapter = (): void => adapter?.dispose(); + const disposeAdapter = async (): Promise => await adapter?.dispose(); window.addEventListener('beforeunload', disposeAdapter); return () => window.removeEventListener('beforeunload', disposeAdapter); }, [adapter]);