From 5b0b82b18b78646a3070976cc7073f19b696a5db Mon Sep 17 00:00:00 2001
From: Daniel Strolle <dstrolle@microsoft.com>
Date: Wed, 4 Dec 2024 13:08:09 -0600
Subject: [PATCH] update to support transfer

---
 .../AudioEffects/AudioEffectsContainer.js     |   3 +-
 Project/src/MakeCall/CallCard.js              | 327 +++++++++++-------
 Project/src/MakeCall/CallSurvey.js            |   3 +-
 Project/src/MakeCall/IncomingCallCard.js      |  20 +-
 Project/src/MakeCall/Lobby.js                 |   3 +-
 Project/src/MakeCall/MakeCall.js              | 101 +++---
 .../RawVideoAccess/CustomVideoEffects.js      |   1 -
 Project/src/MakeCall/RemoteParticipantCard.js |  12 +-
 Project/src/MakeCall/StreamRenderer.js        |   3 +-
 .../VideoEffects/VideoEffectsContainer.js     |   3 +-
 10 files changed, 288 insertions(+), 188 deletions(-)

diff --git a/Project/src/MakeCall/AudioEffects/AudioEffectsContainer.js b/Project/src/MakeCall/AudioEffects/AudioEffectsContainer.js
index e2cb87e3..e5b066cf 100644
--- a/Project/src/MakeCall/AudioEffects/AudioEffectsContainer.js
+++ b/Project/src/MakeCall/AudioEffects/AudioEffectsContainer.js
@@ -15,7 +15,6 @@ export const LoadingSpinner = () => {
 export default class AudioEffectsContainer extends React.Component {
     constructor(props) {
         super(props);
-        this.call = props.call;
         this.deviceManager = props.deviceManager;
         this.localAudioStreamFeatureApi = null;
         this.localAudioStream = null;
@@ -68,7 +67,7 @@ export default class AudioEffectsContainer extends React.Component {
     }
 
     initLocalAudioStreamFeatureApi() {
-        const localAudioStream = this.call.localAudioStreams.find(a => {
+        const localAudioStream = this.props.call.localAudioStreams.find(a => {
             return a.mediaStreamType === 'Audio';
         });
 
diff --git a/Project/src/MakeCall/CallCard.js b/Project/src/MakeCall/CallCard.js
index a773c359..42e47362 100644
--- a/Project/src/MakeCall/CallCard.js
+++ b/Project/src/MakeCall/CallCard.js
@@ -1,5 +1,5 @@
 import React from "react";
-import { MessageBar, MessageBarType } from 'office-ui-fabric-react'
+import { MessageBar, MessageBarType, TextField } from 'office-ui-fabric-react'
 import { FunctionalStreamRenderer as StreamRenderer } from "./FunctionalStreamRenderer";
 import AddParticipantPopover from "./AddParticipantPopover";
 import RemoteParticipantCard from "./RemoteParticipantCard";
@@ -25,38 +25,39 @@ export default class CallCard extends React.Component {
     constructor(props) {
         super(props);
         this.callFinishConnectingResolve = undefined;
-        this.call = props.call;
-        this.localVideoStream = this.call.localVideoStreams.find(lvs => {
+        this.localVideoStream = this.props.call.localVideoStreams.find(lvs => {
             return lvs.mediaStreamType === 'Video' || lvs.mediaStreamType === 'RawMedia'
         });
         this.localScreenSharingStream = undefined;
         this.deviceManager = props.deviceManager;
+        this.destinationUserId = null;
         this.remoteVolumeLevelSubscription = undefined;
         this.handleRemoteVolumeSubscription = undefined;
         this.streamIsAvailableListeners = new Map();
         this.videoStreamsUpdatedListeners = new Map();
         this.identifier = props.identityMri;
-        this.spotlightFeature = this.call.feature(Features.Spotlight);
-        this.raiseHandFeature = this.call.feature(Features.RaiseHand);
-        this.capabilitiesFeature = this.call.feature(Features.Capabilities);
+        this.spotlightFeature = this.props.call.feature(Features.Spotlight);
+        this.raiseHandFeature = this.props.call.feature(Features.RaiseHand);
+        this.capabilitiesFeature = this.props.call.feature(Features.Capabilities);
         this.capabilities = this.capabilitiesFeature.capabilities;
-        this.dominantSpeakersFeature = this.call.feature(Features.DominantSpeakers);
-        this.recordingFeature = this.call.feature(Features.Recording);
-        this.transcriptionFeature = this.call.feature(Features.Transcription);
-        this.lobby = this.call.lobby;
+        this.dominantSpeakersFeature = this.props.call.feature(Features.DominantSpeakers);
+        this.recordingFeature = this.props.call.feature(Features.Recording);
+        this.transcriptionFeature = this.props.call.feature(Features.Transcription);
+        this.lobby = this.props.call.lobby;
         if (Features.Reaction) {
-            this.meetingReaction = this.call.feature(Features.Reaction);
+            this.meetingReaction = this.props.call.feature(Features.Reaction);
         }
         if (Features.PPTLive) {
-            this.pptLiveFeature = this.call.feature(Features.PPTLive);
+            this.pptLiveFeature = this.props.call.feature(Features.PPTLive);
             this.pptLiveHtml = React.createRef();
         }
+        if (Features.Transfer) {
+            this.transferFeature = this.props.call.feature(Features.Transfer);
+        }
         this.isTeamsUser = props.isTeamsUser;
         this.dummyStreamTimeout = undefined;
         this.state = {
             ovc: 4,
-            callState: this.call.state,
-            callId: this.call.id,
             remoteParticipants: [],
             allRemoteParticipantStreams: [],
             remoteScreenShareStream: undefined,
@@ -67,11 +68,11 @@ export default class CallCard extends React.Component {
             canSpotlight: this.capabilities.spotlightParticipant?.isPresent || this.capabilities.spotlightParticipant?.reason === 'FeatureNotSupported',
             canMuteOthers: this.capabilities.muteOthers?.isPresent || this.capabilities.muteOthers?.reason === 'FeatureNotSupported',
             canReact: this.capabilities.useReactions?.isPresent || this.capabilities.useReactions?.reason === 'FeatureNotSupported',
-            videoOn: this.call.isLocalVideoStarted,
-            screenSharingOn: this.call.isScreenSharingOn,
-            micMuted: this.call.isMuted,
+            videoOn: !!props.call.localVideoStreams[0],
+            screenSharingOn: props.callIsScreenShareOn,
+            micMuted: this.props.call.isMuted,
             incomingAudioMuted: false,
-            onHold: this.call.state === 'LocalHold' || this.call.state === 'RemoteHold',
+            onHold: this.props.call.state === 'LocalHold' || this.props.call.state === 'RemoteHold',
             outgoingAudioMediaAccessActive: false,
             cameraDeviceOptions: props.cameraDeviceOptions ? props.cameraDeviceOptions : [],
             speakerDeviceOptions: props.speakerDeviceOptions ? props.speakerDeviceOptions : [],
@@ -102,47 +103,48 @@ export default class CallCard extends React.Component {
             pptLiveActive: false,
             isRecordingActive: false,
             isTranscriptionActive: false,
-            lobbyParticipantsCount: this.lobby?.participants.length
+            lobbyParticipantsCount: this.lobby?.participants.length,
+            transferArgs: undefined
         };
         this.selectedRemoteParticipants = new Set();
         this.dataChannelRef = React.createRef();
         this.localVideoPreviewRef = React.createRef();
         this.localScreenSharingPreviewRef = React.createRef();
-        this.isSetCallConstraints = this.call.setConstraints !== undefined;
+        this.isSetCallConstraints = this.props.call.setConstraints !== undefined;
     }
 
     componentWillUnmount() {
-        this.call.off('stateChanged', () => { });
+        this.props.call.off('stateChanged', () => { });
         this.deviceManager.off('videoDevicesUpdated', () => { });
         this.deviceManager.off('audioDevicesUpdated', () => { });
         this.deviceManager.off('selectedSpeakerChanged', () => { });
         this.deviceManager.off('selectedMicrophoneChanged', () => { });
-        this.call.off('localVideoStreamsUpdated', () => { });
-        this.call.off('idChanged', () => { });
-        this.call.off('isMutedChanged', () => { });
-        this.call.off('isIncomingAudioMutedChanged', () => { });
-        this.call.off('isScreenSharingOnChanged', () => { });
-        this.call.off('remoteParticipantsUpdated', () => { });
+        this.props.call.off('localVideoStreamsUpdated', () => { });
+        this.props.call.off('idChanged', () => { });
+        this.props.call.off('isMutedChanged', () => { });
+        this.props.call.off('isIncomingAudioMutedChanged', () => { });
+        this.props.call.off('isScreenSharingOnChanged', () => { });
+        this.props.call.off('remoteParticipantsUpdated', () => { });
         this.state.mediaCollector?.off('sampleReported', () => { });
         this.state.mediaCollector?.off('summaryReported', () => { });
-        this.call.feature(Features.DominantSpeakers).off('dominantSpeakersChanged', () => { });
-        this.call.feature(Features.Spotlight).off('spotlightChanged', this.spotlightStateChangedHandler);
-        this.call.feature(Features.RaiseHand).off('raisedHandEvent', this.raiseHandChangedHandler);
-        this.call.feature(Features.RaiseHand).off('loweredHandEvent', this.raiseHandChangedHandler);
+        this.props.call.feature(Features.DominantSpeakers).off('dominantSpeakersChanged', () => { });
+        this.props.call.feature(Features.Spotlight).off('spotlightChanged', this.spotlightStateChangedHandler);
+        this.props.call.feature(Features.RaiseHand).off('raisedHandEvent', this.raiseHandChangedHandler);
+        this.props.call.feature(Features.RaiseHand).off('loweredHandEvent', this.raiseHandChangedHandler);
         this.recordingFeature.off('isRecordingActiveChanged', this.isRecordingActiveChangedHandler);
         this.transcriptionFeature.off('isTranscriptionActiveChanged', this.isTranscriptionActiveChangedHandler);
         this.lobby?.off('lobbyParticipantsUpdated', () => { });
         if (Features.Reaction) {
-            this.call.feature(Features.Reaction).off('reaction', this.reactionChangeHandler);
+            this.props.call.feature(Features.Reaction).off('reaction', this.reactionChangeHandler);
         }
         if (Features.PPTLive) {
-            this.call.feature(Features.PPTLive).off('isActiveChanged', this.pptLiveChangedHandler);
+            this.props.call.feature(Features.PPTLive).off('isActiveChanged', this.pptLiveChangedHandler);
         }
         this.dominantSpeakersFeature.off('dominantSpeakersChanged', this.dominantSpeakersChanged);
             }
 
     componentDidMount() {
-        if (this.call) {
+        if (this.props.call) {
             this.deviceManager.on('videoDevicesUpdated', async e => {
                 e.added.forEach(addedCameraDevice => {
                     const addedCameraDeviceOption = { key: addedCameraDevice.id, text: addedCameraDevice.name };
@@ -212,53 +214,53 @@ export default class CallCard extends React.Component {
             });
 
             const callStateChanged = () => {
-                console.log('Call state changed ', this.call.state);
-                if (this.call.state !== 'None' &&
-                    this.call.state !== 'Connecting' &&
-                    this.call.state !== 'Incoming') {
+                console.log('Call state changed ', this.props.call.state);
+                if (this.props.call.state !== 'None' &&
+                    this.props.call.state !== 'Connecting' &&
+                    this.props.call.state !== 'Incoming') {
                     if (this.callFinishConnectingResolve) {
                         this.callFinishConnectingResolve();
                     }
                 }
 
-                if (this.call.state !== 'Disconnected') {
-                    this.setState({ callState: this.call.state });
+                if (this.props.call.state !== 'Disconnected') {
+                    this.setState({ callState: this.props.call.state });
                 }
 
-                if (this.call.state === 'LocalHold' || this.call.state === 'RemoteHold') {
+                if (this.props.call.state === 'LocalHold' || this.props.call.state === 'RemoteHold') {
                     this.setState({ canRaiseHands: false });
                     this.setState({ canSpotlight: false });
                 }
-                if (this.call.state === 'Connected') {
+                if (this.props.call.state === 'Connected') {
                     this.setState({ canRaiseHands:  this.capabilities.raiseHand?.isPresent || this.capabilities.raiseHand?.reason === 'FeatureNotSupported' });
                     this.setState({ canSpotlight: this.capabilities.spotlightParticipant?.isPresent || this.capabilities.spotlightParticipant?.reason === 'FeatureNotSupported' });
                 }
             }
             callStateChanged();
-            this.call.on('stateChanged', callStateChanged);
+            this.props.call.on('stateChanged', callStateChanged);
 
-            this.call.on('idChanged', () => {
-                console.log('Call id Changed ', this.call.id);
-                this.setState({ callId: this.call.id });
+            this.props.call.on('idChanged', () => {
+                console.log('Call id Changed ', this.props.call.id);
+                this.setState({ callId: this.props.call.id });
             });
 
-            this.call.on('isMutedChanged', () => {
-                console.log('Local microphone muted changed ', this.call.isMuted);
-                this.setState({ micMuted: this.call.isMuted });
+            this.props.call.on('isMutedChanged', () => {
+                console.log('Local microphone muted changed ', this.props.call.isMuted);
+                this.setState({ micMuted: this.props.call.isMuted });
             });
 
-            this.call.on('isIncomingAudioMutedChanged', () => {
-                console.log('Incoming audio muted changed  ', this.call.isIncomingAudioMuted);
-                this.setState({ incomingAudioMuted: this.call.isIncomingAudioMuted });
+            this.props.call.on('isIncomingAudioMutedChanged', () => {
+                console.log('Incoming audio muted changed  ', this.props.call.isIncomingAudioMuted);
+                this.setState({ incomingAudioMuted: this.props.call.isIncomingAudioMuted });
             });
 
-            this.call.on('isLocalVideoStartedChanged', () => {
-                this.setState({ videoOn: this.call.isLocalVideoStarted });
+            this.props.call.on('isLocalVideoStartedChanged', () => {
+                this.setState({ videoOn: this.props.call.isLocalVideoStarted });
             });
 
-            this.call.on('isScreenSharingOnChanged', () => {
-                this.setState({ screenSharingOn: this.call.isScreenSharingOn });
-                if (!this.call.isScreenSharing) {
+            this.props.call.on('isScreenSharingOnChanged', () => {
+                this.setState({ screenSharingOn: this.props.call.isScreenSharingOn });
+                if (!this.props.call.isScreenSharing) {
                     if (this.state.localScreenSharingMode == 'StartWithDummy') {
                         clearTimeout(this.dummyStreamTimeout);
                         this.dummyStreamTimeout = undefined;
@@ -267,7 +269,7 @@ export default class CallCard extends React.Component {
                 }
             });
 
-            this.call.on('mutedByOthers', () => {
+            this.props.call.on('mutedByOthers', () => {
                 const messageBarText = 'You have been muted by someone else';
                 this.setState(prevState => ({
                     ...prevState,
@@ -309,10 +311,10 @@ export default class CallCard extends React.Component {
                 }
             }
 
-            this.call.remoteParticipants.forEach(rp => handleParticipant(rp));
+            this.props.call.remoteParticipants.forEach(rp => handleParticipant(rp));
 
-            this.call.on('remoteParticipantsUpdated', e => {
-                console.log(`Call=${this.call.callId}, remoteParticipantsUpdated, added=${e.added}, removed=${e.removed}`);
+            this.props.call.on('remoteParticipantsUpdated', e => {
+                console.log(`Call=${this.props.call.callId}, remoteParticipantsUpdated, added=${e.added}, removed=${e.removed}`);
                 e.added.forEach(participant => {
                     console.log('participantAdded', participant);
                     handleParticipant(participant)
@@ -342,7 +344,7 @@ export default class CallCard extends React.Component {
                     });
                 });
             });
-            const mediaCollector = this.call.feature(Features.MediaStats).createCollector();
+            const mediaCollector = this.props.call.feature(Features.MediaStats).createCollector();
             this.setState({ mediaCollector });
             mediaCollector.on('sampleReported', (data) => {
                 if (this.state.logMediaStats) {
@@ -399,12 +401,12 @@ export default class CallCard extends React.Component {
                     this.dominantSpeakersChanged();
                     if (this.state.dominantSpeakerMode) {
 
-                        const newDominantSpeakerIdentifier = this.call.feature(Features.DominantSpeakers).dominantSpeakers.speakersList[0];
+                        const newDominantSpeakerIdentifier = this.props.call.feature(Features.DominantSpeakers).dominantSpeakers.speakersList[0];
                         if (newDominantSpeakerIdentifier) {
                             console.log(`DominantSpeaker changed, new dominant speaker: ${newDominantSpeakerIdentifier ? utils.getIdentifierText(newDominantSpeakerIdentifier) : `None`}`);
 
                             // Set the new dominant remote participant
-                            const newDominantRemoteParticipant = utils.getRemoteParticipantObjFromIdentifier(this.call, newDominantSpeakerIdentifier);
+                            const newDominantRemoteParticipant = utils.getRemoteParticipantObjFromIdentifier(this.props.call, newDominantSpeakerIdentifier);
 
                             // Get the new dominant remote participant's stream tuples
                             const streamsToRender = [];
@@ -444,13 +446,19 @@ export default class CallCard extends React.Component {
                 }
             };
 
-            const dominantSpeakerIdentifier = this.call.feature(Features.DominantSpeakers).dominantSpeakers.speakersList[0];
+            const dominantSpeakerIdentifier = this.props.call.feature(Features.DominantSpeakers).dominantSpeakers.speakersList[0];
             if (dominantSpeakerIdentifier) {
                 this.setState({ dominantRemoteParticipant: utils.getRemoteParticipantObjFromIdentifier(dominantSpeakerIdentifier) })
             }
-            this.call.feature(Features.DominantSpeakers).on('dominantSpeakersChanged', dominantSpeakersChangedHandler);
+            this.props.call.feature(Features.DominantSpeakers).on('dominantSpeakersChanged', dominantSpeakersChangedHandler);
+
+            const transferCallback = args => {
+                this.setState({ transferArgs: args });
+            };
 
-            const ovcFeature = this.call.feature(Features.OptimalVideoCount);
+            this.props.call.feature(Features.Transfer).off('transferRequested', transferCallback);
+
+            const ovcFeature = this.props.call.feature(Features.OptimalVideoCount);
             const ovcChangedHandler = () => {
                 if (this.state.ovc !== ovcFeature.optimalVideoCount) {
                     this.setState({ ovc: ovcFeature.optimalVideoCount });
@@ -468,12 +476,13 @@ export default class CallCard extends React.Component {
             this.recordingFeature.on('isRecordingActiveChanged', this.isRecordingActiveChangedHandler);
             this.transcriptionFeature.on('isTranscriptionActiveChanged', this.isTranscriptionActiveChangedHandler);
             this.lobby?.on('lobbyParticipantsUpdated', this.lobbyParticipantsUpdatedHandler);
+            this.transferFeature.on("transferRequested", transferCallback);
         }
     }
 
     updateListOfParticipantsToRender(reason) {
 
-        const ovcFeature = this.call.feature(Features.OptimalVideoCount);
+        const ovcFeature = this.props.call.feature(Features.OptimalVideoCount);
         const optimalVideoCount = ovcFeature.optimalVideoCount;
         console.log(`updateListOfParticipantsToRender because ${reason}, ovc is ${optimalVideoCount}`);
         console.log(`updateListOfParticipantsToRender currently rendering ${this.state.allRemoteParticipantStreams.length} streams`);
@@ -595,7 +604,7 @@ export default class CallCard extends React.Component {
         if (this.pptLiveHtml) {
             if (pptLiveActive) {
                 this.pptLiveHtml.current.appendChild(this.pptLiveFeature.target);
-                if (this.call.isScreenSharingOn) {
+                if (this.props.call.isScreenSharingOn) {
                     try {
                         await this.handleScreenSharingOnOff();
                     } catch {
@@ -604,7 +613,7 @@ export default class CallCard extends React.Component {
                 }
             } else {
                 this.pptLiveHtml.current.removeChild(this.pptLiveHtml.current.lastElementChild);
-                if (!this.call.isScreenSharingOn && this.state.canShareScreen) {
+                if (!this.props.call.isScreenSharingOn && this.state.canShareScreen) {
                     try {
                         await this.handleScreenSharingOnOff();
                     } catch {
@@ -652,7 +661,7 @@ export default class CallCard extends React.Component {
     dominantSpeakersChanged = () => {
         const dominantSpeakersMris = this.dominantSpeakersFeature.dominantSpeakers.speakersList;
         const remoteParticipants = dominantSpeakersMris.map(dominantSpeakerMri => {
-            const remoteParticipant = utils.getRemoteParticipantObjFromIdentifier(this.call, dominantSpeakerMri);
+            const remoteParticipant = utils.getRemoteParticipantObjFromIdentifier(this.props.call, dominantSpeakerMri);
             return remoteParticipant;
         });
 
@@ -669,9 +678,9 @@ export default class CallCard extends React.Component {
                 this.localVideoStream = new LocalVideoStream(cameraDeviceInfo);
             }
 
-            if (this.call.state === 'None' ||
-                this.call.state === 'Connecting' ||
-                this.call.state === 'Incoming') {
+            if (this.props.call.state === 'None' ||
+                this.props.call.state === 'Connecting' ||
+                this.props.call.state === 'Incoming') {
                 if (this.state.videoOn) {
                     this.setState({ videoOn: false });
                 } else {
@@ -679,15 +688,15 @@ export default class CallCard extends React.Component {
                 }
                 await this.watchForCallFinishConnecting();
                 if (this.state.videoOn && this.state.canOnVideo) {
-                    await this.call.startVideo(this.localVideoStream);
+                    await this.props.call.startVideo(this.localVideoStream);
                 } else {
-                    await this.call.stopVideo(this.localVideoStream);
+                    await this.props.call.stopVideo(this.localVideoStream);
                 }
             } else {
                 if (!this.state.videoOn && this.state.canOnVideo) {
-                    await this.call.startVideo(this.localVideoStream);
+                    await this.props.call.startVideo(this.localVideoStream);
                 } else {
-                    await this.call.stopVideo(this.localVideoStream);
+                    await this.props.call.stopVideo(this.localVideoStream);
                 }
             }
         } catch (e) {
@@ -709,12 +718,12 @@ export default class CallCard extends React.Component {
 
     async handleMicOnOff() {
         try {
-            if (!this.call.isMuted) {
-                await this.call.mute();
+            if (!this.props.call.isMuted) {
+                await this.props.call.mute();
             } else {
-                await this.call.unmute();
+                await this.props.call.unmute();
             }
-            this.setState({ micMuted: this.call.isMuted });
+            this.setState({ micMuted: this.props.call.isMuted });
         } catch (e) {
             console.error(e);
         }
@@ -722,7 +731,7 @@ export default class CallCard extends React.Component {
 
     async handleMuteAllRemoteParticipants() {
         try {
-            await this.call.muteAllRemoteParticipants?.();
+            await this.props.call.muteAllRemoteParticipants?.();
         } catch (e) {
             console.error('Failed to mute all other participants.', e);
         }
@@ -783,12 +792,12 @@ export default class CallCard extends React.Component {
 
     async handleIncomingAudioOnOff() {
         try {
-            if (!this.call.isIncomingAudioMuted) {
-                await this.call.muteIncomingAudio();
+            if (!this.props.call.isIncomingAudioMuted) {
+                await this.props.call.muteIncomingAudio();
             } else {
-                await this.call.unmuteIncomingAudio();
+                await this.props.call.unmuteIncomingAudio();
             }
-            this.setState({ incomingAudioMuted: this.call.isIncomingAudioMuted });
+            this.setState({ incomingAudioMuted: this.props.call.isIncomingAudioMuted });
         } catch (e) {
             console.error(e);
         }
@@ -796,19 +805,41 @@ export default class CallCard extends React.Component {
 
     async handleHoldUnhold() {
         try {
-            if (this.call.state === 'LocalHold') {
-                this.call.resume();
+            if (this.props.call.state === 'LocalHold') {
+                this.props.call.resume();
             } else {
-                this.call.hold();
+                this.props.call.hold();
             }
         } catch (e) {
             console.error(e);
         }
     }
 
+    async handleTransferToParticipant() {
+        if (!this.destinationUserId.value) {
+            return;
+        }
+        try {
+            var id = {communicationUserId: this.destinationUserId.value}
+
+            await this.props.call.hold();
+
+            const transfer = this.transferFeature.transfer({ targetParticipant: id });
+            transfer.on('stateChanged', () => {
+                console.log(`EVENT, transferStateChanged=${transfer.state}`)
+
+                if (transfer.state === 'Transferred') {
+                    this.props.call.hangUp();
+                }
+            });
+        } catch(e) {
+            console.error(e);
+        }
+    }
+
     async handleOutgoingAudioEffect() {
         if (this.state.outgoingAudioMediaAccessActive) {
-            this.call.stopAudio();
+            this.props.call.stopAudio();
         } else {
             this.startOutgoingAudioEffect();
         }
@@ -853,13 +884,13 @@ export default class CallCard extends React.Component {
     async startOutgoingAudioEffect() {
         const stream = this.getDummyAudioStream();
         const customLocalAudioStream = new LocalAudioStream(stream);
-        this.call.startAudio(customLocalAudioStream);
+        this.props.call.startAudio(customLocalAudioStream);
     }
 
     async handleScreenSharingOnOff() {
         try {
-            if (this.call.isScreenSharingOn) {
-                await this.call.stopScreenSharing();
+            if (this.props.call.isScreenSharingOn) {
+                await this.props.call.stopScreenSharing();
                 this.setState({ localScreenSharingMode: undefined });
             } else if (this.state.canShareScreen) {
                 await this.startScreenSharing();
@@ -870,15 +901,15 @@ export default class CallCard extends React.Component {
     }
 
     async startScreenSharing() {
-        await this.call.startScreenSharing();
-        this.localScreenSharingStream = this.call.localVideoStreams.find(ss => ss.mediaStreamType === 'ScreenSharing');
+        await this.props.call.startScreenSharing();
+        this.localScreenSharingStream = this.props.call.localVideoStreams.find(ss => ss.mediaStreamType === 'ScreenSharing');
         this.setState({ localScreenSharingMode: 'StartWithNormal', pptLiveActive: false });
     }
     
     async handleRawScreenSharingOnOff() {
         try {
-            if (this.call.isScreenSharingOn) {
-                await this.call.stopScreenSharing();
+            if (this.props.call.isScreenSharingOn) {
+                await this.props.call.stopScreenSharing();
                 clearImmediate(this.dummyStreamTimeout);
                 this.dummyStreamTimeout = undefined;
                 this.setState({ localScreenSharingMode: undefined });
@@ -916,7 +947,7 @@ export default class CallCard extends React.Component {
                     this.dummyStreamTimeout = setTimeout(createShapes, 0);
                     const dummyStream = canvas.captureStream(FPS);
                     this.localScreenSharingStream = new LocalVideoStream(dummyStream);
-                    await this.call.startScreenSharing(this.localScreenSharingStream);
+                    await this.props.call.startScreenSharing(this.localScreenSharingStream);
                     this.setState({ localScreenSharingMode: 'StartWithDummy'});
                 }
             }
@@ -941,7 +972,7 @@ export default class CallCard extends React.Component {
                 // Turn on dominant speaker mode
                 this.setState({ dominantSpeakerMode: true });
                 // Dispose of all remote participants's stream renderers
-                const dominantSpeakerIdentifier = this.call.feature(Features.DominantSpeakers).dominantSpeakers.speakersList[0];
+                const dominantSpeakerIdentifier = this.props.call.feature(Features.DominantSpeakers).dominantSpeakers.speakersList[0];
                 if (!dominantSpeakerIdentifier) {
                     this.state.allRemoteParticipantStreams.forEach(v => {
                         v.streamRendererComponentRef.current.disposeRenderer();
@@ -952,7 +983,7 @@ export default class CallCard extends React.Component {
                 }
 
                 // Set the dominant remote participant obj
-                const dominantRemoteParticipant = utils.getRemoteParticipantObjFromIdentifier(this.call, dominantSpeakerIdentifier);
+                const dominantRemoteParticipant = utils.getRemoteParticipantObjFromIdentifier(this.props.call, dominantSpeakerIdentifier);
                 this.setState({ dominantRemoteParticipant: dominantRemoteParticipant });
                 // Dispose of all the remote participants's stream renderers except for the dominant speaker
                 this.state.allRemoteParticipantStreams.forEach(v => {
@@ -972,8 +1003,8 @@ export default class CallCard extends React.Component {
         this.setState({ selectedCameraDeviceId: cameraDeviceInfo.id });
         if (this.localVideoStream.mediaStreamType === 'RawMedia' && this.state.videoOn) {
             this.localVideoStream?.switchSource(cameraDeviceInfo);
-             await this.call.stopVideo(this.localVideoStream);
-             await this.call.startVideo(this.localVideoStream);
+             await this.props.call.stopVideo(this.localVideoStream);
+             await this.props.call.startVideo(this.localVideoStream);
         } else {
             this.localVideoStream?.switchSource(cameraDeviceInfo);
         }
@@ -1026,7 +1057,7 @@ export default class CallCard extends React.Component {
             meetingAudioConferenceDetails:  async() => {
                 let messageBarText = "call in (audio only) details: \n";
                 try {
-                    const audioConferencingfeature = this.call.feature(Features.TeamsMeetingAudioConferencing);
+                    const audioConferencingfeature = this.props.call.feature(Features.TeamsMeetingAudioConferencing);
                     const audioConferenceDetails = await audioConferencingfeature.getTeamsMeetingAudioConferencingDetails();
                     console.log(`meetingAudioConferenceDetails: ${JSON.stringify(audioConferenceDetails)}`)
                     messageBarText += `Conference Id: ${audioConferenceDetails.phoneConferenceId}\n`;
@@ -1088,7 +1119,7 @@ export default class CallCard extends React.Component {
         }
         // Include the stop all spotlight option only if the local participant has  the capability 
         // and the current spotlighted participant count is greater than 0
-        if ((this.call.role == 'Presenter' || this.call.role == 'Organizer' || this.call.role == 'Co-organizer')
+        if ((this.props.call.role == 'Presenter' || this.props.call.role == 'Organizer' || this.props.call.role == 'Co-organizer')
             && this.spotlightFeature.getSpotlightedParticipants().length) {
             menuItems.push({
                 key: 'Stop All Spotlight',
@@ -1117,7 +1148,7 @@ export default class CallCard extends React.Component {
             this.selectedRemoteParticipants.delete(identifier);
         }
         const selectedParticipants = [];
-        const allParticipants = new Set(this.call.remoteParticipants.map(rp => rp.identifier));
+        const allParticipants = new Set(this.props.call.remoteParticipants.map(rp => rp.identifier));
         this.selectedRemoteParticipants.forEach(identifier => {
             if (allParticipants.has(identifier)) {
                 selectedParticipants.push(identifier);
@@ -1127,7 +1158,7 @@ export default class CallCard extends React.Component {
     }
 
     handleMediaConstraint = (constraints) => {
-        this.call.setConstraints(constraints);
+        this.props.call.setConstraints(constraints);
     }
 
     render() {
@@ -1163,8 +1194,8 @@ export default class CallCard extends React.Component {
                         </div>
                     </div>
                     {
-                        this.call &&
-                        <CurrentCallInformation sentResolution={this.state.sentResolution} call={this.call} />
+                        this.props.call &&
+                        <CurrentCallInformation sentResolution={this.state.sentResolution} call={this.props.call} />
                     }
                 </div>
                 <div className="ms-Grid-row">
@@ -1178,13 +1209,13 @@ export default class CallCard extends React.Component {
                         <div className="ms-Grid-col ms-lg4">
                             <div>
                                 {   this.state.showAddParticipantPanel &&
-                                    <AddParticipantPopover call={this.call} />
+                                    <AddParticipantPopover call={this.props.call} />
                                 }
                             </div>
                             <div>
                                 {
                                     (this.state.lobbyParticipantsCount > 0) &&
-                                    <Lobby call={this.call} capabilitiesFeature={this.capabilitiesFeature} lobbyParticipantsCount={this.state.lobbyParticipantsCount} />
+                                    <Lobby call={this.props.call} capabilitiesFeature={this.capabilitiesFeature} lobbyParticipantsCount={this.state.lobbyParticipantsCount} />
                                 }
                             </div>
                             {
@@ -1203,7 +1234,7 @@ export default class CallCard extends React.Component {
                                         <RemoteParticipantCard
                                             key={`${utils.getIdentifierText(remoteParticipant.identifier)}`}
                                             remoteParticipant={remoteParticipant}
-                                            call={this.call}
+                                            call={this.props.call}
                                             menuOptionsHandler={this.getParticipantMenuCallBacks()}
                                             onSelectionChanged={(identifier, isChecked) => this.remoteParticipantSelectionChanged(identifier, isChecked)}
                                             capabilitiesFeature={this.capabilitiesFeature}
@@ -1228,7 +1259,7 @@ export default class CallCard extends React.Component {
                                         remoteParticipant={v.participant}
                                         dominantSpeakerMode={this.state.dominantSpeakerMode}
                                         dominantRemoteParticipant={this.state.dominantRemoteParticipant}
-                                        call={this.call}
+                                        call={this.props.call}
                                         showMediaStats={this.state.logMediaStats}
                                     />
                                 )
@@ -1243,7 +1274,7 @@ export default class CallCard extends React.Component {
                                             remoteParticipant={this.state.remoteScreenShareStream.participant}
                                             dominantSpeakerMode={this.state.dominantSpeakerMode}
                                             dominantRemoteParticipant={this.state.dominantRemoteParticipant}
-                                            call={this.call}
+                                            call={this.props.call}
                                             showMediaStats={this.state.logMediaStats}
                                         />
                                 )
@@ -1280,7 +1311,7 @@ export default class CallCard extends React.Component {
                             }
                         </span>
                         <span className="in-call-button"
-                            onClick={() => this.call.hangUp()}>
+                            onClick={() => this.props.call.hangUp()}>
                             <Icon iconName="DeclineCall" />
                         </span>
                         <span className="in-call-button"
@@ -1496,6 +1527,62 @@ export default class CallCard extends React.Component {
                             style={{ cursor: 'pointer' }}>
                                 {emojis[4]}
                         </span>
+                        <span className="in-call-button"
+                            title={`${this.state.transferOn ? 'Hide' : 'Show'} transfer`}
+                            variant="secondary"
+                            onClick={() => this.setState(prevState =>{
+                                return{
+                                        ...prevState,
+                                        transferOn : !prevState.transferOn
+                                }
+                                })}>
+                            {
+                                !this.state.transferOn &&
+                                <Icon iconName="TransferCall" />
+                            }
+                            {
+                                this.state.transferOn &&
+                                <Icon iconName="Hide2" />
+                            }
+                        </span>
+                        {this.state.transferArgs && 
+                        <>
+                            <span className="in-call-button"
+                                title="Settings"
+                                variant="secondary"
+                                onClick={() => {
+                                    this.state.transferArgs.accept();
+
+                                }}>
+                                <Icon iconName="Accept" />
+                            </span>
+                            <span className="in-call-button"
+                                onClick={() => {
+                                    this.state.transferArgs.reject();
+                                }}>
+                                <Icon iconName="DeclineCall" />
+                            </span>
+                        </>}
+                        {this.state.transferOn &&
+                            <div className="ms-Grid-row separator-top">
+                                    <div className="ms-Grid-col ms-lg6 call-input-panel text-left">
+                                        <TextField
+                                            label="Transfer Identity"
+                                            componentRef={(val) => this.destinationUserId = val} 
+                                        />
+                                    </div>
+                                    <div className="ms-Grid-col ms-sm6 text-left pl-2 mt-3">
+                                        <span className="in-call-button"
+                                            title={`transfer to identity`}
+                                            variant="secondary"
+                                            onClick={() => this.handleTransferToParticipant()}>
+                                            {
+                                                <Icon iconName="TransferCall" />
+                                            }
+                                        </span>
+                                    </div>
+                            </div>
+                        }
                         <ParticipantMenuOptions
                             id={this.identifier}
                             appendMenuitems={this.getMenuItems()}
@@ -1557,7 +1644,7 @@ export default class CallCard extends React.Component {
                                         }
                                         {
                                             (this.state.callState === 'Connected') && !this.state.micMuted && !this.state.incomingAudioMuted &&
-                                            <VolumeVisualizer call={this.call} deviceManager={this.deviceManager} remoteVolumeLevel={this.state.remoteVolumeLevel} />
+                                            <VolumeVisualizer call={this.props.call} deviceManager={this.deviceManager} remoteVolumeLevel={this.state.remoteVolumeLevel} />
                                         }
                                     </div>
                                 </div>
@@ -1609,7 +1696,7 @@ export default class CallCard extends React.Component {
 
                             </div>
                             <div className='ms-Grid-col ms-sm12 ms-md5 md-lg6'>
-                                <VideoEffectsContainer call={this.call} />
+                                <VideoEffectsContainer call={this.props.call} />
                             </div>
                         </div>
                     </div>
@@ -1695,7 +1782,7 @@ export default class CallCard extends React.Component {
                         <div className="md-grid-row">
                             {
                                 this.state.captionOn &&
-                                <CallCaption call={this.call} isTeamsUser={this.isTeamsUser}/>
+                                <CallCaption call={this.props.call} isTeamsUser={this.isTeamsUser}/>
                             }
                         </div>
                     </div>
@@ -1709,7 +1796,7 @@ export default class CallCard extends React.Component {
                         <div className="md-grid-row">
                         {
                             this.state.callState === 'Connected' &&
-                                <DataChannelCard call={this.call} ref={this.dataChannelRef} remoteParticipants={this.state.remoteParticipants} />
+                                <DataChannelCard call={this.props.call} ref={this.dataChannelRef} remoteParticipants={this.state.remoteParticipants} />
                         }
                         </div>
                     </div>
@@ -1721,7 +1808,7 @@ export default class CallCard extends React.Component {
                             <h3>Audio effects and enhancements</h3>
                         </div>
                         <div className='ms-Grid-row'>
-                            <AudioEffectsContainer call={this.call} deviceManager={this.deviceManager} />
+                            <AudioEffectsContainer call={this.props.call} deviceManager={this.deviceManager} />
                         </div>
                     </div>
                 }
diff --git a/Project/src/MakeCall/CallSurvey.js b/Project/src/MakeCall/CallSurvey.js
index 927b0d2c..d369f87f 100644
--- a/Project/src/MakeCall/CallSurvey.js
+++ b/Project/src/MakeCall/CallSurvey.js
@@ -11,7 +11,6 @@ import { TextField } from 'office-ui-fabric-react';
 export default class CallSurvey extends React.Component {
     constructor(props) {
         super(props);
-        this.call = props.call;
         this.state = {
             overallIssue: '',
             overallRating: 0,
@@ -85,7 +84,7 @@ export default class CallSurvey extends React.Component {
         if (this.state.screenShareRating !== 0) rating.screenshareRating = { score: this.state.screenShareRating };
         if (this.state.screenShareIssue) rating.screenshareRating.issues = [this.state.screenShareIssue];
         
-        this.call.feature(Features.CallSurvey).submitSurvey(rating).then((res) => {
+        this.props.call.feature(Features.CallSurvey).submitSurvey(rating).then((res) => {
             if (this.appInsights && this.state.improvementSuggestion !== '') {
                 this.appInsights.trackEvent({
                     name: "CallSurvey", properties: {
diff --git a/Project/src/MakeCall/IncomingCallCard.js b/Project/src/MakeCall/IncomingCallCard.js
index 89bd7429..24cb253b 100644
--- a/Project/src/MakeCall/IncomingCallCard.js
+++ b/Project/src/MakeCall/IncomingCallCard.js
@@ -55,25 +55,37 @@ export default class IncomingCallCard extends React.Component {
                 <div className="ms-Grid-row text-center">
                     <span className="incoming-call-button"
                         title={'Answer call with microphone unmuted and video off'}
-                        onClick={async () => this.incomingCall.accept(await this.acceptCallMicrophoneUnmutedVideoOff())}>
+                        onClick={async () => {
+                            this.incomingCall.accept(await this.acceptCallMicrophoneUnmutedVideoOff());
+                            this.props.onReject();
+                        }}>
                         <Icon iconName="Microphone"/>
                         <Icon iconName="VideoOff"/>
                     </span>
                     <span className="incoming-call-button"
                         title={'Answer call with microphone unmuted and video on'}
-                        onClick={async () => this.incomingCall.accept(await this.acceptCallMicrophoneUnmutedVideoOn())}>
+                        onClick={async () => {
+                            this.incomingCall.accept(await this.acceptCallMicrophoneUnmutedVideoOn());
+                            this.props.onReject();
+                        }}>
                         <Icon iconName="Microphone"/>
                         <Icon iconName="Video"/>
                     </span>
                     <span className="incoming-call-button"
                         title={'Answer call with microphone muted and video on'}
-                        onClick={async () => this.incomingCall.accept(await this.acceptCallMicrophoneMutedVideoOn())}>
+                        onClick={async () => {
+                            this.incomingCall.accept(await this.acceptCallMicrophoneMutedVideoOn());
+                            this.props.onReject();
+                        }}>
                         <Icon iconName="MicOff"/>
                         <Icon iconName="Video"/>
                     </span>
                     <span className="incoming-call-button"
                         title={'Answer call with microphone muted and video off'}
-                        onClick={async () => this.incomingCall.accept(await this.acceptCallMicrophoneMutedVideoOff())}>
+                        onClick={async () => {
+                            this.incomingCall.accept(await this.acceptCallMicrophoneMutedVideoOff());
+                            this.props.onReject();
+                        }}>
                         <Icon iconName="MicOff"/>
                         <Icon iconName="VideoOff"/>
                     </span>
diff --git a/Project/src/MakeCall/Lobby.js b/Project/src/MakeCall/Lobby.js
index e5a747d6..58fd3e77 100644
--- a/Project/src/MakeCall/Lobby.js
+++ b/Project/src/MakeCall/Lobby.js
@@ -6,8 +6,7 @@ import { Features } from '@azure/communication-calling';
 export default class Lobby extends React.Component {
     constructor(props) {
         super(props);
-        this.call = props.call;
-        this.lobby = this.call.lobby;
+        this.lobby = this.props.call.lobby;
         this.capabilitiesFeature = props.capabilitiesFeature;
         this.capabilities = this.capabilitiesFeature.capabilities;
         this.state = {
diff --git a/Project/src/MakeCall/MakeCall.js b/Project/src/MakeCall/MakeCall.js
index 2555ef5d..e609915e 100644
--- a/Project/src/MakeCall/MakeCall.js
+++ b/Project/src/MakeCall/MakeCall.js
@@ -51,7 +51,7 @@ export default class MakeCall extends React.Component {
             id: undefined,
             loggedIn: false,
             isCallClientActiveInAnotherTab: false,
-            call: undefined,
+            call: [],
             callSurvey: undefined,
             incomingCall: undefined,
             showCallSampleCode: false,
@@ -159,7 +159,9 @@ export default class MakeCall extends React.Component {
                     console.log(`callsUpdated, added=${e.added}, removed=${e.removed}`);
 
                     e.added.forEach(call => {
-                        this.setState({ call: call });
+                        this.setState(prevState => ({ 
+                            call: [...prevState.call, call]
+                        }));
 
                         const diagnosticChangedListener = (diagnosticInfo) => {
                                 const rmsg = `UFD Diagnostic changed:
@@ -201,22 +203,24 @@ export default class MakeCall extends React.Component {
                     });
 
                     e.removed.forEach(call => {
-                        if (this.state.call && this.state.call === call) {
-                            this.displayCallEndReason(this.state.call.callEndReason);
+                        const newCallsArray = this.state.call.filter(c => c.id != call.id)
+
+                        if (newCallsArray.length < this.state.call.length) {
+                            this.setState({call: newCallsArray});
+
+                            if (newCallsArray.length === 0) {
+                                this.displayCallEndReason(call.callEndReason, false);
+                            }
                         }
                     });
                 });
                 this.callAgent.on('incomingCall', args => {
                     const incomingCall = args.incomingCall;
-                    if (this.state.call) {
-                        incomingCall.reject();
-                        return;
-                    }
-
+                    
                     this.setState({ incomingCall: incomingCall });
 
                     incomingCall.on('callEnded', args => {
-                        this.displayCallEndReason(args.callEndReason);
+                        this.displayCallEndReason(args.callEndReason, true);
                     });
 
                 });
@@ -230,12 +234,15 @@ export default class MakeCall extends React.Component {
         }
     }
 
-    displayCallEndReason = (callEndReason) => {
+    displayCallEndReason = (callEndReason, incoming) => {
         if (callEndReason.code !== 0 || callEndReason.subCode !== 0) {
             this.setState({ callSurvey: this.state.call, callError: `Call end reason: code: ${callEndReason.code}, subcode: ${callEndReason.subCode}` });
         }
 
-        this.setState({ call: null, callSurvey: this.state.call, incomingCall: null });
+        if (incoming) {
+            this.setState({incomingCall: null});
+            return;
+        }
     }
 
     placeCall = async (withVideo) => {
@@ -968,7 +975,7 @@ this.callAgent.on('incomingCall', async (args) => {
                             </MessageBar>
                         }
                         {
-                            !this.state.incomingCall && !this.state.call && !this.state.callSurvey &&
+                            !this.state.incomingCall && !this.state.call[0] && !this.state.callSurvey &&
                             <div>
                                 <div className="ms-Grid-row mt-3">
                                     <div className="call-input-panel mb-5 ms-Grid-col ms-sm12 ms-md12 ms-lg12 ms-xl6 ms-xxl3">
@@ -981,17 +988,17 @@ this.callAgent.on('incomingCall', async (args) => {
                                             <div className="md-Grid-col ml-2 mt-0 ms-sm11 ms-md11 ms-lg9 ms-xl9 ms-xxl11">
                                                 <TextField
                                                     className="mt-0"
-                                                    disabled={this.state.call || !this.state.loggedIn}
+                                                    disabled={this.state.call[0] || !this.state.loggedIn}
                                                     label={`Enter an Identity to make a call to. You can specify multiple Identities to call by using \",\" separated values.`}
                                                     placeholder="8:acs:<ACA resource ID>_<GUID>"
                                                     componentRef={(val) => this.destinationUserIds = val} />
                                                 <TextField
-                                                    disabled={this.state.call || !this.state.loggedIn}
+                                                    disabled={this.state.call[0] || !this.state.loggedIn}
                                                     label="Destination Phone Identity or Phone Identities"
                                                     placeholder="4:+18881231234"
                                                     componentRef={(val) => this.destinationPhoneIds = val} />
                                                 <TextField
-                                                    disabled={this.state.call || !this.state.loggedIn}
+                                                    disabled={this.state.call[0] || !this.state.loggedIn}
                                                     label="If calling a Phone Identity, your Alternate Caller Id must be specified."
                                                     placeholder="4:+18881231234"
                                                     componentRef={(val) => this.alternateCallerId = val} />
@@ -1001,21 +1008,21 @@ this.callAgent.on('incomingCall', async (args) => {
                                             className="primary-button"
                                             iconProps={{ iconName: 'Phone', style: { verticalAlign: 'middle', fontSize: 'large' } }}
                                             text="Place call"
-                                            disabled={this.state.call || !this.state.loggedIn}
+                                            disabled={this.state.call[0] || !this.state.loggedIn}
                                             onClick={() => this.placeCall(false)}>
                                         </PrimaryButton>
                                         <PrimaryButton
                                             className="primary-button"
                                             iconProps={{ iconName: 'Video', style: { verticalAlign: 'middle', fontSize: 'large' } }}
                                             text="Place call with video"
-                                            disabled={this.state.call || !this.state.loggedIn}
+                                            disabled={this.state.call[0] || !this.state.loggedIn}
                                             onClick={() => this.placeCall(true)}>
                                         </PrimaryButton>
                                         <PrimaryButton 
                                             className="primary-button"
                                             iconProps={{iconName: 'Settings', style: {verticalAlign: 'middle', fontSize: 'large'}}}
                                             text={this.state.showCustomContext ? 'Remove context' : 'Custom context'}
-                                            disabled={this.state.call || !this.state.loggedIn}
+                                            disabled={this.state.call[0] || !this.state.loggedIn}
                                             onClick={() => this.setState({showCustomContext: !this.state.showCustomContext})}>
                                         </PrimaryButton>
                                         <div className="ms-Grid-row" 
@@ -1023,7 +1030,7 @@ this.callAgent.on('incomingCall', async (args) => {
                                             <div className="md-Grid-col ml-2 mt-0 ms-sm11 ms-md11 ms-lg9 ms-xl9 ms-xxl11">
                                                 <TextField
                                                     className="mt-0"
-                                                    disabled={this.state.call || !this.state.loggedIn}
+                                                    disabled={this.state.call[0] || !this.state.loggedIn}
                                                     label="Add user to user value"
                                                     placeholder=""
                                                     componentRef={(val) => this.userToUser = val} />
@@ -1035,14 +1042,14 @@ this.callAgent.on('incomingCall', async (args) => {
                                                 <div className="md-Grid-col inline-flex ml-2 mt-0 ms-sm11 ms-md11 ms-lg9 ms-xl9 ms-xxl11">
                                                     <TextField
                                                         className="mt-0 ms-sm6 ms-md6 ms-lg6 ms-xl6 ms-xxl6"
-                                                        disabled={this.state.call || !this.state.loggedIn}
+                                                        disabled={this.state.call[0] || !this.state.loggedIn}
                                                         label="Custom header key"
                                                         placeholder=""
                                                         onChange={() => this.xHeadersChanged()}
                                                         componentRef={(val) => this.xHeaders[i].key = val} />
                                                     <TextField
                                                         className="mt-0 ml-2 ms-sm6 ms-md6 ms-lg6 ms-xl6 ms-xxl6"
-                                                        disabled={this.state.call || !this.state.loggedIn}
+                                                        disabled={this.state.call[0] || !this.state.loggedIn}
                                                         label="Custom header value"
                                                         placeholder=""
                                                         onChange={() => this.xHeadersChanged()}
@@ -1061,52 +1068,52 @@ this.callAgent.on('incomingCall', async (args) => {
                                         </div>
                                         <div className="ms-Grid-row">
                                             <div className="md-Grid-col ml-2 ms-sm11 ms-md11 ms-lg9 ms-xl9 ms-xxl11">
-                                                <div className={this.state.call || !this.state.loggedIn ? "call-input-panel-input-label-disabled" : ""}>
+                                                <div className={this.state.call[0] || !this.state.loggedIn ? "call-input-panel-input-label-disabled" : ""}>
                                                     Enter meeting link
                                                 </div>
                                                 <div className="ml-3">
                                                     <TextField
                                                         className="mb-3 mt-0"
-                                                        disabled={this.state.call || !this.state.loggedIn}
+                                                        disabled={this.state.call[0] || !this.state.loggedIn}
                                                         label="Meeting link"
                                                         defaultValue={new URLSearchParams(window.location.search).get(URL_PARAM.MEETING_LINK) ?? ''}
                                                         componentRef={(val) => this.meetingLink = val} />
                                                 </div>
-                                                <div className={this.state.call || !this.state.loggedIn ? "call-input-panel-input-label-disabled" : ""}>
+                                                <div className={this.state.call[0] || !this.state.loggedIn ? "call-input-panel-input-label-disabled" : ""}>
                                                     Or enter meeting id (and) passcode
                                                 </div>
                                                 <div className="ml-3">
                                                     <TextField
                                                         className="mb-3 mt-0"
-                                                        disabled={this.state.call || !this.state.loggedIn}
+                                                        disabled={this.state.call[0] || !this.state.loggedIn}
                                                         label="Meeting id"
                                                         componentRef={(val) => this.meetingId = val} />
                                                     <TextField
                                                         className="mb-3"
-                                                        disabled={this.state.call || !this.state.loggedIn}
+                                                        disabled={this.state.call[0] || !this.state.loggedIn}
                                                         label="Meeting passcode (optional)"
                                                         componentRef={(val) => this.passcode = val} />
                                                 </div>
-                                                <div className={this.state.call || !this.state.loggedIn ? "call-input-panel-input-label-disabled" : ""}>
+                                                <div className={this.state.call[0] || !this.state.loggedIn ? "call-input-panel-input-label-disabled" : ""}>
                                                     Or enter meeting coordinates (Thread Id, Message Id, Organizer Id, and Tenant Id)
                                                 </div>
                                                 <div className="ml-3">
                                                     <TextField
                                                         className="mt-0"
-                                                        disabled={this.state.call || !this.state.loggedIn}
+                                                        disabled={this.state.call[0] || !this.state.loggedIn}
                                                         label="Thread Id"
                                                         componentRef={(val) => this.threadId = val} />
                                                     <TextField
-                                                        disabled={this.state.call || !this.state.loggedIn}
+                                                        disabled={this.state.call[0] || !this.state.loggedIn}
                                                         label="Message Id"
                                                         componentRef={(val) => this.messageId = val} />
                                                     <TextField
-                                                        disabled={this.state.call || !this.state.loggedIn}
+                                                        disabled={this.state.call[0] || !this.state.loggedIn}
                                                         label="Organizer Id"
                                                         componentRef={(val) => this.organizerId = val} />
                                                     <TextField
                                                         className="mb-3"
-                                                        disabled={this.state.call || !this.state.loggedIn}
+                                                        disabled={this.state.call[0] || !this.state.loggedIn}
                                                         label="Tenant Id"
                                                         componentRef={(val) => this.tenantId = val} />
                                                 </div>
@@ -1115,13 +1122,13 @@ this.callAgent.on('incomingCall', async (args) => {
                                         <PrimaryButton className="primary-button"
                                             iconProps={{ iconName: 'Group', style: { verticalAlign: 'middle', fontSize: 'large' } }}
                                             text="Join Teams meeting"
-                                            disabled={this.state.call || !this.state.loggedIn}
+                                            disabled={this.state.call[0] || !this.state.loggedIn}
                                             onClick={() => this.joinTeamsMeeting(false)}>
                                         </PrimaryButton>
                                         <PrimaryButton className="primary-button"
                                             iconProps={{ iconName: 'Video', style: { verticalAlign: 'middle', fontSize: 'large' } }}
                                             text="Join Teams meeting with video"
-                                            disabled={this.state.call || !this.state.loggedIn}
+                                            disabled={this.state.call[0] || !this.state.loggedIn}
                                             onClick={() => this.joinTeamsMeeting(true)}>
                                         </PrimaryButton>
                                     </div>
@@ -1134,7 +1141,7 @@ this.callAgent.on('incomingCall', async (args) => {
                                                 <div className="ms-Grid-col ms-sm11 ms-md11 ms-lg9 ms-xl9 ms-xxl11">
                                                     <TextField
                                                         className="mb-3 mt-0"
-                                                        disabled={this.state.call || !this.state.loggedIn}
+                                                        disabled={this.state.call[0] || !this.state.loggedIn}
                                                         label="Group Id"
                                                         defaultValue="29228d3e-040e-4656-a70e-890ab4e173e5"
                                                         componentRef={(val) => this.destinationGroup = val} />
@@ -1144,14 +1151,14 @@ this.callAgent.on('incomingCall', async (args) => {
                                                 className="primary-button"
                                                 iconProps={{ iconName: 'Group', style: { verticalAlign: 'middle', fontSize: 'large' } }}
                                                 text="Join group call"
-                                                disabled={this.state.call || !this.state.loggedIn}
+                                                disabled={this.state.call[0] || !this.state.loggedIn}
                                                 onClick={() => this.joinGroup(false)}>
                                             </PrimaryButton>
                                             <PrimaryButton
                                                 className="primary-button"
                                                 iconProps={{ iconName: 'Video', style: { verticalAlign: 'middle', fontSize: 'large' } }}
                                                 text="Join group call with video"
-                                                disabled={this.state.call || !this.state.loggedIn}
+                                                disabled={this.state.call[0] || !this.state.loggedIn}
                                                 onClick={() => this.joinGroup(true)}>
                                             </PrimaryButton>
                                         </div>
@@ -1160,7 +1167,7 @@ this.callAgent.on('incomingCall', async (args) => {
                                             <div className="ms-Grid-row">
                                                 <div className="md-Grid-col ml-2 ms-sm11 ms-md11 ms-lg9 ms-xl9 ms-xxl11">
                                                     <TextField className="mb-3 mt-0"
-                                                        disabled={this.state.call || !this.state.loggedIn}
+                                                        disabled={this.state.call[0] || !this.state.loggedIn}
                                                         label="Rooms id"
                                                         placeholder="<GUID>"
                                                         componentRef={(val) => this.roomsId = val} />
@@ -1169,13 +1176,13 @@ this.callAgent.on('incomingCall', async (args) => {
                                             <PrimaryButton className="primary-button"
                                                 iconProps={{ iconName: 'Group', style: { verticalAlign: 'middle', fontSize: 'large' } }}
                                                 text="Join Rooms call"
-                                                disabled={this.state.call || !this.state.loggedIn}
+                                                disabled={this.state.call[0] || !this.state.loggedIn}
                                                 onClick={() => this.joinRooms(false)}>
                                             </PrimaryButton>
                                             <PrimaryButton className="primary-button"
                                                 iconProps={{ iconName: 'Video', style: { verticalAlign: 'middle', fontSize: 'large' } }}
                                                 text="Join Rooms call with video"
-                                                disabled={this.state.call || !this.state.loggedIn}
+                                                disabled={this.state.call[0] || !this.state.loggedIn}
                                                 onClick={() => this.joinRooms(true)}>
                                             </PrimaryButton>
                                         </div>
@@ -1186,7 +1193,7 @@ this.callAgent.on('incomingCall', async (args) => {
                                         <h3 className="mb-1">Video Send Constraints</h3>
                                         <MediaConstraint
                                             onChange={this.handleMediaConstraint}
-                                            disabled={this.state.call || !this.state.loggedIn}
+                                            disabled={this.state.call[0] || !this.state.loggedIn}
                                         />
                                     </div>
                                 </div>
@@ -1194,15 +1201,15 @@ this.callAgent.on('incomingCall', async (args) => {
 
                         }
                         {
-                            this.state.call && this.state.isPreCallDiagnosticsCallInProgress &&
+                            this.state.call.length && this.state.isPreCallDiagnosticsCallInProgress &&
                             <div>
                                 Pre Call Diagnostics call in progress...
                             </div>
                         }
                         {
-                            this.state.call && !this.state.callSurvey && !this.state.isPreCallDiagnosticsCallInProgress &&
+                            this.state.call.length === 1 && !this.state.callSurvey && !this.state.isPreCallDiagnosticsCallInProgress &&
                             <CallCard
-                                call={this.state.call}
+                                call={this.state.call[0]}
                                 deviceManager={this.deviceManager}
                                 selectedCameraDeviceId={this.state.selectedCameraDeviceId}
                                 cameraDeviceOptions={this.state.cameraDeviceOptions}
@@ -1215,7 +1222,7 @@ this.callAgent.on('incomingCall', async (args) => {
                                 onShowMicrophoneNotFoundWarning={(show) => { this.setState({ showMicrophoneNotFoundWarning: show }) }} />
                         }
                         {
-                            this.state.incomingCall && !this.state.call &&
+                            this.state.incomingCall &&
                             <IncomingCallCard
                                 incomingCall={this.state.incomingCall}
                                 acceptCallMicrophoneUnmutedVideoOff={async () => await this.getCallOptions({ video: false, micMuted: false })}
@@ -1235,7 +1242,7 @@ this.callAgent.on('incomingCall', async (args) => {
                                     className="secondary-button"
                                     iconProps={{ iconName: 'TestPlan', style: { verticalAlign: 'middle', fontSize: 'large' } }}
                                     text={`Run Pre Call Diagnostics`}
-                                    disabled={this.state.call || !this.state.loggedIn}
+                                    disabled={this.state.call[0] || !this.state.loggedIn}
                                     onClick={() => this.runPreCallDiagnostics()}>
                                 </PrimaryButton>
                                 <PrimaryButton
@@ -1247,7 +1254,7 @@ this.callAgent.on('incomingCall', async (args) => {
                             </div>
                         </div>
                         {
-                            this.state.call && this.state.isPreCallDiagnosticsCallInProgress &&
+                            this.state.call.length && this.state.isPreCallDiagnosticsCallInProgress &&
                             <div>
                                 Pre Call Diagnostics call in progress...
                                 <div className="custom-row">
diff --git a/Project/src/MakeCall/RawVideoAccess/CustomVideoEffects.js b/Project/src/MakeCall/RawVideoAccess/CustomVideoEffects.js
index 8a4a34bd..fcfc5c42 100644
--- a/Project/src/MakeCall/RawVideoAccess/CustomVideoEffects.js
+++ b/Project/src/MakeCall/RawVideoAccess/CustomVideoEffects.js
@@ -5,7 +5,6 @@ export default class CustomVideoEffects extends React.Component {
 
     constructor(props) {
         super(props);
-        this.call = props.call;
         this.stream = props.stream;
         this.isLocal = props.isLocal;
         this.bwStream = undefined;
diff --git a/Project/src/MakeCall/RemoteParticipantCard.js b/Project/src/MakeCall/RemoteParticipantCard.js
index 3c848674..27d63525 100644
--- a/Project/src/MakeCall/RemoteParticipantCard.js
+++ b/Project/src/MakeCall/RemoteParticipantCard.js
@@ -14,15 +14,14 @@ import { ParticipantMenuOptions } from './ParticipantMenuOptions';
 export default class RemoteParticipantCard extends React.Component {
     constructor(props) {
         super(props);
-        this.call = props.call;
         this.remoteParticipant = props.remoteParticipant;
         this.identifier = this.remoteParticipant.identifier;
         this.id = utils.getIdentifierText(this.remoteParticipant.identifier);
         this.isCheckable = isCommunicationUserIdentifier(this.remoteParticipant.identifier) ||
             isMicrosoftTeamsUserIdentifier(this.remoteParticipant.identifier);
 
-        this.spotlightFeature = this.call.feature(Features.Spotlight);
-        this.raiseHandFeature = this.call.feature(Features.RaiseHand);
+        this.spotlightFeature = this.props.call.feature(Features.Spotlight);
+        this.raiseHandFeature = this.props.call.feature(Features.RaiseHand);
         this.capabilitiesFeature = props.capabilitiesFeature;
         this.capabilities = this.capabilitiesFeature.capabilities;
         this.menuOptionsHandler= props.menuOptionsHandler;
@@ -61,6 +60,7 @@ export default class RemoteParticipantCard extends React.Component {
         });
 
         this.remoteParticipant.on('stateChanged', () => {
+            console.log('EVENT, remote participant state: ' + this.remoteParticipant.state)
             this.setState({ state: this.remoteParticipant.state });
         });
 
@@ -104,7 +104,7 @@ export default class RemoteParticipantCard extends React.Component {
 
     handleRemoveParticipant(e, identifier) {
         e.preventDefault();
-        this.call.removeParticipant(identifier).catch((e) => console.error(e))
+        this.props.call.removeParticipant(identifier).catch((e) => console.error(e))
     }
 
     handleMuteParticipant(e, remoteParticipant) {
@@ -151,7 +151,7 @@ export default class RemoteParticipantCard extends React.Component {
     async admitParticipant() {
         console.log('admit');
         try {
-            await this.call.lobby.admit(this.identifier);
+            await this.props.call.lobby.admit(this.identifier);
         } catch (e) {
             console.error(e);
         }
@@ -160,7 +160,7 @@ export default class RemoteParticipantCard extends React.Component {
     async rejectParticipant() {
         console.log('reject');
         try {
-            await this.call.lobby.reject(this.identifier);
+            await this.props.call.lobby.reject(this.identifier);
         } catch (e) {
             console.error(e);
         }
diff --git a/Project/src/MakeCall/StreamRenderer.js b/Project/src/MakeCall/StreamRenderer.js
index 06b3a7c0..58409662 100644
--- a/Project/src/MakeCall/StreamRenderer.js
+++ b/Project/src/MakeCall/StreamRenderer.js
@@ -24,7 +24,6 @@ export default class StreamRenderer extends React.Component {
             videoStats: undefined,
             transportStats: undefined
         };
-        this.call = props.call;
     }
 
     componentDidUpdate(prevProps, prevState, snapshot) {
@@ -177,7 +176,7 @@ export default class StreamRenderer extends React.Component {
                         </h4>
                     }
                 </div>
-                <CustomVideoEffects call={this.call} videoContainerId={this.videoContainerId} remoteParticipantId={this.remoteParticipant.identifier}/>
+                <CustomVideoEffects call={this.props.call} videoContainerId={this.videoContainerId} remoteParticipantId={this.remoteParticipant.identifier}/>
             </div>
         );
     }
diff --git a/Project/src/MakeCall/VideoEffects/VideoEffectsContainer.js b/Project/src/MakeCall/VideoEffects/VideoEffectsContainer.js
index ae66a76c..2ee5d6cf 100644
--- a/Project/src/MakeCall/VideoEffects/VideoEffectsContainer.js
+++ b/Project/src/MakeCall/VideoEffects/VideoEffectsContainer.js
@@ -13,7 +13,6 @@ export const LoadingSpinner = () => {
 export default class VideoEffectsContainer extends React.Component {
     constructor(props) {
         super(props);
-        this.call = props.call;
         this.localVideoStreamFeatureApi = null;
 
         this.state = {
@@ -45,7 +44,7 @@ export default class VideoEffectsContainer extends React.Component {
     }
 
     initLocalVideoStreamFeatureApi() {
-        const localVideoStream =  this.call.localVideoStreams.find(v => { return v.mediaStreamType === 'Video'});
+        const localVideoStream =  this.props.call.localVideoStreams.find(v => { return v.mediaStreamType === 'Video'});
 
         if (!localVideoStream) {
             this.logError('No local video streams found.');