From 99e15b20ba24d4988bd57a3a41892b28d1c06c55 Mon Sep 17 00:00:00 2001
From: KDSBrowne <kert.browne85@gmail.com>
Date: Mon, 19 Nov 2018 23:29:48 +0000
Subject: [PATCH] add panel manager component

---
 bigbluebutton-html5/client/main.jsx           |   2 +
 .../actions-dropdown/component.jsx            |   5 +-
 .../imports/ui/components/app/component.jsx   |   7 +
 .../imports/ui/components/app/container.jsx   |   2 +-
 .../ui/components/breakout-room/service.js    |   5 +-
 .../imports/ui/components/chat/component.jsx  |   5 +-
 .../ui/components/nav-bar/component.jsx       |   8 +-
 .../ui/components/panel-manager/component.jsx | 159 ++++++++++++++++++
 .../imports/ui/components/poll/component.jsx  |   7 +-
 .../components/poll/live-result/component.jsx |   3 +-
 .../user-list/chat-list-item/component.jsx    |  11 +-
 .../breakout-room/component.jsx               |  10 +-
 .../user-dropdown/component.jsx               |   6 +-
 .../user-polls/component.jsx                  |  12 +-
 14 files changed, 205 insertions(+), 37 deletions(-)
 create mode 100644 bigbluebutton-html5/imports/ui/components/panel-manager/component.jsx

