From 27492b0ec143c0ed46e5f4d8ae70798cacc726a8 Mon Sep 17 00:00:00 2001 From: Chukwuebuka Nwankwo Date: Thu, 3 Oct 2024 19:49:01 +0000 Subject: [PATCH 01/17] Implementation of call feature streams. This will be used to create streams that is not tied to participant --- .../src/CallClientState.ts | 32 ++- .../src/CallContext.ts | 54 ++++- .../calling-stateful-client/src/Converter.ts | 2 +- .../src/InternalCallContext.ts | 68 ++++++ .../src/StatefulCallClient.ts | 27 ++- .../src/StreamUtils.test.ts | 2 +- .../src/StreamUtils.ts | 203 +++++++++++++++++- .../src/index-public.ts | 6 +- .../review/beta/communication-react.api.md | 29 ++- .../CallComposite/MockCallAdapter.ts | 9 +- .../adapter/TestUtils.ts | 2 +- 11 files changed, 402 insertions(+), 32 deletions(-) diff --git a/packages/calling-stateful-client/src/CallClientState.ts b/packages/calling-stateful-client/src/CallClientState.ts index 9fba7e91dc5..3f5de6eb622 100644 --- a/packages/calling-stateful-client/src/CallClientState.ts +++ b/packages/calling-stateful-client/src/CallClientState.ts @@ -284,7 +284,19 @@ export interface TogetherModeCallFeatureState { /** * Proxy of {@link @azure/communication-calling#TogetherModeCallFeature.togetherModeStream}. */ - stream: TogetherModeStreamState[]; + streams: Map; + /** + * Proxy of {@link @azure/communication-calling#TogetherModeCallFeature.TogetherModeSeatingMap}. + */ + seatingCoordinates: Map; +} + +/* @conditional-compile-remove(together-mode) */ +/** + * @alpha + */ +export interface CallFeatureStreamState { + feature: 'togetherMode'; } /* @conditional-compile-remove(together-mode) */ @@ -292,7 +304,7 @@ export interface TogetherModeCallFeatureState { * State only version of {@link @azure/communication-calling#TogetherModeVideoStream}. * @alpha */ -export interface TogetherModeStreamState { +export interface TogetherModeStreamState extends CallFeatureStreamState { /** * Proxy of {@link @azure/communication-calling#TogetherModeVideoStream.id}. */ @@ -317,6 +329,22 @@ export interface TogetherModeStreamState { streamSize?: { width: number; height: number }; } +/* @conditional-compile-remove(together-mode) */ +/** + * State only version of {@link @azure/communication-calling#TogetherModeSeatingMap}. + * @alpha + */ +export interface TogetherModeSeatingCoordinates { + // the y coordinate of the participant + top: number; + // the x coordinate of the participant + left: number; + // the width of the participant + width: number; + // the height of the participant + height: number; +} + /** * State only version of {@link @azure/communication-calling#PPTLiveCallFeature}. {@link StatefulCallClient} will * automatically listen for pptLive on the call and update the state exposed by {@link StatefulCallClient} accordingly. diff --git a/packages/calling-stateful-client/src/CallContext.ts b/packages/calling-stateful-client/src/CallContext.ts index fd01055115e..d6af9a3981c 100644 --- a/packages/calling-stateful-client/src/CallContext.ts +++ b/packages/calling-stateful-client/src/CallContext.ts @@ -8,6 +8,7 @@ import { DominantSpeakersInfo, ParticipantRole, ScalingMode, + TogetherModeSeatingMap, VideoDeviceInfo } from '@azure/communication-calling'; import { RaisedHand } from '@azure/communication-calling'; @@ -45,7 +46,9 @@ import { CallAgentState, CallErrors, CallErrorTarget, - CallError + CallError, + TogetherModeStreamState, + TogetherModeSeatingCoordinates } from './CallClientState'; /* @conditional-compile-remove(breakout-rooms) */ import { NotificationTarget, CallNotification, CallNotifications } from './CallClientState'; @@ -454,11 +457,25 @@ export class CallContext { } /* @conditional-compile-remove(together-mode) */ - public setTogetherModeVideoStream(callId: string, addedStream: TogetherModeVideoStream[]): void { + public setTogetherModeVideoStream(callId: string, addedStreams: TogetherModeVideoStream[]): void { this.modifyState((draft: CallClientState) => { const call = draft.calls[this._callIdHistory.latestCallId(callId)]; if (call) { - call.togetherMode = { stream: addedStream }; + const streamsToAdd: Map = new Map(); + for (const stream of addedStreams) { + const streamToAdd: TogetherModeStreamState = { + feature: 'togetherMode', + id: stream.id, + view: undefined, + mediaStreamType: stream.mediaStreamType, + isReceiving: stream.isReceiving + }; + streamsToAdd.set(stream.mediaStreamType, streamToAdd); + } + call.togetherMode.streams = streamsToAdd; + if (!call.togetherMode.seatingCoordinates) { + call.togetherMode.seatingCoordinates = new Map(); + } } }); } @@ -469,15 +486,24 @@ export class CallContext { const call = draft.calls[this._callIdHistory.latestCallId(callId)]; if (call) { for (const stream of removedStream) { - if (stream.mediaStreamType in call.togetherMode.stream) { - // Temporary lint fix: Remove the stream from the list - call.togetherMode.stream = []; + if (stream.mediaStreamType in call.togetherMode.streams) { + call.togetherMode.streams.delete(stream.mediaStreamType); } } } }); } + /* @conditional-compile-remove(together-mode) */ + public setTogetherModeSeatingCoordinates(callId: string, seatingMap: TogetherModeSeatingMap): void { + this.modifyState((draft: CallClientState) => { + const call = draft.calls[this._callIdHistory.latestCallId(callId)]; + if (call) { + call.togetherMode.seatingCoordinates = seatingMap; + } + }); + } + public setCallRaisedHands(callId: string, raisedHands: RaisedHand[]): void { this.modifyState((draft: CallClientState) => { const call = draft.calls[this._callIdHistory.latestCallId(callId)]; @@ -712,6 +738,22 @@ export class CallContext { }); } + public setTogetherModeVideoStreamRendererView( + callId: string, + togetherModeStreamType: string, + view: VideoStreamRendererViewState | undefined + ): void { + this.modifyState((draft: CallClientState) => { + const call = draft.calls[this._callIdHistory.latestCallId(callId)]; + if (call) { + const togetherModeStream = call.togetherMode.streams.get(togetherModeStreamType); + if (togetherModeStream) { + togetherModeStream.view = view; + } + } + }); + } + public setParticipantState(callId: string, participantKey: string, state: RemoteParticipantStatus): void { this.modifyState((draft: CallClientState) => { const call = draft.calls[this._callIdHistory.latestCallId(callId)]; diff --git a/packages/calling-stateful-client/src/Converter.ts b/packages/calling-stateful-client/src/Converter.ts index 71b19b2bccb..2d25ff0f9d2 100644 --- a/packages/calling-stateful-client/src/Converter.ts +++ b/packages/calling-stateful-client/src/Converter.ts @@ -157,7 +157,7 @@ export function convertSdkCallToDeclarativeCall(call: CallCommon): CallState { pptLive: { isActive: false }, raiseHand: { raisedHands: [] }, /* @conditional-compile-remove(together-mode) */ - togetherMode: { stream: [] }, + togetherMode: { streams: new Map(), seatingCoordinates: new Map() }, localParticipantReaction: undefined, transcription: { isTranscriptionActive: false }, screenShareRemoteParticipant: undefined, diff --git a/packages/calling-stateful-client/src/InternalCallContext.ts b/packages/calling-stateful-client/src/InternalCallContext.ts index 500948dbf5b..9ba9c01329b 100644 --- a/packages/calling-stateful-client/src/InternalCallContext.ts +++ b/packages/calling-stateful-client/src/InternalCallContext.ts @@ -5,6 +5,7 @@ import { LocalVideoStream, MediaStreamType, RemoteVideoStream, + RemoteVideoStreamCommon, VideoStreamRenderer } from '@azure/communication-calling'; import { LocalVideoStreamState } from './CallClientState'; @@ -45,6 +46,11 @@ export type LocalRenderInfo = RenderInfo; */ export type RemoteRenderInfo = RenderInfo; +/** + * Internally used to keep track of the status, renderer, and awaiting promise, associated with a CallFeatureVideoStream. + */ +export type CallFeatureRenderInfo = RenderInfo; + /** * Contains internal data used between different Declarative components to share data. */ @@ -55,6 +61,8 @@ export class InternalCallContext { // >. private _localRenderInfos = new Map>(); + // >>. + private _callFeatureRenderInfos = new Map>>(); // Used for keeping track of rendered LocalVideoStreams that are not part of a Call. private _unparentedRenderInfos = new Map(); private _callIdHistory = new CallIdHistory(); @@ -77,6 +85,12 @@ export class InternalCallContext { this._localRenderInfos.delete(oldCallId); this._localRenderInfos.set(newCallId, localRenderInfos); } + + const callFeatureRenderInfos = this._callFeatureRenderInfos.get(oldCallId); + if (callFeatureRenderInfos) { + this._callFeatureRenderInfos.delete(oldCallId); + this._callFeatureRenderInfos.set(newCallId, callFeatureRenderInfos); + } } public getCallIds(): IterableIterator { @@ -221,5 +235,59 @@ export class InternalCallContext { public clearCallRelatedState(): void { this._remoteRenderInfos.clear(); this._localRenderInfos.clear(); + this._callFeatureRenderInfos.clear(); + } + + public getCallFeatureRenderInfosForCall( + callId: string, + featureNameKey: string + ): Map | undefined { + return this._callFeatureRenderInfos.get(this._callIdHistory.latestCallId(callId))?.get(featureNameKey); + } + + public getCallFeatureRenderInfo( + callId: string, + featureNameKey: string, + streamKey: MediaStreamType + ): CallFeatureRenderInfo | undefined { + const callFeatureRenderInfosForCall = this._callFeatureRenderInfos + .get(this._callIdHistory.latestCallId(callId)) + ?.get(featureNameKey) + ?.get(streamKey); + if (!callFeatureRenderInfosForCall) { + return undefined; + } + return callFeatureRenderInfosForCall; + } + + public setCallFeatureRenderInfo( + callId: string, + featureNameKey: string, + streamKey: MediaStreamType, + stream: RemoteVideoStreamCommon, + status: RenderStatus, + renderer: VideoStreamRenderer | undefined + ): void { + let callRenderInfos = this._callFeatureRenderInfos.get(this._callIdHistory.latestCallId(callId)); + if (!callRenderInfos) { + callRenderInfos = new Map>(); + // If the callId is not found, create a new map for the callId. + this._callFeatureRenderInfos.set(this._callIdHistory.latestCallId(callId), callRenderInfos); + } + let featureRenderInfos = callRenderInfos.get(featureNameKey); + if (!featureRenderInfos) { + featureRenderInfos = new Map(); + callRenderInfos.set(featureNameKey, featureRenderInfos); + } + featureRenderInfos.set(streamKey, { stream, status, renderer }); + } + + public deleteCallFeatureRenderInfo(callId: string, featureName: string, streamKey: MediaStreamType): void { + const callFeatureRenderInfoForCall = this._callFeatureRenderInfos.get(this._callIdHistory.latestCallId(callId)); + if (!callFeatureRenderInfoForCall || !callFeatureRenderInfoForCall.get(featureName)) { + return; + } + + callFeatureRenderInfoForCall.get(featureName)?.delete(streamKey); } } diff --git a/packages/calling-stateful-client/src/StatefulCallClient.ts b/packages/calling-stateful-client/src/StatefulCallClient.ts index e371c58167a..d7f0d1d5348 100644 --- a/packages/calling-stateful-client/src/StatefulCallClient.ts +++ b/packages/calling-stateful-client/src/StatefulCallClient.ts @@ -7,11 +7,11 @@ import { CallClient, CallClientOptions, CreateViewOptions, DeviceManager } from import { Features } from '@azure/communication-calling'; import { CallClientState, LocalVideoStreamState, RemoteVideoStreamState } from './CallClientState'; /* @conditional-compile-remove(together-mode) */ -import { TogetherModeStreamState } from './CallClientState'; +import { CallFeatureStreamState, TogetherModeStreamState } from './CallClientState'; import { CallContext } from './CallContext'; import { callAgentDeclaratify, DeclarativeCallAgent } from './CallAgentDeclarative'; import { InternalCallContext } from './InternalCallContext'; -import { createView, disposeView, CreateViewResult } from './StreamUtils'; +import { createView, disposeView, CreateViewResult, createCallFeatureView } from './StreamUtils'; import { CommunicationIdentifier, CommunicationUserIdentifier, getIdentifierKind } from '@azure/communication-common'; import { toFlatCommunicationIdentifier, @@ -113,10 +113,7 @@ export interface StatefulCallClient extends CallClient { createView( callId: string | undefined, participantId: CommunicationIdentifier | undefined, - stream: - | LocalVideoStreamState - | RemoteVideoStreamState - | /* @conditional-compile-remove(together-mode) */ TogetherModeStreamState, + stream: LocalVideoStreamState | RemoteVideoStreamState, options?: CreateViewOptions ): Promise; /** @@ -149,6 +146,13 @@ export interface StatefulCallClient extends CallClient { stream: LocalVideoStreamState | RemoteVideoStreamState ): void; + createCallFeatureView( + callId: string, + stream: CallFeatureStreamState, + options?: CreateViewOptions + ): Promise; + + disposeCallFeatureView(callId: string, stream: CallFeatureStreamState): void; /** * The CallAgent is used to handle calls. * To create the CallAgent, pass a CommunicationTokenCredential object provided from SDK. @@ -405,6 +409,17 @@ export const createStatefulCallClientWithDeps = ( return result; } }); + Object.defineProperty(callClient, 'createCallFeatureView', { + configurable: false, + value: async ( + callId: string | undefined, + stream: TogetherModeStreamState, + options?: CreateViewOptions + ): Promise => { + const result = await createCallFeatureView(context, internalContext, callId, stream, options); + return result; + } + }); Object.defineProperty(callClient, 'disposeView', { configurable: false, value: ( diff --git a/packages/calling-stateful-client/src/StreamUtils.test.ts b/packages/calling-stateful-client/src/StreamUtils.test.ts index 95662c0418c..7db84791d47 100644 --- a/packages/calling-stateful-client/src/StreamUtils.test.ts +++ b/packages/calling-stateful-client/src/StreamUtils.test.ts @@ -90,7 +90,7 @@ function createMockCall(mockCallId: string): CallState { localRecording: { isLocalRecordingActive: false }, raiseHand: { raisedHands: [] }, /* @conditional-compile-remove(together-mode) */ - togetherMode: { stream: [] }, + togetherMode: { streams: new Map(), seatingCoordinates: new Map() }, localParticipantReaction: undefined, transcription: { isTranscriptionActive: false }, screenShareRemoteParticipant: undefined, diff --git a/packages/calling-stateful-client/src/StreamUtils.ts b/packages/calling-stateful-client/src/StreamUtils.ts index 30d8339bc88..53128dee964 100644 --- a/packages/calling-stateful-client/src/StreamUtils.ts +++ b/packages/calling-stateful-client/src/StreamUtils.ts @@ -5,6 +5,7 @@ import { CreateViewOptions, LocalVideoStream, RemoteVideoStream, + TogetherModeVideoStream, VideoStreamRenderer, VideoStreamRendererView } from '@azure/communication-calling'; @@ -37,10 +38,7 @@ async function createViewVideo( context: CallContext, internalContext: InternalCallContext, callId: string, - stream?: - | RemoteVideoStreamState - | LocalVideoStreamState - | /* @conditional-compile-remove(together-mode) */ TogetherModeStreamState, + stream?: RemoteVideoStreamState | LocalVideoStreamState, participantId?: CommunicationIdentifierKind | string, options?: CreateViewOptions ): Promise { @@ -264,6 +262,152 @@ async function createViewVideo( }; } +const getStreamFeatureName = (stream: TogetherModeStreamState): string => { + if ('feature' in stream) { + return stream.feature; + } + throw new Error('Feature name not found'); +}; + +// This function is used to create a view for a stream that is part of a call feature. +async function createCallFeatureViewVideo( + context: CallContext, + internalContext: InternalCallContext, + callId: string, + stream: TogetherModeStreamState /* @conditional-compile-remove(together-mode) */, + options?: CreateViewOptions +): Promise { + const streamEventType = 'createViewCallFeature'; + + const streamType = stream?.mediaStreamType; + const callFeatureStreamId = stream && stream.id; + console.log('CHUK: createCallFeatureViewVideo'); + const streamLogInfo = { + callId, + undefined, + streamId: callFeatureStreamId, + streamType, + streamEventType + }; + + // make different logging announcement based on whether or not we are starting a local or remote + _logStreamEvent(EventNames.CREATING_VIEW, streamLogInfo); + + const featureName = getStreamFeatureName(stream); + + // if we have a participant Id and a stream get the remote info, else get the local render info from state. + const renderInfo = internalContext.getCallFeatureRenderInfo(callId, featureName, stream.mediaStreamType); + if (!renderInfo) { + _logStreamEvent(EventNames.STREAM_NOT_FOUND, streamLogInfo); + return; + } + + if (renderInfo.status === 'Rendered') { + _logStreamEvent(EventNames.STREAM_ALREADY_RENDERED, streamLogInfo); + return; + } + + if (renderInfo.status === 'Rendering') { + // Do not log to console here as this is a very common situation due to UI rerenders while + // the video rendering is in progress. + _logStreamEvent(EventNames.STREAM_RENDERING, streamLogInfo); + return; + } + + // "Stopping" only happens if the stream was in "rendering" but `disposeView` was called. + // Now that `createView` has been re-called, we can flip the state back to "rendering". + if (renderInfo.status === 'Stopping') { + _logStreamEvent(EventNames.STREAM_STOPPING, streamLogInfo); + internalContext.setCallFeatureRenderInfo( + callId, + featureName, + stream.mediaStreamType, + renderInfo.stream as TogetherModeVideoStream, + 'Rendering', + renderInfo.renderer + ); + return; + } + + const renderer = new VideoStreamRenderer(renderInfo.stream); + internalContext.setCallFeatureRenderInfo( + callId, + featureName, + stream.mediaStreamType, + renderInfo.stream as TogetherModeVideoStream, + 'Rendering', + undefined + ); + + let view; + try { + view = await renderer.createView(options); + } catch (e) { + _logStreamEvent(EventNames.CREATE_STREAM_FAIL, streamLogInfo, e); + internalContext.setCallFeatureRenderInfo( + callId, + featureName, + stream.mediaStreamType, + renderInfo.stream, + 'NotRendered', + undefined + ); + throw e; + } + + // Since render could take some time, we need to check if the stream is still valid and if we received a signal to + // stop rendering. + const refreshedRenderInfo = internalContext.getCallFeatureRenderInfo(callId, featureName, stream.mediaStreamType); + + if (!refreshedRenderInfo) { + // RenderInfo was removed. This should not happen unless stream was removed from the call so dispose the renderer + // and clean up the state. + _logStreamEvent(EventNames.RENDER_INFO_NOT_FOUND, streamLogInfo); + renderer.dispose(); + context.setTogetherModeVideoStreamRendererView(callId, stream.mediaStreamType, undefined); + return; + } + + if (refreshedRenderInfo.status === 'Stopping') { + // Stop render was called on this stream after we had started rendering. We will dispose this view and do not + // put the view into the state. + _logStreamEvent(EventNames.CREATED_STREAM_STOPPING, streamLogInfo); + renderer.dispose(); + internalContext.setCallFeatureRenderInfo( + callId, + featureName, + stream.mediaStreamType, + refreshedRenderInfo.stream as TogetherModeVideoStream, + 'NotRendered', + undefined + ); + context.setTogetherModeVideoStreamRendererView(callId, stream.mediaStreamType, undefined); + return; + } + + // Else the stream still exists and status is not telling us to stop rendering. Complete the render process by + // updating the state. + internalContext.setCallFeatureRenderInfo( + callId, + featureName, + stream.mediaStreamType, + refreshedRenderInfo.stream as TogetherModeVideoStream, + 'Rendered', + renderer + ); + context.setTogetherModeVideoStreamRendererView( + callId, + stream.mediaStreamType, + convertFromSDKToDeclarativeVideoStreamRendererView(view) + ); + _logStreamEvent(EventNames.VIEW_RENDER_SUCCEED, streamLogInfo); + + return { + renderer, + view + }; +} + async function createViewUnparentedVideo( context: CallContext, internalContext: InternalCallContext, @@ -493,10 +637,7 @@ export function createView( internalContext: InternalCallContext, callId: string | undefined, participantId: CommunicationIdentifierKind | string | undefined, - stream: - | LocalVideoStreamState - | RemoteVideoStreamState - | /* @conditional-compile-remove(together-mode) */ TogetherModeStreamState, + stream: LocalVideoStreamState | RemoteVideoStreamState, options?: CreateViewOptions ): Promise { const streamType = stream.mediaStreamType; @@ -517,6 +658,27 @@ export function createView( } } +/** + * @private + */ +export function createCallFeatureView( + context: CallContext, + internalContext: InternalCallContext, + callId: string | undefined, + stream: TogetherModeStreamState /* @conditional-compile-remove(together-mode) */, + options?: CreateViewOptions +): Promise { + const streamType = stream.mediaStreamType; + + if (callId && isCallFeatureStream(stream)) { + console.log('CHUK: createCallFeatureView'); + return createCallFeatureViewVideo(context, internalContext, callId, stream, options); + } else { + _logStreamEvent(EventNames.CREATE_STREAM_INVALID_PARAMS, { streamType }); + return Promise.resolve(undefined); + } +} + /** * @private */ @@ -586,6 +748,22 @@ export function disposeAllViewsFromCall( } } } + // const callFeatureStreams = internalContext.getLocalRenderInfosForCall(callId); + // if (localStreams) { + // for (const localStreamAndRenderer of localStreams.values()) { + // if (localStreamAndRenderer && localStreamAndRenderer.renderer) { + // // We don't want to accept SDK stream as parameter but we also don't cache the declarative stream so we have to + // // convert the SDK stream to declarative stream which is not pretty so this could use some further refactoring. + // disposeView( + // context, + // internalContext, + // callId, + // undefined, + // convertSdkLocalStreamToDeclarativeLocalStream(localStreamAndRenderer.stream) + // ); + // } + // } + // } } /** @@ -597,3 +775,12 @@ export function disposeAllViews(context: CallContext, internalContext: InternalC disposeAllViewsFromCall(context, internalContext, callId); } } + +/** + * @private + */ +function isCallFeatureStream( + stream?: TogetherModeStreamState | LocalVideoStreamState | RemoteVideoStreamState +): boolean { + return (stream && 'feature' in stream) || false; +} diff --git a/packages/calling-stateful-client/src/index-public.ts b/packages/calling-stateful-client/src/index-public.ts index 1c0f7dacae9..3198d156bc8 100644 --- a/packages/calling-stateful-client/src/index-public.ts +++ b/packages/calling-stateful-client/src/index-public.ts @@ -34,7 +34,11 @@ export type { RaiseHandCallFeatureState as RaiseHandCallFeature } from './CallCl /* @conditional-compile-remove(together-mode) */ export type { TogetherModeCallFeatureState as TogetherModeCallFeature } from './CallClientState'; /* @conditional-compile-remove(together-mode) */ -export type { TogetherModeStreamState } from './CallClientState'; +export type { + CallFeatureStreamState, + TogetherModeStreamState, + TogetherModeSeatingCoordinates +} from './CallClientState'; export type { RaisedHandState } from './CallClientState'; export type { DeclarativeCallAgent, IncomingCallManagement } from './CallAgentDeclarative'; diff --git a/packages/communication-react/review/beta/communication-react.api.md b/packages/communication-react/review/beta/communication-react.api.md index 8676a2ad86a..0de8dd7da2b 100644 --- a/packages/communication-react/review/beta/communication-react.api.md +++ b/packages/communication-react/review/beta/communication-react.api.md @@ -1027,6 +1027,12 @@ export type CallErrors = { // @public export type CallErrorTarget = 'Call.addParticipant' | 'Call.dispose' | 'Call.feature' | 'Call.hangUp' | 'Call.hold' | 'Call.mute' | 'Call.muteIncomingAudio' | 'Call.off' | 'Call.on' | 'Call.removeParticipant' | 'Call.resume' | 'Call.sendDtmf' | 'Call.startAudio' | 'Call.startScreenSharing' | 'Call.startVideo' | 'Call.stopScreenSharing' | 'Call.stopAudio' | 'Call.stopVideo' | 'Call.unmute' | 'Call.unmuteIncomingAudio' | 'CallAgent.dispose' | 'CallAgent.feature' | 'CallAgent.join' | 'CallAgent.off' | 'CallAgent.on' | 'CallAgent.startCall' | 'CallClient.createCallAgent' | 'CallClient.createTeamsCallAgent' | 'CallClient.feature' | 'CallClient.getDeviceManager' | /* @conditional-compile-remove(calling-beta-sdk) */ 'CallClient.getEnvironmentInfo' | 'DeviceManager.askDevicePermission' | 'DeviceManager.getCameras' | 'DeviceManager.getMicrophones' | 'DeviceManager.getSpeakers' | 'DeviceManager.off' | 'DeviceManager.on' | 'DeviceManager.selectMicrophone' | 'DeviceManager.selectSpeaker' | 'IncomingCall.accept' | 'IncomingCall.reject' | /* @conditional-compile-remove(calling-beta-sdk) */ /* @conditional-compile-remove(teams-identity-support) */ 'TeamsCall.addParticipant' | 'VideoEffectsFeature.startEffects' | /* @conditional-compile-remove(calling-beta-sdk) */ 'CallAgent.handlePushNotification' | /* @conditional-compile-remove(calling-beta-sdk) */ 'Call.admit' | /* @conditional-compile-remove(calling-beta-sdk) */ 'Call.rejectParticipant' | /* @conditional-compile-remove(calling-beta-sdk) */ 'Call.admitAll' | /* @conditional-compile-remove(soft-mute) */ 'Call.mutedByOthers' | 'Call.muteAllRemoteParticipants' | 'Call.setConstraints'; +// @alpha (undocumented) +export interface CallFeatureStreamState { + // (undocumented) + feature: 'togetherMode'; +} + // @public export type CallIdChangedListener = (event: { callId: string; @@ -4634,8 +4640,12 @@ export type StartTeamsCallIdentifier = MicrosoftTeamsUserIdentifier | PhoneNumbe // @public export interface StatefulCallClient extends CallClient { createCallAgent(...args: Parameters): Promise; + // (undocumented) + createCallFeatureView(callId: string, stream: CallFeatureStreamState, options?: CreateViewOptions): Promise; createTeamsCallAgent(...args: Parameters): Promise; - createView(callId: string | undefined, participantId: CommunicationIdentifier | undefined, stream: LocalVideoStreamState | RemoteVideoStreamState | /* @conditional-compile-remove(together-mode) */ TogetherModeStreamState, options?: CreateViewOptions): Promise; + createView(callId: string | undefined, participantId: CommunicationIdentifier | undefined, stream: LocalVideoStreamState | RemoteVideoStreamState, options?: CreateViewOptions): Promise; + // (undocumented) + disposeCallFeatureView(callId: string, stream: CallFeatureStreamState): void; disposeView(callId: string | undefined, participantId: CommunicationIdentifier | undefined, stream: LocalVideoStreamState | RemoteVideoStreamState): void; getState(): CallClientState; offStateChange(handler: (state: CallClientState) => void): void; @@ -4823,11 +4833,24 @@ export const toFlatCommunicationIdentifier: (identifier: CommunicationIdentifier // @alpha export interface TogetherModeCallFeature { - stream: TogetherModeStreamState[]; + seatingCoordinates: Map; + streams: Map; +} + +// @alpha +export interface TogetherModeSeatingCoordinates { + // (undocumented) + height: number; + // (undocumented) + left: number; + // (undocumented) + top: number; + // (undocumented) + width: number; } // @alpha -export interface TogetherModeStreamState { +export interface TogetherModeStreamState extends CallFeatureStreamState { id: number; // @public isReceiving: boolean; diff --git a/packages/react-composites/src/composites/CallComposite/MockCallAdapter.ts b/packages/react-composites/src/composites/CallComposite/MockCallAdapter.ts index 8fbfbc2bdd6..02cb8175ab8 100644 --- a/packages/react-composites/src/composites/CallComposite/MockCallAdapter.ts +++ b/packages/react-composites/src/composites/CallComposite/MockCallAdapter.ts @@ -110,8 +110,8 @@ export class _MockCallAdapter implements CallAdapter { createStreamView(): Promise { throw Error('createStreamView not implemented'); } - startTogetherMode(): Promise { - throw Error('startTogetherMode not implemented'); + createFeatureStreamView(): Promise { + throw Error('createFeatureStreamView not implemented'); } disposeStreamView(): Promise { return Promise.resolve(); @@ -125,6 +125,9 @@ export class _MockCallAdapter implements CallAdapter { disposeRemoteVideoStreamView(): Promise { return Promise.resolve(); } + disposeFeatureStreamView(): Promise { + return Promise.resolve(); + } // eslint-disable-next-line @typescript-eslint/no-unused-vars askDevicePermission(constrain: PermissionConstraints): Promise { throw Error('askDevicePermission not implemented'); @@ -259,7 +262,7 @@ const createDefaultCallAdapterState = (role?: ParticipantRole): CallAdapterState remoteParticipantsEnded: {}, raiseHand: { raisedHands: [] }, /* @conditional-compile-remove(together-mode) */ - togetherMode: { stream: [] }, + togetherMode: { streams: new Map(), seatingCoordinates: new Map() }, pptLive: { isActive: false }, localParticipantReaction: undefined, role, diff --git a/packages/react-composites/src/composites/CallWithChatComposite/adapter/TestUtils.ts b/packages/react-composites/src/composites/CallWithChatComposite/adapter/TestUtils.ts index 4eef39f30e4..a175a050442 100644 --- a/packages/react-composites/src/composites/CallWithChatComposite/adapter/TestUtils.ts +++ b/packages/react-composites/src/composites/CallWithChatComposite/adapter/TestUtils.ts @@ -244,7 +244,7 @@ function createMockCall(mockCallId: string): CallState { dominantSpeakers: undefined, raiseHand: { raisedHands: [] }, /* @conditional-compile-remove(together-mode) */ - togetherMode: { stream: [] }, + togetherMode: { streams: new Map(), seatingCoordinates: new Map() }, pptLive: { isActive: false }, localParticipantReaction: undefined, captionsFeature: { From 723da075210d4d89441dc4853dc8f60c8e7576c3 Mon Sep 17 00:00:00 2001 From: Chukwuebuka Nwankwo Date: Fri, 4 Oct 2024 17:27:34 +0000 Subject: [PATCH 02/17] Included docs and conditional compilation tag --- .../src/CallClientState.ts | 12 ++--- .../src/CallContext.ts | 13 +++-- .../src/InternalCallContext.ts | 17 ++++--- .../src/StatefulCallClient.ts | 34 +++++++++++++- .../src/StreamUtils.ts | 47 +++++++++---------- .../src/index-public.ts | 2 +- .../review/beta/communication-react.api.md | 6 +-- .../CallComposite/MockCallAdapter.ts | 2 + 8 files changed, 83 insertions(+), 50 deletions(-) diff --git a/packages/calling-stateful-client/src/CallClientState.ts b/packages/calling-stateful-client/src/CallClientState.ts index 3f5de6eb622..cde1fb0080f 100644 --- a/packages/calling-stateful-client/src/CallClientState.ts +++ b/packages/calling-stateful-client/src/CallClientState.ts @@ -288,7 +288,7 @@ export interface TogetherModeCallFeatureState { /** * Proxy of {@link @azure/communication-calling#TogetherModeCallFeature.TogetherModeSeatingMap}. */ - seatingCoordinates: Map; + seatingCoordinates: Map; } /* @conditional-compile-remove(together-mode) */ @@ -334,14 +334,14 @@ export interface TogetherModeStreamState extends CallFeatureStreamState { * State only version of {@link @azure/communication-calling#TogetherModeSeatingMap}. * @alpha */ -export interface TogetherModeSeatingCoordinates { - // the y coordinate of the participant +export interface TogetherModeSeatingCoordinatesState { + // the y coordinate of the participant seating position in the together mode stream top: number; - // the x coordinate of the participant + // the x coordinate of the participant seating position in the together mode stream left: number; - // the width of the participant + // the width of the participant in the together mode stream width: number; - // the height of the participant + // the height of the participant in the together mode stream height: number; } diff --git a/packages/calling-stateful-client/src/CallContext.ts b/packages/calling-stateful-client/src/CallContext.ts index d6af9a3981c..ab905177574 100644 --- a/packages/calling-stateful-client/src/CallContext.ts +++ b/packages/calling-stateful-client/src/CallContext.ts @@ -8,7 +8,6 @@ import { DominantSpeakersInfo, ParticipantRole, ScalingMode, - TogetherModeSeatingMap, VideoDeviceInfo } from '@azure/communication-calling'; import { RaisedHand } from '@azure/communication-calling'; @@ -25,7 +24,7 @@ import { CaptionsKind, CaptionsInfo as AcsCaptionsInfo } from '@azure/communicat /* @conditional-compile-remove(unsupported-browser) */ import { EnvironmentInfo } from '@azure/communication-calling'; /* @conditional-compile-remove(together-mode) */ -import { TogetherModeVideoStream } from '@azure/communication-calling'; +import { TogetherModeVideoStream, TogetherModeSeatingMap } from '@azure/communication-calling'; import { AzureLogger, createClientLogger, getLogLevel } from '@azure/logger'; import { EventEmitter } from 'events'; import { enableMapSet, enablePatches, Patch, produce } from 'immer'; @@ -46,10 +45,10 @@ import { CallAgentState, CallErrors, CallErrorTarget, - CallError, - TogetherModeStreamState, - TogetherModeSeatingCoordinates + CallError } from './CallClientState'; +/* @conditional-compile-remove(together-mode) */ +import { TogetherModeStreamState, TogetherModeSeatingCoordinatesState } from './CallClientState'; /* @conditional-compile-remove(breakout-rooms) */ import { NotificationTarget, CallNotification, CallNotifications } from './CallClientState'; import { TeamsIncomingCallState } from './CallClientState'; @@ -474,7 +473,7 @@ export class CallContext { } call.togetherMode.streams = streamsToAdd; if (!call.togetherMode.seatingCoordinates) { - call.togetherMode.seatingCoordinates = new Map(); + call.togetherMode.seatingCoordinates = new Map(); } } }); @@ -495,7 +494,7 @@ export class CallContext { } /* @conditional-compile-remove(together-mode) */ - public setTogetherModeSeatingCoordinates(callId: string, seatingMap: TogetherModeSeatingMap): void { + public setTogetherModeSeatingCoordinatesState(callId: string, seatingMap: TogetherModeSeatingMap): void { this.modifyState((draft: CallClientState) => { const call = draft.calls[this._callIdHistory.latestCallId(callId)]; if (call) { diff --git a/packages/calling-stateful-client/src/InternalCallContext.ts b/packages/calling-stateful-client/src/InternalCallContext.ts index 9ba9c01329b..9f8fe630eb5 100644 --- a/packages/calling-stateful-client/src/InternalCallContext.ts +++ b/packages/calling-stateful-client/src/InternalCallContext.ts @@ -5,7 +5,7 @@ import { LocalVideoStream, MediaStreamType, RemoteVideoStream, - RemoteVideoStreamCommon, + /* @conditional-compile-remove(together-mode) */ RemoteVideoStreamCommon, VideoStreamRenderer } from '@azure/communication-calling'; import { LocalVideoStreamState } from './CallClientState'; @@ -46,6 +46,7 @@ export type LocalRenderInfo = RenderInfo; */ export type RemoteRenderInfo = RenderInfo; +/* @conditional-compile-remove(together-mode) */ /** * Internally used to keep track of the status, renderer, and awaiting promise, associated with a CallFeatureVideoStream. */ @@ -61,6 +62,7 @@ export class InternalCallContext { // >. private _localRenderInfos = new Map>(); + /* @conditional-compile-remove(together-mode) */ // >>. private _callFeatureRenderInfos = new Map>>(); // Used for keeping track of rendered LocalVideoStreams that are not part of a Call. @@ -85,7 +87,7 @@ export class InternalCallContext { this._localRenderInfos.delete(oldCallId); this._localRenderInfos.set(newCallId, localRenderInfos); } - + /* @conditional-compile-remove(together-mode) */ const callFeatureRenderInfos = this._callFeatureRenderInfos.get(oldCallId); if (callFeatureRenderInfos) { this._callFeatureRenderInfos.delete(oldCallId); @@ -238,13 +240,14 @@ export class InternalCallContext { this._callFeatureRenderInfos.clear(); } + /* @conditional-compile-remove(together-mode) */ public getCallFeatureRenderInfosForCall( - callId: string, - featureNameKey: string - ): Map | undefined { - return this._callFeatureRenderInfos.get(this._callIdHistory.latestCallId(callId))?.get(featureNameKey); + callId: string + ): Map> | undefined { + return this._callFeatureRenderInfos.get(this._callIdHistory.latestCallId(callId)); } + /* @conditional-compile-remove(together-mode) */ public getCallFeatureRenderInfo( callId: string, featureNameKey: string, @@ -260,6 +263,7 @@ export class InternalCallContext { return callFeatureRenderInfosForCall; } + /* @conditional-compile-remove(together-mode) */ public setCallFeatureRenderInfo( callId: string, featureNameKey: string, @@ -282,6 +286,7 @@ export class InternalCallContext { featureRenderInfos.set(streamKey, { stream, status, renderer }); } + /* @conditional-compile-remove(together-mode) */ public deleteCallFeatureRenderInfo(callId: string, featureName: string, streamKey: MediaStreamType): void { const callFeatureRenderInfoForCall = this._callFeatureRenderInfos.get(this._callIdHistory.latestCallId(callId)); if (!callFeatureRenderInfoForCall || !callFeatureRenderInfoForCall.get(featureName)) { diff --git a/packages/calling-stateful-client/src/StatefulCallClient.ts b/packages/calling-stateful-client/src/StatefulCallClient.ts index d7f0d1d5348..30988752a07 100644 --- a/packages/calling-stateful-client/src/StatefulCallClient.ts +++ b/packages/calling-stateful-client/src/StatefulCallClient.ts @@ -11,7 +11,9 @@ import { CallFeatureStreamState, TogetherModeStreamState } from './CallClientSta import { CallContext } from './CallContext'; import { callAgentDeclaratify, DeclarativeCallAgent } from './CallAgentDeclarative'; import { InternalCallContext } from './InternalCallContext'; -import { createView, disposeView, CreateViewResult, createCallFeatureView } from './StreamUtils'; +import { createView, disposeView, CreateViewResult } from './StreamUtils'; +/* @conditional-compile-remove(together-mode) */ +import { createCallFeatureView } from './StreamUtils'; import { CommunicationIdentifier, CommunicationUserIdentifier, getIdentifierKind } from '@azure/communication-common'; import { toFlatCommunicationIdentifier, @@ -146,12 +148,41 @@ export interface StatefulCallClient extends CallClient { stream: LocalVideoStreamState | RemoteVideoStreamState ): void; + /* @conditional-compile-remove(together-mode) */ + /** + * Renders a {@link CallFeatureStreamState} + * {@link VideoStreamRendererViewState} under the relevant {@link CallFeatureStreamState} + * {@link @azure/communication-calling#VideoStreamRenderer.createView}. + * + * Scenario 1: Render CallFeatureStreamState + * - CallId is required and stream of type CallFeatureStreamState is required + * - Resulting {@link VideoStreamRendererViewState} is stored in the given callId and participantId in + * {@link CallClientState} + * + * @param callId - CallId for the given stream. Can be undefined if the stream is not part of any call. + * @param stream - The LocalVideoStreamState or RemoteVideoStreamState to start rendering. + * @param options - Options that are passed to the {@link @azure/communication-calling#VideoStreamRenderer}. + */ createCallFeatureView( callId: string, stream: CallFeatureStreamState, options?: CreateViewOptions ): Promise; + /* @conditional-compile-remove(together-mode) */ + /** + * Stops rendering a {@link CallFeatureStreamState} and removes the + * {@link VideoStreamRendererView} from the relevant {@link CallFeatureStreamState} in {@link CallClientState} or + * {@link @azure/communication-calling#VideoStreamRenderer.dispose}. + * + * Its important to disposeView to clean up resources properly. + * + * Scenario 1: Dispose CallFeatureStreamState + * - CallId is required and stream of type CallFeatureStreamState is required + * + * @param callId - CallId for the given stream. Can be undefined if the stream is not part of any call. + * @param stream - The LocalVideoStreamState or RemoteVideoStreamState to dispose. + */ disposeCallFeatureView(callId: string, stream: CallFeatureStreamState): void; /** * The CallAgent is used to handle calls. @@ -409,6 +440,7 @@ export const createStatefulCallClientWithDeps = ( return result; } }); + /* @conditional-compile-remove(together-mode) */ Object.defineProperty(callClient, 'createCallFeatureView', { configurable: false, value: async ( diff --git a/packages/calling-stateful-client/src/StreamUtils.ts b/packages/calling-stateful-client/src/StreamUtils.ts index 53128dee964..eefc0400717 100644 --- a/packages/calling-stateful-client/src/StreamUtils.ts +++ b/packages/calling-stateful-client/src/StreamUtils.ts @@ -5,12 +5,13 @@ import { CreateViewOptions, LocalVideoStream, RemoteVideoStream, - TogetherModeVideoStream, VideoStreamRenderer, VideoStreamRendererView } from '@azure/communication-calling'; +/* @conditional-compile-remove(together-mode) */ +import { TogetherModeVideoStream } from '@azure/communication-calling'; import { CommunicationIdentifierKind } from '@azure/communication-common'; -import { LocalVideoStreamState, RemoteVideoStreamState } from './CallClientState'; +import { CallFeatureStreamState, LocalVideoStreamState, RemoteVideoStreamState } from './CallClientState'; /* @conditional-compile-remove(together-mode) */ import { TogetherModeStreamState } from './CallClientState'; import { CallContext } from './CallContext'; @@ -262,6 +263,7 @@ async function createViewVideo( }; } +/* @conditional-compile-remove(together-mode) */ const getStreamFeatureName = (stream: TogetherModeStreamState): string => { if ('feature' in stream) { return stream.feature; @@ -269,19 +271,19 @@ const getStreamFeatureName = (stream: TogetherModeStreamState): string => { throw new Error('Feature name not found'); }; +/* @conditional-compile-remove(together-mode) */ // This function is used to create a view for a stream that is part of a call feature. async function createCallFeatureViewVideo( context: CallContext, internalContext: InternalCallContext, callId: string, - stream: TogetherModeStreamState /* @conditional-compile-remove(together-mode) */, + stream: TogetherModeStreamState, options?: CreateViewOptions ): Promise { const streamEventType = 'createViewCallFeature'; const streamType = stream?.mediaStreamType; const callFeatureStreamId = stream && stream.id; - console.log('CHUK: createCallFeatureViewVideo'); const streamLogInfo = { callId, undefined, @@ -671,7 +673,6 @@ export function createCallFeatureView( const streamType = stream.mediaStreamType; if (callId && isCallFeatureStream(stream)) { - console.log('CHUK: createCallFeatureView'); return createCallFeatureViewVideo(context, internalContext, callId, stream, options); } else { _logStreamEvent(EventNames.CREATE_STREAM_INVALID_PARAMS, { streamType }); @@ -748,22 +749,20 @@ export function disposeAllViewsFromCall( } } } - // const callFeatureStreams = internalContext.getLocalRenderInfosForCall(callId); - // if (localStreams) { - // for (const localStreamAndRenderer of localStreams.values()) { - // if (localStreamAndRenderer && localStreamAndRenderer.renderer) { - // // We don't want to accept SDK stream as parameter but we also don't cache the declarative stream so we have to - // // convert the SDK stream to declarative stream which is not pretty so this could use some further refactoring. - // disposeView( - // context, - // internalContext, - // callId, - // undefined, - // convertSdkLocalStreamToDeclarativeLocalStream(localStreamAndRenderer.stream) - // ); - // } - // } - // } + const callFeatureStreams = internalContext.getCallFeatureRenderInfosForCall(callId); + if (callFeatureStreams) { + for (const [, featureStreams] of callFeatureStreams.entries()) { + for (const [, streamAndRenderer] of featureStreams.entries()) { + disposeView( + context, + internalContext, + callId, + undefined, + convertSdkRemoteStreamToDeclarativeRemoteStream(streamAndRenderer.stream as RemoteVideoStream) + ); + } + } + } } /** @@ -779,8 +778,6 @@ export function disposeAllViews(context: CallContext, internalContext: InternalC /** * @private */ -function isCallFeatureStream( - stream?: TogetherModeStreamState | LocalVideoStreamState | RemoteVideoStreamState -): boolean { - return (stream && 'feature' in stream) || false; +function isCallFeatureStream(stream: CallFeatureStreamState): boolean { + return 'feature' in stream || false; } diff --git a/packages/calling-stateful-client/src/index-public.ts b/packages/calling-stateful-client/src/index-public.ts index 3198d156bc8..7d31ac64f1e 100644 --- a/packages/calling-stateful-client/src/index-public.ts +++ b/packages/calling-stateful-client/src/index-public.ts @@ -37,7 +37,7 @@ export type { TogetherModeCallFeatureState as TogetherModeCallFeature } from './ export type { CallFeatureStreamState, TogetherModeStreamState, - TogetherModeSeatingCoordinates + TogetherModeSeatingCoordinatesState } from './CallClientState'; export type { RaisedHandState } from './CallClientState'; diff --git a/packages/communication-react/review/beta/communication-react.api.md b/packages/communication-react/review/beta/communication-react.api.md index 0de8dd7da2b..4ddeff99159 100644 --- a/packages/communication-react/review/beta/communication-react.api.md +++ b/packages/communication-react/review/beta/communication-react.api.md @@ -4640,11 +4640,9 @@ export type StartTeamsCallIdentifier = MicrosoftTeamsUserIdentifier | PhoneNumbe // @public export interface StatefulCallClient extends CallClient { createCallAgent(...args: Parameters): Promise; - // (undocumented) createCallFeatureView(callId: string, stream: CallFeatureStreamState, options?: CreateViewOptions): Promise; createTeamsCallAgent(...args: Parameters): Promise; createView(callId: string | undefined, participantId: CommunicationIdentifier | undefined, stream: LocalVideoStreamState | RemoteVideoStreamState, options?: CreateViewOptions): Promise; - // (undocumented) disposeCallFeatureView(callId: string, stream: CallFeatureStreamState): void; disposeView(callId: string | undefined, participantId: CommunicationIdentifier | undefined, stream: LocalVideoStreamState | RemoteVideoStreamState): void; getState(): CallClientState; @@ -4833,12 +4831,12 @@ export const toFlatCommunicationIdentifier: (identifier: CommunicationIdentifier // @alpha export interface TogetherModeCallFeature { - seatingCoordinates: Map; + seatingCoordinates: Map; streams: Map; } // @alpha -export interface TogetherModeSeatingCoordinates { +export interface TogetherModeSeatingCoordinatesState { // (undocumented) height: number; // (undocumented) diff --git a/packages/react-composites/src/composites/CallComposite/MockCallAdapter.ts b/packages/react-composites/src/composites/CallComposite/MockCallAdapter.ts index 02cb8175ab8..847700aac5b 100644 --- a/packages/react-composites/src/composites/CallComposite/MockCallAdapter.ts +++ b/packages/react-composites/src/composites/CallComposite/MockCallAdapter.ts @@ -110,6 +110,7 @@ export class _MockCallAdapter implements CallAdapter { createStreamView(): Promise { throw Error('createStreamView not implemented'); } + /* @conditional-compile-remove(together-mode) */ createFeatureStreamView(): Promise { throw Error('createFeatureStreamView not implemented'); } @@ -125,6 +126,7 @@ export class _MockCallAdapter implements CallAdapter { disposeRemoteVideoStreamView(): Promise { return Promise.resolve(); } + /* @conditional-compile-remove(together-mode) */ disposeFeatureStreamView(): Promise { return Promise.resolve(); } From 6f0c40da7512b665c327ebbf8a43977861cb2a50 Mon Sep 17 00:00:00 2001 From: Chukwuebuka Nwankwo Date: Tue, 8 Oct 2024 10:30:13 +0000 Subject: [PATCH 03/17] Include missing conditional compile tags --- .../calling-stateful-client/src/CallClientState.ts | 9 +++++---- packages/calling-stateful-client/src/CallContext.ts | 2 ++ .../src/InternalCallContext.ts | 5 ++++- .../calling-stateful-client/src/StatefulCallClient.ts | 2 ++ packages/calling-stateful-client/src/StreamUtils.ts | 8 ++++++-- .../review/beta/communication-react.api.md | 11 +++++++---- 6 files changed, 26 insertions(+), 11 deletions(-) diff --git a/packages/calling-stateful-client/src/CallClientState.ts b/packages/calling-stateful-client/src/CallClientState.ts index cde1fb0080f..2cc886d7b2a 100644 --- a/packages/calling-stateful-client/src/CallClientState.ts +++ b/packages/calling-stateful-client/src/CallClientState.ts @@ -278,7 +278,7 @@ export interface RaiseHandCallFeatureState { /** * State only version of {@link @azure/communication-calling#TogetherModeCallFeature}. {@link StatefulCallClient} will * automatically listen for raised hands on the call and update the state exposed by {@link StatefulCallClient} accordingly. - * @alpha + * @beta */ export interface TogetherModeCallFeatureState { /** @@ -293,7 +293,7 @@ export interface TogetherModeCallFeatureState { /* @conditional-compile-remove(together-mode) */ /** - * @alpha + * @beta */ export interface CallFeatureStreamState { feature: 'togetherMode'; @@ -302,7 +302,7 @@ export interface CallFeatureStreamState { /* @conditional-compile-remove(together-mode) */ /** * State only version of {@link @azure/communication-calling#TogetherModeVideoStream}. - * @alpha + * @beta */ export interface TogetherModeStreamState extends CallFeatureStreamState { /** @@ -332,7 +332,7 @@ export interface TogetherModeStreamState extends CallFeatureStreamState { /* @conditional-compile-remove(together-mode) */ /** * State only version of {@link @azure/communication-calling#TogetherModeSeatingMap}. - * @alpha + * @beta */ export interface TogetherModeSeatingCoordinatesState { // the y coordinate of the participant seating position in the together mode stream @@ -653,6 +653,7 @@ export interface CallState { /* @conditional-compile-remove(together-mode) */ /** * Proxy of {@link @azure/communication-calling#TogetherModeCallFeature}. + * @beta */ togetherMode: TogetherModeCallFeatureState; /** diff --git a/packages/calling-stateful-client/src/CallContext.ts b/packages/calling-stateful-client/src/CallContext.ts index ab905177574..5011a0d9ca8 100644 --- a/packages/calling-stateful-client/src/CallContext.ts +++ b/packages/calling-stateful-client/src/CallContext.ts @@ -737,6 +737,7 @@ export class CallContext { }); } + /* @conditional-compile-remove(together-mode) */ public setTogetherModeVideoStreamRendererView( callId: string, togetherModeStreamType: string, @@ -744,6 +745,7 @@ export class CallContext { ): void { this.modifyState((draft: CallClientState) => { const call = draft.calls[this._callIdHistory.latestCallId(callId)]; + /* @conditional-compile-remove(together-mode) */ if (call) { const togetherModeStream = call.togetherMode.streams.get(togetherModeStreamType); if (togetherModeStream) { diff --git a/packages/calling-stateful-client/src/InternalCallContext.ts b/packages/calling-stateful-client/src/InternalCallContext.ts index 9f8fe630eb5..c565886e2eb 100644 --- a/packages/calling-stateful-client/src/InternalCallContext.ts +++ b/packages/calling-stateful-client/src/InternalCallContext.ts @@ -5,9 +5,10 @@ import { LocalVideoStream, MediaStreamType, RemoteVideoStream, - /* @conditional-compile-remove(together-mode) */ RemoteVideoStreamCommon, VideoStreamRenderer } from '@azure/communication-calling'; +/* @conditional-compile-remove(together-mode) */ +import { RemoteVideoStreamCommon } from '@azure/communication-calling'; import { LocalVideoStreamState } from './CallClientState'; import type { CallContext } from './CallContext'; import { CallIdHistory } from './CallIdHistory'; @@ -89,6 +90,7 @@ export class InternalCallContext { } /* @conditional-compile-remove(together-mode) */ const callFeatureRenderInfos = this._callFeatureRenderInfos.get(oldCallId); + /* @conditional-compile-remove(together-mode) */ if (callFeatureRenderInfos) { this._callFeatureRenderInfos.delete(oldCallId); this._callFeatureRenderInfos.set(newCallId, callFeatureRenderInfos); @@ -237,6 +239,7 @@ export class InternalCallContext { public clearCallRelatedState(): void { this._remoteRenderInfos.clear(); this._localRenderInfos.clear(); + /* @conditional-compile-remove(together-mode) */ this._callFeatureRenderInfos.clear(); } diff --git a/packages/calling-stateful-client/src/StatefulCallClient.ts b/packages/calling-stateful-client/src/StatefulCallClient.ts index 30988752a07..e5fb458d6a6 100644 --- a/packages/calling-stateful-client/src/StatefulCallClient.ts +++ b/packages/calling-stateful-client/src/StatefulCallClient.ts @@ -162,6 +162,7 @@ export interface StatefulCallClient extends CallClient { * @param callId - CallId for the given stream. Can be undefined if the stream is not part of any call. * @param stream - The LocalVideoStreamState or RemoteVideoStreamState to start rendering. * @param options - Options that are passed to the {@link @azure/communication-calling#VideoStreamRenderer}. + * @beta */ createCallFeatureView( callId: string, @@ -182,6 +183,7 @@ export interface StatefulCallClient extends CallClient { * * @param callId - CallId for the given stream. Can be undefined if the stream is not part of any call. * @param stream - The LocalVideoStreamState or RemoteVideoStreamState to dispose. + * @beta */ disposeCallFeatureView(callId: string, stream: CallFeatureStreamState): void; /** diff --git a/packages/calling-stateful-client/src/StreamUtils.ts b/packages/calling-stateful-client/src/StreamUtils.ts index eefc0400717..3b7992dc575 100644 --- a/packages/calling-stateful-client/src/StreamUtils.ts +++ b/packages/calling-stateful-client/src/StreamUtils.ts @@ -11,9 +11,9 @@ import { /* @conditional-compile-remove(together-mode) */ import { TogetherModeVideoStream } from '@azure/communication-calling'; import { CommunicationIdentifierKind } from '@azure/communication-common'; -import { CallFeatureStreamState, LocalVideoStreamState, RemoteVideoStreamState } from './CallClientState'; +import { LocalVideoStreamState, RemoteVideoStreamState } from './CallClientState'; /* @conditional-compile-remove(together-mode) */ -import { TogetherModeStreamState } from './CallClientState'; +import { CallFeatureStreamState, TogetherModeStreamState } from './CallClientState'; import { CallContext } from './CallContext'; import { convertSdkLocalStreamToDeclarativeLocalStream, @@ -660,6 +660,7 @@ export function createView( } } +/* @conditional-compile-remove(together-mode) */ /** * @private */ @@ -749,7 +750,9 @@ export function disposeAllViewsFromCall( } } } + /* @conditional-compile-remove(together-mode) */ const callFeatureStreams = internalContext.getCallFeatureRenderInfosForCall(callId); + /* @conditional-compile-remove(together-mode) */ if (callFeatureStreams) { for (const [, featureStreams] of callFeatureStreams.entries()) { for (const [, streamAndRenderer] of featureStreams.entries()) { @@ -775,6 +778,7 @@ export function disposeAllViews(context: CallContext, internalContext: InternalC } } +/* @conditional-compile-remove(together-mode) */ /** * @private */ diff --git a/packages/communication-react/review/beta/communication-react.api.md b/packages/communication-react/review/beta/communication-react.api.md index 4ddeff99159..1f319d2bff5 100644 --- a/packages/communication-react/review/beta/communication-react.api.md +++ b/packages/communication-react/review/beta/communication-react.api.md @@ -1027,7 +1027,7 @@ export type CallErrors = { // @public export type CallErrorTarget = 'Call.addParticipant' | 'Call.dispose' | 'Call.feature' | 'Call.hangUp' | 'Call.hold' | 'Call.mute' | 'Call.muteIncomingAudio' | 'Call.off' | 'Call.on' | 'Call.removeParticipant' | 'Call.resume' | 'Call.sendDtmf' | 'Call.startAudio' | 'Call.startScreenSharing' | 'Call.startVideo' | 'Call.stopScreenSharing' | 'Call.stopAudio' | 'Call.stopVideo' | 'Call.unmute' | 'Call.unmuteIncomingAudio' | 'CallAgent.dispose' | 'CallAgent.feature' | 'CallAgent.join' | 'CallAgent.off' | 'CallAgent.on' | 'CallAgent.startCall' | 'CallClient.createCallAgent' | 'CallClient.createTeamsCallAgent' | 'CallClient.feature' | 'CallClient.getDeviceManager' | /* @conditional-compile-remove(calling-beta-sdk) */ 'CallClient.getEnvironmentInfo' | 'DeviceManager.askDevicePermission' | 'DeviceManager.getCameras' | 'DeviceManager.getMicrophones' | 'DeviceManager.getSpeakers' | 'DeviceManager.off' | 'DeviceManager.on' | 'DeviceManager.selectMicrophone' | 'DeviceManager.selectSpeaker' | 'IncomingCall.accept' | 'IncomingCall.reject' | /* @conditional-compile-remove(calling-beta-sdk) */ /* @conditional-compile-remove(teams-identity-support) */ 'TeamsCall.addParticipant' | 'VideoEffectsFeature.startEffects' | /* @conditional-compile-remove(calling-beta-sdk) */ 'CallAgent.handlePushNotification' | /* @conditional-compile-remove(calling-beta-sdk) */ 'Call.admit' | /* @conditional-compile-remove(calling-beta-sdk) */ 'Call.rejectParticipant' | /* @conditional-compile-remove(calling-beta-sdk) */ 'Call.admitAll' | /* @conditional-compile-remove(soft-mute) */ 'Call.mutedByOthers' | 'Call.muteAllRemoteParticipants' | 'Call.setConstraints'; -// @alpha (undocumented) +// @beta (undocumented) export interface CallFeatureStreamState { // (undocumented) feature: 'togetherMode'; @@ -1165,6 +1165,7 @@ export interface CallState { spotlight?: SpotlightCallFeatureState; startTime: Date; state: CallState_2; + // @beta togetherMode: TogetherModeCallFeature; totalParticipantCount?: number; transcription: TranscriptionCallFeature; @@ -4640,9 +4641,11 @@ export type StartTeamsCallIdentifier = MicrosoftTeamsUserIdentifier | PhoneNumbe // @public export interface StatefulCallClient extends CallClient { createCallAgent(...args: Parameters): Promise; + // @beta createCallFeatureView(callId: string, stream: CallFeatureStreamState, options?: CreateViewOptions): Promise; createTeamsCallAgent(...args: Parameters): Promise; createView(callId: string | undefined, participantId: CommunicationIdentifier | undefined, stream: LocalVideoStreamState | RemoteVideoStreamState, options?: CreateViewOptions): Promise; + // @beta disposeCallFeatureView(callId: string, stream: CallFeatureStreamState): void; disposeView(callId: string | undefined, participantId: CommunicationIdentifier | undefined, stream: LocalVideoStreamState | RemoteVideoStreamState): void; getState(): CallClientState; @@ -4829,13 +4832,13 @@ export type TeamsOutboundCallAdapterArgs = TeamsCallAdapterArgsCommon & { // @public export const toFlatCommunicationIdentifier: (identifier: CommunicationIdentifier) => string; -// @alpha +// @beta export interface TogetherModeCallFeature { seatingCoordinates: Map; streams: Map; } -// @alpha +// @beta export interface TogetherModeSeatingCoordinatesState { // (undocumented) height: number; @@ -4847,7 +4850,7 @@ export interface TogetherModeSeatingCoordinatesState { width: number; } -// @alpha +// @beta export interface TogetherModeStreamState extends CallFeatureStreamState { id: number; // @public From 4e95fed512bb87e9e13904be1c1816be024a9a06 Mon Sep 17 00:00:00 2001 From: Chukwuebuka Nwankwo Date: Tue, 8 Oct 2024 10:42:45 +0000 Subject: [PATCH 04/17] fixed failing test --- .../react-composites/tests/browser/call/hermetic/fixture.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/react-composites/tests/browser/call/hermetic/fixture.ts b/packages/react-composites/tests/browser/call/hermetic/fixture.ts index 0ce977c8002..2d29e8e4f5f 100644 --- a/packages/react-composites/tests/browser/call/hermetic/fixture.ts +++ b/packages/react-composites/tests/browser/call/hermetic/fixture.ts @@ -95,7 +95,7 @@ export function defaultMockCallAdapterState( remoteParticipantsEnded: {}, raiseHand: { raisedHands: [] }, /* @conditional-compile-remove(together-mode) */ - togetherMode: { stream: [] }, + togetherMode: { streams: new Map(), seatingCoordinates: new Map() }, pptLive: { isActive: false }, role: role ?? 'Unknown', dominantSpeakers: dominantSpeakers, @@ -540,7 +540,7 @@ const defaultEndedCallState: CallState = { remoteParticipantsEnded: {}, raiseHand: { raisedHands: [] }, /* @conditional-compile-remove(together-mode) */ - togetherMode: { stream: [] }, + togetherMode: { streams: new Map(), seatingCoordinates: new Map() }, pptLive: { isActive: false }, captionsFeature: { captions: [], From 9985249dafee39f6d837804f213ab6b05d1e68f3 Mon Sep 17 00:00:00 2001 From: Chukwuebuka Nwankwo Date: Tue, 8 Oct 2024 17:28:16 +0000 Subject: [PATCH 05/17] Updated CallAdapterCallOperations APIs --- .../src/handlers/createCommonHandlers.ts | 62 ++++++++++++++++++- .../review/beta/communication-react.api.md | 12 ++++ .../adapter/AzureCommunicationCallAdapter.ts | 22 +++++++ .../CallComposite/adapter/CallAdapter.ts | 31 ++++++++++ .../CallComposite/hooks/useHandlers.ts | 8 +++ .../AzureCommunicationCallWithChatAdapter.ts | 11 ++++ .../adapter/CallWithChatAdapter.ts | 31 ++++++++++ .../adapter/CallWithChatBackedCallAdapter.ts | 9 +++ 8 files changed, 185 insertions(+), 1 deletion(-) diff --git a/packages/calling-component-bindings/src/handlers/createCommonHandlers.ts b/packages/calling-component-bindings/src/handlers/createCommonHandlers.ts index d4f21e82ad8..bda79ab5089 100644 --- a/packages/calling-component-bindings/src/handlers/createCommonHandlers.ts +++ b/packages/calling-component-bindings/src/handlers/createCommonHandlers.ts @@ -106,6 +106,10 @@ export interface CommonCallingHandlers { onMuteParticipant: (userId: string) => Promise; /* @conditional-compile-remove(soft-mute) */ onMuteAllRemoteParticipants: () => Promise; + /* @conditional-compile-remove(together-mode) */ + onCreateTogetherModeStreamView: (options?: VideoStreamOptions) => Promise; + /* @conditional-compile-remove(together-mode) */ + onDisposeTogetherModeStreamViews: () => Promise; } /** @@ -715,6 +719,58 @@ export const createDefaultCommonCallingHandlers = memoizeOne( } : undefined; + /* @conditional-compile-remove(together-mode) */ + const onCreateTogetherModeStreamView = async ( + options = { scalingMode: 'Fit', isMirrored: true } as VideoStreamOptions + ): Promise => { + if (!call) { + return; + } + const callState = callClient.getState().calls[call.id]; + if (!callState) { + return; + } + const togetherModeStreams = callState.togetherMode.streams; + const createViewStreamResults: Map = new Map(); + if (!togetherModeStreams.size) { + const togetherModeFeature = call?.feature(Features.TogetherMode); + await togetherModeFeature?.start(); + } else { + for (const stream of togetherModeStreams) { + if (!stream[1].view) { + const createViewResult = await callClient.createCallFeatureView(call.id, stream[1], options); + createViewStreamResults.set( + stream[0], + createViewResult?.view ? { view: createViewResult?.view } : undefined + ); + } + } + } + const createViewResult = createViewStreamResults.get('Video'); + return createViewResult?.view ? { view: createViewResult?.view } : undefined; + }; + /* @conditional-compile-remove(together-mode) */ + const onDisposeTogetherModeStreamViews = async (): Promise => { + if (!call) { + return; + } + const callState = callClient.getState().calls[call.id]; + if (!callState) { + throw new Error(`Call Not Found: ${call.id}`); + } + + const togetherModeStreams = callState.togetherMode.streams; + + if (!togetherModeStreams.size) { + return; + } + + for (const stream of togetherModeStreams) { + if (stream[1].view) { + callClient.disposeCallFeatureView(call.id, stream[1]); + } + } + }; return { onHangUp, onToggleHold, @@ -768,7 +824,11 @@ export const createDefaultCommonCallingHandlers = memoizeOne( /* @conditional-compile-remove(soft-mute) */ onMuteAllRemoteParticipants, onAcceptCall: notImplemented, - onRejectCall: notImplemented + onRejectCall: notImplemented, + /* @conditional-compile-remove(together-mode) */ + onCreateTogetherModeStreamView, + /* @conditional-compile-remove(together-mode) */ + onDisposeTogetherModeStreamViews }; } ); diff --git a/packages/communication-react/review/beta/communication-react.api.md b/packages/communication-react/review/beta/communication-react.api.md index 89af7af8197..a2e5ae5ad88 100644 --- a/packages/communication-react/review/beta/communication-react.api.md +++ b/packages/communication-react/review/beta/communication-react.api.md @@ -441,7 +441,11 @@ export interface CallAdapterCallOperations { // (undocumented) addParticipant(participant: CommunicationUserIdentifier): Promise; allowUnsupportedBrowserVersion(): void; + // @beta + createFeatureStreamView(featureName: string, options?: VideoStreamOptions): Promise; createStreamView(remoteUserId?: string, options?: VideoStreamOptions): Promise; + // @beta + disposeFeatureStreamView(featureName: string, options?: VideoStreamOptions): Promise; disposeLocalVideoStreamView(): Promise; disposeRemoteVideoStreamView(remoteUserId: string): Promise; disposeScreenShareStreamView(remoteUserId: string): Promise; @@ -1192,10 +1196,14 @@ export interface CallWithChatAdapterManagement { addParticipant(participant: CommunicationUserIdentifier): Promise; allowUnsupportedBrowserVersion(): void; askDevicePermission(constrain: PermissionConstraints): Promise; + // @beta + createFeatureStreamView(featureName: string, options?: VideoStreamOptions): Promise; createStreamView(remoteUserId?: string, options?: VideoStreamOptions): Promise; // @beta deleteImage(imageId: string): Promise; deleteMessage(messageId: string): Promise; + // @beta + disposeFeatureStreamView(featureName: string, options?: VideoStreamOptions): Promise; disposeLocalVideoStreamView(): Promise; disposeRemoteVideoStreamView(remoteUserId: string): Promise; disposeScreenShareStreamView(remoteUserId: string): Promise; @@ -2157,6 +2165,8 @@ export interface CommonCallingHandlers { // (undocumented) onCreateRemoteStreamView: (userId: string, options?: VideoStreamOptions) => Promise; // (undocumented) + onCreateTogetherModeStreamView: (options?: VideoStreamOptions) => Promise; + // (undocumented) onDisposeLocalScreenShareStreamView: () => Promise; // (undocumented) onDisposeLocalStreamView: () => Promise; @@ -2167,6 +2177,8 @@ export interface CommonCallingHandlers { // (undocumented) onDisposeRemoteVideoStreamView: (userId: string) => Promise; // (undocumented) + onDisposeTogetherModeStreamViews: () => Promise; + // (undocumented) onHangUp: (forEveryone?: boolean) => Promise; // (undocumented) onLowerHand: () => Promise; diff --git a/packages/react-composites/src/composites/CallComposite/adapter/AzureCommunicationCallAdapter.ts b/packages/react-composites/src/composites/CallComposite/adapter/AzureCommunicationCallAdapter.ts index 92f5ac6c564..01093b6b04a 100644 --- a/packages/react-composites/src/composites/CallComposite/adapter/AzureCommunicationCallAdapter.ts +++ b/packages/react-composites/src/composites/CallComposite/adapter/AzureCommunicationCallAdapter.ts @@ -595,6 +595,10 @@ export class AzureCommunicationCallAdapter { + if (featureName === 'togetherMode') { + /* @conditional-compile-remove(together-mode) */ + return await this.handlers.onCreateTogetherModeStreamView(options); + } + } + /* @conditional-compile-remove(together-mode) */ + public async disposeFeatureStreamView(featureName: string, options?: VideoStreamOptions): Promise { + if (featureName === 'togetherMode') { + /* @conditional-compile-remove(together-mode) */ + return await this.handlers.onDisposeTogetherModeStreamViews(); + } + } + public async leaveCall(forEveryone?: boolean): Promise { if (this.getState().page === 'transferring') { const transferCall = this.callAgent.calls.filter( diff --git a/packages/react-composites/src/composites/CallComposite/adapter/CallAdapter.ts b/packages/react-composites/src/composites/CallComposite/adapter/CallAdapter.ts index 2481511df97..2db5bf91fc5 100644 --- a/packages/react-composites/src/composites/CallComposite/adapter/CallAdapter.ts +++ b/packages/react-composites/src/composites/CallComposite/adapter/CallAdapter.ts @@ -633,6 +633,37 @@ export interface CallAdapterCallOperations { * @public */ disposeStreamView(remoteUserId?: string, options?: VideoStreamOptions): Promise; + /* @conditional-compile-remove(together-mode) */ + /** + * Create the html view for a stream. + * + * @remarks + * This method is implemented for composite + * + * @param featureName - Name of feature to render + * @param options - Options to control how video streams are rendered {@link @azure/communication-calling#VideoStreamOptions } + * + * @beta + */ + createFeatureStreamView( + featureName: string, + options?: VideoStreamOptions + ): Promise; + + /* @conditional-compile-remove(together-mode) */ + /** + * Dispose the html view for a stream. + * + * @remarks + * This method is implemented for composite + * + * + * @param featureName - Name of the feature to dispose + * @param options - Options to control how video streams are rendered {@link @azure/communication-calling#VideoStreamOptions } + * + * @beta + */ + disposeFeatureStreamView(featureName: string, options?: VideoStreamOptions): Promise; /** * Dispose the html view for a screen share stream * diff --git a/packages/react-composites/src/composites/CallComposite/hooks/useHandlers.ts b/packages/react-composites/src/composites/CallComposite/hooks/useHandlers.ts index 878a6af325a..7e6c569613c 100644 --- a/packages/react-composites/src/composites/CallComposite/hooks/useHandlers.ts +++ b/packages/react-composites/src/composites/CallComposite/hooks/useHandlers.ts @@ -231,6 +231,14 @@ const createCompositeHandlers = memoizeOne( /* @conditional-compile-remove(soft-mute) */ onMuteAllRemoteParticipants: async (): Promise => { await adapter.muteAllRemoteParticipants(); + }, + /* @conditional-compile-remove(together-mode) */ + onCreateTogetherModeStreamView: async (options) => { + return await adapter.createFeatureStreamView('togetherMode', options); + }, + /* @conditional-compile-remove(together-mode) */ + onDisposeTogetherModeStreamViews: async () => { + return await adapter.disposeFeatureStreamView('togetherMode'); } }; } diff --git a/packages/react-composites/src/composites/CallWithChatComposite/adapter/AzureCommunicationCallWithChatAdapter.ts b/packages/react-composites/src/composites/CallWithChatComposite/adapter/AzureCommunicationCallWithChatAdapter.ts index 72f18a939c5..39b19538588 100644 --- a/packages/react-composites/src/composites/CallWithChatComposite/adapter/AzureCommunicationCallWithChatAdapter.ts +++ b/packages/react-composites/src/composites/CallWithChatComposite/adapter/AzureCommunicationCallWithChatAdapter.ts @@ -526,6 +526,17 @@ export class AzureCommunicationCallWithChatAdapter implements CallWithChatAdapte public async disposeLocalVideoStreamView(): Promise { await this.callAdapter.disposeLocalVideoStreamView(); } + /* @conditional-compile-remove(together-mode) */ + public async createFeatureStreamView( + featureName: string, + options?: VideoStreamOptions + ): Promise { + return await this.callAdapter.createFeatureStreamView(featureName, options); + } + /* @conditional-compile-remove(together-mode) */ + public async disposeFeatureStreamView(featureName: string, options?: VideoStreamOptions): Promise { + await this.callAdapter.disposeFeatureStreamView(featureName, options); + } /** Fetch initial Call and Chat data such as chat messages. */ public async fetchInitialData(): Promise { return await this.executeWithResolvedChatAdapter((adapter) => { diff --git a/packages/react-composites/src/composites/CallWithChatComposite/adapter/CallWithChatAdapter.ts b/packages/react-composites/src/composites/CallWithChatComposite/adapter/CallWithChatAdapter.ts index 81ce18a6bcc..7e0dd3ff3ab 100644 --- a/packages/react-composites/src/composites/CallWithChatComposite/adapter/CallWithChatAdapter.ts +++ b/packages/react-composites/src/composites/CallWithChatComposite/adapter/CallWithChatAdapter.ts @@ -226,6 +226,37 @@ export interface CallWithChatAdapterManagement { * @public */ disposeStreamView(remoteUserId?: string, options?: VideoStreamOptions): Promise; + /* @conditional-compile-remove(together-mode) */ + /** + * Create the html view for a stream. + * + * @remarks + * This method is implemented for composite + * + * @param featureName - Name of feature to render + * @param options - Options to control how video streams are rendered {@link @azure/communication-calling#VideoStreamOptions } + * + * @beta + */ + createFeatureStreamView( + featureName: string, + options?: VideoStreamOptions + ): Promise; + + /* @conditional-compile-remove(together-mode) */ + /** + * Dispose the html view for a stream. + * + * @remarks + * This method is implemented for composite + * + * + * @param featureName - Name of the feature to dispose + * @param options - Options to control how video streams are rendered {@link @azure/communication-calling#VideoStreamOptions } + * + * @beta + */ + disposeFeatureStreamView(featureName: string, options?: VideoStreamOptions): Promise; /** * Dispose the html view for a screen share stream * diff --git a/packages/react-composites/src/composites/CallWithChatComposite/adapter/CallWithChatBackedCallAdapter.ts b/packages/react-composites/src/composites/CallWithChatComposite/adapter/CallWithChatBackedCallAdapter.ts index 9ec26cd83e4..ce066cc9e8e 100644 --- a/packages/react-composites/src/composites/CallWithChatComposite/adapter/CallWithChatBackedCallAdapter.ts +++ b/packages/react-composites/src/composites/CallWithChatComposite/adapter/CallWithChatBackedCallAdapter.ts @@ -134,6 +134,12 @@ export class CallWithChatBackedCallAdapter implements CallAdapter { options?: VideoStreamOptions ): Promise => await this.callWithChatAdapter.createStreamView(remoteUserId, options); + /* @conditional-compile-remove(together-mode) */ + public createFeatureStreamView = async ( + featureName: string, + options?: VideoStreamOptions + ): Promise => + await this.callWithChatAdapter.createFeatureStreamView(featureName, options); public disposeStreamView = async (remoteUserId?: string, options?: VideoStreamOptions): Promise => await this.callWithChatAdapter.disposeStreamView(remoteUserId, options); public disposeScreenShareStreamView(remoteUserId: string): Promise { @@ -145,6 +151,9 @@ export class CallWithChatBackedCallAdapter implements CallAdapter { public disposeLocalVideoStreamView(): Promise { return this.callWithChatAdapter.disposeLocalVideoStreamView(); } + /* @conditional-compile-remove(together-mode) */ + public disposeFeatureStreamView = async (featureName: string, options?: VideoStreamOptions): Promise => + await this.callWithChatAdapter.disposeStreamView(featureName, options); public holdCall = async (): Promise => { await this.callWithChatAdapter.holdCall(); }; From ea8d7fa45ae0b9aae180aeb86b8527ea34c4e41b Mon Sep 17 00:00:00 2001 From: Chukwuebuka Nwankwo Date: Tue, 8 Oct 2024 17:51:40 +0000 Subject: [PATCH 06/17] fixed lint issue --- .../CallComposite/adapter/AzureCommunicationCallAdapter.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/react-composites/src/composites/CallComposite/adapter/AzureCommunicationCallAdapter.ts b/packages/react-composites/src/composites/CallComposite/adapter/AzureCommunicationCallAdapter.ts index 01093b6b04a..ce6cedf9dbd 100644 --- a/packages/react-composites/src/composites/CallComposite/adapter/AzureCommunicationCallAdapter.ts +++ b/packages/react-composites/src/composites/CallComposite/adapter/AzureCommunicationCallAdapter.ts @@ -811,7 +811,7 @@ export class AzureCommunicationCallAdapter { + public async disposeFeatureStreamView(featureName: string): Promise { if (featureName === 'togetherMode') { /* @conditional-compile-remove(together-mode) */ return await this.handlers.onDisposeTogetherModeStreamViews(); From d1c525a7b10c4beeb5f6d0535ff9804e93e1fda5 Mon Sep 17 00:00:00 2001 From: Chukwuebuka Nwankwo Date: Tue, 8 Oct 2024 19:13:00 +0000 Subject: [PATCH 07/17] fixed failing test --- packages/react-composites/tests/app/lib/MockCallAdapter.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/packages/react-composites/tests/app/lib/MockCallAdapter.ts b/packages/react-composites/tests/app/lib/MockCallAdapter.ts index 4f7b1a278e0..4c578083bde 100644 --- a/packages/react-composites/tests/app/lib/MockCallAdapter.ts +++ b/packages/react-composites/tests/app/lib/MockCallAdapter.ts @@ -91,6 +91,12 @@ export class MockCallAdapter implements CallAdapter { disposeStreamView(): Promise { throw Error('disposeStreamView not implemented'); } + createFeatureStreamView(): Promise { + throw Error('createFeatureStreamView not implemented'); + } + disposeFeatureStreamView(): Promise { + throw Error('disposeFeatureStreamView not implemented'); + } disposeScreenShareStreamView(): Promise { return Promise.resolve(); } From cd629379411a2b9806cc97a5653e8e75f1dbc66c Mon Sep 17 00:00:00 2001 From: Chukwuebuka Nwankwo Date: Tue, 8 Oct 2024 21:23:52 +0000 Subject: [PATCH 08/17] Updated tags --- .../src/handlers/createCommonHandlers.ts | 10 ++++++++++ packages/calling-stateful-client/src/StreamUtils.ts | 10 ++++------ .../review/beta/communication-react.api.md | 4 ++-- 3 files changed, 16 insertions(+), 8 deletions(-) diff --git a/packages/calling-component-bindings/src/handlers/createCommonHandlers.ts b/packages/calling-component-bindings/src/handlers/createCommonHandlers.ts index bda79ab5089..b6107d80af3 100644 --- a/packages/calling-component-bindings/src/handlers/createCommonHandlers.ts +++ b/packages/calling-component-bindings/src/handlers/createCommonHandlers.ts @@ -107,8 +107,18 @@ export interface CommonCallingHandlers { /* @conditional-compile-remove(soft-mute) */ onMuteAllRemoteParticipants: () => Promise; /* @conditional-compile-remove(together-mode) */ + /** + * Call back to create a view for together mode + * + * @beta + */ onCreateTogetherModeStreamView: (options?: VideoStreamOptions) => Promise; /* @conditional-compile-remove(together-mode) */ + /** + * Call back to dispose together mode views + * + * @beta + */ onDisposeTogetherModeStreamViews: () => Promise; } diff --git a/packages/calling-stateful-client/src/StreamUtils.ts b/packages/calling-stateful-client/src/StreamUtils.ts index 3b7992dc575..8202e5668c0 100644 --- a/packages/calling-stateful-client/src/StreamUtils.ts +++ b/packages/calling-stateful-client/src/StreamUtils.ts @@ -8,8 +8,6 @@ import { VideoStreamRenderer, VideoStreamRendererView } from '@azure/communication-calling'; -/* @conditional-compile-remove(together-mode) */ -import { TogetherModeVideoStream } from '@azure/communication-calling'; import { CommunicationIdentifierKind } from '@azure/communication-common'; import { LocalVideoStreamState, RemoteVideoStreamState } from './CallClientState'; /* @conditional-compile-remove(together-mode) */ @@ -324,7 +322,7 @@ async function createCallFeatureViewVideo( callId, featureName, stream.mediaStreamType, - renderInfo.stream as TogetherModeVideoStream, + renderInfo.stream, 'Rendering', renderInfo.renderer ); @@ -336,7 +334,7 @@ async function createCallFeatureViewVideo( callId, featureName, stream.mediaStreamType, - renderInfo.stream as TogetherModeVideoStream, + renderInfo.stream, 'Rendering', undefined ); @@ -379,7 +377,7 @@ async function createCallFeatureViewVideo( callId, featureName, stream.mediaStreamType, - refreshedRenderInfo.stream as TogetherModeVideoStream, + refreshedRenderInfo.stream, 'NotRendered', undefined ); @@ -393,7 +391,7 @@ async function createCallFeatureViewVideo( callId, featureName, stream.mediaStreamType, - refreshedRenderInfo.stream as TogetherModeVideoStream, + refreshedRenderInfo.stream, 'Rendered', renderer ); diff --git a/packages/communication-react/review/beta/communication-react.api.md b/packages/communication-react/review/beta/communication-react.api.md index a2e5ae5ad88..bda360c5b04 100644 --- a/packages/communication-react/review/beta/communication-react.api.md +++ b/packages/communication-react/review/beta/communication-react.api.md @@ -2164,7 +2164,7 @@ export interface CommonCallingHandlers { onCreateLocalStreamView: (options?: VideoStreamOptions) => Promise; // (undocumented) onCreateRemoteStreamView: (userId: string, options?: VideoStreamOptions) => Promise; - // (undocumented) + // @beta onCreateTogetherModeStreamView: (options?: VideoStreamOptions) => Promise; // (undocumented) onDisposeLocalScreenShareStreamView: () => Promise; @@ -2176,7 +2176,7 @@ export interface CommonCallingHandlers { onDisposeRemoteStreamView: (userId: string) => Promise; // (undocumented) onDisposeRemoteVideoStreamView: (userId: string) => Promise; - // (undocumented) + // @beta onDisposeTogetherModeStreamViews: () => Promise; // (undocumented) onHangUp: (forEveryone?: boolean) => Promise; From bf66778d8ac4fdac0254f8b1cc426fa6b9de6e8e Mon Sep 17 00:00:00 2001 From: Chukwuebuka Nwankwo Date: Wed, 9 Oct 2024 00:53:53 +0000 Subject: [PATCH 09/17] Updated together mode create view result type --- .../src/handlers/createCommonHandlers.ts | 20 ++++++++-------- .../review/beta/communication-react.api.md | 18 ++++++++------- .../src/types/TogetherModeTypes.ts | 12 ++++++++++ packages/react-components/src/types/index.ts | 1 + .../CallComposite/MockCallAdapter.ts | 4 ++-- .../adapter/AzureCommunicationCallAdapter.ts | 23 ++++++++----------- .../CallComposite/adapter/CallAdapter.ts | 8 +++---- .../CallComposite/hooks/useHandlers.ts | 4 ++-- .../AzureCommunicationCallWithChatAdapter.ts | 12 +++++----- .../adapter/CallWithChatAdapter.ts | 13 +++-------- .../adapter/CallWithChatBackedCallAdapter.ts | 12 +++++----- .../tests/app/lib/MockCallAdapter.ts | 4 ++-- 12 files changed, 67 insertions(+), 64 deletions(-) create mode 100644 packages/react-components/src/types/TogetherModeTypes.ts diff --git a/packages/calling-component-bindings/src/handlers/createCommonHandlers.ts b/packages/calling-component-bindings/src/handlers/createCommonHandlers.ts index b6107d80af3..9f9336b1441 100644 --- a/packages/calling-component-bindings/src/handlers/createCommonHandlers.ts +++ b/packages/calling-component-bindings/src/handlers/createCommonHandlers.ts @@ -35,6 +35,7 @@ import { Features } from '@azure/communication-calling'; import { TeamsCaptions } from '@azure/communication-calling'; import { Reaction } from '@azure/communication-calling'; import { _ComponentCallingHandlers } from './createHandlers'; +import { TogetherModeStreamViewResult } from '@internal/react-components/dist/dist-esm/types/TogetherModeTypes'; /** * Object containing all the handlers required for calling components. @@ -112,7 +113,7 @@ export interface CommonCallingHandlers { * * @beta */ - onCreateTogetherModeStreamView: (options?: VideoStreamOptions) => Promise; + onCreateTogetherModeStreamView: (options?: VideoStreamOptions) => Promise; /* @conditional-compile-remove(together-mode) */ /** * Call back to dispose together mode views @@ -732,7 +733,7 @@ export const createDefaultCommonCallingHandlers = memoizeOne( /* @conditional-compile-remove(together-mode) */ const onCreateTogetherModeStreamView = async ( options = { scalingMode: 'Fit', isMirrored: true } as VideoStreamOptions - ): Promise => { + ): Promise => { if (!call) { return; } @@ -741,7 +742,7 @@ export const createDefaultCommonCallingHandlers = memoizeOne( return; } const togetherModeStreams = callState.togetherMode.streams; - const createViewStreamResults: Map = new Map(); + const togetherModeCreateViewResult: TogetherModeStreamViewResult = {}; if (!togetherModeStreams.size) { const togetherModeFeature = call?.feature(Features.TogetherMode); await togetherModeFeature?.start(); @@ -749,15 +750,16 @@ export const createDefaultCommonCallingHandlers = memoizeOne( for (const stream of togetherModeStreams) { if (!stream[1].view) { const createViewResult = await callClient.createCallFeatureView(call.id, stream[1], options); - createViewStreamResults.set( - stream[0], - createViewResult?.view ? { view: createViewResult?.view } : undefined - ); + // SDK currently only supports 1 Video media stream type + if (stream[1].mediaStreamType === 'Video') { + togetherModeCreateViewResult.mainVideoView = createViewResult?.view + ? { view: createViewResult?.view } + : undefined; + } } } } - const createViewResult = createViewStreamResults.get('Video'); - return createViewResult?.view ? { view: createViewResult?.view } : undefined; + return togetherModeCreateViewResult; }; /* @conditional-compile-remove(together-mode) */ const onDisposeTogetherModeStreamViews = async (): Promise => { diff --git a/packages/communication-react/review/beta/communication-react.api.md b/packages/communication-react/review/beta/communication-react.api.md index bda360c5b04..62b98ed098b 100644 --- a/packages/communication-react/review/beta/communication-react.api.md +++ b/packages/communication-react/review/beta/communication-react.api.md @@ -111,6 +111,8 @@ import { TeamsIncomingCall } from '@azure/communication-calling'; import { TeamsMeetingIdLocator } from '@azure/communication-calling'; import { TeamsMeetingLinkLocator } from '@azure/communication-calling'; import { Theme } from '@fluentui/react'; +import { TogetherModeStreamViewResult } from '@internal/react-components/dist/dist-esm/types/TogetherModeTypes'; +import { TogetherModeStreamViewResult as TogetherModeStreamViewResult_2 } from '@internal/react-components/dist/dist-esm/types'; import { TransferEventArgs } from '@azure/communication-calling'; import { TypingIndicatorReceivedEvent } from '@azure/communication-chat'; import { UnknownIdentifier } from '@azure/communication-common'; @@ -441,16 +443,16 @@ export interface CallAdapterCallOperations { // (undocumented) addParticipant(participant: CommunicationUserIdentifier): Promise; allowUnsupportedBrowserVersion(): void; - // @beta - createFeatureStreamView(featureName: string, options?: VideoStreamOptions): Promise; createStreamView(remoteUserId?: string, options?: VideoStreamOptions): Promise; // @beta - disposeFeatureStreamView(featureName: string, options?: VideoStreamOptions): Promise; + createTogetherModeStreamViews(options?: VideoStreamOptions): Promise; disposeLocalVideoStreamView(): Promise; disposeRemoteVideoStreamView(remoteUserId: string): Promise; disposeScreenShareStreamView(remoteUserId: string): Promise; // @deprecated disposeStreamView(remoteUserId?: string, options?: VideoStreamOptions): Promise; + // @beta + disposeTogetherModeStreamViews(options?: VideoStreamOptions): Promise; holdCall(): Promise; leaveCall(forEveryone?: boolean): Promise; lowerHand(): Promise; @@ -1196,18 +1198,18 @@ export interface CallWithChatAdapterManagement { addParticipant(participant: CommunicationUserIdentifier): Promise; allowUnsupportedBrowserVersion(): void; askDevicePermission(constrain: PermissionConstraints): Promise; - // @beta - createFeatureStreamView(featureName: string, options?: VideoStreamOptions): Promise; createStreamView(remoteUserId?: string, options?: VideoStreamOptions): Promise; // @beta + createTogetherModeStreamViews(options?: VideoStreamOptions): Promise; + // @beta deleteImage(imageId: string): Promise; deleteMessage(messageId: string): Promise; - // @beta - disposeFeatureStreamView(featureName: string, options?: VideoStreamOptions): Promise; disposeLocalVideoStreamView(): Promise; disposeRemoteVideoStreamView(remoteUserId: string): Promise; disposeScreenShareStreamView(remoteUserId: string): Promise; disposeStreamView(remoteUserId?: string, options?: VideoStreamOptions): Promise; + // @beta + disposeTogetherModeStreamViews(): Promise; // (undocumented) downloadResourceToCache(resourceDetails: ResourceDetails): Promise; fetchInitialData(): Promise; @@ -2165,7 +2167,7 @@ export interface CommonCallingHandlers { // (undocumented) onCreateRemoteStreamView: (userId: string, options?: VideoStreamOptions) => Promise; // @beta - onCreateTogetherModeStreamView: (options?: VideoStreamOptions) => Promise; + onCreateTogetherModeStreamView: (options?: VideoStreamOptions) => Promise; // (undocumented) onDisposeLocalScreenShareStreamView: () => Promise; // (undocumented) diff --git a/packages/react-components/src/types/TogetherModeTypes.ts b/packages/react-components/src/types/TogetherModeTypes.ts new file mode 100644 index 00000000000..6f4a8c8c28f --- /dev/null +++ b/packages/react-components/src/types/TogetherModeTypes.ts @@ -0,0 +1,12 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +import { CreateVideoStreamViewResult } from './VideoGalleryParticipant'; + +/** + * Interface representing the result of a Together Mode stream view. + */ +export interface TogetherModeStreamViewResult { + mainVideoView?: CreateVideoStreamViewResult; + panomaricVideoView?: CreateVideoStreamViewResult; +} diff --git a/packages/react-components/src/types/index.ts b/packages/react-components/src/types/index.ts index 07b84e17660..a1ce7ab3381 100644 --- a/packages/react-components/src/types/index.ts +++ b/packages/react-components/src/types/index.ts @@ -15,3 +15,4 @@ export * from './SurveyIssuesHeadingStrings'; export * from './CallSurveyImprovementSuggestions'; export * from './ReactionTypes'; export * from './Attachment'; +export * from './TogetherModeTypes'; diff --git a/packages/react-composites/src/composites/CallComposite/MockCallAdapter.ts b/packages/react-composites/src/composites/CallComposite/MockCallAdapter.ts index 1130c637b78..e54013dce9e 100644 --- a/packages/react-composites/src/composites/CallComposite/MockCallAdapter.ts +++ b/packages/react-composites/src/composites/CallComposite/MockCallAdapter.ts @@ -111,7 +111,7 @@ export class _MockCallAdapter implements CallAdapter { throw Error('createStreamView not implemented'); } /* @conditional-compile-remove(together-mode) */ - createFeatureStreamView(): Promise { + createTogetherModeStreamViews(): Promise { throw Error('createFeatureStreamView not implemented'); } disposeStreamView(): Promise { @@ -127,7 +127,7 @@ export class _MockCallAdapter implements CallAdapter { return Promise.resolve(); } /* @conditional-compile-remove(together-mode) */ - disposeFeatureStreamView(): Promise { + disposeTogetherModeStreamViews(): Promise { return Promise.resolve(); } // eslint-disable-next-line @typescript-eslint/no-unused-vars diff --git a/packages/react-composites/src/composites/CallComposite/adapter/AzureCommunicationCallAdapter.ts b/packages/react-composites/src/composites/CallComposite/adapter/AzureCommunicationCallAdapter.ts index 6e0d21494f1..3265477fa88 100644 --- a/packages/react-composites/src/composites/CallComposite/adapter/AzureCommunicationCallAdapter.ts +++ b/packages/react-composites/src/composites/CallComposite/adapter/AzureCommunicationCallAdapter.ts @@ -124,6 +124,7 @@ import { CallingSoundSubscriber } from './CallingSoundSubscriber'; import { CallingSounds } from './CallAdapter'; /* @conditional-compile-remove(DNS) */ import { DeepNoiseSuppressionEffectDependency } from '@internal/calling-component-bindings'; +import { TogetherModeStreamViewResult } from '@internal/react-components/dist/dist-esm/types'; type CallTypeOf = AgentType extends CallAgent ? Call : TeamsCall; /** @@ -596,9 +597,9 @@ export class AzureCommunicationCallAdapter { - if (featureName === 'togetherMode') { - /* @conditional-compile-remove(together-mode) */ - return await this.handlers.onCreateTogetherModeStreamView(options); - } + ): Promise { + return await this.handlers.onCreateTogetherModeStreamView(options); } + /* @conditional-compile-remove(together-mode) */ - public async disposeFeatureStreamView(featureName: string): Promise { - if (featureName === 'togetherMode') { - /* @conditional-compile-remove(together-mode) */ - return await this.handlers.onDisposeTogetherModeStreamViews(); - } + public async disposeTogetherModeStreamViews(): Promise { + return await this.handlers.onDisposeTogetherModeStreamViews(); } public async leaveCall(forEveryone?: boolean): Promise { diff --git a/packages/react-composites/src/composites/CallComposite/adapter/CallAdapter.ts b/packages/react-composites/src/composites/CallComposite/adapter/CallAdapter.ts index 2db5bf91fc5..75c6016c592 100644 --- a/packages/react-composites/src/composites/CallComposite/adapter/CallAdapter.ts +++ b/packages/react-composites/src/composites/CallComposite/adapter/CallAdapter.ts @@ -48,6 +48,7 @@ import { CallSurvey, CallSurveyResponse } from '@azure/communication-calling'; import { ReactionResources } from '@internal/react-components'; /* @conditional-compile-remove(DNS) */ import { DeepNoiseSuppressionEffectDependency } from '@internal/calling-component-bindings'; +import { TogetherModeStreamViewResult } from '@internal/react-components/dist/dist-esm/types'; /** * Major UI screens shown in the {@link CallComposite}. @@ -645,10 +646,7 @@ export interface CallAdapterCallOperations { * * @beta */ - createFeatureStreamView( - featureName: string, - options?: VideoStreamOptions - ): Promise; + createTogetherModeStreamViews(options?: VideoStreamOptions): Promise; /* @conditional-compile-remove(together-mode) */ /** @@ -663,7 +661,7 @@ export interface CallAdapterCallOperations { * * @beta */ - disposeFeatureStreamView(featureName: string, options?: VideoStreamOptions): Promise; + disposeTogetherModeStreamViews(options?: VideoStreamOptions): Promise; /** * Dispose the html view for a screen share stream * diff --git a/packages/react-composites/src/composites/CallComposite/hooks/useHandlers.ts b/packages/react-composites/src/composites/CallComposite/hooks/useHandlers.ts index 7e6c569613c..d35e9cc6661 100644 --- a/packages/react-composites/src/composites/CallComposite/hooks/useHandlers.ts +++ b/packages/react-composites/src/composites/CallComposite/hooks/useHandlers.ts @@ -234,11 +234,11 @@ const createCompositeHandlers = memoizeOne( }, /* @conditional-compile-remove(together-mode) */ onCreateTogetherModeStreamView: async (options) => { - return await adapter.createFeatureStreamView('togetherMode', options); + return await adapter.createTogetherModeStreamViews(options); }, /* @conditional-compile-remove(together-mode) */ onDisposeTogetherModeStreamViews: async () => { - return await adapter.disposeFeatureStreamView('togetherMode'); + return await adapter.disposeTogetherModeStreamViews(); } }; } diff --git a/packages/react-composites/src/composites/CallWithChatComposite/adapter/AzureCommunicationCallWithChatAdapter.ts b/packages/react-composites/src/composites/CallWithChatComposite/adapter/AzureCommunicationCallWithChatAdapter.ts index 39b19538588..f5b8ce351ce 100644 --- a/packages/react-composites/src/composites/CallWithChatComposite/adapter/AzureCommunicationCallWithChatAdapter.ts +++ b/packages/react-composites/src/composites/CallWithChatComposite/adapter/AzureCommunicationCallWithChatAdapter.ts @@ -108,6 +108,7 @@ import { VideoBackgroundImage, VideoBackgroundEffect } from '../../CallComposite import { CallSurvey, CallSurveyResponse } from '@azure/communication-calling'; /* @conditional-compile-remove(breakout-rooms) */ import { busyWait } from '../../common/utils'; +import { TogetherModeStreamViewResult } from '@internal/react-components/dist/dist-esm/types'; type CallWithChatAdapterStateChangedHandler = (newState: CallWithChatAdapterState) => void; @@ -527,15 +528,14 @@ export class AzureCommunicationCallWithChatAdapter implements CallWithChatAdapte await this.callAdapter.disposeLocalVideoStreamView(); } /* @conditional-compile-remove(together-mode) */ - public async createFeatureStreamView( - featureName: string, + public async createTogetherModeStreamViews( options?: VideoStreamOptions - ): Promise { - return await this.callAdapter.createFeatureStreamView(featureName, options); + ): Promise { + return await this.callAdapter.createTogetherModeStreamViews(options); } /* @conditional-compile-remove(together-mode) */ - public async disposeFeatureStreamView(featureName: string, options?: VideoStreamOptions): Promise { - await this.callAdapter.disposeFeatureStreamView(featureName, options); + public async disposeTogetherModeStreamViews(): Promise { + await this.callAdapter.disposeTogetherModeStreamViews(); } /** Fetch initial Call and Chat data such as chat messages. */ public async fetchInitialData(): Promise { diff --git a/packages/react-composites/src/composites/CallWithChatComposite/adapter/CallWithChatAdapter.ts b/packages/react-composites/src/composites/CallWithChatComposite/adapter/CallWithChatAdapter.ts index 7e0dd3ff3ab..2967dfc5a19 100644 --- a/packages/react-composites/src/composites/CallWithChatComposite/adapter/CallWithChatAdapter.ts +++ b/packages/react-composites/src/composites/CallWithChatComposite/adapter/CallWithChatAdapter.ts @@ -68,6 +68,7 @@ import { SpotlightChangedListener } from '../../CallComposite/adapter/CallAdapte import { VideoBackgroundImage, VideoBackgroundEffect } from '../../CallComposite'; import { CallSurvey, CallSurveyResponse } from '@azure/communication-calling'; +import { TogetherModeStreamViewResult } from '@internal/react-components/dist/dist-esm/types'; /** * Functionality for managing the current call with chat. @@ -233,15 +234,11 @@ export interface CallWithChatAdapterManagement { * @remarks * This method is implemented for composite * - * @param featureName - Name of feature to render * @param options - Options to control how video streams are rendered {@link @azure/communication-calling#VideoStreamOptions } * * @beta */ - createFeatureStreamView( - featureName: string, - options?: VideoStreamOptions - ): Promise; + createTogetherModeStreamViews(options?: VideoStreamOptions): Promise; /* @conditional-compile-remove(together-mode) */ /** @@ -250,13 +247,9 @@ export interface CallWithChatAdapterManagement { * @remarks * This method is implemented for composite * - * - * @param featureName - Name of the feature to dispose - * @param options - Options to control how video streams are rendered {@link @azure/communication-calling#VideoStreamOptions } - * * @beta */ - disposeFeatureStreamView(featureName: string, options?: VideoStreamOptions): Promise; + disposeTogetherModeStreamViews(): Promise; /** * Dispose the html view for a screen share stream * diff --git a/packages/react-composites/src/composites/CallWithChatComposite/adapter/CallWithChatBackedCallAdapter.ts b/packages/react-composites/src/composites/CallWithChatComposite/adapter/CallWithChatBackedCallAdapter.ts index ce066cc9e8e..4e94cd97239 100644 --- a/packages/react-composites/src/composites/CallWithChatComposite/adapter/CallWithChatBackedCallAdapter.ts +++ b/packages/react-composites/src/composites/CallWithChatComposite/adapter/CallWithChatBackedCallAdapter.ts @@ -27,6 +27,7 @@ import { StopCaptionsAdapterOptions } from '../../CallComposite/adapter/CallAdapter'; import { CallSurvey, CallSurveyResponse } from '@azure/communication-calling'; +import { TogetherModeStreamViewResult } from '@internal/react-components/dist/dist-esm/types'; /* eslint-disable @typescript-eslint/no-explicit-any */ /* eslint-disable @typescript-eslint/explicit-module-boundary-types */ @@ -135,11 +136,10 @@ export class CallWithChatBackedCallAdapter implements CallAdapter { ): Promise => await this.callWithChatAdapter.createStreamView(remoteUserId, options); /* @conditional-compile-remove(together-mode) */ - public createFeatureStreamView = async ( - featureName: string, + public createTogetherModeStreamViews = async ( options?: VideoStreamOptions - ): Promise => - await this.callWithChatAdapter.createFeatureStreamView(featureName, options); + ): Promise => + await this.callWithChatAdapter.createTogetherModeStreamViews(options); public disposeStreamView = async (remoteUserId?: string, options?: VideoStreamOptions): Promise => await this.callWithChatAdapter.disposeStreamView(remoteUserId, options); public disposeScreenShareStreamView(remoteUserId: string): Promise { @@ -152,8 +152,8 @@ export class CallWithChatBackedCallAdapter implements CallAdapter { return this.callWithChatAdapter.disposeLocalVideoStreamView(); } /* @conditional-compile-remove(together-mode) */ - public disposeFeatureStreamView = async (featureName: string, options?: VideoStreamOptions): Promise => - await this.callWithChatAdapter.disposeStreamView(featureName, options); + public disposeTogetherModeStreamViews = async (): Promise => + await this.callWithChatAdapter.disposeTogetherModeStreamViews(); public holdCall = async (): Promise => { await this.callWithChatAdapter.holdCall(); }; diff --git a/packages/react-composites/tests/app/lib/MockCallAdapter.ts b/packages/react-composites/tests/app/lib/MockCallAdapter.ts index 4c578083bde..f954b7dc770 100644 --- a/packages/react-composites/tests/app/lib/MockCallAdapter.ts +++ b/packages/react-composites/tests/app/lib/MockCallAdapter.ts @@ -91,10 +91,10 @@ export class MockCallAdapter implements CallAdapter { disposeStreamView(): Promise { throw Error('disposeStreamView not implemented'); } - createFeatureStreamView(): Promise { + createTogetherModeStreamViews(): Promise { throw Error('createFeatureStreamView not implemented'); } - disposeFeatureStreamView(): Promise { + disposeTogetherModeStreamViews(): Promise { throw Error('disposeFeatureStreamView not implemented'); } disposeScreenShareStreamView(): Promise { From 13549113a33a28bf28de531f84f8aa00fba2a83b Mon Sep 17 00:00:00 2001 From: Chukwuebuka Nwankwo Date: Wed, 9 Oct 2024 17:24:16 +0000 Subject: [PATCH 10/17] Updated conditional tags --- .../src/handlers/createCommonHandlers.ts | 1 + .../CallComposite/adapter/AzureCommunicationCallAdapter.ts | 1 + .../src/composites/CallComposite/adapter/CallAdapter.ts | 1 + .../adapter/AzureCommunicationCallWithChatAdapter.ts | 1 + .../CallWithChatComposite/adapter/CallWithChatAdapter.ts | 1 + .../adapter/CallWithChatBackedCallAdapter.ts | 1 + 6 files changed, 6 insertions(+) diff --git a/packages/calling-component-bindings/src/handlers/createCommonHandlers.ts b/packages/calling-component-bindings/src/handlers/createCommonHandlers.ts index 9f9336b1441..6076909cca0 100644 --- a/packages/calling-component-bindings/src/handlers/createCommonHandlers.ts +++ b/packages/calling-component-bindings/src/handlers/createCommonHandlers.ts @@ -35,6 +35,7 @@ import { Features } from '@azure/communication-calling'; import { TeamsCaptions } from '@azure/communication-calling'; import { Reaction } from '@azure/communication-calling'; import { _ComponentCallingHandlers } from './createHandlers'; +/* @conditional-compile-remove(together-mode) */ import { TogetherModeStreamViewResult } from '@internal/react-components/dist/dist-esm/types/TogetherModeTypes'; /** diff --git a/packages/react-composites/src/composites/CallComposite/adapter/AzureCommunicationCallAdapter.ts b/packages/react-composites/src/composites/CallComposite/adapter/AzureCommunicationCallAdapter.ts index 3265477fa88..3691a3ca93c 100644 --- a/packages/react-composites/src/composites/CallComposite/adapter/AzureCommunicationCallAdapter.ts +++ b/packages/react-composites/src/composites/CallComposite/adapter/AzureCommunicationCallAdapter.ts @@ -124,6 +124,7 @@ import { CallingSoundSubscriber } from './CallingSoundSubscriber'; import { CallingSounds } from './CallAdapter'; /* @conditional-compile-remove(DNS) */ import { DeepNoiseSuppressionEffectDependency } from '@internal/calling-component-bindings'; +/* @conditional-compile-remove(together-mode) */ import { TogetherModeStreamViewResult } from '@internal/react-components/dist/dist-esm/types'; type CallTypeOf = AgentType extends CallAgent ? Call : TeamsCall; diff --git a/packages/react-composites/src/composites/CallComposite/adapter/CallAdapter.ts b/packages/react-composites/src/composites/CallComposite/adapter/CallAdapter.ts index 75c6016c592..9238fd7021f 100644 --- a/packages/react-composites/src/composites/CallComposite/adapter/CallAdapter.ts +++ b/packages/react-composites/src/composites/CallComposite/adapter/CallAdapter.ts @@ -48,6 +48,7 @@ import { CallSurvey, CallSurveyResponse } from '@azure/communication-calling'; import { ReactionResources } from '@internal/react-components'; /* @conditional-compile-remove(DNS) */ import { DeepNoiseSuppressionEffectDependency } from '@internal/calling-component-bindings'; +/* @conditional-compile-remove(together-mode) */ import { TogetherModeStreamViewResult } from '@internal/react-components/dist/dist-esm/types'; /** diff --git a/packages/react-composites/src/composites/CallWithChatComposite/adapter/AzureCommunicationCallWithChatAdapter.ts b/packages/react-composites/src/composites/CallWithChatComposite/adapter/AzureCommunicationCallWithChatAdapter.ts index f5b8ce351ce..af1fc3af2e2 100644 --- a/packages/react-composites/src/composites/CallWithChatComposite/adapter/AzureCommunicationCallWithChatAdapter.ts +++ b/packages/react-composites/src/composites/CallWithChatComposite/adapter/AzureCommunicationCallWithChatAdapter.ts @@ -108,6 +108,7 @@ import { VideoBackgroundImage, VideoBackgroundEffect } from '../../CallComposite import { CallSurvey, CallSurveyResponse } from '@azure/communication-calling'; /* @conditional-compile-remove(breakout-rooms) */ import { busyWait } from '../../common/utils'; +/* @conditional-compile-remove(together-mode) */ import { TogetherModeStreamViewResult } from '@internal/react-components/dist/dist-esm/types'; type CallWithChatAdapterStateChangedHandler = (newState: CallWithChatAdapterState) => void; diff --git a/packages/react-composites/src/composites/CallWithChatComposite/adapter/CallWithChatAdapter.ts b/packages/react-composites/src/composites/CallWithChatComposite/adapter/CallWithChatAdapter.ts index 2967dfc5a19..22c7961c71c 100644 --- a/packages/react-composites/src/composites/CallWithChatComposite/adapter/CallWithChatAdapter.ts +++ b/packages/react-composites/src/composites/CallWithChatComposite/adapter/CallWithChatAdapter.ts @@ -68,6 +68,7 @@ import { SpotlightChangedListener } from '../../CallComposite/adapter/CallAdapte import { VideoBackgroundImage, VideoBackgroundEffect } from '../../CallComposite'; import { CallSurvey, CallSurveyResponse } from '@azure/communication-calling'; +/* @conditional-compile-remove(together-mode) */ import { TogetherModeStreamViewResult } from '@internal/react-components/dist/dist-esm/types'; /** diff --git a/packages/react-composites/src/composites/CallWithChatComposite/adapter/CallWithChatBackedCallAdapter.ts b/packages/react-composites/src/composites/CallWithChatComposite/adapter/CallWithChatBackedCallAdapter.ts index 4e94cd97239..f3c85d402ab 100644 --- a/packages/react-composites/src/composites/CallWithChatComposite/adapter/CallWithChatBackedCallAdapter.ts +++ b/packages/react-composites/src/composites/CallWithChatComposite/adapter/CallWithChatBackedCallAdapter.ts @@ -27,6 +27,7 @@ import { StopCaptionsAdapterOptions } from '../../CallComposite/adapter/CallAdapter'; import { CallSurvey, CallSurveyResponse } from '@azure/communication-calling'; +/* @conditional-compile-remove(together-mode) */ import { TogetherModeStreamViewResult } from '@internal/react-components/dist/dist-esm/types'; /* eslint-disable @typescript-eslint/no-explicit-any */ From 3b61bad82152c44aba1c3b00fc9e2ab81fe6b242 Mon Sep 17 00:00:00 2001 From: Chukwuebuka Nwankwo Date: Thu, 10 Oct 2024 17:48:03 +0000 Subject: [PATCH 11/17] Updated call feature streams --- .../src/CallClientState.ts | 8 +- .../src/index-public.ts | 3 +- .../review/beta/communication-react.api.md | 181 +++--------------- .../CallComposite/adapter/CallAdapter.ts | 2 +- .../CallComposite/components/CallControls.tsx | 2 +- 5 files changed, 40 insertions(+), 156 deletions(-) diff --git a/packages/calling-stateful-client/src/CallClientState.ts b/packages/calling-stateful-client/src/CallClientState.ts index bbc3523d274..c36dbded607 100644 --- a/packages/calling-stateful-client/src/CallClientState.ts +++ b/packages/calling-stateful-client/src/CallClientState.ts @@ -291,12 +291,18 @@ export interface TogetherModeCallFeatureState { seatingCoordinates: Map; } +/* @conditional-compile-remove(together-mode) */ +/** + * @beta + */ +export type CallFeatureStreamName = 'togetherMode'; + /* @conditional-compile-remove(together-mode) */ /** * @beta */ export interface CallFeatureStreamState { - feature: 'togetherMode'; + feature: CallFeatureStreamName; } /* @conditional-compile-remove(together-mode) */ diff --git a/packages/calling-stateful-client/src/index-public.ts b/packages/calling-stateful-client/src/index-public.ts index 7d31ac64f1e..b19c30351b3 100644 --- a/packages/calling-stateful-client/src/index-public.ts +++ b/packages/calling-stateful-client/src/index-public.ts @@ -37,7 +37,8 @@ export type { TogetherModeCallFeatureState as TogetherModeCallFeature } from './ export type { CallFeatureStreamState, TogetherModeStreamState, - TogetherModeSeatingCoordinatesState + TogetherModeSeatingCoordinatesState, + CallFeatureStreamName } from './CallClientState'; export type { RaisedHandState } from './CallClientState'; diff --git a/packages/communication-react/review/beta/communication-react.api.md b/packages/communication-react/review/beta/communication-react.api.md index 62b98ed098b..c12c17219ed 100644 --- a/packages/communication-react/review/beta/communication-react.api.md +++ b/packages/communication-react/review/beta/communication-react.api.md @@ -8,7 +8,6 @@ import { AddPhoneNumberOptions } from '@azure/communication-calling'; import { AudioDeviceInfo } from '@azure/communication-calling'; -import { AudioEffectsStartConfig } from '@azure/communication-calling'; import { BackgroundBlurConfig } from '@azure/communication-calling'; import { BackgroundBlurEffect } from '@azure/communication-calling'; import { BackgroundReplacementConfig } from '@azure/communication-calling'; @@ -45,9 +44,6 @@ import type { CommunicationUserKind } from '@azure/communication-common'; import { CreateViewOptions } from '@azure/communication-calling'; import { DeviceAccess } from '@azure/communication-calling'; import { DeviceManager } from '@azure/communication-calling'; -import type { DiagnosticFlag } from '@azure/communication-calling'; -import type { DiagnosticQuality } from '@azure/communication-calling'; -import type { DiagnosticValueType } from '@azure/communication-calling'; import { DominantSpeakersInfo } from '@azure/communication-calling'; import { DtmfTone as DtmfTone_2 } from '@azure/communication-calling'; import { EnvironmentInfo } from '@azure/communication-calling'; @@ -76,12 +72,10 @@ import { LatestNetworkDiagnostics } from '@azure/communication-calling'; import { LocalRecordingInfo } from '@azure/communication-calling'; import { LocalVideoStream } from '@azure/communication-calling'; import type { MediaDiagnosticChangedEventArgs } from '@azure/communication-calling'; -import type { MediaDiagnosticType } from '@azure/communication-calling'; import { MediaStreamType } from '@azure/communication-calling'; import { MicrosoftTeamsAppIdentifier } from '@azure/communication-common'; import { MicrosoftTeamsUserIdentifier } from '@azure/communication-common'; import type { NetworkDiagnosticChangedEventArgs } from '@azure/communication-calling'; -import type { NetworkDiagnosticType } from '@azure/communication-calling'; import { PartialTheme } from '@fluentui/react'; import { ParticipantCapabilities } from '@azure/communication-calling'; import { ParticipantRole } from '@azure/communication-calling'; @@ -100,7 +94,6 @@ import { RemoteParticipantState as RemoteParticipantState_2 } from '@azure/commu import { RoomCallLocator } from '@azure/communication-calling'; import { ScalingMode } from '@azure/communication-calling'; import { SendMessageOptions } from '@azure/communication-chat'; -import type { ServerDiagnosticType } from '@azure/communication-calling'; import { SpotlightedParticipant } from '@azure/communication-calling'; import { StartCallOptions } from '@azure/communication-calling'; import { StartCaptionsOptions } from '@azure/communication-calling'; @@ -111,8 +104,6 @@ import { TeamsIncomingCall } from '@azure/communication-calling'; import { TeamsMeetingIdLocator } from '@azure/communication-calling'; import { TeamsMeetingLinkLocator } from '@azure/communication-calling'; import { Theme } from '@fluentui/react'; -import { TogetherModeStreamViewResult } from '@internal/react-components/dist/dist-esm/types/TogetherModeTypes'; -import { TogetherModeStreamViewResult as TogetherModeStreamViewResult_2 } from '@internal/react-components/dist/dist-esm/types'; import { TransferEventArgs } from '@azure/communication-calling'; import { TypingIndicatorReceivedEvent } from '@azure/communication-chat'; import { UnknownIdentifier } from '@azure/communication-common'; @@ -444,15 +435,11 @@ export interface CallAdapterCallOperations { addParticipant(participant: CommunicationUserIdentifier): Promise; allowUnsupportedBrowserVersion(): void; createStreamView(remoteUserId?: string, options?: VideoStreamOptions): Promise; - // @beta - createTogetherModeStreamViews(options?: VideoStreamOptions): Promise; disposeLocalVideoStreamView(): Promise; disposeRemoteVideoStreamView(remoteUserId: string): Promise; disposeScreenShareStreamView(remoteUserId: string): Promise; // @deprecated disposeStreamView(remoteUserId?: string, options?: VideoStreamOptions): Promise; - // @beta - disposeTogetherModeStreamViews(options?: VideoStreamOptions): Promise; holdCall(): Promise; leaveCall(forEveryone?: boolean): Promise; lowerHand(): Promise; @@ -470,16 +457,12 @@ export interface CallAdapterCallOperations { setSpokenLanguage(language: string): Promise; startCamera(options?: VideoStreamOptions): Promise; startCaptions(options?: StartCaptionsAdapterOptions): Promise; - // @beta - startNoiseSuppressionEffect(): Promise; startScreenShare(): Promise; startSpotlight(userIds?: string[]): Promise; startVideoBackgroundEffect(videoBackgroundEffect: VideoBackgroundEffect): Promise; stopAllSpotlight(): Promise; stopCamera(): Promise; stopCaptions(options?: StopCaptionsAdapterOptions): Promise; - // @beta - stopNoiseSuppressionEffect(): Promise; stopScreenShare(): Promise; stopSpotlight(userIds?: string[]): Promise; stopVideoBackgroundEffects(): Promise; @@ -507,9 +490,6 @@ export type CallAdapterClientState = { cameraStatus?: 'On' | 'Off'; videoBackgroundImages?: VideoBackgroundImage[]; onResolveVideoEffectDependency?: () => Promise; - onResolveDeepNoiseSuppressionDependency?: () => Promise; - deepNoiseSuppressionOnByDefault?: boolean; - hideDeepNoiseSuppressionButton?: boolean; selectedVideoBackgroundEffect?: VideoBackgroundEffect; acceptedTransferCallState?: CallState; hideAttendeeNames?: boolean; @@ -529,7 +509,7 @@ export interface CallAdapterDeviceManagement { } // @public -export type CallAdapterLocator = TeamsMeetingLinkLocator | GroupCallLocator | RoomCallLocator | /* @conditional-compile-remove(call-participants-locator) */ CallParticipantsLocator | TeamsMeetingIdLocator; +export type CallAdapterLocator = TeamsMeetingLinkLocator | GroupCallLocator | RoomCallLocator | CallParticipantsLocator | TeamsMeetingIdLocator; // @public export type CallAdapterState = CallAdapterUiState & CallAdapterClientState; @@ -595,7 +575,7 @@ export const CallAgentProvider: (props: CallAgentProviderProps) => JSX.Element; // @public export interface CallAgentProviderProps { // (undocumented) - callAgent?: CallAgent | /* @conditional-compile-remove(teams-identity-support) */ TeamsCallAgent; + callAgent?: CallAgent | TeamsCallAgent; // (undocumented) children: React_2.ReactNode; } @@ -607,7 +587,7 @@ export interface CallAgentState { // @public export interface CallAndChatLocator { - callLocator: GroupCallLocator | /* @conditional-compile-remove(call-participants-locator) */ CallParticipantsLocator; + callLocator: GroupCallLocator | CallParticipantsLocator; chatThreadId: string; } @@ -776,7 +756,7 @@ export type CallCompositeOptions = { }; // @public -export type CallCompositePage = 'accessDeniedTeamsMeeting' | 'call' | 'configuration' | 'hold' | 'joinCallFailedDueToNoNetwork' | 'leftCall' | 'leaving' | 'lobby' | 'removedFromCall' | /* @conditional-compile-remove(unsupported-browser) */ 'unsupportedEnvironment' | 'transferring' | 'badRequest'; +export type CallCompositePage = 'accessDeniedTeamsMeeting' | 'call' | 'configuration' | 'hold' | 'joinCallFailedDueToNoNetwork' | 'leftCall' | 'leaving' | 'lobby' | 'removedFromCall' | 'unsupportedEnvironment' | 'transferring' | 'badRequest'; // @public export interface CallCompositeProps extends BaseCompositeProps { @@ -1032,13 +1012,7 @@ export type CallErrors = { }; // @public -export type CallErrorTarget = 'Call.addParticipant' | 'Call.dispose' | 'Call.feature' | 'Call.hangUp' | 'Call.hold' | 'Call.mute' | 'Call.muteIncomingAudio' | 'Call.off' | 'Call.on' | 'Call.removeParticipant' | 'Call.resume' | 'Call.sendDtmf' | 'Call.startAudio' | 'Call.startScreenSharing' | 'Call.startVideo' | 'Call.stopScreenSharing' | 'Call.stopAudio' | 'Call.stopVideo' | 'Call.unmute' | 'Call.unmuteIncomingAudio' | 'CallAgent.dispose' | 'CallAgent.feature' | 'CallAgent.join' | 'CallAgent.off' | 'CallAgent.on' | 'CallAgent.startCall' | 'CallClient.createCallAgent' | 'CallClient.createTeamsCallAgent' | 'CallClient.feature' | 'CallClient.getDeviceManager' | /* @conditional-compile-remove(calling-beta-sdk) */ 'CallClient.getEnvironmentInfo' | 'DeviceManager.askDevicePermission' | 'DeviceManager.getCameras' | 'DeviceManager.getMicrophones' | 'DeviceManager.getSpeakers' | 'DeviceManager.off' | 'DeviceManager.on' | 'DeviceManager.selectMicrophone' | 'DeviceManager.selectSpeaker' | 'IncomingCall.accept' | 'IncomingCall.reject' | /* @conditional-compile-remove(calling-beta-sdk) */ /* @conditional-compile-remove(teams-identity-support) */ 'TeamsCall.addParticipant' | 'VideoEffectsFeature.startEffects' | /* @conditional-compile-remove(calling-beta-sdk) */ 'CallAgent.handlePushNotification' | /* @conditional-compile-remove(calling-beta-sdk) */ 'Call.admit' | /* @conditional-compile-remove(calling-beta-sdk) */ 'Call.rejectParticipant' | /* @conditional-compile-remove(calling-beta-sdk) */ 'Call.admitAll' | /* @conditional-compile-remove(soft-mute) */ 'Call.mutedByOthers' | 'Call.muteAllRemoteParticipants' | 'Call.setConstraints'; - -// @beta (undocumented) -export interface CallFeatureStreamState { - // (undocumented) - feature: 'togetherMode'; -} +export type CallErrorTarget = 'Call.addParticipant' | 'Call.dispose' | 'Call.feature' | 'Call.hangUp' | 'Call.hold' | 'Call.mute' | 'Call.muteIncomingAudio' | 'Call.off' | 'Call.on' | 'Call.removeParticipant' | 'Call.resume' | 'Call.sendDtmf' | 'Call.startAudio' | 'Call.startScreenSharing' | 'Call.startVideo' | 'Call.stopScreenSharing' | 'Call.stopAudio' | 'Call.stopVideo' | 'Call.unmute' | 'Call.unmuteIncomingAudio' | 'CallAgent.dispose' | 'CallAgent.feature' | 'CallAgent.join' | 'CallAgent.off' | 'CallAgent.on' | 'CallAgent.startCall' | 'CallClient.createCallAgent' | 'CallClient.createTeamsCallAgent' | 'CallClient.feature' | 'CallClient.getDeviceManager' | 'CallClient.getEnvironmentInfo' | 'DeviceManager.askDevicePermission' | 'DeviceManager.getCameras' | 'DeviceManager.getMicrophones' | 'DeviceManager.getSpeakers' | 'DeviceManager.off' | 'DeviceManager.on' | 'DeviceManager.selectMicrophone' | 'DeviceManager.selectSpeaker' | 'IncomingCall.accept' | 'IncomingCall.reject' | 'TeamsCall.addParticipant' | 'VideoEffectsFeature.startEffects' | 'CallAgent.handlePushNotification' | 'Call.admit' | 'Call.rejectParticipant' | 'Call.admitAll' | 'Call.mutedByOthers' | 'Call.muteAllRemoteParticipants' | 'Call.setConstraints'; // @public export type CallIdChangedListener = (event: { @@ -1067,7 +1041,6 @@ export interface CallingHandlers extends CommonCallingHandlers { // @public export type CallingHandlersOptions = { onResolveVideoBackgroundEffectsDependency?: () => Promise; - onResolveDeepNoiseSuppressionDependency?: () => Promise; }; // @public @@ -1128,7 +1101,7 @@ export const CallProvider: (props: CallProviderProps) => JSX.Element; // @public export interface CallProviderProps { // (undocumented) - call?: Call | /* @conditional-compile-remove(teams-identity-support) */ TeamsCall; + call?: Call | TeamsCall; // (undocumented) children: React_2.ReactNode; } @@ -1147,7 +1120,7 @@ export interface CallState { endTime: Date | undefined; hideAttendeeNames?: boolean; id: string; - info?: TeamsCallInfo | /* @conditional-compile-remove(calling-beta-sdk) */ CallInfo; + info?: TeamsCallInfo | CallInfo; isMuted: boolean; isScreenSharingOn: boolean; kind: CallKind; @@ -1172,8 +1145,6 @@ export interface CallState { spotlight?: SpotlightCallFeatureState; startTime: Date; state: CallState_2; - // @beta - togetherMode: TogetherModeCallFeature; totalParticipantCount?: number; transcription: TranscriptionCallFeature; transfer: TransferFeature; @@ -1200,16 +1171,12 @@ export interface CallWithChatAdapterManagement { askDevicePermission(constrain: PermissionConstraints): Promise; createStreamView(remoteUserId?: string, options?: VideoStreamOptions): Promise; // @beta - createTogetherModeStreamViews(options?: VideoStreamOptions): Promise; - // @beta deleteImage(imageId: string): Promise; deleteMessage(messageId: string): Promise; disposeLocalVideoStreamView(): Promise; disposeRemoteVideoStreamView(remoteUserId: string): Promise; disposeScreenShareStreamView(remoteUserId: string): Promise; disposeStreamView(remoteUserId?: string, options?: VideoStreamOptions): Promise; - // @beta - disposeTogetherModeStreamViews(): Promise; // (undocumented) downloadResourceToCache(resourceDetails: ResourceDetails): Promise; fetchInitialData(): Promise; @@ -1235,7 +1202,7 @@ export interface CallWithChatAdapterManagement { resumeCall(): Promise; returnFromBreakoutRoom(): Promise; sendDtmfTone: (dtmfTone: DtmfTone_2) => Promise; - sendMessage(content: string, options?: SendMessageOptions | /* @conditional-compile-remove(file-sharing-acs) */ MessageOptions): Promise; + sendMessage(content: string, options?: SendMessageOptions | MessageOptions): Promise; sendReadReceipt(chatMessageId: string): Promise; sendTypingIndicator(): Promise; setCamera(sourceInfo: VideoDeviceInfo, options?: VideoStreamOptions): Promise; @@ -1247,23 +1214,19 @@ export interface CallWithChatAdapterManagement { startCall(participants: (MicrosoftTeamsAppIdentifier | PhoneNumberIdentifier | CommunicationUserIdentifier | MicrosoftTeamsUserIdentifier | UnknownIdentifier)[], options?: StartCallOptions): Call | undefined; startCamera(options?: VideoStreamOptions): Promise; startCaptions(options?: StartCaptionsAdapterOptions): Promise; - // @beta - startNoiseSuppressionEffect(): Promise; startScreenShare(): Promise; startSpotlight(userIds?: string[]): Promise; startVideoBackgroundEffect(videoBackgroundEffect: VideoBackgroundEffect): Promise; stopAllSpotlight(): Promise; stopCamera(): Promise; stopCaptions(options?: StopCaptionsAdapterOptions): Promise; - // @beta - stopNoiseSuppressionEffect(): Promise; stopScreenShare(): Promise; stopSpotlight(userIds?: string[]): Promise; stopVideoBackgroundEffects(): Promise; submitSurvey(survey: CallSurvey): Promise; unmute(): Promise; updateBackgroundPickerImages(backgroundImages: VideoBackgroundImage[]): void; - updateMessage(messageId: string, content: string, options?: Record | /* @conditional-compile-remove(file-sharing-acs) */ MessageOptions): Promise; + updateMessage(messageId: string, content: string, options?: Record | MessageOptions): Promise; updateSelectedVideoBackgroundEffect(selectedVideoBackground: VideoBackgroundEffect): void; // @beta uploadImage(image: Blob, imageFilename: string): Promise; @@ -1398,21 +1361,15 @@ export interface CallWithChatClientState { alternateCallerId?: string; call?: CallState; chat?: ChatThreadClientState; - // @beta - deepNoiseSuppressionOnByDefault?: boolean; devices: DeviceManagerState; displayName: string | undefined; environmentInfo?: EnvironmentInfo; hideAttendeeNames?: boolean; - // @beta - hideDeepNoiseSuppressionButton?: boolean; isTeamsCall: boolean; isTeamsMeeting: boolean; latestCallErrors: AdapterErrors; latestCallNotifications: AdapterNotifications; latestChatErrors: AdapterErrors; - // @beta - onResolveDeepNoiseSuppressionDependency?: () => Promise; onResolveVideoEffectDependency?: () => Promise; reactions?: ReactionResources; selectedVideoBackgroundEffect?: VideoBackgroundEffect; @@ -1602,7 +1559,7 @@ export interface CallWithChatControlOptions extends CommonCallControlOptions { } // @public -export type CallWithChatEvent = 'callError' | 'chatError' | 'callEnded' | 'isMutedChanged' | 'callIdChanged' | 'isLocalScreenSharingActiveChanged' | 'displayNameChanged' | 'isSpeakingChanged' | 'callParticipantsJoined' | 'callParticipantsLeft' | 'selectedMicrophoneChanged' | 'selectedSpeakerChanged' | 'isCaptionsActiveChanged' | 'captionsReceived' | 'isCaptionLanguageChanged' | 'isSpokenLanguageChanged' | 'capabilitiesChanged' | 'spotlightChanged' | /* @conditional-compile-remove(breakout-rooms) */ 'breakoutRoomsUpdated' | 'messageReceived' | 'messageEdited' | 'messageDeleted' | 'messageSent' | 'messageRead' | 'chatParticipantsAdded' | 'chatParticipantsRemoved' | 'chatInitialized'; +export type CallWithChatEvent = 'callError' | 'chatError' | 'callEnded' | 'isMutedChanged' | 'callIdChanged' | 'isLocalScreenSharingActiveChanged' | 'displayNameChanged' | 'isSpeakingChanged' | 'callParticipantsJoined' | 'callParticipantsLeft' | 'selectedMicrophoneChanged' | 'selectedSpeakerChanged' | 'isCaptionsActiveChanged' | 'captionsReceived' | 'isCaptionLanguageChanged' | 'isSpokenLanguageChanged' | 'capabilitiesChanged' | 'spotlightChanged' | 'breakoutRoomsUpdated' | 'messageReceived' | 'messageEdited' | 'messageDeleted' | 'messageSent' | 'messageRead' | 'chatParticipantsAdded' | 'chatParticipantsRemoved' | 'chatInitialized'; // @beta export const CameraAndMicrophoneSitePermissions: (props: CameraAndMicrophoneSitePermissionsProps) => JSX.Element; @@ -1856,11 +1813,11 @@ export interface ChatAdapterThreadManagement { loadPreviousChatMessages(messagesToLoad: number): Promise; removeParticipant(userId: string): Promise; removeResourceFromCache(resourceDetails: ResourceDetails): void; - sendMessage(content: string, options?: SendMessageOptions | /* @conditional-compile-remove(file-sharing-acs) */ MessageOptions): Promise; + sendMessage(content: string, options?: SendMessageOptions | MessageOptions): Promise; sendReadReceipt(chatMessageId: string): Promise; sendTypingIndicator(): Promise; setTopic(topicName: string): Promise; - updateMessage(messageId: string, content: string, options?: Record | /* @conditional-compile-remove(file-sharing-acs) */ MessageOptions): Promise; + updateMessage(messageId: string, content: string, options?: Record | MessageOptions): Promise; uploadImage(image: Blob, imageFilename: string): Promise; } @@ -1870,7 +1827,7 @@ export type ChatAdapterUiState = { }; // @public -export type ChatAttachmentType = 'unknown' | 'image' | /* @conditional-compile-remove(file-sharing-teams-interop) @conditional-compile-remove(file-sharing-acs) */ 'file'; +export type ChatAttachmentType = 'unknown' | 'image' | 'file'; // @public export type ChatBaseSelectorProps = { @@ -1972,11 +1929,11 @@ export type ChatErrors = { }; // @public -export type ChatErrorTarget = 'ChatClient.createChatThread' | 'ChatClient.deleteChatThread' | 'ChatClient.getChatThreadClient' | 'ChatClient.listChatThreads' | 'ChatClient.off' | 'ChatClient.on' | 'ChatClient.startRealtimeNotifications' | 'ChatClient.stopRealtimeNotifications' | 'ChatThreadClient.addParticipants' | 'ChatThreadClient.deleteMessage' | 'ChatThreadClient.getMessage' | 'ChatThreadClient.getProperties' | 'ChatThreadClient.listMessages' | 'ChatThreadClient.listParticipants' | 'ChatThreadClient.listReadReceipts' | 'ChatThreadClient.removeParticipant' | 'ChatThreadClient.sendMessage' | 'ChatThreadClient.sendReadReceipt' | 'ChatThreadClient.sendTypingNotification' | 'ChatThreadClient.updateMessage' | /* @conditional-compile-remove(chat-beta-sdk) */ 'ChatThreadClient.updateProperties' | 'ChatThreadClient.updateTopic' | /* @conditional-compile-remove(chat-beta-sdk) */ 'ChatThreadClient.uploadImage' | /* @conditional-compile-remove(chat-beta-sdk) */ 'ChatThreadClient.deleteImage'; +export type ChatErrorTarget = 'ChatClient.createChatThread' | 'ChatClient.deleteChatThread' | 'ChatClient.getChatThreadClient' | 'ChatClient.listChatThreads' | 'ChatClient.off' | 'ChatClient.on' | 'ChatClient.startRealtimeNotifications' | 'ChatClient.stopRealtimeNotifications' | 'ChatThreadClient.addParticipants' | 'ChatThreadClient.deleteMessage' | 'ChatThreadClient.getMessage' | 'ChatThreadClient.getProperties' | 'ChatThreadClient.listMessages' | 'ChatThreadClient.listParticipants' | 'ChatThreadClient.listReadReceipts' | 'ChatThreadClient.removeParticipant' | 'ChatThreadClient.sendMessage' | 'ChatThreadClient.sendReadReceipt' | 'ChatThreadClient.sendTypingNotification' | 'ChatThreadClient.updateMessage' | 'ChatThreadClient.updateProperties' | 'ChatThreadClient.updateTopic' | 'ChatThreadClient.uploadImage' | 'ChatThreadClient.deleteImage'; // @public export type ChatHandlers = { - onSendMessage: (content: string, options?: SendMessageOptions | /* @conditional-compile-remove(file-sharing-acs) */ MessageOptions) => Promise; + onSendMessage: (content: string, options?: SendMessageOptions | MessageOptions) => Promise; onUploadImage: (image: Blob, imageFilename: string) => Promise; onDeleteImage: (imageId: string) => Promise; onMessageSeen: (chatMessageId: string) => Promise; @@ -2094,11 +2051,6 @@ export type CommonCallAdapterOptions = { videoBackgroundImages?: VideoBackgroundImage[]; onResolveDependency?: () => Promise; }; - deepNoiseSuppressionOptions?: { - onResolveDependency?: () => Promise; - deepNoiseSuppressionOnByDefault?: boolean; - hideDeepNoiseSuppressionButton?: boolean; - }; onFetchProfile?: OnFetchProfileCallback; callingSounds?: CallingSounds; reactionResources?: ReactionResources; @@ -2111,7 +2063,7 @@ export type CommonCallControlOptions = { cameraButton?: boolean | { disabled: boolean; }; - endCallButton?: boolean | /* @conditional-compile-remove(end-call-options) */ { + endCallButton?: boolean | { hangUpForEveryone?: false | 'endCallOptions'; disableEndCallModal?: boolean; }; @@ -2166,8 +2118,6 @@ export interface CommonCallingHandlers { onCreateLocalStreamView: (options?: VideoStreamOptions) => Promise; // (undocumented) onCreateRemoteStreamView: (userId: string, options?: VideoStreamOptions) => Promise; - // @beta - onCreateTogetherModeStreamView: (options?: VideoStreamOptions) => Promise; // (undocumented) onDisposeLocalScreenShareStreamView: () => Promise; // (undocumented) @@ -2178,8 +2128,6 @@ export interface CommonCallingHandlers { onDisposeRemoteStreamView: (userId: string) => Promise; // (undocumented) onDisposeRemoteVideoStreamView: (userId: string) => Promise; - // @beta - onDisposeTogetherModeStreamViews: () => Promise; // (undocumented) onHangUp: (forEveryone?: boolean) => Promise; // (undocumented) @@ -2221,8 +2169,6 @@ export interface CommonCallingHandlers { // (undocumented) onStartLocalVideo: () => Promise; // (undocumented) - onStartNoiseSuppressionEffect: () => Promise; - // (undocumented) onStartScreenShare: () => Promise; // (undocumented) onStartSpotlight: (userIds?: string[]) => Promise; @@ -2231,8 +2177,6 @@ export interface CommonCallingHandlers { // (undocumented) onStopCaptions: () => Promise; // (undocumented) - onStopNoiseSuppressionEffect: () => Promise; - // (undocumented) onStopScreenShare: () => Promise; // (undocumented) onStopSpotlight: (userIds?: string[]) => Promise; @@ -2581,7 +2525,6 @@ export const createDefaultChatHandlers: (chatClient: StatefulChatClient, chatThr // @public export const createDefaultTeamsCallingHandlers: (callClient: StatefulCallClient, callAgent?: TeamsCallAgent, deviceManager?: StatefulDeviceManager, call?: TeamsCall, options?: { onResolveVideoBackgroundEffectsDependency?: () => Promise; - onResolveDeepNoiseSuppressionDependency?: () => Promise; }) => TeamsCallingHandlers; // @public @@ -2591,10 +2534,10 @@ export const createStatefulCallClient: (args: StatefulCallClientArgs, options?: export const createStatefulChatClient: (args: StatefulChatClientArgs, options?: StatefulChatClientOptions) => StatefulChatClient; // @public (undocumented) -export const createTeamsCallAdapter: (args: TeamsCallAdapterArgs | /* @conditional-compile-remove(teams-identity-support-beta) */ TeamsOutboundCallAdapterArgs) => Promise; +export const createTeamsCallAdapter: (args: TeamsCallAdapterArgs | TeamsOutboundCallAdapterArgs) => Promise; // @public -export const createTeamsCallAdapterFromClient: (callClient: StatefulCallClient, callAgent: TeamsCallAgent, locator: CallAdapterLocator | /* @conditional-compile-remove(teams-identity-support-beta) */ StartTeamsCallIdentifier[], options?: TeamsAdapterOptions) => Promise; +export const createTeamsCallAdapterFromClient: (callClient: StatefulCallClient, callAgent: TeamsCallAgent, locator: CallAdapterLocator | StartTeamsCallIdentifier[], options?: TeamsAdapterOptions) => Promise; // @public export interface CreateVideoStreamViewResult { @@ -2670,11 +2613,6 @@ export type DeclarativeCallAgent = CallAgent & IncomingCallManagement; // @public export type DeclarativeTeamsCallAgent = TeamsCallAgent & TeamsIncomingCallManagement; -// @beta -export type DeepNoiseSuppressionEffectDependency = { - deepNoiseSuppressionEffect: AudioEffectsStartConfig; -}; - // @public export const DEFAULT_COMPONENT_ICONS: { ChatMessageOptions: React_2.JSX.Element; @@ -3574,7 +3512,7 @@ export interface MentionPopoverStrings { } // @public -export type Message = ChatMessage | SystemMessage | CustomMessage | /* @conditional-compile-remove(data-loss-prevention) */ BlockedMessage; +export type Message = ChatMessage | SystemMessage | CustomMessage | BlockedMessage; // @public export type MessageAttachedStatus = 'bottom' | 'top' | boolean; @@ -3606,7 +3544,7 @@ export type MessageOptions = { // @public export type MessageProps = { message: Message; - strings: MessageThreadStrings & /* @conditional-compile-remove(rich-text-editor) */ Partial; + strings: MessageThreadStrings & Partial; messageContainerStyle?: ComponentSlotStyle; showDate?: boolean; disableEditing?: boolean; @@ -3670,7 +3608,7 @@ export const MessageThread: (props: MessageThreadProps) => JSX.Element; // @public export type MessageThreadProps = { userId: string; - messages: (ChatMessage | SystemMessage | CustomMessage | /* @conditional-compile-remove(data-loss-prevention) */ BlockedMessage)[]; + messages: (ChatMessage | SystemMessage | CustomMessage | BlockedMessage)[]; participantCount?: number; readReceiptsBySenderId?: ReadReceiptsBySenderId; styles?: MessageThreadStyles; @@ -3773,18 +3711,12 @@ export interface MicrophoneButtonContextualMenuStyles extends IContextualMenuSty // @public export interface MicrophoneButtonProps extends ControlBarButtonProps { enableDeviceSelectionMenu?: boolean; - // @beta - isDeepNoiseSuppressionOn?: boolean; microphones?: OptionsDevice[]; - // @beta - onClickNoiseSuppression?: () => void; onSelectMicrophone?: (device: OptionsDevice) => Promise; onSelectSpeaker?: (device: OptionsDevice) => Promise; onToggleMicrophone?: () => Promise; selectedMicrophone?: OptionsDevice; selectedSpeaker?: OptionsDevice; - // @beta - showNoiseSuppressionButton?: boolean; speakers?: OptionsDevice[]; strings?: Partial; styles?: Partial; @@ -3802,12 +3734,6 @@ export type MicrophoneButtonSelector = (state: CallClientState, props: CallingBa // @public export interface MicrophoneButtonStrings { - // @beta - deepNoiseSuppressionOffAnnouncement?: string; - // @beta - deepNoiseSuppressionOnAnnouncement?: string; - // @beta - deepNoiseSuppressionTitle?: string; microphoneActionTurnedOffAnnouncement?: string; microphoneActionTurnedOnAnnouncement?: string; microphoneAriaDescription?: string; @@ -3960,12 +3886,6 @@ export type OnRenderAvatarCallback = ( userId?: string, options?: CustomAvatarOptions, defaultOnRender?: (props: CustomAvatarOptions) => JSX.Element) => JSX.Element | undefined; -// @beta -export const onResolveDeepNoiseSuppressionDependency: () => Promise; - -// @beta -export const onResolveDeepNoiseSuppressionDependencyLazy: () => Promise; - // @public export const onResolveVideoEffectDependency: () => Promise; @@ -4280,18 +4200,10 @@ export interface RecordingCallFeature { lastStoppedRecording?: RecordingInfo[]; } -// @beta -export type RemoteDiagnosticState = { - readonly diagnostic: NetworkDiagnosticType | MediaDiagnosticType | ServerDiagnosticType; - readonly value: DiagnosticQuality | DiagnosticFlag; - readonly valueType: DiagnosticValueType; -}; - // @public export interface RemoteParticipantState { callEndReason?: CallEndReason; contentSharingStream?: HTMLElement; - diagnostics?: Record; displayName?: string; identifier: CommunicationIdentifierKind; isMuted: boolean; @@ -4649,7 +4561,7 @@ export interface SpotlightState { } // @public -export type StartCallIdentifier = (MicrosoftTeamsAppIdentifier | PhoneNumberIdentifier | CommunicationUserIdentifier | MicrosoftTeamsUserIdentifier | UnknownIdentifier) | /* @conditional-compile-remove(start-call-beta) */ CommunicationIdentifier; +export type StartCallIdentifier = (MicrosoftTeamsAppIdentifier | PhoneNumberIdentifier | CommunicationUserIdentifier | MicrosoftTeamsUserIdentifier | UnknownIdentifier) | CommunicationIdentifier; // @public export interface StartCaptionsAdapterOptions extends StartCaptionsOptions { @@ -4662,12 +4574,8 @@ export type StartTeamsCallIdentifier = MicrosoftTeamsUserIdentifier | PhoneNumbe // @public export interface StatefulCallClient extends CallClient { createCallAgent(...args: Parameters): Promise; - // @beta - createCallFeatureView(callId: string, stream: CallFeatureStreamState, options?: CreateViewOptions): Promise; createTeamsCallAgent(...args: Parameters): Promise; createView(callId: string | undefined, participantId: CommunicationIdentifier | undefined, stream: LocalVideoStreamState | RemoteVideoStreamState, options?: CreateViewOptions): Promise; - // @beta - disposeCallFeatureView(callId: string, stream: CallFeatureStreamState): void; disposeView(callId: string | undefined, participantId: CommunicationIdentifier | undefined, stream: LocalVideoStreamState | RemoteVideoStreamState): void; getState(): CallClientState; offStateChange(handler: (state: CallClientState) => void): void; @@ -4676,7 +4584,7 @@ export interface StatefulCallClient extends CallClient { // @public export type StatefulCallClientArgs = { - userId: CommunicationUserIdentifier | /* @conditional-compile-remove(teams-identity-support) */ MicrosoftTeamsUserIdentifier; + userId: CommunicationUserIdentifier | MicrosoftTeamsUserIdentifier; }; // @public @@ -4814,7 +4722,7 @@ export interface TeamsCallAdapter extends CommonCallAdapter { // @public export type TeamsCallAdapterArgs = TeamsCallAdapterArgsCommon & { - locator: TeamsMeetingLinkLocator | /* @conditional-compile-remove(call-participants-locator) */ CallParticipantsLocator | TeamsMeetingIdLocator; + locator: TeamsMeetingLinkLocator | CallParticipantsLocator | TeamsMeetingIdLocator; }; // @public @@ -4827,7 +4735,7 @@ export type TeamsCallAdapterArgsCommon = { // @public export interface TeamsCallingHandlers extends CommonCallingHandlers { // (undocumented) - onStartCall: (participants: CommunicationIdentifier[], options?: StartCallOptions) => undefined | /* @conditional-compile-remove(teams-identity-support) */ TeamsCall; + onStartCall: (participants: CommunicationIdentifier[], options?: StartCallOptions) => undefined | TeamsCall; } // @public @@ -4853,37 +4761,6 @@ export type TeamsOutboundCallAdapterArgs = TeamsCallAdapterArgsCommon & { // @public export const toFlatCommunicationIdentifier: (identifier: CommunicationIdentifier) => string; -// @beta -export interface TogetherModeCallFeature { - seatingCoordinates: Map; - streams: Map; -} - -// @beta -export interface TogetherModeSeatingCoordinatesState { - // (undocumented) - height: number; - // (undocumented) - left: number; - // (undocumented) - top: number; - // (undocumented) - width: number; -} - -// @beta -export interface TogetherModeStreamState extends CallFeatureStreamState { - id: number; - // @public - isReceiving: boolean; - mediaStreamType: MediaStreamType; - streamSize?: { - width: number; - height: number; - }; - view?: VideoStreamRendererViewState; -} - // @public export type TopicChangedListener = (event: { topic: string; @@ -5037,13 +4914,13 @@ export const usePropsFor: JSX.Element>(compon export const useSelector: (selector: ParamT, selectorProps?: ParamT extends Selector ? Parameters[1] : undefined, type?: 'calling' | 'chat') => ParamT extends Selector ? ReturnType : undefined; // @public -export const useTeamsCall: () => undefined | /* @conditional-compile-remove(teams-identity-support) */ TeamsCall; +export const useTeamsCall: () => undefined | TeamsCall; // @public -export const useTeamsCallAdapter: (args: Partial, afterCreate?: (adapter: TeamsCallAdapter) => Promise, beforeDispose?: (adapter: TeamsCallAdapter) => Promise) => TeamsCallAdapter | undefined; +export const useTeamsCallAdapter: (args: Partial, afterCreate?: (adapter: TeamsCallAdapter) => Promise, beforeDispose?: (adapter: TeamsCallAdapter) => Promise) => TeamsCallAdapter | undefined; // @public -export const useTeamsCallAgent: () => undefined | /* @conditional-compile-remove(teams-identity-support) */ TeamsCallAgent; +export const useTeamsCallAgent: () => undefined | TeamsCallAgent; // @public export const useTheme: () => Theme; @@ -5103,7 +4980,7 @@ export interface VideoBackgroundReplacementEffect extends BackgroundReplacementC export const VideoGallery: (props: VideoGalleryProps) => JSX.Element; // @public (undocumented) -export type VideoGalleryLayout = 'default' | 'floatingLocalVideo' | 'speaker' | /* @conditional-compile-remove(large-gallery) */ 'largeGallery' | 'focusedContent'; +export type VideoGalleryLayout = 'default' | 'floatingLocalVideo' | 'speaker' | 'focusedContent'; // @public export interface VideoGalleryLocalParticipant extends VideoGalleryParticipant { diff --git a/packages/react-composites/src/composites/CallComposite/adapter/CallAdapter.ts b/packages/react-composites/src/composites/CallComposite/adapter/CallAdapter.ts index 9238fd7021f..fcfc54e7a45 100644 --- a/packages/react-composites/src/composites/CallComposite/adapter/CallAdapter.ts +++ b/packages/react-composites/src/composites/CallComposite/adapter/CallAdapter.ts @@ -662,7 +662,7 @@ export interface CallAdapterCallOperations { * * @beta */ - disposeTogetherModeStreamViews(options?: VideoStreamOptions): Promise; + disposeTogetherModeStreamViews(): Promise; /** * Dispose the html view for a screen share stream * diff --git a/packages/react-composites/src/composites/CallComposite/components/CallControls.tsx b/packages/react-composites/src/composites/CallComposite/components/CallControls.tsx index 6dd1d397658..edd5f02ed53 100644 --- a/packages/react-composites/src/composites/CallComposite/components/CallControls.tsx +++ b/packages/react-composites/src/composites/CallComposite/components/CallControls.tsx @@ -43,7 +43,7 @@ import { callStatusSelector } from '../../CallComposite/selectors/callStatusSele /* @conditional-compile-remove(DNS) */ import { _isSafari } from '../../CallComposite/utils'; import { getIsRoomsCall, getReactionResources, getRole } from '../selectors/baseSelectors'; -/* @conditional-compile-remove(calling-environment-info) */ +/* @conditional-compile-remove(DNS) */ import { getEnvironmentInfo } from '../selectors/baseSelectors'; /* @conditional-compile-remove(DNS) */ import { From ecb470dd1ed02a7b3dd431cade892f6d184936ca Mon Sep 17 00:00:00 2001 From: Chukwuebuka Nwankwo Date: Thu, 10 Oct 2024 18:51:32 +0000 Subject: [PATCH 12/17] fix merge conflict --- .../review/beta/communication-react.api.md | 184 +++++++++++++++--- 1 file changed, 155 insertions(+), 29 deletions(-) diff --git a/packages/communication-react/review/beta/communication-react.api.md b/packages/communication-react/review/beta/communication-react.api.md index c12c17219ed..fed93ccf495 100644 --- a/packages/communication-react/review/beta/communication-react.api.md +++ b/packages/communication-react/review/beta/communication-react.api.md @@ -8,6 +8,7 @@ import { AddPhoneNumberOptions } from '@azure/communication-calling'; import { AudioDeviceInfo } from '@azure/communication-calling'; +import { AudioEffectsStartConfig } from '@azure/communication-calling'; import { BackgroundBlurConfig } from '@azure/communication-calling'; import { BackgroundBlurEffect } from '@azure/communication-calling'; import { BackgroundReplacementConfig } from '@azure/communication-calling'; @@ -44,6 +45,9 @@ import type { CommunicationUserKind } from '@azure/communication-common'; import { CreateViewOptions } from '@azure/communication-calling'; import { DeviceAccess } from '@azure/communication-calling'; import { DeviceManager } from '@azure/communication-calling'; +import type { DiagnosticFlag } from '@azure/communication-calling'; +import type { DiagnosticQuality } from '@azure/communication-calling'; +import type { DiagnosticValueType } from '@azure/communication-calling'; import { DominantSpeakersInfo } from '@azure/communication-calling'; import { DtmfTone as DtmfTone_2 } from '@azure/communication-calling'; import { EnvironmentInfo } from '@azure/communication-calling'; @@ -72,10 +76,12 @@ import { LatestNetworkDiagnostics } from '@azure/communication-calling'; import { LocalRecordingInfo } from '@azure/communication-calling'; import { LocalVideoStream } from '@azure/communication-calling'; import type { MediaDiagnosticChangedEventArgs } from '@azure/communication-calling'; +import type { MediaDiagnosticType } from '@azure/communication-calling'; import { MediaStreamType } from '@azure/communication-calling'; import { MicrosoftTeamsAppIdentifier } from '@azure/communication-common'; import { MicrosoftTeamsUserIdentifier } from '@azure/communication-common'; import type { NetworkDiagnosticChangedEventArgs } from '@azure/communication-calling'; +import type { NetworkDiagnosticType } from '@azure/communication-calling'; import { PartialTheme } from '@fluentui/react'; import { ParticipantCapabilities } from '@azure/communication-calling'; import { ParticipantRole } from '@azure/communication-calling'; @@ -94,6 +100,7 @@ import { RemoteParticipantState as RemoteParticipantState_2 } from '@azure/commu import { RoomCallLocator } from '@azure/communication-calling'; import { ScalingMode } from '@azure/communication-calling'; import { SendMessageOptions } from '@azure/communication-chat'; +import type { ServerDiagnosticType } from '@azure/communication-calling'; import { SpotlightedParticipant } from '@azure/communication-calling'; import { StartCallOptions } from '@azure/communication-calling'; import { StartCaptionsOptions } from '@azure/communication-calling'; @@ -104,6 +111,8 @@ import { TeamsIncomingCall } from '@azure/communication-calling'; import { TeamsMeetingIdLocator } from '@azure/communication-calling'; import { TeamsMeetingLinkLocator } from '@azure/communication-calling'; import { Theme } from '@fluentui/react'; +import { TogetherModeStreamViewResult } from '@internal/react-components/dist/dist-esm/types/TogetherModeTypes'; +import { TogetherModeStreamViewResult as TogetherModeStreamViewResult_2 } from '@internal/react-components/dist/dist-esm/types'; import { TransferEventArgs } from '@azure/communication-calling'; import { TypingIndicatorReceivedEvent } from '@azure/communication-chat'; import { UnknownIdentifier } from '@azure/communication-common'; @@ -435,11 +444,15 @@ export interface CallAdapterCallOperations { addParticipant(participant: CommunicationUserIdentifier): Promise; allowUnsupportedBrowserVersion(): void; createStreamView(remoteUserId?: string, options?: VideoStreamOptions): Promise; + // @beta + createTogetherModeStreamViews(options?: VideoStreamOptions): Promise; disposeLocalVideoStreamView(): Promise; disposeRemoteVideoStreamView(remoteUserId: string): Promise; disposeScreenShareStreamView(remoteUserId: string): Promise; // @deprecated disposeStreamView(remoteUserId?: string, options?: VideoStreamOptions): Promise; + // @beta + disposeTogetherModeStreamViews(): Promise; holdCall(): Promise; leaveCall(forEveryone?: boolean): Promise; lowerHand(): Promise; @@ -457,12 +470,16 @@ export interface CallAdapterCallOperations { setSpokenLanguage(language: string): Promise; startCamera(options?: VideoStreamOptions): Promise; startCaptions(options?: StartCaptionsAdapterOptions): Promise; + // @beta + startNoiseSuppressionEffect(): Promise; startScreenShare(): Promise; startSpotlight(userIds?: string[]): Promise; startVideoBackgroundEffect(videoBackgroundEffect: VideoBackgroundEffect): Promise; stopAllSpotlight(): Promise; stopCamera(): Promise; stopCaptions(options?: StopCaptionsAdapterOptions): Promise; + // @beta + stopNoiseSuppressionEffect(): Promise; stopScreenShare(): Promise; stopSpotlight(userIds?: string[]): Promise; stopVideoBackgroundEffects(): Promise; @@ -490,6 +507,9 @@ export type CallAdapterClientState = { cameraStatus?: 'On' | 'Off'; videoBackgroundImages?: VideoBackgroundImage[]; onResolveVideoEffectDependency?: () => Promise; + onResolveDeepNoiseSuppressionDependency?: () => Promise; + deepNoiseSuppressionOnByDefault?: boolean; + hideDeepNoiseSuppressionButton?: boolean; selectedVideoBackgroundEffect?: VideoBackgroundEffect; acceptedTransferCallState?: CallState; hideAttendeeNames?: boolean; @@ -509,7 +529,7 @@ export interface CallAdapterDeviceManagement { } // @public -export type CallAdapterLocator = TeamsMeetingLinkLocator | GroupCallLocator | RoomCallLocator | CallParticipantsLocator | TeamsMeetingIdLocator; +export type CallAdapterLocator = TeamsMeetingLinkLocator | GroupCallLocator | RoomCallLocator | /* @conditional-compile-remove(call-participants-locator) */ CallParticipantsLocator | TeamsMeetingIdLocator; // @public export type CallAdapterState = CallAdapterUiState & CallAdapterClientState; @@ -575,7 +595,7 @@ export const CallAgentProvider: (props: CallAgentProviderProps) => JSX.Element; // @public export interface CallAgentProviderProps { // (undocumented) - callAgent?: CallAgent | TeamsCallAgent; + callAgent?: CallAgent | /* @conditional-compile-remove(teams-identity-support) */ TeamsCallAgent; // (undocumented) children: React_2.ReactNode; } @@ -587,7 +607,7 @@ export interface CallAgentState { // @public export interface CallAndChatLocator { - callLocator: GroupCallLocator | CallParticipantsLocator; + callLocator: GroupCallLocator | /* @conditional-compile-remove(call-participants-locator) */ CallParticipantsLocator; chatThreadId: string; } @@ -756,7 +776,7 @@ export type CallCompositeOptions = { }; // @public -export type CallCompositePage = 'accessDeniedTeamsMeeting' | 'call' | 'configuration' | 'hold' | 'joinCallFailedDueToNoNetwork' | 'leftCall' | 'leaving' | 'lobby' | 'removedFromCall' | 'unsupportedEnvironment' | 'transferring' | 'badRequest'; +export type CallCompositePage = 'accessDeniedTeamsMeeting' | 'call' | 'configuration' | 'hold' | 'joinCallFailedDueToNoNetwork' | 'leftCall' | 'leaving' | 'lobby' | 'removedFromCall' | /* @conditional-compile-remove(unsupported-browser) */ 'unsupportedEnvironment' | 'transferring' | 'badRequest'; // @public export interface CallCompositeProps extends BaseCompositeProps { @@ -1012,7 +1032,16 @@ export type CallErrors = { }; // @public -export type CallErrorTarget = 'Call.addParticipant' | 'Call.dispose' | 'Call.feature' | 'Call.hangUp' | 'Call.hold' | 'Call.mute' | 'Call.muteIncomingAudio' | 'Call.off' | 'Call.on' | 'Call.removeParticipant' | 'Call.resume' | 'Call.sendDtmf' | 'Call.startAudio' | 'Call.startScreenSharing' | 'Call.startVideo' | 'Call.stopScreenSharing' | 'Call.stopAudio' | 'Call.stopVideo' | 'Call.unmute' | 'Call.unmuteIncomingAudio' | 'CallAgent.dispose' | 'CallAgent.feature' | 'CallAgent.join' | 'CallAgent.off' | 'CallAgent.on' | 'CallAgent.startCall' | 'CallClient.createCallAgent' | 'CallClient.createTeamsCallAgent' | 'CallClient.feature' | 'CallClient.getDeviceManager' | 'CallClient.getEnvironmentInfo' | 'DeviceManager.askDevicePermission' | 'DeviceManager.getCameras' | 'DeviceManager.getMicrophones' | 'DeviceManager.getSpeakers' | 'DeviceManager.off' | 'DeviceManager.on' | 'DeviceManager.selectMicrophone' | 'DeviceManager.selectSpeaker' | 'IncomingCall.accept' | 'IncomingCall.reject' | 'TeamsCall.addParticipant' | 'VideoEffectsFeature.startEffects' | 'CallAgent.handlePushNotification' | 'Call.admit' | 'Call.rejectParticipant' | 'Call.admitAll' | 'Call.mutedByOthers' | 'Call.muteAllRemoteParticipants' | 'Call.setConstraints'; +export type CallErrorTarget = 'Call.addParticipant' | 'Call.dispose' | 'Call.feature' | 'Call.hangUp' | 'Call.hold' | 'Call.mute' | 'Call.muteIncomingAudio' | 'Call.off' | 'Call.on' | 'Call.removeParticipant' | 'Call.resume' | 'Call.sendDtmf' | 'Call.startAudio' | 'Call.startScreenSharing' | 'Call.startVideo' | 'Call.stopScreenSharing' | 'Call.stopAudio' | 'Call.stopVideo' | 'Call.unmute' | 'Call.unmuteIncomingAudio' | 'CallAgent.dispose' | 'CallAgent.feature' | 'CallAgent.join' | 'CallAgent.off' | 'CallAgent.on' | 'CallAgent.startCall' | 'CallClient.createCallAgent' | 'CallClient.createTeamsCallAgent' | 'CallClient.feature' | 'CallClient.getDeviceManager' | /* @conditional-compile-remove(calling-beta-sdk) */ 'CallClient.getEnvironmentInfo' | 'DeviceManager.askDevicePermission' | 'DeviceManager.getCameras' | 'DeviceManager.getMicrophones' | 'DeviceManager.getSpeakers' | 'DeviceManager.off' | 'DeviceManager.on' | 'DeviceManager.selectMicrophone' | 'DeviceManager.selectSpeaker' | 'IncomingCall.accept' | 'IncomingCall.reject' | /* @conditional-compile-remove(calling-beta-sdk) */ /* @conditional-compile-remove(teams-identity-support) */ 'TeamsCall.addParticipant' | 'VideoEffectsFeature.startEffects' | /* @conditional-compile-remove(calling-beta-sdk) */ 'CallAgent.handlePushNotification' | /* @conditional-compile-remove(calling-beta-sdk) */ 'Call.admit' | /* @conditional-compile-remove(calling-beta-sdk) */ 'Call.rejectParticipant' | /* @conditional-compile-remove(calling-beta-sdk) */ 'Call.admitAll' | /* @conditional-compile-remove(soft-mute) */ 'Call.mutedByOthers' | 'Call.muteAllRemoteParticipants' | 'Call.setConstraints'; + +// @beta (undocumented) +export type CallFeatureStreamName = 'togetherMode'; + +// @beta (undocumented) +export interface CallFeatureStreamState { + // (undocumented) + feature: CallFeatureStreamName; +} // @public export type CallIdChangedListener = (event: { @@ -1041,6 +1070,7 @@ export interface CallingHandlers extends CommonCallingHandlers { // @public export type CallingHandlersOptions = { onResolveVideoBackgroundEffectsDependency?: () => Promise; + onResolveDeepNoiseSuppressionDependency?: () => Promise; }; // @public @@ -1101,7 +1131,7 @@ export const CallProvider: (props: CallProviderProps) => JSX.Element; // @public export interface CallProviderProps { // (undocumented) - call?: Call | TeamsCall; + call?: Call | /* @conditional-compile-remove(teams-identity-support) */ TeamsCall; // (undocumented) children: React_2.ReactNode; } @@ -1120,7 +1150,7 @@ export interface CallState { endTime: Date | undefined; hideAttendeeNames?: boolean; id: string; - info?: TeamsCallInfo | CallInfo; + info?: TeamsCallInfo | /* @conditional-compile-remove(calling-beta-sdk) */ CallInfo; isMuted: boolean; isScreenSharingOn: boolean; kind: CallKind; @@ -1145,6 +1175,8 @@ export interface CallState { spotlight?: SpotlightCallFeatureState; startTime: Date; state: CallState_2; + // @beta + togetherMode: TogetherModeCallFeature; totalParticipantCount?: number; transcription: TranscriptionCallFeature; transfer: TransferFeature; @@ -1171,12 +1203,16 @@ export interface CallWithChatAdapterManagement { askDevicePermission(constrain: PermissionConstraints): Promise; createStreamView(remoteUserId?: string, options?: VideoStreamOptions): Promise; // @beta + createTogetherModeStreamViews(options?: VideoStreamOptions): Promise; + // @beta deleteImage(imageId: string): Promise; deleteMessage(messageId: string): Promise; disposeLocalVideoStreamView(): Promise; disposeRemoteVideoStreamView(remoteUserId: string): Promise; disposeScreenShareStreamView(remoteUserId: string): Promise; disposeStreamView(remoteUserId?: string, options?: VideoStreamOptions): Promise; + // @beta + disposeTogetherModeStreamViews(): Promise; // (undocumented) downloadResourceToCache(resourceDetails: ResourceDetails): Promise; fetchInitialData(): Promise; @@ -1202,7 +1238,7 @@ export interface CallWithChatAdapterManagement { resumeCall(): Promise; returnFromBreakoutRoom(): Promise; sendDtmfTone: (dtmfTone: DtmfTone_2) => Promise; - sendMessage(content: string, options?: SendMessageOptions | MessageOptions): Promise; + sendMessage(content: string, options?: SendMessageOptions | /* @conditional-compile-remove(file-sharing-acs) */ MessageOptions): Promise; sendReadReceipt(chatMessageId: string): Promise; sendTypingIndicator(): Promise; setCamera(sourceInfo: VideoDeviceInfo, options?: VideoStreamOptions): Promise; @@ -1214,19 +1250,23 @@ export interface CallWithChatAdapterManagement { startCall(participants: (MicrosoftTeamsAppIdentifier | PhoneNumberIdentifier | CommunicationUserIdentifier | MicrosoftTeamsUserIdentifier | UnknownIdentifier)[], options?: StartCallOptions): Call | undefined; startCamera(options?: VideoStreamOptions): Promise; startCaptions(options?: StartCaptionsAdapterOptions): Promise; + // @beta + startNoiseSuppressionEffect(): Promise; startScreenShare(): Promise; startSpotlight(userIds?: string[]): Promise; startVideoBackgroundEffect(videoBackgroundEffect: VideoBackgroundEffect): Promise; stopAllSpotlight(): Promise; stopCamera(): Promise; stopCaptions(options?: StopCaptionsAdapterOptions): Promise; + // @beta + stopNoiseSuppressionEffect(): Promise; stopScreenShare(): Promise; stopSpotlight(userIds?: string[]): Promise; stopVideoBackgroundEffects(): Promise; submitSurvey(survey: CallSurvey): Promise; unmute(): Promise; updateBackgroundPickerImages(backgroundImages: VideoBackgroundImage[]): void; - updateMessage(messageId: string, content: string, options?: Record | MessageOptions): Promise; + updateMessage(messageId: string, content: string, options?: Record | /* @conditional-compile-remove(file-sharing-acs) */ MessageOptions): Promise; updateSelectedVideoBackgroundEffect(selectedVideoBackground: VideoBackgroundEffect): void; // @beta uploadImage(image: Blob, imageFilename: string): Promise; @@ -1361,15 +1401,21 @@ export interface CallWithChatClientState { alternateCallerId?: string; call?: CallState; chat?: ChatThreadClientState; + // @beta + deepNoiseSuppressionOnByDefault?: boolean; devices: DeviceManagerState; displayName: string | undefined; environmentInfo?: EnvironmentInfo; hideAttendeeNames?: boolean; + // @beta + hideDeepNoiseSuppressionButton?: boolean; isTeamsCall: boolean; isTeamsMeeting: boolean; latestCallErrors: AdapterErrors; latestCallNotifications: AdapterNotifications; latestChatErrors: AdapterErrors; + // @beta + onResolveDeepNoiseSuppressionDependency?: () => Promise; onResolveVideoEffectDependency?: () => Promise; reactions?: ReactionResources; selectedVideoBackgroundEffect?: VideoBackgroundEffect; @@ -1559,7 +1605,7 @@ export interface CallWithChatControlOptions extends CommonCallControlOptions { } // @public -export type CallWithChatEvent = 'callError' | 'chatError' | 'callEnded' | 'isMutedChanged' | 'callIdChanged' | 'isLocalScreenSharingActiveChanged' | 'displayNameChanged' | 'isSpeakingChanged' | 'callParticipantsJoined' | 'callParticipantsLeft' | 'selectedMicrophoneChanged' | 'selectedSpeakerChanged' | 'isCaptionsActiveChanged' | 'captionsReceived' | 'isCaptionLanguageChanged' | 'isSpokenLanguageChanged' | 'capabilitiesChanged' | 'spotlightChanged' | 'breakoutRoomsUpdated' | 'messageReceived' | 'messageEdited' | 'messageDeleted' | 'messageSent' | 'messageRead' | 'chatParticipantsAdded' | 'chatParticipantsRemoved' | 'chatInitialized'; +export type CallWithChatEvent = 'callError' | 'chatError' | 'callEnded' | 'isMutedChanged' | 'callIdChanged' | 'isLocalScreenSharingActiveChanged' | 'displayNameChanged' | 'isSpeakingChanged' | 'callParticipantsJoined' | 'callParticipantsLeft' | 'selectedMicrophoneChanged' | 'selectedSpeakerChanged' | 'isCaptionsActiveChanged' | 'captionsReceived' | 'isCaptionLanguageChanged' | 'isSpokenLanguageChanged' | 'capabilitiesChanged' | 'spotlightChanged' | /* @conditional-compile-remove(breakout-rooms) */ 'breakoutRoomsUpdated' | 'messageReceived' | 'messageEdited' | 'messageDeleted' | 'messageSent' | 'messageRead' | 'chatParticipantsAdded' | 'chatParticipantsRemoved' | 'chatInitialized'; // @beta export const CameraAndMicrophoneSitePermissions: (props: CameraAndMicrophoneSitePermissionsProps) => JSX.Element; @@ -1813,11 +1859,11 @@ export interface ChatAdapterThreadManagement { loadPreviousChatMessages(messagesToLoad: number): Promise; removeParticipant(userId: string): Promise; removeResourceFromCache(resourceDetails: ResourceDetails): void; - sendMessage(content: string, options?: SendMessageOptions | MessageOptions): Promise; + sendMessage(content: string, options?: SendMessageOptions | /* @conditional-compile-remove(file-sharing-acs) */ MessageOptions): Promise; sendReadReceipt(chatMessageId: string): Promise; sendTypingIndicator(): Promise; setTopic(topicName: string): Promise; - updateMessage(messageId: string, content: string, options?: Record | MessageOptions): Promise; + updateMessage(messageId: string, content: string, options?: Record | /* @conditional-compile-remove(file-sharing-acs) */ MessageOptions): Promise; uploadImage(image: Blob, imageFilename: string): Promise; } @@ -1827,7 +1873,7 @@ export type ChatAdapterUiState = { }; // @public -export type ChatAttachmentType = 'unknown' | 'image' | 'file'; +export type ChatAttachmentType = 'unknown' | 'image' | /* @conditional-compile-remove(file-sharing-teams-interop) @conditional-compile-remove(file-sharing-acs) */ 'file'; // @public export type ChatBaseSelectorProps = { @@ -1929,11 +1975,11 @@ export type ChatErrors = { }; // @public -export type ChatErrorTarget = 'ChatClient.createChatThread' | 'ChatClient.deleteChatThread' | 'ChatClient.getChatThreadClient' | 'ChatClient.listChatThreads' | 'ChatClient.off' | 'ChatClient.on' | 'ChatClient.startRealtimeNotifications' | 'ChatClient.stopRealtimeNotifications' | 'ChatThreadClient.addParticipants' | 'ChatThreadClient.deleteMessage' | 'ChatThreadClient.getMessage' | 'ChatThreadClient.getProperties' | 'ChatThreadClient.listMessages' | 'ChatThreadClient.listParticipants' | 'ChatThreadClient.listReadReceipts' | 'ChatThreadClient.removeParticipant' | 'ChatThreadClient.sendMessage' | 'ChatThreadClient.sendReadReceipt' | 'ChatThreadClient.sendTypingNotification' | 'ChatThreadClient.updateMessage' | 'ChatThreadClient.updateProperties' | 'ChatThreadClient.updateTopic' | 'ChatThreadClient.uploadImage' | 'ChatThreadClient.deleteImage'; +export type ChatErrorTarget = 'ChatClient.createChatThread' | 'ChatClient.deleteChatThread' | 'ChatClient.getChatThreadClient' | 'ChatClient.listChatThreads' | 'ChatClient.off' | 'ChatClient.on' | 'ChatClient.startRealtimeNotifications' | 'ChatClient.stopRealtimeNotifications' | 'ChatThreadClient.addParticipants' | 'ChatThreadClient.deleteMessage' | 'ChatThreadClient.getMessage' | 'ChatThreadClient.getProperties' | 'ChatThreadClient.listMessages' | 'ChatThreadClient.listParticipants' | 'ChatThreadClient.listReadReceipts' | 'ChatThreadClient.removeParticipant' | 'ChatThreadClient.sendMessage' | 'ChatThreadClient.sendReadReceipt' | 'ChatThreadClient.sendTypingNotification' | 'ChatThreadClient.updateMessage' | /* @conditional-compile-remove(chat-beta-sdk) */ 'ChatThreadClient.updateProperties' | 'ChatThreadClient.updateTopic' | /* @conditional-compile-remove(chat-beta-sdk) */ 'ChatThreadClient.uploadImage' | /* @conditional-compile-remove(chat-beta-sdk) */ 'ChatThreadClient.deleteImage'; // @public export type ChatHandlers = { - onSendMessage: (content: string, options?: SendMessageOptions | MessageOptions) => Promise; + onSendMessage: (content: string, options?: SendMessageOptions | /* @conditional-compile-remove(file-sharing-acs) */ MessageOptions) => Promise; onUploadImage: (image: Blob, imageFilename: string) => Promise; onDeleteImage: (imageId: string) => Promise; onMessageSeen: (chatMessageId: string) => Promise; @@ -2051,6 +2097,11 @@ export type CommonCallAdapterOptions = { videoBackgroundImages?: VideoBackgroundImage[]; onResolveDependency?: () => Promise; }; + deepNoiseSuppressionOptions?: { + onResolveDependency?: () => Promise; + deepNoiseSuppressionOnByDefault?: boolean; + hideDeepNoiseSuppressionButton?: boolean; + }; onFetchProfile?: OnFetchProfileCallback; callingSounds?: CallingSounds; reactionResources?: ReactionResources; @@ -2063,7 +2114,7 @@ export type CommonCallControlOptions = { cameraButton?: boolean | { disabled: boolean; }; - endCallButton?: boolean | { + endCallButton?: boolean | /* @conditional-compile-remove(end-call-options) */ { hangUpForEveryone?: false | 'endCallOptions'; disableEndCallModal?: boolean; }; @@ -2118,6 +2169,8 @@ export interface CommonCallingHandlers { onCreateLocalStreamView: (options?: VideoStreamOptions) => Promise; // (undocumented) onCreateRemoteStreamView: (userId: string, options?: VideoStreamOptions) => Promise; + // @beta + onCreateTogetherModeStreamView: (options?: VideoStreamOptions) => Promise; // (undocumented) onDisposeLocalScreenShareStreamView: () => Promise; // (undocumented) @@ -2128,6 +2181,8 @@ export interface CommonCallingHandlers { onDisposeRemoteStreamView: (userId: string) => Promise; // (undocumented) onDisposeRemoteVideoStreamView: (userId: string) => Promise; + // @beta + onDisposeTogetherModeStreamViews: () => Promise; // (undocumented) onHangUp: (forEveryone?: boolean) => Promise; // (undocumented) @@ -2169,6 +2224,8 @@ export interface CommonCallingHandlers { // (undocumented) onStartLocalVideo: () => Promise; // (undocumented) + onStartNoiseSuppressionEffect: () => Promise; + // (undocumented) onStartScreenShare: () => Promise; // (undocumented) onStartSpotlight: (userIds?: string[]) => Promise; @@ -2177,6 +2234,8 @@ export interface CommonCallingHandlers { // (undocumented) onStopCaptions: () => Promise; // (undocumented) + onStopNoiseSuppressionEffect: () => Promise; + // (undocumented) onStopScreenShare: () => Promise; // (undocumented) onStopSpotlight: (userIds?: string[]) => Promise; @@ -2525,6 +2584,7 @@ export const createDefaultChatHandlers: (chatClient: StatefulChatClient, chatThr // @public export const createDefaultTeamsCallingHandlers: (callClient: StatefulCallClient, callAgent?: TeamsCallAgent, deviceManager?: StatefulDeviceManager, call?: TeamsCall, options?: { onResolveVideoBackgroundEffectsDependency?: () => Promise; + onResolveDeepNoiseSuppressionDependency?: () => Promise; }) => TeamsCallingHandlers; // @public @@ -2534,10 +2594,10 @@ export const createStatefulCallClient: (args: StatefulCallClientArgs, options?: export const createStatefulChatClient: (args: StatefulChatClientArgs, options?: StatefulChatClientOptions) => StatefulChatClient; // @public (undocumented) -export const createTeamsCallAdapter: (args: TeamsCallAdapterArgs | TeamsOutboundCallAdapterArgs) => Promise; +export const createTeamsCallAdapter: (args: TeamsCallAdapterArgs | /* @conditional-compile-remove(teams-identity-support-beta) */ TeamsOutboundCallAdapterArgs) => Promise; // @public -export const createTeamsCallAdapterFromClient: (callClient: StatefulCallClient, callAgent: TeamsCallAgent, locator: CallAdapterLocator | StartTeamsCallIdentifier[], options?: TeamsAdapterOptions) => Promise; +export const createTeamsCallAdapterFromClient: (callClient: StatefulCallClient, callAgent: TeamsCallAgent, locator: CallAdapterLocator | /* @conditional-compile-remove(teams-identity-support-beta) */ StartTeamsCallIdentifier[], options?: TeamsAdapterOptions) => Promise; // @public export interface CreateVideoStreamViewResult { @@ -2613,6 +2673,11 @@ export type DeclarativeCallAgent = CallAgent & IncomingCallManagement; // @public export type DeclarativeTeamsCallAgent = TeamsCallAgent & TeamsIncomingCallManagement; +// @beta +export type DeepNoiseSuppressionEffectDependency = { + deepNoiseSuppressionEffect: AudioEffectsStartConfig; +}; + // @public export const DEFAULT_COMPONENT_ICONS: { ChatMessageOptions: React_2.JSX.Element; @@ -3512,7 +3577,7 @@ export interface MentionPopoverStrings { } // @public -export type Message = ChatMessage | SystemMessage | CustomMessage | BlockedMessage; +export type Message = ChatMessage | SystemMessage | CustomMessage | /* @conditional-compile-remove(data-loss-prevention) */ BlockedMessage; // @public export type MessageAttachedStatus = 'bottom' | 'top' | boolean; @@ -3544,7 +3609,7 @@ export type MessageOptions = { // @public export type MessageProps = { message: Message; - strings: MessageThreadStrings & Partial; + strings: MessageThreadStrings & /* @conditional-compile-remove(rich-text-editor) */ Partial; messageContainerStyle?: ComponentSlotStyle; showDate?: boolean; disableEditing?: boolean; @@ -3608,7 +3673,7 @@ export const MessageThread: (props: MessageThreadProps) => JSX.Element; // @public export type MessageThreadProps = { userId: string; - messages: (ChatMessage | SystemMessage | CustomMessage | BlockedMessage)[]; + messages: (ChatMessage | SystemMessage | CustomMessage | /* @conditional-compile-remove(data-loss-prevention) */ BlockedMessage)[]; participantCount?: number; readReceiptsBySenderId?: ReadReceiptsBySenderId; styles?: MessageThreadStyles; @@ -3711,12 +3776,18 @@ export interface MicrophoneButtonContextualMenuStyles extends IContextualMenuSty // @public export interface MicrophoneButtonProps extends ControlBarButtonProps { enableDeviceSelectionMenu?: boolean; + // @beta + isDeepNoiseSuppressionOn?: boolean; microphones?: OptionsDevice[]; + // @beta + onClickNoiseSuppression?: () => void; onSelectMicrophone?: (device: OptionsDevice) => Promise; onSelectSpeaker?: (device: OptionsDevice) => Promise; onToggleMicrophone?: () => Promise; selectedMicrophone?: OptionsDevice; selectedSpeaker?: OptionsDevice; + // @beta + showNoiseSuppressionButton?: boolean; speakers?: OptionsDevice[]; strings?: Partial; styles?: Partial; @@ -3734,6 +3805,12 @@ export type MicrophoneButtonSelector = (state: CallClientState, props: CallingBa // @public export interface MicrophoneButtonStrings { + // @beta + deepNoiseSuppressionOffAnnouncement?: string; + // @beta + deepNoiseSuppressionOnAnnouncement?: string; + // @beta + deepNoiseSuppressionTitle?: string; microphoneActionTurnedOffAnnouncement?: string; microphoneActionTurnedOnAnnouncement?: string; microphoneAriaDescription?: string; @@ -3886,6 +3963,12 @@ export type OnRenderAvatarCallback = ( userId?: string, options?: CustomAvatarOptions, defaultOnRender?: (props: CustomAvatarOptions) => JSX.Element) => JSX.Element | undefined; +// @beta +export const onResolveDeepNoiseSuppressionDependency: () => Promise; + +// @beta +export const onResolveDeepNoiseSuppressionDependencyLazy: () => Promise; + // @public export const onResolveVideoEffectDependency: () => Promise; @@ -4200,10 +4283,18 @@ export interface RecordingCallFeature { lastStoppedRecording?: RecordingInfo[]; } +// @beta +export type RemoteDiagnosticState = { + readonly diagnostic: NetworkDiagnosticType | MediaDiagnosticType | ServerDiagnosticType; + readonly value: DiagnosticQuality | DiagnosticFlag; + readonly valueType: DiagnosticValueType; +}; + // @public export interface RemoteParticipantState { callEndReason?: CallEndReason; contentSharingStream?: HTMLElement; + diagnostics?: Record; displayName?: string; identifier: CommunicationIdentifierKind; isMuted: boolean; @@ -4561,7 +4652,7 @@ export interface SpotlightState { } // @public -export type StartCallIdentifier = (MicrosoftTeamsAppIdentifier | PhoneNumberIdentifier | CommunicationUserIdentifier | MicrosoftTeamsUserIdentifier | UnknownIdentifier) | CommunicationIdentifier; +export type StartCallIdentifier = (MicrosoftTeamsAppIdentifier | PhoneNumberIdentifier | CommunicationUserIdentifier | MicrosoftTeamsUserIdentifier | UnknownIdentifier) | /* @conditional-compile-remove(start-call-beta) */ CommunicationIdentifier; // @public export interface StartCaptionsAdapterOptions extends StartCaptionsOptions { @@ -4574,8 +4665,12 @@ export type StartTeamsCallIdentifier = MicrosoftTeamsUserIdentifier | PhoneNumbe // @public export interface StatefulCallClient extends CallClient { createCallAgent(...args: Parameters): Promise; + // @beta + createCallFeatureView(callId: string, stream: CallFeatureStreamState, options?: CreateViewOptions): Promise; createTeamsCallAgent(...args: Parameters): Promise; createView(callId: string | undefined, participantId: CommunicationIdentifier | undefined, stream: LocalVideoStreamState | RemoteVideoStreamState, options?: CreateViewOptions): Promise; + // @beta + disposeCallFeatureView(callId: string, stream: CallFeatureStreamState): void; disposeView(callId: string | undefined, participantId: CommunicationIdentifier | undefined, stream: LocalVideoStreamState | RemoteVideoStreamState): void; getState(): CallClientState; offStateChange(handler: (state: CallClientState) => void): void; @@ -4584,7 +4679,7 @@ export interface StatefulCallClient extends CallClient { // @public export type StatefulCallClientArgs = { - userId: CommunicationUserIdentifier | MicrosoftTeamsUserIdentifier; + userId: CommunicationUserIdentifier | /* @conditional-compile-remove(teams-identity-support) */ MicrosoftTeamsUserIdentifier; }; // @public @@ -4722,7 +4817,7 @@ export interface TeamsCallAdapter extends CommonCallAdapter { // @public export type TeamsCallAdapterArgs = TeamsCallAdapterArgsCommon & { - locator: TeamsMeetingLinkLocator | CallParticipantsLocator | TeamsMeetingIdLocator; + locator: TeamsMeetingLinkLocator | /* @conditional-compile-remove(call-participants-locator) */ CallParticipantsLocator | TeamsMeetingIdLocator; }; // @public @@ -4735,7 +4830,7 @@ export type TeamsCallAdapterArgsCommon = { // @public export interface TeamsCallingHandlers extends CommonCallingHandlers { // (undocumented) - onStartCall: (participants: CommunicationIdentifier[], options?: StartCallOptions) => undefined | TeamsCall; + onStartCall: (participants: CommunicationIdentifier[], options?: StartCallOptions) => undefined | /* @conditional-compile-remove(teams-identity-support) */ TeamsCall; } // @public @@ -4761,6 +4856,37 @@ export type TeamsOutboundCallAdapterArgs = TeamsCallAdapterArgsCommon & { // @public export const toFlatCommunicationIdentifier: (identifier: CommunicationIdentifier) => string; +// @beta +export interface TogetherModeCallFeature { + seatingCoordinates: Map; + streams: Map; +} + +// @beta +export interface TogetherModeSeatingCoordinatesState { + // (undocumented) + height: number; + // (undocumented) + left: number; + // (undocumented) + top: number; + // (undocumented) + width: number; +} + +// @beta +export interface TogetherModeStreamState extends CallFeatureStreamState { + id: number; + // @public + isReceiving: boolean; + mediaStreamType: MediaStreamType; + streamSize?: { + width: number; + height: number; + }; + view?: VideoStreamRendererViewState; +} + // @public export type TopicChangedListener = (event: { topic: string; @@ -4914,13 +5040,13 @@ export const usePropsFor: JSX.Element>(compon export const useSelector: (selector: ParamT, selectorProps?: ParamT extends Selector ? Parameters[1] : undefined, type?: 'calling' | 'chat') => ParamT extends Selector ? ReturnType : undefined; // @public -export const useTeamsCall: () => undefined | TeamsCall; +export const useTeamsCall: () => undefined | /* @conditional-compile-remove(teams-identity-support) */ TeamsCall; // @public -export const useTeamsCallAdapter: (args: Partial, afterCreate?: (adapter: TeamsCallAdapter) => Promise, beforeDispose?: (adapter: TeamsCallAdapter) => Promise) => TeamsCallAdapter | undefined; +export const useTeamsCallAdapter: (args: Partial, afterCreate?: (adapter: TeamsCallAdapter) => Promise, beforeDispose?: (adapter: TeamsCallAdapter) => Promise) => TeamsCallAdapter | undefined; // @public -export const useTeamsCallAgent: () => undefined | TeamsCallAgent; +export const useTeamsCallAgent: () => undefined | /* @conditional-compile-remove(teams-identity-support) */ TeamsCallAgent; // @public export const useTheme: () => Theme; @@ -4980,7 +5106,7 @@ export interface VideoBackgroundReplacementEffect extends BackgroundReplacementC export const VideoGallery: (props: VideoGalleryProps) => JSX.Element; // @public (undocumented) -export type VideoGalleryLayout = 'default' | 'floatingLocalVideo' | 'speaker' | 'focusedContent'; +export type VideoGalleryLayout = 'default' | 'floatingLocalVideo' | 'speaker' | /* @conditional-compile-remove(large-gallery) */ 'largeGallery' | 'focusedContent'; // @public export interface VideoGalleryLocalParticipant extends VideoGalleryParticipant { From 3abfd2d67043c68168f353f7b384ae25bf7db71b Mon Sep 17 00:00:00 2001 From: Chukwuebuka Nwankwo Date: Thu, 10 Oct 2024 18:53:47 +0000 Subject: [PATCH 13/17] change log update --- ...cation-react-14569316-6850-4cc1-8ce1-326ec40aefdb.json | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 change/@azure-communication-react-14569316-6850-4cc1-8ce1-326ec40aefdb.json diff --git a/change/@azure-communication-react-14569316-6850-4cc1-8ce1-326ec40aefdb.json b/change/@azure-communication-react-14569316-6850-4cc1-8ce1-326ec40aefdb.json new file mode 100644 index 00000000000..55d2a7eefc2 --- /dev/null +++ b/change/@azure-communication-react-14569316-6850-4cc1-8ce1-326ec40aefdb.json @@ -0,0 +1,8 @@ +{ + "type": "prerelease", + "area": "feature", + "workstream": "togetherMode", + "comment": "Implemented support logic for call feature streams. This functionality will facilitate the creation of call feature streams that are independent of any participant in the call or the device manager", + "packageName": "@azure/communication-react", + "dependentChangeType": "patch" +} From ce9cb8d74d629f1b310848172eba89cfdd59d9a8 Mon Sep 17 00:00:00 2001 From: Chukwuebuka Nwankwo Date: Tue, 15 Oct 2024 17:40:56 +0000 Subject: [PATCH 14/17] Addressed comments --- .../src/handlers/createCommonHandlers.ts | 27 +-- .../src/CallClientState.ts | 66 +++--- .../src/CallContext.ts | 57 +++-- .../src/CallFeatureStreamUtils.ts | 195 ++++++++++++++++++ .../calling-stateful-client/src/Converter.ts | 2 +- .../src/StatefulCallClient.ts | 8 +- .../src/StreamUtils.test.ts | 2 +- .../src/StreamUtils.ts | 178 ---------------- .../src/TogetherModeSubscriber.ts | 9 +- .../src/index-public.ts | 7 +- .../review/beta/communication-react.api.md | 32 +-- .../CallComposite/MockCallAdapter.ts | 2 +- .../CallComposite/components/CallControls.tsx | 2 +- .../adapter/TestUtils.ts | 2 +- 14 files changed, 320 insertions(+), 269 deletions(-) create mode 100644 packages/calling-stateful-client/src/CallFeatureStreamUtils.ts diff --git a/packages/calling-component-bindings/src/handlers/createCommonHandlers.ts b/packages/calling-component-bindings/src/handlers/createCommonHandlers.ts index 7c85d09a8aa..fff7b5db77f 100644 --- a/packages/calling-component-bindings/src/handlers/createCommonHandlers.ts +++ b/packages/calling-component-bindings/src/handlers/createCommonHandlers.ts @@ -744,20 +744,17 @@ export const createDefaultCommonCallingHandlers = memoizeOne( } const togetherModeStreams = callState.togetherMode.streams; const togetherModeCreateViewResult: TogetherModeStreamViewResult = {}; - if (!togetherModeStreams.size) { + if (!togetherModeStreams.mainVideoStream) { const togetherModeFeature = call?.feature(Features.TogetherMode); await togetherModeFeature?.start(); } else { - for (const stream of togetherModeStreams) { - if (!stream[1].view) { - const createViewResult = await callClient.createCallFeatureView(call.id, stream[1], options); - // SDK currently only supports 1 Video media stream type - if (stream[1].mediaStreamType === 'Video') { - togetherModeCreateViewResult.mainVideoView = createViewResult?.view - ? { view: createViewResult?.view } - : undefined; - } - } + const mainVideoStream = togetherModeStreams.mainVideoStream; + if (mainVideoStream && !mainVideoStream.view) { + const createViewResult = await callClient.createCallFeatureView(call.id, mainVideoStream, options); + // SDK currently only supports 1 Video media stream type + togetherModeCreateViewResult.mainVideoView = createViewResult?.view + ? { view: createViewResult?.view } + : undefined; } } return togetherModeCreateViewResult; @@ -774,14 +771,12 @@ export const createDefaultCommonCallingHandlers = memoizeOne( const togetherModeStreams = callState.togetherMode.streams; - if (!togetherModeStreams.size) { + if (!togetherModeStreams.mainVideoStream) { return; } - for (const stream of togetherModeStreams) { - if (stream[1].view) { - callClient.disposeCallFeatureView(call.id, stream[1]); - } + if (togetherModeStreams.mainVideoStream.view) { + callClient.disposeCallFeatureView(call.id, togetherModeStreams.mainVideoStream); } }; return { diff --git a/packages/calling-stateful-client/src/CallClientState.ts b/packages/calling-stateful-client/src/CallClientState.ts index c36dbded607..9282a65fd99 100644 --- a/packages/calling-stateful-client/src/CallClientState.ts +++ b/packages/calling-stateful-client/src/CallClientState.ts @@ -27,7 +27,8 @@ import type { NetworkDiagnosticType, DiagnosticValueType, DiagnosticQuality, - DiagnosticFlag + DiagnosticFlag, + TogetherModeSeatingPosition } from '@azure/communication-calling'; import { TeamsCallInfo } from '@azure/communication-calling'; import { CallInfo } from '@azure/communication-calling'; @@ -274,23 +275,6 @@ export interface RaiseHandCallFeatureState { localParticipantRaisedHand?: RaisedHandState; } -/* @conditional-compile-remove(together-mode) */ -/** - * State only version of {@link @azure/communication-calling#TogetherModeCallFeature}. {@link StatefulCallClient} will - * automatically listen for raised hands on the call and update the state exposed by {@link StatefulCallClient} accordingly. - * @beta - */ -export interface TogetherModeCallFeatureState { - /** - * Proxy of {@link @azure/communication-calling#TogetherModeCallFeature.togetherModeStream}. - */ - streams: Map; - /** - * Proxy of {@link @azure/communication-calling#TogetherModeCallFeature.TogetherModeSeatingMap}. - */ - seatingCoordinates: Map; -} - /* @conditional-compile-remove(together-mode) */ /** * @beta @@ -310,7 +294,7 @@ export interface CallFeatureStreamState { * State only version of {@link @azure/communication-calling#TogetherModeVideoStream}. * @beta */ -export interface TogetherModeStreamState extends CallFeatureStreamState { +export interface TogetherModeStreamViewState extends CallFeatureStreamState { /** * Proxy of {@link @azure/communication-calling#TogetherModeVideoStream.id}. */ @@ -333,6 +317,11 @@ export interface TogetherModeStreamState extends CallFeatureStreamState { * Proxy of {@link @azure/communication-calling#RemoteVideoStream.size}. */ streamSize?: { width: number; height: number }; + + /** + * Proxy of {@link @azure/communication-calling#TogetherModeVideoStream.position}. + */ + recalculateSeatingPositions: (width: number, height: number) => void; } /* @conditional-compile-remove(together-mode) */ @@ -340,15 +329,36 @@ export interface TogetherModeStreamState extends CallFeatureStreamState { * State only version of {@link @azure/communication-calling#TogetherModeSeatingMap}. * @beta */ -export interface TogetherModeSeatingCoordinatesState { - // the y coordinate of the participant seating position in the together mode stream - top: number; - // the x coordinate of the participant seating position in the together mode stream - left: number; - // the width of the participant in the together mode stream - width: number; - // the height of the participant in the together mode stream - height: number; +export interface TogetherModeSeatingPositionState { + participantId: string; + position: TogetherModeSeatingPosition; +} + +/* @conditional-compile-remove(together-mode) */ +/** + * Interface representing the streams in Together Mode. + * + * @beta + */ +export interface TogetherModeStreamsState { + mainVideoStream?: TogetherModeStreamViewState; +} + +/* @conditional-compile-remove(together-mode) */ +/** + * State only version of {@link @azure/communication-calling#TogetherModeCallFeature}. {@link StatefulCallClient} will + * automatically listen for raised hands on the call and update the state exposed by {@link StatefulCallClient} accordingly. + * @beta + */ +export interface TogetherModeCallFeatureState { + /** + * Proxy of {@link @azure/communication-calling#TogetherModeCallFeature.togetherModeStream}. + */ + streams: TogetherModeStreamsState; + /** + * Proxy of {@link @azure/communication-calling#TogetherModeCallFeature.TogetherModeSeatingMap}. + */ + seatingPositions: TogetherModeSeatingPositionState[]; } /** diff --git a/packages/calling-stateful-client/src/CallContext.ts b/packages/calling-stateful-client/src/CallContext.ts index f7568bb489e..83c5a3cf687 100644 --- a/packages/calling-stateful-client/src/CallContext.ts +++ b/packages/calling-stateful-client/src/CallContext.ts @@ -47,8 +47,6 @@ import { CallErrorTarget, CallError } from './CallClientState'; -/* @conditional-compile-remove(together-mode) */ -import { TogetherModeStreamState, TogetherModeSeatingCoordinatesState } from './CallClientState'; /* @conditional-compile-remove(breakout-rooms) */ import { NotificationTarget, CallNotification, CallNotifications } from './CallClientState'; import { TeamsIncomingCallState } from './CallClientState'; @@ -68,6 +66,12 @@ import { SpotlightedParticipant } from '@azure/communication-calling'; import { LocalRecordingInfo } from '@azure/communication-calling'; /* @conditional-compile-remove(local-recording-notification) */ import { RecordingInfo } from '@azure/communication-calling'; +/* @conditional-compile-remove(together-mode) */ +import { + TogetherModeStreamsState, + TogetherModeStreamViewState, + TogetherModeSeatingPositionState +} from './CallClientState'; enableMapSet(); // Needed to generate state diff for verbose logging. @@ -459,25 +463,33 @@ export class CallContext { } /* @conditional-compile-remove(together-mode) */ - public setTogetherModeVideoStream(callId: string, addedStreams: TogetherModeVideoStream[]): void { + public setTogetherModeVideoStream( + callId: string, + addedStreams: TogetherModeVideoStream[], + recalculateSeatingPositions: (width: number, height: number) => void + ): void { this.modifyState((draft: CallClientState) => { const call = draft.calls[this._callIdHistory.latestCallId(callId)]; if (call) { - const streamsToAdd: Map = new Map(); + const togetherModeStreams: TogetherModeStreamsState = { + mainVideoStream: undefined + }; for (const stream of addedStreams) { - const streamToAdd: TogetherModeStreamState = { + const streamToAdd: TogetherModeStreamViewState = { feature: 'togetherMode', id: stream.id, view: undefined, mediaStreamType: stream.mediaStreamType, - isReceiving: stream.isReceiving + isReceiving: stream.isReceiving, + recalculateSeatingPositions }; - streamsToAdd.set(stream.mediaStreamType, streamToAdd); - } - call.togetherMode.streams = streamsToAdd; - if (!call.togetherMode.seatingCoordinates) { - call.togetherMode.seatingCoordinates = new Map(); + // SDK does not support panomaric media type + if (stream.mediaStreamType === 'Video') { + togetherModeStreams.mainVideoStream = streamToAdd; + call.togetherMode.seatingPositions = []; + } } + call.togetherMode.streams = togetherModeStreams; } }); } @@ -488,8 +500,9 @@ export class CallContext { const call = draft.calls[this._callIdHistory.latestCallId(callId)]; if (call) { for (const stream of removedStream) { - if (stream.mediaStreamType in call.togetherMode.streams) { - call.togetherMode.streams.delete(stream.mediaStreamType); + if (stream.mediaStreamType === 'Video') { + call.togetherMode.streams.mainVideoStream = undefined; + call.togetherMode.seatingPositions = []; } } } @@ -501,7 +514,15 @@ export class CallContext { this.modifyState((draft: CallClientState) => { const call = draft.calls[this._callIdHistory.latestCallId(callId)]; if (call) { - call.togetherMode.seatingCoordinates = seatingMap; + const seatingPositions: TogetherModeSeatingPositionState[] = []; + for (const [key, value] of seatingMap.entries()) { + const participantPosition: TogetherModeSeatingPositionState = { + participantId: key, + position: value + }; + seatingPositions.push(participantPosition); + } + call.togetherMode.seatingPositions = seatingPositions; } }); } @@ -755,9 +776,11 @@ export class CallContext { const call = draft.calls[this._callIdHistory.latestCallId(callId)]; /* @conditional-compile-remove(together-mode) */ if (call) { - const togetherModeStream = call.togetherMode.streams.get(togetherModeStreamType); - if (togetherModeStream) { - togetherModeStream.view = view; + if (togetherModeStreamType === 'Video') { + const togetherModeStream = call.togetherMode.streams.mainVideoStream; + if (togetherModeStream) { + togetherModeStream.view = view; + } } } }); diff --git a/packages/calling-stateful-client/src/CallFeatureStreamUtils.ts b/packages/calling-stateful-client/src/CallFeatureStreamUtils.ts new file mode 100644 index 00000000000..cd83fe078ad --- /dev/null +++ b/packages/calling-stateful-client/src/CallFeatureStreamUtils.ts @@ -0,0 +1,195 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +/* @conditional-compile-remove(together-mode) */ +import { CreateViewOptions, VideoStreamRenderer } from '@azure/communication-calling'; +/* @conditional-compile-remove(together-mode) */ +import { CallContext } from './CallContext'; +/* @conditional-compile-remove(together-mode) */ +import { CallFeatureStreamState, CreateViewResult } from './index-public'; +/* @conditional-compile-remove(together-mode) */ +import { InternalCallContext } from './InternalCallContext'; +/* @conditional-compile-remove(together-mode) */ +import { _logStreamEvent } from './StreamUtilsLogging'; +/* @conditional-compile-remove(together-mode) */ +import { EventNames } from './Logger'; +/* @conditional-compile-remove(together-mode) */ +import { convertFromSDKToDeclarativeVideoStreamRendererView } from './Converter'; +/* @conditional-compile-remove(together-mode) */ +import { TogetherModeStreamViewState } from './CallClientState'; + +/* @conditional-compile-remove(together-mode) */ +/** + * @private + * + */ +export function createCallFeatureView( + context: CallContext, + internalContext: InternalCallContext, + callId: string | undefined, + stream: TogetherModeStreamViewState /* @conditional-compile-remove(together-mode) */, + options?: CreateViewOptions +): Promise { + const streamType = stream.mediaStreamType; + + if (callId && isCallFeatureStream(stream)) { + return createCallFeatureViewVideo(context, internalContext, callId, stream, options); + } else { + _logStreamEvent(EventNames.CREATE_STREAM_INVALID_PARAMS, { streamType }); + return Promise.resolve(undefined); + } +} + +/* @conditional-compile-remove(together-mode) */ +// This function is used to create a view for a stream that is part of a call feature. +async function createCallFeatureViewVideo( + context: CallContext, + internalContext: InternalCallContext, + callId: string, + stream: TogetherModeStreamViewState, + options?: CreateViewOptions +): Promise { + const streamEventType = 'createViewCallFeature'; + + const streamType = stream?.mediaStreamType; + const callFeatureStreamId = stream && stream.id; + const streamLogInfo = { + callId, + undefined, + streamId: callFeatureStreamId, + streamType, + streamEventType + }; + + // make different logging announcement based on whether or not we are starting a local or remote + _logStreamEvent(EventNames.CREATING_VIEW, streamLogInfo); + + const featureName = getStreamFeatureName(stream); + + // if we have a participant Id and a stream get the remote info, else get the local render info from state. + const renderInfo = internalContext.getCallFeatureRenderInfo(callId, featureName, stream.mediaStreamType); + if (!renderInfo) { + _logStreamEvent(EventNames.STREAM_NOT_FOUND, streamLogInfo); + return; + } + + if (renderInfo.status === 'Rendered') { + _logStreamEvent(EventNames.STREAM_ALREADY_RENDERED, streamLogInfo); + return; + } + + if (renderInfo.status === 'Rendering') { + // Do not log to console here as this is a very common situation due to UI rerenders while + // the video rendering is in progress. + _logStreamEvent(EventNames.STREAM_RENDERING, streamLogInfo); + return; + } + + // "Stopping" only happens if the stream was in "rendering" but `disposeView` was called. + // Now that `createView` has been re-called, we can flip the state back to "rendering". + if (renderInfo.status === 'Stopping') { + _logStreamEvent(EventNames.STREAM_STOPPING, streamLogInfo); + internalContext.setCallFeatureRenderInfo( + callId, + featureName, + stream.mediaStreamType, + renderInfo.stream, + 'Rendering', + renderInfo.renderer + ); + return; + } + + const renderer = new VideoStreamRenderer(renderInfo.stream); + internalContext.setCallFeatureRenderInfo( + callId, + featureName, + stream.mediaStreamType, + renderInfo.stream, + 'Rendering', + undefined + ); + + let view; + try { + view = await renderer.createView(options); + } catch (e) { + _logStreamEvent(EventNames.CREATE_STREAM_FAIL, streamLogInfo, e); + internalContext.setCallFeatureRenderInfo( + callId, + featureName, + stream.mediaStreamType, + renderInfo.stream, + 'NotRendered', + undefined + ); + throw e; + } + + // Since render could take some time, we need to check if the stream is still valid and if we received a signal to + // stop rendering. + const refreshedRenderInfo = internalContext.getCallFeatureRenderInfo(callId, featureName, stream.mediaStreamType); + + if (!refreshedRenderInfo) { + // RenderInfo was removed. This should not happen unless stream was removed from the call so dispose the renderer + // and clean up the state. + _logStreamEvent(EventNames.RENDER_INFO_NOT_FOUND, streamLogInfo); + renderer.dispose(); + context.setTogetherModeVideoStreamRendererView(callId, stream.mediaStreamType, undefined); + return; + } + + if (refreshedRenderInfo.status === 'Stopping') { + // Stop render was called on this stream after we had started rendering. We will dispose this view and do not + // put the view into the state. + _logStreamEvent(EventNames.CREATED_STREAM_STOPPING, streamLogInfo); + renderer.dispose(); + internalContext.setCallFeatureRenderInfo( + callId, + featureName, + stream.mediaStreamType, + refreshedRenderInfo.stream, + 'NotRendered', + undefined + ); + context.setTogetherModeVideoStreamRendererView(callId, stream.mediaStreamType, undefined); + return; + } + + // Else the stream still exists and status is not telling us to stop rendering. Complete the render process by + // updating the state. + internalContext.setCallFeatureRenderInfo( + callId, + featureName, + stream.mediaStreamType, + refreshedRenderInfo.stream, + 'Rendered', + renderer + ); + context.setTogetherModeVideoStreamRendererView( + callId, + stream.mediaStreamType, + convertFromSDKToDeclarativeVideoStreamRendererView(view) + ); + _logStreamEvent(EventNames.VIEW_RENDER_SUCCEED, streamLogInfo); + + return { + renderer, + view + }; +} + +/* @conditional-compile-remove(together-mode) */ +const getStreamFeatureName = (stream: TogetherModeStreamViewState): string => { + if ('feature' in stream) { + return stream.feature; + } + throw new Error('Feature name not found'); +}; + +/* @conditional-compile-remove(together-mode) */ +/** + * @private + */ +function isCallFeatureStream(stream: CallFeatureStreamState): boolean { + return 'feature' in stream || false; +} diff --git a/packages/calling-stateful-client/src/Converter.ts b/packages/calling-stateful-client/src/Converter.ts index 594f6cb64bd..1bdab65e474 100644 --- a/packages/calling-stateful-client/src/Converter.ts +++ b/packages/calling-stateful-client/src/Converter.ts @@ -157,7 +157,7 @@ export function convertSdkCallToDeclarativeCall(call: CallCommon): CallState { pptLive: { isActive: false }, raiseHand: { raisedHands: [] }, /* @conditional-compile-remove(together-mode) */ - togetherMode: { streams: new Map(), seatingCoordinates: new Map() }, + togetherMode: { streams: {}, seatingPositions: [] }, localParticipantReaction: undefined, transcription: { isTranscriptionActive: false }, screenShareRemoteParticipant: undefined, diff --git a/packages/calling-stateful-client/src/StatefulCallClient.ts b/packages/calling-stateful-client/src/StatefulCallClient.ts index e5fb458d6a6..45157b32f77 100644 --- a/packages/calling-stateful-client/src/StatefulCallClient.ts +++ b/packages/calling-stateful-client/src/StatefulCallClient.ts @@ -7,13 +7,11 @@ import { CallClient, CallClientOptions, CreateViewOptions, DeviceManager } from import { Features } from '@azure/communication-calling'; import { CallClientState, LocalVideoStreamState, RemoteVideoStreamState } from './CallClientState'; /* @conditional-compile-remove(together-mode) */ -import { CallFeatureStreamState, TogetherModeStreamState } from './CallClientState'; +import { CallFeatureStreamState, TogetherModeStreamViewState } from './CallClientState'; import { CallContext } from './CallContext'; import { callAgentDeclaratify, DeclarativeCallAgent } from './CallAgentDeclarative'; import { InternalCallContext } from './InternalCallContext'; import { createView, disposeView, CreateViewResult } from './StreamUtils'; -/* @conditional-compile-remove(together-mode) */ -import { createCallFeatureView } from './StreamUtils'; import { CommunicationIdentifier, CommunicationUserIdentifier, getIdentifierKind } from '@azure/communication-common'; import { toFlatCommunicationIdentifier, @@ -26,6 +24,8 @@ import { DeclarativeTeamsCallAgent, teamsCallAgentDeclaratify } from './TeamsCal /* @conditional-compile-remove(teams-identity-support) */ import { MicrosoftTeamsUserIdentifier } from '@azure/communication-common'; import { videoStreamRendererViewDeclaratify } from './VideoStreamRendererViewDeclarative'; +/* @conditional-compile-remove(together-mode) */ +import { createCallFeatureView } from './CallFeatureStreamUtils'; /** * Defines the methods that allow CallClient {@link @azure/communication-calling#CallClient} to be used statefully. @@ -447,7 +447,7 @@ export const createStatefulCallClientWithDeps = ( configurable: false, value: async ( callId: string | undefined, - stream: TogetherModeStreamState, + stream: TogetherModeStreamViewState, options?: CreateViewOptions ): Promise => { const result = await createCallFeatureView(context, internalContext, callId, stream, options); diff --git a/packages/calling-stateful-client/src/StreamUtils.test.ts b/packages/calling-stateful-client/src/StreamUtils.test.ts index aeec0d6033e..ca5409811ee 100644 --- a/packages/calling-stateful-client/src/StreamUtils.test.ts +++ b/packages/calling-stateful-client/src/StreamUtils.test.ts @@ -90,7 +90,7 @@ function createMockCall(mockCallId: string): CallState { localRecording: { isLocalRecordingActive: false }, raiseHand: { raisedHands: [] }, /* @conditional-compile-remove(together-mode) */ - togetherMode: { streams: new Map(), seatingCoordinates: new Map() }, + togetherMode: { streams: {}, seatingPositions: [] }, localParticipantReaction: undefined, transcription: { isTranscriptionActive: false }, screenShareRemoteParticipant: undefined, diff --git a/packages/calling-stateful-client/src/StreamUtils.ts b/packages/calling-stateful-client/src/StreamUtils.ts index 8202e5668c0..4ca1296e0b6 100644 --- a/packages/calling-stateful-client/src/StreamUtils.ts +++ b/packages/calling-stateful-client/src/StreamUtils.ts @@ -10,8 +10,6 @@ import { } from '@azure/communication-calling'; import { CommunicationIdentifierKind } from '@azure/communication-common'; import { LocalVideoStreamState, RemoteVideoStreamState } from './CallClientState'; -/* @conditional-compile-remove(together-mode) */ -import { CallFeatureStreamState, TogetherModeStreamState } from './CallClientState'; import { CallContext } from './CallContext'; import { convertSdkLocalStreamToDeclarativeLocalStream, @@ -261,153 +259,6 @@ async function createViewVideo( }; } -/* @conditional-compile-remove(together-mode) */ -const getStreamFeatureName = (stream: TogetherModeStreamState): string => { - if ('feature' in stream) { - return stream.feature; - } - throw new Error('Feature name not found'); -}; - -/* @conditional-compile-remove(together-mode) */ -// This function is used to create a view for a stream that is part of a call feature. -async function createCallFeatureViewVideo( - context: CallContext, - internalContext: InternalCallContext, - callId: string, - stream: TogetherModeStreamState, - options?: CreateViewOptions -): Promise { - const streamEventType = 'createViewCallFeature'; - - const streamType = stream?.mediaStreamType; - const callFeatureStreamId = stream && stream.id; - const streamLogInfo = { - callId, - undefined, - streamId: callFeatureStreamId, - streamType, - streamEventType - }; - - // make different logging announcement based on whether or not we are starting a local or remote - _logStreamEvent(EventNames.CREATING_VIEW, streamLogInfo); - - const featureName = getStreamFeatureName(stream); - - // if we have a participant Id and a stream get the remote info, else get the local render info from state. - const renderInfo = internalContext.getCallFeatureRenderInfo(callId, featureName, stream.mediaStreamType); - if (!renderInfo) { - _logStreamEvent(EventNames.STREAM_NOT_FOUND, streamLogInfo); - return; - } - - if (renderInfo.status === 'Rendered') { - _logStreamEvent(EventNames.STREAM_ALREADY_RENDERED, streamLogInfo); - return; - } - - if (renderInfo.status === 'Rendering') { - // Do not log to console here as this is a very common situation due to UI rerenders while - // the video rendering is in progress. - _logStreamEvent(EventNames.STREAM_RENDERING, streamLogInfo); - return; - } - - // "Stopping" only happens if the stream was in "rendering" but `disposeView` was called. - // Now that `createView` has been re-called, we can flip the state back to "rendering". - if (renderInfo.status === 'Stopping') { - _logStreamEvent(EventNames.STREAM_STOPPING, streamLogInfo); - internalContext.setCallFeatureRenderInfo( - callId, - featureName, - stream.mediaStreamType, - renderInfo.stream, - 'Rendering', - renderInfo.renderer - ); - return; - } - - const renderer = new VideoStreamRenderer(renderInfo.stream); - internalContext.setCallFeatureRenderInfo( - callId, - featureName, - stream.mediaStreamType, - renderInfo.stream, - 'Rendering', - undefined - ); - - let view; - try { - view = await renderer.createView(options); - } catch (e) { - _logStreamEvent(EventNames.CREATE_STREAM_FAIL, streamLogInfo, e); - internalContext.setCallFeatureRenderInfo( - callId, - featureName, - stream.mediaStreamType, - renderInfo.stream, - 'NotRendered', - undefined - ); - throw e; - } - - // Since render could take some time, we need to check if the stream is still valid and if we received a signal to - // stop rendering. - const refreshedRenderInfo = internalContext.getCallFeatureRenderInfo(callId, featureName, stream.mediaStreamType); - - if (!refreshedRenderInfo) { - // RenderInfo was removed. This should not happen unless stream was removed from the call so dispose the renderer - // and clean up the state. - _logStreamEvent(EventNames.RENDER_INFO_NOT_FOUND, streamLogInfo); - renderer.dispose(); - context.setTogetherModeVideoStreamRendererView(callId, stream.mediaStreamType, undefined); - return; - } - - if (refreshedRenderInfo.status === 'Stopping') { - // Stop render was called on this stream after we had started rendering. We will dispose this view and do not - // put the view into the state. - _logStreamEvent(EventNames.CREATED_STREAM_STOPPING, streamLogInfo); - renderer.dispose(); - internalContext.setCallFeatureRenderInfo( - callId, - featureName, - stream.mediaStreamType, - refreshedRenderInfo.stream, - 'NotRendered', - undefined - ); - context.setTogetherModeVideoStreamRendererView(callId, stream.mediaStreamType, undefined); - return; - } - - // Else the stream still exists and status is not telling us to stop rendering. Complete the render process by - // updating the state. - internalContext.setCallFeatureRenderInfo( - callId, - featureName, - stream.mediaStreamType, - refreshedRenderInfo.stream, - 'Rendered', - renderer - ); - context.setTogetherModeVideoStreamRendererView( - callId, - stream.mediaStreamType, - convertFromSDKToDeclarativeVideoStreamRendererView(view) - ); - _logStreamEvent(EventNames.VIEW_RENDER_SUCCEED, streamLogInfo); - - return { - renderer, - view - }; -} - async function createViewUnparentedVideo( context: CallContext, internalContext: InternalCallContext, @@ -658,27 +509,6 @@ export function createView( } } -/* @conditional-compile-remove(together-mode) */ -/** - * @private - */ -export function createCallFeatureView( - context: CallContext, - internalContext: InternalCallContext, - callId: string | undefined, - stream: TogetherModeStreamState /* @conditional-compile-remove(together-mode) */, - options?: CreateViewOptions -): Promise { - const streamType = stream.mediaStreamType; - - if (callId && isCallFeatureStream(stream)) { - return createCallFeatureViewVideo(context, internalContext, callId, stream, options); - } else { - _logStreamEvent(EventNames.CREATE_STREAM_INVALID_PARAMS, { streamType }); - return Promise.resolve(undefined); - } -} - /** * @private */ @@ -775,11 +605,3 @@ export function disposeAllViews(context: CallContext, internalContext: InternalC disposeAllViewsFromCall(context, internalContext, callId); } } - -/* @conditional-compile-remove(together-mode) */ -/** - * @private - */ -function isCallFeatureStream(stream: CallFeatureStreamState): boolean { - return 'feature' in stream || false; -} diff --git a/packages/calling-stateful-client/src/TogetherModeSubscriber.ts b/packages/calling-stateful-client/src/TogetherModeSubscriber.ts index 64f791becc0..c3706e812f9 100644 --- a/packages/calling-stateful-client/src/TogetherModeSubscriber.ts +++ b/packages/calling-stateful-client/src/TogetherModeSubscriber.ts @@ -7,9 +7,6 @@ import { TogetherModeCallFeature, TogetherModeVideoStream } from '@azure/communi import { CallContext } from './CallContext'; /* @conditional-compile-remove(together-mode) */ import { CallIdRef } from './CallIdRef'; -/** - * @private - */ /* @conditional-compile-remove(together-mode) */ /** @@ -36,12 +33,16 @@ export class TogetherModeSubscriber { this._togetherMode.off('togetherModeStreamsUpdated', this.onTogetherModeStreamUpdated); }; + private recalculateSeatingPositions = (width: number, height: number): void => { + this._togetherMode.sceneSize = { width, height }; + }; + private onTogetherModeStreamUpdated = (args: { added: TogetherModeVideoStream[]; removed: TogetherModeVideoStream[]; }): void => { if (args.added) { - this._context.setTogetherModeVideoStream(this._callIdRef.callId, args.added); + this._context.setTogetherModeVideoStream(this._callIdRef.callId, args.added, this.recalculateSeatingPositions); } if (args.removed) { this._context.removeTogetherModeVideoStream(this._callIdRef.callId, args.removed); diff --git a/packages/calling-stateful-client/src/index-public.ts b/packages/calling-stateful-client/src/index-public.ts index b19c30351b3..ea08377e495 100644 --- a/packages/calling-stateful-client/src/index-public.ts +++ b/packages/calling-stateful-client/src/index-public.ts @@ -36,9 +36,10 @@ export type { TogetherModeCallFeatureState as TogetherModeCallFeature } from './ /* @conditional-compile-remove(together-mode) */ export type { CallFeatureStreamState, - TogetherModeStreamState, - TogetherModeSeatingCoordinatesState, - CallFeatureStreamName + TogetherModeStreamViewState, + TogetherModeSeatingPositionState, + CallFeatureStreamName, + TogetherModeStreamsState } from './CallClientState'; export type { RaisedHandState } from './CallClientState'; diff --git a/packages/communication-react/review/beta/communication-react.api.md b/packages/communication-react/review/beta/communication-react.api.md index 5255316be91..057e1615407 100644 --- a/packages/communication-react/review/beta/communication-react.api.md +++ b/packages/communication-react/review/beta/communication-react.api.md @@ -111,8 +111,9 @@ import { TeamsIncomingCall } from '@azure/communication-calling'; import { TeamsMeetingIdLocator } from '@azure/communication-calling'; import { TeamsMeetingLinkLocator } from '@azure/communication-calling'; import { Theme } from '@fluentui/react'; -import { TogetherModeStreamViewResult } from '@internal/react-components/dist/dist-esm/types/TogetherModeTypes'; -import { TogetherModeStreamViewResult as TogetherModeStreamViewResult_2 } from '@internal/react-components/dist/dist-esm/types'; +import type { TogetherModeSeatingPosition } from '@azure/communication-calling'; +import { TogetherModeStreamViewResult } from '@internal/react-components/dist/dist-esm/types'; +import { TogetherModeStreamViewResult as TogetherModeStreamViewResult_2 } from '@internal/react-components/dist/dist-esm/types/TogetherModeTypes'; import { TransferEventArgs } from '@azure/communication-calling'; import { TypingIndicatorReceivedEvent } from '@azure/communication-chat'; import { UnknownIdentifier } from '@azure/communication-common'; @@ -445,7 +446,7 @@ export interface CallAdapterCallOperations { allowUnsupportedBrowserVersion(): void; createStreamView(remoteUserId?: string, options?: VideoStreamOptions): Promise; // @beta - createTogetherModeStreamViews(options?: VideoStreamOptions): Promise; + createTogetherModeStreamViews(options?: VideoStreamOptions): Promise; disposeLocalVideoStreamView(): Promise; disposeRemoteVideoStreamView(remoteUserId: string): Promise; disposeScreenShareStreamView(remoteUserId: string): Promise; @@ -1211,7 +1212,7 @@ export interface CallWithChatAdapterManagement { askDevicePermission(constrain: PermissionConstraints): Promise; createStreamView(remoteUserId?: string, options?: VideoStreamOptions): Promise; // @beta - createTogetherModeStreamViews(options?: VideoStreamOptions): Promise; + createTogetherModeStreamViews(options?: VideoStreamOptions): Promise; // @beta deleteImage(imageId: string): Promise; deleteMessage(messageId: string): Promise; @@ -2194,7 +2195,7 @@ export interface CommonCallingHandlers { // (undocumented) onCreateRemoteStreamView: (userId: string, options?: VideoStreamOptions) => Promise; // @beta - onCreateTogetherModeStreamView: (options?: VideoStreamOptions) => Promise; + onCreateTogetherModeStreamView: (options?: VideoStreamOptions) => Promise; // (undocumented) onDisposeLocalScreenShareStreamView: () => Promise; // (undocumented) @@ -4898,28 +4899,31 @@ export const toFlatCommunicationIdentifier: (identifier: CommunicationIdentifier // @beta export interface TogetherModeCallFeature { - seatingCoordinates: Map; - streams: Map; + seatingPositions: TogetherModeSeatingPositionState[]; + streams: TogetherModeStreamsState; } // @beta -export interface TogetherModeSeatingCoordinatesState { +export interface TogetherModeSeatingPositionState { // (undocumented) - height: number; - // (undocumented) - left: number; + participantId: string; // (undocumented) - top: number; + position: TogetherModeSeatingPosition; +} + +// @beta +export interface TogetherModeStreamsState { // (undocumented) - width: number; + mainVideoStream?: TogetherModeStreamViewState; } // @beta -export interface TogetherModeStreamState extends CallFeatureStreamState { +export interface TogetherModeStreamViewState extends CallFeatureStreamState { id: number; // @public isReceiving: boolean; mediaStreamType: MediaStreamType; + recalculateSeatingPositions: (width: number, height: number) => void; streamSize?: { width: number; height: number; diff --git a/packages/react-composites/src/composites/CallComposite/MockCallAdapter.ts b/packages/react-composites/src/composites/CallComposite/MockCallAdapter.ts index e54013dce9e..f5596d4e244 100644 --- a/packages/react-composites/src/composites/CallComposite/MockCallAdapter.ts +++ b/packages/react-composites/src/composites/CallComposite/MockCallAdapter.ts @@ -264,7 +264,7 @@ const createDefaultCallAdapterState = (role?: ParticipantRole): CallAdapterState remoteParticipantsEnded: {}, raiseHand: { raisedHands: [] }, /* @conditional-compile-remove(together-mode) */ - togetherMode: { streams: new Map(), seatingCoordinates: new Map() }, + togetherMode: { streams: {}, seatingPositions: [] }, pptLive: { isActive: false }, localParticipantReaction: undefined, role, diff --git a/packages/react-composites/src/composites/CallComposite/components/CallControls.tsx b/packages/react-composites/src/composites/CallComposite/components/CallControls.tsx index ba9d28aff2b..c6976e1ee70 100644 --- a/packages/react-composites/src/composites/CallComposite/components/CallControls.tsx +++ b/packages/react-composites/src/composites/CallComposite/components/CallControls.tsx @@ -43,7 +43,7 @@ import { callStatusSelector } from '../../CallComposite/selectors/callStatusSele /* @conditional-compile-remove(DNS) */ import { _isSafari } from '../../CallComposite/utils'; import { getIsRoomsCall, getReactionResources, getRole } from '../selectors/baseSelectors'; -/* @conditional-compile-remove(DNS) */ +/* @conditional-compile-remove(unsupported-browser) */ import { getEnvironmentInfo } from '../selectors/baseSelectors'; /* @conditional-compile-remove(DNS) */ import { diff --git a/packages/react-composites/src/composites/CallWithChatComposite/adapter/TestUtils.ts b/packages/react-composites/src/composites/CallWithChatComposite/adapter/TestUtils.ts index 9ad84d45cd8..b5acefb7e25 100644 --- a/packages/react-composites/src/composites/CallWithChatComposite/adapter/TestUtils.ts +++ b/packages/react-composites/src/composites/CallWithChatComposite/adapter/TestUtils.ts @@ -244,7 +244,7 @@ function createMockCall(mockCallId: string): CallState { dominantSpeakers: undefined, raiseHand: { raisedHands: [] }, /* @conditional-compile-remove(together-mode) */ - togetherMode: { streams: new Map(), seatingCoordinates: new Map() }, + togetherMode: { streams: {}, seatingPositions: [] }, pptLive: { isActive: false }, localParticipantReaction: undefined, captionsFeature: { From 87e9defaa3e91e7aaef5998352bbb16c858fb4c0 Mon Sep 17 00:00:00 2001 From: Chukwuebuka Nwankwo Date: Thu, 24 Oct 2024 15:24:20 +0000 Subject: [PATCH 15/17] Addresse comments by moving createCallFeatureView and disposeCallFeatureView to its own file --- ...-206ba832-b149-4702-9fbe-2995f7b92f36.json | 8 ++ .../src/CallClientState.ts | 41 ++----- .../src/CallContext.ts | 98 +++++++++++----- .../src/CallFeatureStreamUtils.ts | 108 +++++++++++++++++- .../src/CallSubscriber.ts | 1 + .../calling-stateful-client/src/Converter.ts | 27 ++++- .../src/InternalCallContext.ts | 7 +- .../src/StatefulCallClient.ts | 9 +- .../src/StreamUtils.test.ts | 2 +- .../src/TogetherModeSubscriber.ts | 96 ++++++++++++++-- .../src/TogetherModeVideoStreamSubscriber.ts | 62 ++++++++++ .../review/beta/communication-react.api.md | 25 ++-- .../CallComposite/MockCallAdapter.ts | 10 +- .../adapter/TestUtils.ts | 2 +- 14 files changed, 395 insertions(+), 101 deletions(-) create mode 100644 change/@azure-communication-react-206ba832-b149-4702-9fbe-2995f7b92f36.json create mode 100644 packages/calling-stateful-client/src/TogetherModeVideoStreamSubscriber.ts diff --git a/change/@azure-communication-react-206ba832-b149-4702-9fbe-2995f7b92f36.json b/change/@azure-communication-react-206ba832-b149-4702-9fbe-2995f7b92f36.json new file mode 100644 index 00000000000..c1c737b1635 --- /dev/null +++ b/change/@azure-communication-react-206ba832-b149-4702-9fbe-2995f7b92f36.json @@ -0,0 +1,8 @@ +{ + "type": "prerelease", + "area": "feature", + "workstream": "togetherMode", + "comment": "Addressed comments by moving createCallFeatureView and disposeCallFeatureView to its own file", + "packageName": "@azure/communication-react", + "dependentChangeType": "patch" +} diff --git a/packages/calling-stateful-client/src/CallClientState.ts b/packages/calling-stateful-client/src/CallClientState.ts index 9282a65fd99..6393308a416 100644 --- a/packages/calling-stateful-client/src/CallClientState.ts +++ b/packages/calling-stateful-client/src/CallClientState.ts @@ -27,8 +27,7 @@ import type { NetworkDiagnosticType, DiagnosticValueType, DiagnosticQuality, - DiagnosticFlag, - TogetherModeSeatingPosition + DiagnosticFlag } from '@azure/communication-calling'; import { TeamsCallInfo } from '@azure/communication-calling'; import { CallInfo } from '@azure/communication-calling'; @@ -286,7 +285,7 @@ export type CallFeatureStreamName = 'togetherMode'; * @beta */ export interface CallFeatureStreamState { - feature: CallFeatureStreamName; + feature?: CallFeatureStreamName; } /* @conditional-compile-remove(together-mode) */ @@ -294,35 +293,7 @@ export interface CallFeatureStreamState { * State only version of {@link @azure/communication-calling#TogetherModeVideoStream}. * @beta */ -export interface TogetherModeStreamViewState extends CallFeatureStreamState { - /** - * Proxy of {@link @azure/communication-calling#TogetherModeVideoStream.id}. - */ - id: number; - /** - * Proxy of {@link @azure/communication-calling#TogetherModeVideoStream.mediaStreamType}. - */ - mediaStreamType: MediaStreamType; - /** - * Proxy of {@link @azure/communication-calling#TogetherModeVideoStream.isReceiving}. - * @public - */ - isReceiving: boolean; - /** - * {@link VideoStreamRendererView} that is managed by createView/disposeView in {@link StatefulCallClient} - * API. This can be undefined if the stream has not yet been rendered and defined after createView creates the view. - */ - view?: VideoStreamRendererViewState; - /** - * Proxy of {@link @azure/communication-calling#RemoteVideoStream.size}. - */ - streamSize?: { width: number; height: number }; - - /** - * Proxy of {@link @azure/communication-calling#TogetherModeVideoStream.position}. - */ - recalculateSeatingPositions: (width: number, height: number) => void; -} +export interface TogetherModeStreamViewState extends RemoteVideoStreamState, CallFeatureStreamState {} /* @conditional-compile-remove(together-mode) */ /** @@ -331,7 +302,10 @@ export interface TogetherModeStreamViewState extends CallFeatureStreamState { */ export interface TogetherModeSeatingPositionState { participantId: string; - position: TogetherModeSeatingPosition; + top: number; + left: number; + width: number; + height: number; } /* @conditional-compile-remove(together-mode) */ @@ -351,6 +325,7 @@ export interface TogetherModeStreamsState { * @beta */ export interface TogetherModeCallFeatureState { + isActive: boolean; /** * Proxy of {@link @azure/communication-calling#TogetherModeCallFeature.togetherModeStream}. */ diff --git a/packages/calling-stateful-client/src/CallContext.ts b/packages/calling-stateful-client/src/CallContext.ts index 83c5a3cf687..b4cd2f03742 100644 --- a/packages/calling-stateful-client/src/CallContext.ts +++ b/packages/calling-stateful-client/src/CallContext.ts @@ -67,11 +67,7 @@ import { LocalRecordingInfo } from '@azure/communication-calling'; /* @conditional-compile-remove(local-recording-notification) */ import { RecordingInfo } from '@azure/communication-calling'; /* @conditional-compile-remove(together-mode) */ -import { - TogetherModeStreamsState, - TogetherModeStreamViewState, - TogetherModeSeatingPositionState -} from './CallClientState'; +import { TogetherModeStreamViewState, TogetherModeSeatingPositionState } from './CallClientState'; enableMapSet(); // Needed to generate state diff for verbose logging. @@ -463,33 +459,78 @@ export class CallContext { } /* @conditional-compile-remove(together-mode) */ - public setTogetherModeVideoStream( + public setTogetherModeVideoStreams( callId: string, - addedStreams: TogetherModeVideoStream[], - recalculateSeatingPositions: (width: number, height: number) => void + addRemoteVideoStream: TogetherModeStreamViewState[], + removeRemoteVideoStream: TogetherModeStreamViewState[] ): void { this.modifyState((draft: CallClientState) => { const call = draft.calls[this._callIdHistory.latestCallId(callId)]; if (call) { - const togetherModeStreams: TogetherModeStreamsState = { - mainVideoStream: undefined - }; - for (const stream of addedStreams) { - const streamToAdd: TogetherModeStreamViewState = { - feature: 'togetherMode', - id: stream.id, - view: undefined, - mediaStreamType: stream.mediaStreamType, - isReceiving: stream.isReceiving, - recalculateSeatingPositions - }; - // SDK does not support panomaric media type + for (const stream of removeRemoteVideoStream) { if (stream.mediaStreamType === 'Video') { - togetherModeStreams.mainVideoStream = streamToAdd; + call.togetherMode.streams.mainVideoStream = undefined; + call.togetherMode.isActive = false; call.togetherMode.seatingPositions = []; } } - call.togetherMode.streams = togetherModeStreams; + + for (const newStream of addRemoteVideoStream) { + // This should only be called by the subscriber and some properties are add by other components so if the + // stream already exists, only update the values that subscriber knows about. + const mainVideoStream = call.togetherMode.streams.mainVideoStream; + if (mainVideoStream && mainVideoStream.id === newStream.id) { + mainVideoStream.mediaStreamType = newStream.mediaStreamType; + mainVideoStream.isAvailable = newStream.isAvailable; + mainVideoStream.isReceiving = newStream.isReceiving; + } else { + call.togetherMode.streams.mainVideoStream = newStream; + } + call.togetherMode.isActive = true; + } + } + }); + } + + /* @conditional-compile-remove(together-mode) */ + public setTogetherModeVideoStreamIsAvailable(callId: string, streamId: number, isAvailable: boolean): void { + this.modifyState((draft: CallClientState) => { + const call = draft.calls[this._callIdHistory.latestCallId(callId)]; + if (call) { + const stream = call.togetherMode.streams.mainVideoStream; + if (stream && stream?.id === streamId) { + stream.isReceiving = isAvailable; + } + } + }); + } + + /* @conditional-compile-remove(together-mode) */ + public setTogetherModeVideoStreamIsReceiving(callId: string, streamId: number, isReceiving: boolean): void { + this.modifyState((draft: CallClientState) => { + const call = draft.calls[this._callIdHistory.latestCallId(callId)]; + if (call) { + const stream = call.togetherMode.streams.mainVideoStream; + if (stream && stream?.id === streamId) { + stream.isReceiving = isReceiving; + } + } + }); + } + + /* @conditional-compile-remove(together-mode) */ + public setTogetherModeVideoStreamSize( + callId: string, + streamId: number, + size: { width: number; height: number } + ): void { + this.modifyState((draft: CallClientState) => { + const call = draft.calls[this._callIdHistory.latestCallId(callId)]; + if (call) { + const stream = call.togetherMode.streams.mainVideoStream; + if (stream && stream?.id === streamId) { + stream.streamSize = size; + } } }); } @@ -502,7 +543,7 @@ export class CallContext { for (const stream of removedStream) { if (stream.mediaStreamType === 'Video') { call.togetherMode.streams.mainVideoStream = undefined; - call.togetherMode.seatingPositions = []; + call.togetherMode.isActive = false; } } } @@ -510,7 +551,7 @@ export class CallContext { } /* @conditional-compile-remove(together-mode) */ - public setTogetherModeSeatingCoordinatesState(callId: string, seatingMap: TogetherModeSeatingMap): void { + public setTogetherModeSeatingCoordinates(callId: string, seatingMap: TogetherModeSeatingMap): void { this.modifyState((draft: CallClientState) => { const call = draft.calls[this._callIdHistory.latestCallId(callId)]; if (call) { @@ -518,8 +559,12 @@ export class CallContext { for (const [key, value] of seatingMap.entries()) { const participantPosition: TogetherModeSeatingPositionState = { participantId: key, - position: value + top: value.top, + left: value.left, + width: value.width, + height: value.height }; + seatingPositions.push(participantPosition); } call.togetherMode.seatingPositions = seatingPositions; @@ -774,7 +819,6 @@ export class CallContext { ): void { this.modifyState((draft: CallClientState) => { const call = draft.calls[this._callIdHistory.latestCallId(callId)]; - /* @conditional-compile-remove(together-mode) */ if (call) { if (togetherModeStreamType === 'Video') { const togetherModeStream = call.togetherMode.streams.mainVideoStream; diff --git a/packages/calling-stateful-client/src/CallFeatureStreamUtils.ts b/packages/calling-stateful-client/src/CallFeatureStreamUtils.ts index cd83fe078ad..52392cfbdbc 100644 --- a/packages/calling-stateful-client/src/CallFeatureStreamUtils.ts +++ b/packages/calling-stateful-client/src/CallFeatureStreamUtils.ts @@ -26,7 +26,7 @@ export function createCallFeatureView( context: CallContext, internalContext: InternalCallContext, callId: string | undefined, - stream: TogetherModeStreamViewState /* @conditional-compile-remove(together-mode) */, + stream: /* @conditional-compile-remove(together-mode) */ TogetherModeStreamViewState, options?: CreateViewOptions ): Promise { const streamType = stream.mediaStreamType; @@ -64,19 +64,16 @@ async function createCallFeatureViewVideo( _logStreamEvent(EventNames.CREATING_VIEW, streamLogInfo); const featureName = getStreamFeatureName(stream); - // if we have a participant Id and a stream get the remote info, else get the local render info from state. const renderInfo = internalContext.getCallFeatureRenderInfo(callId, featureName, stream.mediaStreamType); if (!renderInfo) { _logStreamEvent(EventNames.STREAM_NOT_FOUND, streamLogInfo); return; } - if (renderInfo.status === 'Rendered') { _logStreamEvent(EventNames.STREAM_ALREADY_RENDERED, streamLogInfo); return; } - if (renderInfo.status === 'Rendering') { // Do not log to console here as this is a very common situation due to UI rerenders while // the video rendering is in progress. @@ -179,8 +176,109 @@ async function createCallFeatureViewVideo( } /* @conditional-compile-remove(together-mode) */ +/** + * @private + */ +export function disposeCallFeatureView( + context: CallContext, + internalContext: InternalCallContext, + callId: string | undefined, + stream: /* @conditional-compile-remove(together-mode) */ TogetherModeStreamViewState +): void { + const streamType = stream.mediaStreamType; + if (callId && isCallFeatureStream(stream)) { + return disposeCallFeatureViewVideo(context, internalContext, callId, stream); + } else { + _logStreamEvent(EventNames.DISPOSE_STREAM_INVALID_PARAMS, { streamType }); + return; + } +} + +/* @conditional-compile-remove(together-mode) */ +/** + * @private + */ +function disposeCallFeatureViewVideo( + context: CallContext, + internalContext: InternalCallContext, + callId: string, + stream: /* @conditional-compile-remove(together-mode) */ TogetherModeStreamViewState +): void { + const streamEventType = 'disposeViewCallFeature'; + + const streamType = stream.mediaStreamType; + const callFeatureStreamId = stream && stream.id; + + const streamLogInfo = { callId, undefined, streamId: callFeatureStreamId, streamType }; + + _logStreamEvent(EventNames.START_DISPOSE_STREAM, streamLogInfo); + + const featureName = getStreamFeatureName(stream); + + if (streamEventType === 'disposeViewCallFeature') { + context.setTogetherModeVideoStreamRendererView(callId, streamType, undefined); + } + + const renderInfo = internalContext.getCallFeatureRenderInfo(callId, featureName, stream.mediaStreamType); + if (!renderInfo) { + _logStreamEvent(EventNames.DISPOSE_INFO_NOT_FOUND, streamLogInfo); + return; + } + + // Nothing to dispose of or clean up -- we can safely exit early here. + if (renderInfo.status === 'NotRendered') { + _logStreamEvent(EventNames.STREAM_ALREADY_DISPOSED, streamLogInfo); + return; + } + + // Status is already marked as "stopping" so we can exit early here. This is because stopping only occurs + // when the stream is being created in createView but hasn't been completed being created yet. The createView + // method will see the "stopping" status and perform the cleanup + if (renderInfo.status === 'Stopping') { + _logStreamEvent(EventNames.STREAM_STOPPING, streamLogInfo); + return; + } + + // If the stream is in the middle of being rendered (i.e. has state "Rendering"), we need the status as + // "stopping" without performing any cleanup. This will tell the `createView` method that it should stop + // rendering and clean up the state once the view has finished being created. + if (renderInfo.status === 'Rendering') { + _logStreamEvent(EventNames.STREAM_STOPPING, streamLogInfo); + internalContext.setCallFeatureRenderInfo( + callId, + featureName, + streamType, + renderInfo.stream, + 'Stopping', + renderInfo.renderer + ); + return; + } + + if (renderInfo.renderer) { + _logStreamEvent(EventNames.DISPOSING_RENDERER, streamLogInfo); + renderInfo.renderer.dispose(); + // Else the state must be in the "Rendered" state, so we can dispose the renderer and clean up the state. + internalContext.setCallFeatureRenderInfo( + callId, + featureName, + streamType, + renderInfo.stream, + 'NotRendered', + undefined + ); + context.setTogetherModeVideoStreamRendererView(callId, streamType, undefined); + } else { + _logStreamEvent(EventNames.RENDERER_NOT_FOUND, streamLogInfo); + } +} + +/* @conditional-compile-remove(together-mode) */ +/** + * @private + */ const getStreamFeatureName = (stream: TogetherModeStreamViewState): string => { - if ('feature' in stream) { + if (stream.feature) { return stream.feature; } throw new Error('Feature name not found'); diff --git a/packages/calling-stateful-client/src/CallSubscriber.ts b/packages/calling-stateful-client/src/CallSubscriber.ts index e67119613ba..b86d41a19d1 100644 --- a/packages/calling-stateful-client/src/CallSubscriber.ts +++ b/packages/calling-stateful-client/src/CallSubscriber.ts @@ -127,6 +127,7 @@ export class CallSubscriber { this._togetherModeSubscriber = new TogetherModeSubscriber( this._callIdRef, this._context, + this._internalContext, this._call.feature(Features.TogetherMode) ); diff --git a/packages/calling-stateful-client/src/Converter.ts b/packages/calling-stateful-client/src/Converter.ts index 1bdab65e474..d3d6d95a70d 100644 --- a/packages/calling-stateful-client/src/Converter.ts +++ b/packages/calling-stateful-client/src/Converter.ts @@ -9,6 +9,9 @@ import { IncomingCall, IncomingCallCommon } from '@azure/communication-calling'; + +/* @conditional-compile-remove(together-mode) */ +import { TogetherModeVideoStream as SdkTogetherModeVideoStream } from '@azure/communication-calling'; import { TeamsIncomingCall } from '@azure/communication-calling'; import { TeamsCaptionsInfo } from '@azure/communication-calling'; @@ -28,6 +31,8 @@ import { VideoStreamRendererViewState as DeclarativeVideoStreamRendererView, CallInfoState } from './CallClientState'; +/* @conditional-compile-remove(calling-beta-sdk) */ +import { TogetherModeStreamViewState as DeclarativeTogetherVideoStream } from './CallClientState'; import { CaptionsInfo } from './CallClientState'; import { TeamsIncomingCallState as DeclarativeTeamsIncomingCall } from './CallClientState'; import { _isTeamsIncomingCall } from './TypeGuards'; @@ -67,7 +72,7 @@ export function convertSdkLocalStreamToDeclarativeLocalStream( * @private */ export function convertSdkRemoteStreamToDeclarativeRemoteStream( - stream: SdkRemoteVideoStream + stream: SdkRemoteVideoStream | /* @conditional-compile-remove(together-mode) */ SdkTogetherModeVideoStream ): DeclarativeRemoteVideoStream { return { id: stream.id, @@ -79,6 +84,24 @@ export function convertSdkRemoteStreamToDeclarativeRemoteStream( }; } +/* @conditional-compile-remove(together-mode) */ +/** + * @private + */ +export function convertSdkTogetherStreamToDeclarativeRemoteStream( + stream: SdkTogetherModeVideoStream +): DeclarativeTogetherVideoStream { + return { + feature: 'togetherMode', + id: stream.id, + mediaStreamType: stream.mediaStreamType, + isAvailable: stream.isAvailable, + isReceiving: stream.isReceiving, + view: undefined, + streamSize: stream.size + }; +} + /** * @private */ @@ -157,7 +180,7 @@ export function convertSdkCallToDeclarativeCall(call: CallCommon): CallState { pptLive: { isActive: false }, raiseHand: { raisedHands: [] }, /* @conditional-compile-remove(together-mode) */ - togetherMode: { streams: {}, seatingPositions: [] }, + togetherMode: { isActive: false, streams: {}, seatingPositions: [] }, localParticipantReaction: undefined, transcription: { isTranscriptionActive: false }, screenShareRemoteParticipant: undefined, diff --git a/packages/calling-stateful-client/src/InternalCallContext.ts b/packages/calling-stateful-client/src/InternalCallContext.ts index c565886e2eb..a6e478c09cf 100644 --- a/packages/calling-stateful-client/src/InternalCallContext.ts +++ b/packages/calling-stateful-client/src/InternalCallContext.ts @@ -7,8 +7,6 @@ import { RemoteVideoStream, VideoStreamRenderer } from '@azure/communication-calling'; -/* @conditional-compile-remove(together-mode) */ -import { RemoteVideoStreamCommon } from '@azure/communication-calling'; import { LocalVideoStreamState } from './CallClientState'; import type { CallContext } from './CallContext'; import { CallIdHistory } from './CallIdHistory'; @@ -51,7 +49,7 @@ export type RemoteRenderInfo = RenderInfo; /** * Internally used to keep track of the status, renderer, and awaiting promise, associated with a CallFeatureVideoStream. */ -export type CallFeatureRenderInfo = RenderInfo; +export type CallFeatureRenderInfo = RenderInfo; /** * Contains internal data used between different Declarative components to share data. @@ -66,6 +64,7 @@ export class InternalCallContext { /* @conditional-compile-remove(together-mode) */ // >>. private _callFeatureRenderInfos = new Map>>(); + // Used for keeping track of rendered LocalVideoStreams that are not part of a Call. private _unparentedRenderInfos = new Map(); private _callIdHistory = new CallIdHistory(); @@ -271,7 +270,7 @@ export class InternalCallContext { callId: string, featureNameKey: string, streamKey: MediaStreamType, - stream: RemoteVideoStreamCommon, + stream: RemoteVideoStream, status: RenderStatus, renderer: VideoStreamRenderer | undefined ): void { diff --git a/packages/calling-stateful-client/src/StatefulCallClient.ts b/packages/calling-stateful-client/src/StatefulCallClient.ts index 45157b32f77..326ba074f8b 100644 --- a/packages/calling-stateful-client/src/StatefulCallClient.ts +++ b/packages/calling-stateful-client/src/StatefulCallClient.ts @@ -25,7 +25,7 @@ import { DeclarativeTeamsCallAgent, teamsCallAgentDeclaratify } from './TeamsCal import { MicrosoftTeamsUserIdentifier } from '@azure/communication-common'; import { videoStreamRendererViewDeclaratify } from './VideoStreamRendererViewDeclarative'; /* @conditional-compile-remove(together-mode) */ -import { createCallFeatureView } from './CallFeatureStreamUtils'; +import { createCallFeatureView, disposeCallFeatureView } from './CallFeatureStreamUtils'; /** * Defines the methods that allow CallClient {@link @azure/communication-calling#CallClient} to be used statefully. @@ -465,6 +465,13 @@ export const createStatefulCallClientWithDeps = ( disposeView(context, internalContext, callId, participantIdKind, stream); } }); + /* @conditional-compile-remove(together-mode) */ + Object.defineProperty(callClient, 'disposeCallFeatureView', { + configurable: false, + value: (callId: string | undefined, stream: TogetherModeStreamViewState): void => { + disposeCallFeatureView(context, internalContext, callId, stream); + } + }); return new Proxy(callClient, new ProxyCallClient(context, internalContext)) as StatefulCallClient; }; diff --git a/packages/calling-stateful-client/src/StreamUtils.test.ts b/packages/calling-stateful-client/src/StreamUtils.test.ts index ca5409811ee..ed33c7fd50a 100644 --- a/packages/calling-stateful-client/src/StreamUtils.test.ts +++ b/packages/calling-stateful-client/src/StreamUtils.test.ts @@ -90,7 +90,7 @@ function createMockCall(mockCallId: string): CallState { localRecording: { isLocalRecordingActive: false }, raiseHand: { raisedHands: [] }, /* @conditional-compile-remove(together-mode) */ - togetherMode: { streams: {}, seatingPositions: [] }, + togetherMode: { isActive: false, streams: {}, seatingPositions: [] }, localParticipantReaction: undefined, transcription: { isTranscriptionActive: false }, screenShareRemoteParticipant: undefined, diff --git a/packages/calling-stateful-client/src/TogetherModeSubscriber.ts b/packages/calling-stateful-client/src/TogetherModeSubscriber.ts index c3706e812f9..999ac82808a 100644 --- a/packages/calling-stateful-client/src/TogetherModeSubscriber.ts +++ b/packages/calling-stateful-client/src/TogetherModeSubscriber.ts @@ -2,12 +2,27 @@ // Licensed under the MIT License. /* @conditional-compile-remove(together-mode) */ -import { TogetherModeCallFeature, TogetherModeVideoStream } from '@azure/communication-calling'; +import { + RemoteVideoStream, + TogetherModeCallFeature, + TogetherModeSeatingMap, + TogetherModeVideoStream +} from '@azure/communication-calling'; /* @conditional-compile-remove(together-mode) */ import { CallContext } from './CallContext'; /* @conditional-compile-remove(together-mode) */ import { CallIdRef } from './CallIdRef'; - +/* @conditional-compile-remove(together-mode) */ +import { InternalCallContext } from './InternalCallContext'; +/* @conditional-compile-remove(together-mode) */ +import { disposeCallFeatureView } from './CallFeatureStreamUtils'; +/* @conditional-compile-remove(together-mode) */ +import { + convertSdkRemoteStreamToDeclarativeRemoteStream, + convertSdkTogetherStreamToDeclarativeRemoteStream +} from './Converter'; +/* @conditional-compile-remove(together-mode) */ +import { TogetherModeVideoStreamSubscriber } from './TogetherModeVideoStreamSubscriber'; /* @conditional-compile-remove(together-mode) */ /** * TogetherModeSubscriber is responsible for subscribing to together mode events and updating the call context accordingly. @@ -15,37 +30,94 @@ import { CallIdRef } from './CallIdRef'; export class TogetherModeSubscriber { private _callIdRef: CallIdRef; private _context: CallContext; + private _internalContext: InternalCallContext; private _togetherMode: TogetherModeCallFeature; + private _featureName = 'togetherMode'; + private _togetherModeVideoStreamSubscribers: Map; - constructor(callIdRef: CallIdRef, context: CallContext, togetherMode: TogetherModeCallFeature) { + constructor( + callIdRef: CallIdRef, + context: CallContext, + internalContext: InternalCallContext, + togetherMode: TogetherModeCallFeature + ) { this._callIdRef = callIdRef; this._context = context; + this._internalContext = internalContext; this._togetherMode = togetherMode; - + this._togetherModeVideoStreamSubscribers = new Map(); this.subscribe(); } private subscribe = (): void => { this._togetherMode.on('togetherModeStreamsUpdated', this.onTogetherModeStreamUpdated); + this._togetherMode.on('togetherModeSceneUpdated', this.onSceneUpdated); + this._togetherMode.on('togetherModeSeatingUpdated', this.onSeatUpdated); }; public unsubscribe = (): void => { this._togetherMode.off('togetherModeStreamsUpdated', this.onTogetherModeStreamUpdated); + this._togetherMode.off('togetherModeSceneUpdated', this.onSceneUpdated); + this._togetherMode.off('togetherModeSeatingUpdated', this.onSeatUpdated); + }; + + private onSceneUpdated = (args: TogetherModeSeatingMap): void => { + this._context.setTogetherModeSeatingCoordinates(this._callIdRef.callId, args); }; - private recalculateSeatingPositions = (width: number, height: number): void => { - this._togetherMode.sceneSize = { width, height }; + private onSeatUpdated = (args: TogetherModeSeatingMap): void => { + this._context.setTogetherModeSeatingCoordinates(this._callIdRef.callId, args); + }; + + private addRemoteVideoStreamSubscriber = (togetherModeVideoStream: TogetherModeVideoStream): void => { + this._togetherModeVideoStreamSubscribers.get(togetherModeVideoStream.id)?.unsubscribe(); + this._togetherModeVideoStreamSubscribers.set( + togetherModeVideoStream.id, + new TogetherModeVideoStreamSubscriber(this._callIdRef, togetherModeVideoStream, this._context) + ); + }; + + private updateTogetherModeStreams = ( + addedStreams: TogetherModeVideoStream[], + removedStreams: TogetherModeVideoStream[] + ): void => { + for (const stream of removedStreams) { + this._togetherModeVideoStreamSubscribers.get(stream.id)?.unsubscribe(); + disposeCallFeatureView( + this._context, + this._internalContext, + this._callIdRef.callId, + convertSdkRemoteStreamToDeclarativeRemoteStream(stream) + ); + this._internalContext.deleteCallFeatureRenderInfo( + this._callIdRef.callId, + this._featureName, + stream.mediaStreamType + ); + } + + for (const stream of addedStreams) { + this._internalContext.setCallFeatureRenderInfo( + this._callIdRef.callId, + this._featureName, + stream.mediaStreamType, + stream as RemoteVideoStream, + 'NotRendered', + undefined + ); + this.addRemoteVideoStreamSubscriber(stream); + } + this._context.setTogetherModeVideoStreams( + this._callIdRef.callId, + addedStreams.map(convertSdkTogetherStreamToDeclarativeRemoteStream), + removedStreams.map(convertSdkTogetherStreamToDeclarativeRemoteStream) + ); }; private onTogetherModeStreamUpdated = (args: { added: TogetherModeVideoStream[]; removed: TogetherModeVideoStream[]; }): void => { - if (args.added) { - this._context.setTogetherModeVideoStream(this._callIdRef.callId, args.added, this.recalculateSeatingPositions); - } - if (args.removed) { - this._context.removeTogetherModeVideoStream(this._callIdRef.callId, args.removed); - } + this.updateTogetherModeStreams(args.added, args.removed); }; } diff --git a/packages/calling-stateful-client/src/TogetherModeVideoStreamSubscriber.ts b/packages/calling-stateful-client/src/TogetherModeVideoStreamSubscriber.ts new file mode 100644 index 00000000000..5ce633273ce --- /dev/null +++ b/packages/calling-stateful-client/src/TogetherModeVideoStreamSubscriber.ts @@ -0,0 +1,62 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +/* @conditional-compile-remove(together-mode) */ +import { TogetherModeVideoStream } from '@azure/communication-calling'; +/* @conditional-compile-remove(together-mode) */ +import { CallContext } from './CallContext'; +/* @conditional-compile-remove(together-mode) */ +import { CallIdRef } from './CallIdRef'; + +/* @conditional-compile-remove(together-mode) */ +/** + * @private + */ +export class TogetherModeVideoStreamSubscriber { + private _callIdRef: CallIdRef; + private _togetherModeStream: TogetherModeVideoStream; + private _context: CallContext; + + constructor(callIdRef: CallIdRef, stream: TogetherModeVideoStream, context: CallContext) { + this._callIdRef = callIdRef; + this._togetherModeStream = stream; + this._context = context; + this.subscribe(); + } + + private subscribe = (): void => { + this._togetherModeStream.on('isAvailableChanged', this.isAvailableChanged); + this._togetherModeStream.on('isReceivingChanged', this.isReceivingChanged); + this._togetherModeStream.on('sizeChanged', this.isSizeChanged); + }; + + public unsubscribe = (): void => { + this._togetherModeStream.off('isAvailableChanged', this.isAvailableChanged); + this._togetherModeStream.off('isReceivingChanged', this.isReceivingChanged); + this._togetherModeStream.off('sizeChanged', this.isSizeChanged); + }; + + private isAvailableChanged = (): void => { + this._context.setTogetherModeVideoStreamIsAvailable( + this._callIdRef.callId, + this._togetherModeStream.id, + this._togetherModeStream.isAvailable + ); + }; + + private isReceivingChanged = (): void => { + this._context.setTogetherModeVideoStreamIsReceiving( + this._callIdRef.callId, + this._togetherModeStream.id, + this._togetherModeStream.isReceiving + ); + }; + + private isSizeChanged = (): void => { + this._context.setTogetherModeVideoStreamSize( + this._callIdRef.callId, + this._togetherModeStream.id, + this._togetherModeStream.size + ); + }; +} diff --git a/packages/communication-react/review/beta/communication-react.api.md b/packages/communication-react/review/beta/communication-react.api.md index 057e1615407..f097cd15003 100644 --- a/packages/communication-react/review/beta/communication-react.api.md +++ b/packages/communication-react/review/beta/communication-react.api.md @@ -111,7 +111,6 @@ import { TeamsIncomingCall } from '@azure/communication-calling'; import { TeamsMeetingIdLocator } from '@azure/communication-calling'; import { TeamsMeetingLinkLocator } from '@azure/communication-calling'; import { Theme } from '@fluentui/react'; -import type { TogetherModeSeatingPosition } from '@azure/communication-calling'; import { TogetherModeStreamViewResult } from '@internal/react-components/dist/dist-esm/types'; import { TogetherModeStreamViewResult as TogetherModeStreamViewResult_2 } from '@internal/react-components/dist/dist-esm/types/TogetherModeTypes'; import { TransferEventArgs } from '@azure/communication-calling'; @@ -1049,7 +1048,7 @@ export type CallFeatureStreamName = 'togetherMode'; // @beta (undocumented) export interface CallFeatureStreamState { // (undocumented) - feature: CallFeatureStreamName; + feature?: CallFeatureStreamName; } // @public @@ -4899,16 +4898,24 @@ export const toFlatCommunicationIdentifier: (identifier: CommunicationIdentifier // @beta export interface TogetherModeCallFeature { + // (undocumented) + isActive: boolean; seatingPositions: TogetherModeSeatingPositionState[]; streams: TogetherModeStreamsState; } // @beta export interface TogetherModeSeatingPositionState { + // (undocumented) + height: number; + // (undocumented) + left: number; // (undocumented) participantId: string; // (undocumented) - position: TogetherModeSeatingPosition; + top: number; + // (undocumented) + width: number; } // @beta @@ -4918,17 +4925,7 @@ export interface TogetherModeStreamsState { } // @beta -export interface TogetherModeStreamViewState extends CallFeatureStreamState { - id: number; - // @public - isReceiving: boolean; - mediaStreamType: MediaStreamType; - recalculateSeatingPositions: (width: number, height: number) => void; - streamSize?: { - width: number; - height: number; - }; - view?: VideoStreamRendererViewState; +export interface TogetherModeStreamViewState extends RemoteVideoStreamState, CallFeatureStreamState { } // @public diff --git a/packages/react-composites/src/composites/CallComposite/MockCallAdapter.ts b/packages/react-composites/src/composites/CallComposite/MockCallAdapter.ts index f5596d4e244..c03b11fd244 100644 --- a/packages/react-composites/src/composites/CallComposite/MockCallAdapter.ts +++ b/packages/react-composites/src/composites/CallComposite/MockCallAdapter.ts @@ -114,6 +114,14 @@ export class _MockCallAdapter implements CallAdapter { createTogetherModeStreamViews(): Promise { throw Error('createFeatureStreamView not implemented'); } + /* @conditional-compile-remove(together-mode) */ + startTogetherMode(): Promise { + throw Error('startTogetherMode not implemented'); + } + /* @conditional-compile-remove(together-mode) */ + setTogetherModeSceneSize(width: number, height: number): void { + throw Error('Setting Together Mode scene size not implemented'); + } disposeStreamView(): Promise { return Promise.resolve(); } @@ -264,7 +272,7 @@ const createDefaultCallAdapterState = (role?: ParticipantRole): CallAdapterState remoteParticipantsEnded: {}, raiseHand: { raisedHands: [] }, /* @conditional-compile-remove(together-mode) */ - togetherMode: { streams: {}, seatingPositions: [] }, + togetherMode: { isActive: false, streams: {}, seatingPositions: [] }, pptLive: { isActive: false }, localParticipantReaction: undefined, role, diff --git a/packages/react-composites/src/composites/CallWithChatComposite/adapter/TestUtils.ts b/packages/react-composites/src/composites/CallWithChatComposite/adapter/TestUtils.ts index b5acefb7e25..523da817080 100644 --- a/packages/react-composites/src/composites/CallWithChatComposite/adapter/TestUtils.ts +++ b/packages/react-composites/src/composites/CallWithChatComposite/adapter/TestUtils.ts @@ -244,7 +244,7 @@ function createMockCall(mockCallId: string): CallState { dominantSpeakers: undefined, raiseHand: { raisedHands: [] }, /* @conditional-compile-remove(together-mode) */ - togetherMode: { streams: {}, seatingPositions: [] }, + togetherMode: { isActive: false, streams: {}, seatingPositions: [] }, pptLive: { isActive: false }, localParticipantReaction: undefined, captionsFeature: { From e04a9ca250c9a4b7037c4ca3df131f0023952f53 Mon Sep 17 00:00:00 2001 From: Chukwuebuka Nwankwo Date: Thu, 24 Oct 2024 16:30:10 +0000 Subject: [PATCH 16/17] Addressed comments --- packages/calling-stateful-client/src/CallClientState.ts | 7 +++++++ packages/calling-stateful-client/src/CallContext.ts | 8 ++++---- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/packages/calling-stateful-client/src/CallClientState.ts b/packages/calling-stateful-client/src/CallClientState.ts index bffb6813814..47452b86f5f 100644 --- a/packages/calling-stateful-client/src/CallClientState.ts +++ b/packages/calling-stateful-client/src/CallClientState.ts @@ -295,12 +295,19 @@ export interface TogetherModeStreamViewState extends RemoteVideoStreamState, Cal /** * State only version of {@link @azure/communication-calling#TogetherModeSeatingMap}. * @beta + * + * Represents the seating position of a participant in Together Mode. */ export interface TogetherModeSeatingPositionState { + // The participant id of the participant in the seating position. participantId: string; + // The top left offset from the top of the together mode view. top: number; + // The left offset position from the left of the together mode view. left: number; + // The width of the seating area width: number; + // The height of the seating area. height: number; } diff --git a/packages/calling-stateful-client/src/CallContext.ts b/packages/calling-stateful-client/src/CallContext.ts index 0eea9f5d52c..820e91a33b2 100644 --- a/packages/calling-stateful-client/src/CallContext.ts +++ b/packages/calling-stateful-client/src/CallContext.ts @@ -459,13 +459,13 @@ export class CallContext { /* @conditional-compile-remove(together-mode) */ public setTogetherModeVideoStreams( callId: string, - addRemoteVideoStream: TogetherModeStreamViewState[], - removeRemoteVideoStream: TogetherModeStreamViewState[] + addedStreams: TogetherModeStreamViewState[], + removedStreams: TogetherModeStreamViewState[] ): void { this.modifyState((draft: CallClientState) => { const call = draft.calls[this._callIdHistory.latestCallId(callId)]; if (call) { - for (const stream of removeRemoteVideoStream) { + for (const stream of removedStreams) { if (stream.mediaStreamType === 'Video') { call.togetherMode.streams.mainVideoStream = undefined; call.togetherMode.isActive = false; @@ -473,7 +473,7 @@ export class CallContext { } } - for (const newStream of addRemoteVideoStream) { + for (const newStream of addedStreams) { // This should only be called by the subscriber and some properties are add by other components so if the // stream already exists, only update the values that subscriber knows about. const mainVideoStream = call.togetherMode.streams.mainVideoStream; From 6bf4544fbc7114ff3a0337ac5cbed31ec9927423 Mon Sep 17 00:00:00 2001 From: Chukwuebuka Nwankwo Date: Thu, 24 Oct 2024 17:33:34 +0000 Subject: [PATCH 17/17] Fix lint issue --- .../src/composites/CallComposite/MockCallAdapter.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/react-composites/src/composites/CallComposite/MockCallAdapter.ts b/packages/react-composites/src/composites/CallComposite/MockCallAdapter.ts index cb93f3c6a47..288c26e7e79 100644 --- a/packages/react-composites/src/composites/CallComposite/MockCallAdapter.ts +++ b/packages/react-composites/src/composites/CallComposite/MockCallAdapter.ts @@ -112,7 +112,7 @@ export class _MockCallAdapter implements CallAdapter { } /* @conditional-compile-remove(together-mode) */ createTogetherModeStreamViews(): Promise { - throw Error('createFeatureStreamView not implemented'); + throw Error('createTogetherModeStreamViews not implemented'); } /* @conditional-compile-remove(together-mode) */ startTogetherMode(): Promise { @@ -120,7 +120,7 @@ export class _MockCallAdapter implements CallAdapter { } /* @conditional-compile-remove(together-mode) */ setTogetherModeSceneSize(width: number, height: number): void { - throw Error('Setting Together Mode scene size not implemented'); + throw Error(`Setting Together Mode scene to width ${width} and height ${height} is not implemented`); } disposeStreamView(): Promise { return Promise.resolve();