From ff6cba8d5306a6ccd8bb84bd529505e2a2ac47b7 Mon Sep 17 00:00:00 2001
From: Mario Jr <mariogasparoni@gmail.com>
Date: Sat, 6 Mar 2021 22:09:43 -0300
Subject: [PATCH] Store information about user status during breakout room
 audio transfer

Currently this information is lost everytime breakout-room component is
unmounted, causing the panel to shows wrong information during next renders
Fixes #11333
---
 .../audio/audio-controls/container.jsx        |  7 +++-
 .../imports/ui/components/audio/service.js    | 10 ++---
 .../ui/components/breakout-room/component.jsx | 32 ++++++++++++----
 .../ui/components/breakout-room/container.jsx |  8 +++-
 .../ui/services/audio-manager/index.js        | 38 ++++++++++++++++---
 5 files changed, 74 insertions(+), 21 deletions(-)

diff --git a/bigbluebutton-html5/imports/ui/components/audio/audio-controls/container.jsx b/bigbluebutton-html5/imports/ui/components/audio/audio-controls/container.jsx
index c59170c9ce..f179c49584 100755
--- a/bigbluebutton-html5/imports/ui/components/audio/audio-controls/container.jsx
+++ b/bigbluebutton-html5/imports/ui/components/audio/audio-controls/container.jsx
@@ -55,8 +55,11 @@ const {
 } = Service;
 
 export default lockContextContainer(withModalMounter(withTracker(({ mountModal, userLocks }) => {
-  if (Service.isReturningFromBreakoutAudioTransfer()) {
-    Service.setReturningFromBreakoutAudioTransfer(false);
+  const { status } = Service.getBreakoutAudioTransferStatus();
+
+  if (status === AudioManager.BREAKOUT_AUDIO_TRANSFER_STATES.RETURNING) {
+    Service.setBreakoutAudioTransferStatus(null,
+      AudioManager.BREAKOUT_AUDIO_TRANSFER_STATES.DISCONNECTED);
     Service.recoverMicState();
   }
 
diff --git a/bigbluebutton-html5/imports/ui/components/audio/service.js b/bigbluebutton-html5/imports/ui/components/audio/service.js
index 6d55831504..5bc9b9a977 100755
--- a/bigbluebutton-html5/imports/ui/components/audio/service.js
+++ b/bigbluebutton-html5/imports/ui/components/audio/service.js
@@ -125,10 +125,10 @@ export default {
   updateAudioConstraints:
     constraints => AudioManager.updateAudioConstraints(constraints),
   recoverMicState,
-  setReturningFromBreakoutAudioTransfer: (value) => {
-    AudioManager.returningFromBreakoutAudioTransfer = value;
-  },
-  isReturningFromBreakoutAudioTransfer:
-    () => AudioManager.returningFromBreakoutAudioTransfer,
   isReconnecting: () => AudioManager.isReconnecting,
+  setBreakoutAudioTransferStatus:
+    (breakoutId, status) => AudioManager
+      .setBreakoutAudioTransferStatus(breakoutId, status),
+  getBreakoutAudioTransferStatus: () => AudioManager
+    .getBreakoutAudioTransferStatus(),
 };
diff --git a/bigbluebutton-html5/imports/ui/components/breakout-room/component.jsx b/bigbluebutton-html5/imports/ui/components/breakout-room/component.jsx
index bdb8898a57..c77e05d398 100644
--- a/bigbluebutton-html5/imports/ui/components/breakout-room/component.jsx
+++ b/bigbluebutton-html5/imports/ui/components/breakout-room/component.jsx
@@ -6,6 +6,7 @@ import { Session } from 'meteor/session';
 import logger from '/imports/startup/client/logger';
 import { styles } from './styles';
 import BreakoutRoomContainer from './breakout-remaining-time/container';
+import AudioManager from '/imports/ui/services/audio-manager';
 
 const intlMessages = defineMessages({
   breakoutTitle: {
@@ -101,6 +102,7 @@ class BreakoutRoom extends PureComponent {
       breakoutRoomUser,
       breakoutRooms,
       closeBreakoutPanel,
+      setBreakoutAudioTransferStatus,
       isMicrophoneUser,
       isReconnecting,
     } = this.props;
@@ -125,6 +127,8 @@ class BreakoutRoom extends PureComponent {
 
     if (joinedAudioOnly && (!isMicrophoneUser || isReconnecting)) {
       this.clearJoinedAudioOnly();
+      setBreakoutAudioTransferStatus('',
+        AudioManager.BREAKOUT_AUDIO_TRANSFER_STATES.DISCONNECTED);
     }
   }
 
@@ -173,21 +177,32 @@ class BreakoutRoom extends PureComponent {
       intl,
       isUserInBreakoutRoom,
       exitAudio,
-      setReturningFromBreakoutAudioTransfer,
+      setBreakoutAudioTransferStatus,
+      getBreakoutAudioTransferStatus,
     } = this.props;
 
     const {
       joinedAudioOnly,
-      breakoutId: stateBreakoutId,
+      breakoutId: _stateBreakoutId,
       requestedBreakoutId,
       waiting,
     } = this.state;
 
+    const {
+      breakoutMeetingId: currentAudioTransferBreakoutId,
+      status,
+    } = getBreakoutAudioTransferStatus();
+
+    const isInBreakoutAudioTransfer = status
+      === AudioManager.BREAKOUT_AUDIO_TRANSFER_STATES.CONNECTED;
+
+    const stateBreakoutId = _stateBreakoutId || currentAudioTransferBreakoutId;
     const moderatorJoinedAudio = isMicrophoneUser && amIModerator;
     const disable = waiting && requestedBreakoutId !== breakoutId;
-    const audioAction = joinedAudioOnly
+    const audioAction = joinedAudioOnly || isInBreakoutAudioTransfer
       ? () => {
-        setReturningFromBreakoutAudioTransfer(true);
+        setBreakoutAudioTransferStatus(breakoutId,
+          AudioManager.BREAKOUT_AUDIO_TRANSFER_STATES.RETURNING);
         this.returnBackToMeeeting(breakoutId);
         return logger.debug({
           logCode: 'breakoutroom_return_main_audio',
@@ -195,6 +210,8 @@ class BreakoutRoom extends PureComponent {
         }, 'Returning to main audio (breakout room audio closed)');
       }
       : () => {
+        setBreakoutAudioTransferStatus(breakoutId,
+          AudioManager.BREAKOUT_AUDIO_TRANSFER_STATES.CONNECTED);
         this.transferUserToBreakoutRoom(breakoutId);
         return logger.debug({
           logCode: 'breakoutroom_join_audio_from_main_room',
@@ -234,9 +251,10 @@ class BreakoutRoom extends PureComponent {
               (
                 <Button
                   label={
-                      stateBreakoutId === breakoutId && joinedAudioOnly
-                      ? intl.formatMessage(intlMessages.breakoutReturnAudio)
-                      : intl.formatMessage(intlMessages.breakoutJoinAudio)
+                      stateBreakoutId === breakoutId
+                        && (joinedAudioOnly || isInBreakoutAudioTransfer)
+                        ? intl.formatMessage(intlMessages.breakoutReturnAudio)
+                        : intl.formatMessage(intlMessages.breakoutJoinAudio)
                   }
                   className={styles.button}
                   disabled={stateBreakoutId !== breakoutId && joinedAudioOnly}
diff --git a/bigbluebutton-html5/imports/ui/components/breakout-room/container.jsx b/bigbluebutton-html5/imports/ui/components/breakout-room/container.jsx
index f092a239a7..d4f1e30915 100644
--- a/bigbluebutton-html5/imports/ui/components/breakout-room/container.jsx
+++ b/bigbluebutton-html5/imports/ui/components/breakout-room/container.jsx
@@ -26,7 +26,10 @@ export default withTracker((props) => {
   const isMicrophoneUser = AudioService.isConnected() && !AudioService.isListenOnly();
   const isMeteorConnected = Meteor.status().connected;
   const isReconnecting = AudioService.isReconnecting();
-  const { setReturningFromBreakoutAudioTransfer } = AudioService;
+  const {
+    setBreakoutAudioTransferStatus,
+    getBreakoutAudioTransferStatus,
+  } = AudioService;
 
   return {
     ...props,
@@ -43,7 +46,8 @@ export default withTracker((props) => {
     isMeteorConnected,
     isUserInBreakoutRoom,
     exitAudio: () => AudioManager.exitAudio(),
-    setReturningFromBreakoutAudioTransfer,
     isReconnecting,
+    setBreakoutAudioTransferStatus,
+    getBreakoutAudioTransferStatus,
   };
 })(BreakoutContainer);
diff --git a/bigbluebutton-html5/imports/ui/services/audio-manager/index.js b/bigbluebutton-html5/imports/ui/services/audio-manager/index.js
index 23c2b4e8bd..91042209a7 100755
--- a/bigbluebutton-html5/imports/ui/services/audio-manager/index.js
+++ b/bigbluebutton-html5/imports/ui/services/audio-manager/index.js
@@ -30,6 +30,12 @@ const CALL_STATES = {
   AUTOPLAY_BLOCKED: 'autoplayBlocked',
 };
 
+const BREAKOUT_AUDIO_TRANSFER_STATES = {
+  CONNECTED: 'connected',
+  DISCONNECTED: 'disconnected',
+  RETURNING: 'returning',
+};
+
 class AudioManager {
   constructor() {
     this._inputDevice = {
@@ -37,7 +43,10 @@ class AudioManager {
       tracker: new Tracker.Dependency(),
     };
 
-    this._returningFromBreakoutAudioTransfer = false;
+    this._breakoutAudioTransferStatus = {
+      status: BREAKOUT_AUDIO_TRANSFER_STATES.DISCONNECTED,
+      breakoutMeetingId: null,
+    };
 
     this.defineProperties({
       isMuted: false,
@@ -59,6 +68,8 @@ class AudioManager {
     this.failedMediaElements = [];
     this.handlePlayElementFailed = this.handlePlayElementFailed.bind(this);
     this.monitor = this.monitor.bind(this);
+
+    this.BREAKOUT_AUDIO_TRANSFER_STATES = BREAKOUT_AUDIO_TRANSFER_STATES;
   }
 
   init(userData, audioEventHandler) {
@@ -534,12 +545,29 @@ class AudioManager {
       ? this.bridge.inputDeviceId : DEFAULT_INPUT_DEVICE_ID;
   }
 
-  get returningFromBreakoutAudioTransfer() {
-    return this._returningFromBreakoutAudioTransfer;
+  /**
+   * Sets the current status for breakout audio transfer
+   * @param {String}  breakoutMeetingId         The id of the current breakout
+   *                                            audio transfer.
+   * @param {String} status                     The status to be set for
+   *                                            audio transfer. Valid values
+   *                                            are 'connected', 'disconnected'
+   *                                            and 'returning'
+   */
+  setBreakoutAudioTransferStatus(breakoutMeetingId, status) {
+    const data = this._breakoutAudioTransferStatus;
+
+    if (typeof breakoutMeetingId === 'string') {
+      data.breakoutMeetingId = breakoutMeetingId;
+    }
+
+    if (typeof status === 'string') {
+      data.status = status;
+    }
   }
 
-  set returningFromBreakoutAudioTransfer(value) {
-    this._returningFromBreakoutAudioTransfer = value;
+  getBreakoutAudioTransferStatus() {
+    return this._breakoutAudioTransferStatus;
   }
 
   set userData(value) {
-- 
GitLab