diff --git a/bigbluebutton-html5/client/main.jsx b/bigbluebutton-html5/client/main.jsx
index c7715123a1..b674ebf094 100755
--- a/bigbluebutton-html5/client/main.jsx
+++ b/bigbluebutton-html5/client/main.jsx
@@ -34,6 +34,8 @@ Meteor.startup(() => {
     }
     authenticatedRouteHandler(() => {
       // set defaults
+      Session.set('openPanel', '');
+
       Session.set('isChatOpen', false);
       Session.set('idChatOpen', '');
       Session.set('isMeetingEnded', false);
diff --git a/bigbluebutton-html5/imports/ui/components/actions-bar/actions-dropdown/component.jsx b/bigbluebutton-html5/imports/ui/components/actions-bar/actions-dropdown/component.jsx
index 0da0a77e1c..5aa94094ba 100755
--- a/bigbluebutton-html5/imports/ui/components/actions-bar/actions-dropdown/component.jsx
+++ b/bigbluebutton-html5/imports/ui/components/actions-bar/actions-dropdown/component.jsx
@@ -127,7 +127,10 @@ class ActionsDropdown extends Component {
           label={intl.formatMessage(intlMessages.pollBtnLabel)}
           description={intl.formatMessage(intlMessages.pollBtnDesc)}
           key={this.pollId}
-          onClick={() => togglePollMenu()}
+          onClick={() => {
+            Session.set('openPanel', 'poll');
+            Session.set('forcePollOpen', true);
+          }}
         />
         : null),
       (isUserPresenter ?
diff --git a/bigbluebutton-html5/imports/ui/components/app/component.jsx b/bigbluebutton-html5/imports/ui/components/app/component.jsx
index 865c027833..868d9c0990 100755
--- a/bigbluebutton-html5/imports/ui/components/app/component.jsx
+++ b/bigbluebutton-html5/imports/ui/components/app/component.jsx
@@ -17,6 +17,8 @@ import { styles } from './styles';
 import UserListContainer from '../user-list/container';
 import ChatContainer from '../chat/container';
 import PollContainer from '/imports/ui/components/poll/container';
+import PanelManager from '/imports/ui/components/panel-manager/component';
+import { Session } from 'meteor/session';
 
 const MOBILE_MEDIA = 'only screen and (max-width: 40em)';
 const USERLIST_COMPACT_WIDTH = 50;
@@ -108,6 +110,10 @@ class App extends Component {
     this.setState({ enableResize: shouldEnableResize });
   }
 
+  renderPanel() {
+    return <PanelManager openPanel={Session.get('openPanel')} />;
+  }
+
   renderPoll() {
     const { pollIsOpen } = this.props;
 
@@ -346,6 +352,7 @@ class App extends Component {
           {this.renderPoll()}
           {this.renderBreakoutRoom()}
           {this.renderSidebar()}
+          {this.renderPanel()}
         </section>
         <PollingContainer />
         <ModalContainer />
diff --git a/bigbluebutton-html5/imports/ui/components/app/container.jsx b/bigbluebutton-html5/imports/ui/components/app/container.jsx
index 600aee2578..4bbbbd1781 100755
--- a/bigbluebutton-html5/imports/ui/components/app/container.jsx
+++ b/bigbluebutton-html5/imports/ui/components/app/container.jsx
@@ -67,7 +67,6 @@ const AppContainer = (props) => {
   );
 };
 
-
 export default injectIntl(withModalMounter(withTracker(({ intl, baseControls }) => {
   const currentUser = Users.findOne({ userId: Auth.userID });
   const isMeetingBreakout = meetingIsBreakout();
@@ -117,6 +116,7 @@ export default injectIntl(withModalMounter(withTracker(({ intl, baseControls })
     pollIsOpen: Session.get('isPollOpen') && Session.get('isUserListOpen'),
     customStyle: getFromUserSettings('customStyle', false),
     customStyleUrl: getFromUserSettings('customStyleUrl', false),
+    openPanel: Session.get('openPanel'),
   };
 })(AppContainer)));
 
diff --git a/bigbluebutton-html5/imports/ui/components/breakout-room/service.js b/bigbluebutton-html5/imports/ui/components/breakout-room/service.js
index 9158ea9d37..025cfb7651 100644
--- a/bigbluebutton-html5/imports/ui/components/breakout-room/service.js
+++ b/bigbluebutton-html5/imports/ui/components/breakout-room/service.js
@@ -25,9 +25,9 @@ const breakoutRoomUser = (breakoutId) => {
   return breakoutUser;
 };
 
-
 const endAllBreakouts = () => {
   makeCall('endAllBreakouts');
+  closeBreakoutPanel();
 };
 
 const requestJoinURL = (breakoutId) => {
@@ -63,8 +63,7 @@ const isModerator = () => {
   return mappedUser.isModerator;
 };
 
-
-const closeBreakoutPanel = () => Session.set('breakoutRoomIsOpen', false);
+const closeBreakoutPanel = () => Session.set('openPanel', 'userlist');
 
 export default {
   findBreakouts,
diff --git a/bigbluebutton-html5/imports/ui/components/chat/component.jsx b/bigbluebutton-html5/imports/ui/components/chat/component.jsx
index 7c6faba8da..2379ed9fb9 100644
--- a/bigbluebutton-html5/imports/ui/components/chat/component.jsx
+++ b/bigbluebutton-html5/imports/ui/components/chat/component.jsx
@@ -56,7 +56,7 @@ const Chat = (props) => {
         >
           <Button
             onClick={() => {
-              Session.set('isChatOpen', false);
+              Session.set('openPanel', 'userlist');
             }}
             aria-label={intl.formatMessage(intlMessages.hideChatLabel, { 0: title })}
             accessKey={HIDE_CHAT_AK}
@@ -74,8 +74,7 @@ const Chat = (props) => {
               hideLabel
               onClick={() => {
                 actions.handleClosePrivateChat(chatID);
-                Session.set('isChatOpen', false);
-                Session.set('idChatOpen', '');
+                Session.set('openPanel', 'userlist');
               }}
               aria-label={intl.formatMessage(intlMessages.closeChatLabel, { 0: title })}
               label={intl.formatMessage(intlMessages.closeChatLabel, { 0: title })}
diff --git a/bigbluebutton-html5/imports/ui/components/nav-bar/component.jsx b/bigbluebutton-html5/imports/ui/components/nav-bar/component.jsx
index fb8ae22b3a..bc0bc62eae 100755
--- a/bigbluebutton-html5/imports/ui/components/nav-bar/component.jsx
+++ b/bigbluebutton-html5/imports/ui/components/nav-bar/component.jsx
@@ -16,6 +16,7 @@ import { styles } from './styles.scss';
 import Button from '../button/component';
 import RecordingIndicator from './recording-indicator/component';
 import SettingsDropdownContainer from './settings-dropdown/container';
+import { Session } from 'meteor/session';
 
 const intlMessages = defineMessages({
   toggleUserListLabel: {
@@ -109,7 +110,12 @@ class NavBar extends Component {
   }
 
   handleToggleUserList() {
-    this.props.toggleUserList();
+    Session.set(
+      'openPanel',
+      Session.get('openPanel') === 'userlist'
+        ? ''
+        : 'userlist',
+    );
   }
 
   inviteUserToBreakout(breakout) {
diff --git a/bigbluebutton-html5/imports/ui/components/panel-manager/component.jsx b/bigbluebutton-html5/imports/ui/components/panel-manager/component.jsx
new file mode 100644
index 0000000000..7dd6c99a1e
--- /dev/null
+++ b/bigbluebutton-html5/imports/ui/components/panel-manager/component.jsx
@@ -0,0 +1,159 @@
+import React, { Component } from 'react';
+import BreakoutRoomContainer from '/imports/ui/components/breakout-room/container';
+import UserListContainer from '/imports/ui/components/user-list/container';
+import ChatContainer from '/imports/ui/components/chat/container';
+import PollContainer from '/imports/ui/components/poll/container';
+import { defineMessages, injectIntl } from 'react-intl';
+import Resizable from 're-resizable';
+import { styles } from '/imports/ui/components/app/styles';
+import cx from 'classnames';
+
+const intlMessages = defineMessages({
+  chatLabel: {
+    id: 'app.chat.label',
+    description: 'Aria-label for Chat Section',
+  },
+  userListLabel: {
+    id: 'app.userList.label',
+    description: 'Aria-label for Userlist Nav',
+  },
+});
+
+class PanelManager extends Component {
+  constructor() {
+    super();
+  }
+
+  renderUserList() {
+    const {
+      intl,
+    } = this.props;
+
+    const userListStyle = {};
+
+    return (
+      <div
+        className={cx(styles.userList, userListStyle)}
+        aria-label={intl.formatMessage(intlMessages.userListLabel)}
+      >
+        <UserListContainer />
+      </div>
+    );
+  }
+
+  renderUserListResizable() {
+    // Variables for resizing user-list.
+    const USERLIST_MIN_WIDTH_PX = 150;
+    const USERLIST_MAX_WIDTH_PX = 240;
+    const USERLIST_DEFAULT_WIDTH_RELATIVE = 18;
+
+    // decide whether using pixel or percentage unit as a default width for userList
+    const USERLIST_DEFAULT_WIDTH = (window.innerWidth * (USERLIST_DEFAULT_WIDTH_RELATIVE / 100.0)) < USERLIST_MAX_WIDTH_PX ? `${USERLIST_DEFAULT_WIDTH_RELATIVE}%` : USERLIST_MAX_WIDTH_PX;
+
+    const resizableEnableOptions = {
+      top: false,
+      right: true,
+      bottom: false,
+      left: false,
+      topRight: false,
+      bottomRight: false,
+      bottomLeft: false,
+      topLeft: false,
+    };
+
+    return (
+      <Resizable
+        defaultSize={{ width: USERLIST_DEFAULT_WIDTH }}
+        minWidth={USERLIST_MIN_WIDTH_PX}
+        maxWidth={USERLIST_MAX_WIDTH_PX}
+        ref={(node) => { this.resizableUserList = node; }}
+        className={styles.resizableUserList}
+        enable={resizableEnableOptions}
+      >
+        {this.renderUserList()}
+      </Resizable>
+    );
+  }
+
+  renderChat() {
+    const { intl } = this.props;
+
+    return (
+      <section
+        className={styles.chat}
+        aria-label={intl.formatMessage(intlMessages.chatLabel)}
+      >
+        <ChatContainer />
+      </section>
+    );
+  }
+
+  renderChatResizable() {
+    // Variables for resizing chat.
+    const CHAT_MIN_WIDTH = '10%';
+    const CHAT_MAX_WIDTH = '25%';
+    const CHAT_DEFAULT_WIDTH = '15%';
+
+    const resizableEnableOptions = {
+      top: false,
+      right: true,
+      bottom: false,
+      left: false,
+      topRight: false,
+      bottomRight: false,
+      bottomLeft: false,
+      topLeft: false,
+    };
+
+    return (
+      <Resizable
+        defaultSize={{ width: CHAT_DEFAULT_WIDTH }}
+        minWidth={CHAT_MIN_WIDTH}
+        maxWidth={CHAT_MAX_WIDTH}
+        ref={(node) => { this.resizableChat = node; }}
+        className={styles.resizableChat}
+        enable={resizableEnableOptions}
+      >
+        {this.renderChat()}
+      </Resizable>
+    );
+  }
+
+  renderPoll() {
+    return (
+      <div className={styles.poll}>
+        <PollContainer />
+      </div>
+    );
+  }
+
+  renderBreakoutRoom() {
+    return (
+      <div className={styles.breakoutRoom}>
+        <BreakoutRoomContainer />
+      </div>
+    );
+  }
+
+  render() {
+    switch (this.props.openPanel) {
+      case 'chat': return [
+        this.renderUserListResizable(),
+        this.renderChatResizable(),
+      ];
+      case 'poll': return [
+        this.renderUserListResizable(),
+        this.renderPoll(),
+      ];
+      case 'breakoutroom': return [
+        this.renderUserListResizable(),
+        this.renderBreakoutRoom(),
+      ];
+      case 'userlist': return this.renderUserListResizable();
+      default: break;
+    }
+    return null;
+  }
+}
+
+export default injectIntl(PanelManager);
diff --git a/bigbluebutton-html5/imports/ui/components/poll/component.jsx b/bigbluebutton-html5/imports/ui/components/poll/component.jsx
index e4377ba24c..e28ce86bdf 100644
--- a/bigbluebutton-html5/imports/ui/components/poll/component.jsx
+++ b/bigbluebutton-html5/imports/ui/components/poll/component.jsx
@@ -248,9 +248,7 @@ class Poll extends Component {
             aria-label={intl.formatMessage(intlMessages.hidePollDesc)}
             className={styles.hideBtn}
             onClick={() => {
-              Session.set('isPollOpen', false);
-              Session.set('forcePollOpen', true);
-              Session.set('isUserListOpen', true);
+              Session.set('openPanel', 'userlist');
             }}
           />
 
@@ -260,9 +258,8 @@ class Poll extends Component {
             if (currentPoll) {
               stopPoll();
             }
-            Session.set('isPollOpen', false);
+            Session.set('openPanel', 'userlist');
             Session.set('forcePollOpen', false);
-            Session.set('isUserListOpen', true);
           }}
             className={styles.closeBtn}
             icon="close"
diff --git a/bigbluebutton-html5/imports/ui/components/poll/live-result/component.jsx b/bigbluebutton-html5/imports/ui/components/poll/live-result/component.jsx
index aa614df8ce..ed5ff11b70 100644
--- a/bigbluebutton-html5/imports/ui/components/poll/live-result/component.jsx
+++ b/bigbluebutton-html5/imports/ui/components/poll/live-result/component.jsx
@@ -117,8 +117,7 @@ class LiveResult extends Component {
           onClick={() => {
             publishPoll();
             stopPoll();
-            Session.set('isUserListOpen', true);
-            Session.set('isPollOpen', false);
+            Session.set('openPanel', 'userlist');
             Session.set('forcePollOpen', false);
           }}
           label={intl.formatMessage(intlMessages.publishLabel)}
diff --git a/bigbluebutton-html5/imports/ui/components/user-list/chat-list-item/component.jsx b/bigbluebutton-html5/imports/ui/components/user-list/chat-list-item/component.jsx
index 59231a6409..13faf278e8 100644
--- a/bigbluebutton-html5/imports/ui/components/user-list/chat-list-item/component.jsx
+++ b/bigbluebutton-html5/imports/ui/components/user-list/chat-list-item/component.jsx
@@ -73,13 +73,12 @@ const ChatListItem = (props) => {
       tabIndex={tabIndex}
       accessKey={isPublicChat(chat) ? TOGGLE_CHAT_PUB_AK : null}
       onClick={() => {
-        toggleChatOpen();
+        Session.set(
+'openPanel',
+          Session.get('openPanel') === 'chat' && Session.get('idChatOpen') === chat.id
+            ? 'userlist' : 'chat',
+        );
         Session.set('idChatOpen', chat.id);
-
-        if (Session.equals('isPollOpen', true)) {
-          Session.set('isPollOpen', false);
-          Session.set('forcePollOpen', true);
-        }
       }}
       id="chat-toggle-button"
       aria-label={isPublicChat(chat) ? intl.formatMessage(intlMessages.titlePublic) : chat.name}
diff --git a/bigbluebutton-html5/imports/ui/components/user-list/user-list-content/breakout-room/component.jsx b/bigbluebutton-html5/imports/ui/components/user-list/user-list-content/breakout-room/component.jsx
index b0df572d51..a7784fb50b 100644
--- a/bigbluebutton-html5/imports/ui/components/user-list/user-list-content/breakout-room/component.jsx
+++ b/bigbluebutton-html5/imports/ui/components/user-list/user-list-content/breakout-room/component.jsx
@@ -12,10 +12,12 @@ const intlMessages = defineMessages({
   },
 });
 const toggleBreakoutPanel = () => {
-  const breakoutPanelState = Session.get('breakoutRoomIsOpen');
-  Session.set('breakoutRoomIsOpen', !breakoutPanelState);
-  Session.set('isChatOpen', false);
-  Session.set('isPollOpen', false);
+  Session.set(
+    'openPanel',
+    Session.get('openPanel') === 'breakoutroom'
+      ? 'userlist'
+      : 'breakoutroom',
+  );
 };
 
 const BreakoutRoomItem = ({
diff --git a/bigbluebutton-html5/imports/ui/components/user-list/user-list-content/user-participants/user-list-item/user-dropdown/component.jsx b/bigbluebutton-html5/imports/ui/components/user-list/user-list-content/user-participants/user-list-item/user-dropdown/component.jsx
index ccb9aa475b..350bcfa3e5 100755
--- a/bigbluebutton-html5/imports/ui/components/user-list/user-list-content/user-participants/user-list-item/user-dropdown/component.jsx
+++ b/bigbluebutton-html5/imports/ui/components/user-list/user-list-content/user-participants/user-list-item/user-dropdown/component.jsx
@@ -236,12 +236,8 @@ class UserDropdown extends Component {
         intl.formatMessage(messages.ChatLabel),
         () => {
           getGroupChatPrivate(currentUser, user);
-          if (Session.equals('isPollOpen', true)) {
-            Session.set('isPollOpen', false);
-            Session.set('forcePollOpen', true);
-          }
+          Session.set('openPanel', 'chat');
           Session.set('idChatOpen', user.id);
-          Session.set('isChatOpen', true);
         },
         'chat',
       ));
diff --git a/bigbluebutton-html5/imports/ui/components/user-list/user-list-content/user-polls/component.jsx b/bigbluebutton-html5/imports/ui/components/user-list/user-list-content/user-polls/component.jsx
index 1d9098daea..5c061aac50 100644
--- a/bigbluebutton-html5/imports/ui/components/user-list/user-list-content/user-polls/component.jsx
+++ b/bigbluebutton-html5/imports/ui/components/user-list/user-list-content/user-polls/component.jsx
@@ -41,12 +41,12 @@ class UserPolls extends Component {
             tabIndex={0}
             className={styles.pollLink}
             onClick={() => {
-              Session.set('isChatOpen', false);
-              Session.set('breakoutRoomIsOpen', false);
-
-              return Session.equals('isPollOpen', true)
-                ? Session.set('isPollOpen', false)
-                : Session.set('isPollOpen', true);
+              Session.set(
+'openPanel',
+                Session.get('openPanel') === 'poll'
+                  ? 'userlist'
+                  : 'poll',
+              );
             }}
           >
             <Icon iconName="polling" className={styles.icon} />
-- 
GitLab