diff --git a/bigbluebutton-html5/imports/startup/client/base.jsx b/bigbluebutton-html5/imports/startup/client/base.jsx
index e82853475ada9a30d30444795599e116596f27a0..040ea81e531f9e111bab4e3b1b9cb4beb83e0e57 100755
--- a/bigbluebutton-html5/imports/startup/client/base.jsx
+++ b/bigbluebutton-html5/imports/startup/client/base.jsx
@@ -1,4 +1,4 @@
-import React, { Component, Fragment } from 'react';
+import React, { Component } from 'react';
 import { withTracker } from 'meteor/react-meteor-data';
 import PropTypes from 'prop-types';
 import Auth from '/imports/ui/services/auth';
@@ -18,9 +18,6 @@ import AudioService from '/imports/ui/components/audio/service';
 import { notify } from '/imports/ui/services/notification';
 import deviceInfo from '/imports/utils/deviceInfo';
 import getFromUserSettings from '/imports/ui/services/users-settings';
-import LayoutManager from '/imports/ui/components/layout/layout-manager';
-import { withLayoutContext } from '/imports/ui/components/layout/context';
-import VideoService from '/imports/ui/components/video-provider/service';
 
 const CHAT_CONFIG = Meteor.settings.public.chat;
 const CHAT_ENABLED = CHAT_CONFIG.enabled;
@@ -104,23 +101,12 @@ class Base extends Component {
       ejected,
       isMeteorConnected,
       subscriptionsReady,
-      layoutContextDispatch,
-      usersVideo,
     } = this.props;
     const {
       loading,
       meetingExisted,
     } = this.state;
 
-    if (usersVideo !== prevProps.usersVideo) {
-      layoutContextDispatch(
-        {
-          type: 'setUsersVideo',
-          value: usersVideo.length,
-        },
-      );
-    }
-
     if (!prevProps.subscriptionsReady && subscriptionsReady) {
       logger.info({ logCode: 'startup_client_subscriptions_ready' }, 'Subscriptions are ready');
     }
@@ -225,14 +211,9 @@ class Base extends Component {
     const { meetingExisted } = this.state;
 
     return (
-      <Fragment>
-        <LayoutManager />
-        {
-          (!meetingExisted && !meetingExist && Auth.loggedIn)
-            ? <LoadingScreen />
-            : this.renderByState()
-        }
-      </Fragment>
+      (!meetingExisted && !meetingExist && Auth.loggedIn)
+        ? <LoadingScreen />
+        : this.renderByState()
     );
   }
 }
@@ -384,7 +365,6 @@ const BaseContainer = withTracker(() => {
     Session.set('openPanel', '');
   }
 
-  const usersVideo = VideoService.getVideoStreams();
   const codeError = Session.get('codeError');
 
   return {
@@ -401,9 +381,8 @@ const BaseContainer = withTracker(() => {
     meetingIsBreakout: AppService.meetingIsBreakout(),
     subscriptionsReady: Session.get('subscriptionsReady'),
     loggedIn,
-    usersVideo,
     codeError,
   };
-})(withLayoutContext(Base));
+})(Base);
 
 export default BaseContainer;
diff --git a/bigbluebutton-html5/imports/startup/client/intl.jsx b/bigbluebutton-html5/imports/startup/client/intl.jsx
index 55a16f072b655f587e655f77a1095740112f095b..3e139e57c74e1a80cd498828926f1889893de47e 100644
--- a/bigbluebutton-html5/imports/startup/client/intl.jsx
+++ b/bigbluebutton-html5/imports/startup/client/intl.jsx
@@ -115,18 +115,6 @@ const defaultProps = {
 };
 
 class IntlStartup extends Component {
-  static saveLocale(localeName) {
-    Settings.application.locale = localeName;
-    if (RTL_LANGUAGES.includes(localeName.substring(0, 2))) {
-      document.body.parentNode.setAttribute('dir', 'rtl');
-      Settings.application.isRTL = true;
-    } else {
-      document.body.parentNode.setAttribute('dir', 'ltr');
-      Settings.application.isRTL = false;
-    }
-    Settings.save();
-  }
-
   constructor(props) {
     super(props);
 
@@ -143,14 +131,14 @@ class IntlStartup extends Component {
     this.fetchLocalizedMessages = this.fetchLocalizedMessages.bind(this);
   }
 
-  componentDidMount() {
+  componentWillMount() {
     const { locale } = this.props;
     this.fetchLocalizedMessages(locale, true);
   }
 
-  componentDidUpdate() {
+  componentWillUpdate(nextProps) {
     const { fetching, normalizedLocale } = this.state;
-    const { locale } = this.props;
+    const { locale } = nextProps;
 
     if (!fetching
       && normalizedLocale
@@ -174,23 +162,35 @@ class IntlStartup extends Component {
         .then(({ messages, normalizedLocale }) => {
           const dasherizedLocale = normalizedLocale.replace('_', '-');
           this.setState({ messages, fetching: false, normalizedLocale: dasherizedLocale }, () => {
-            IntlStartup.saveLocale(dasherizedLocale);
+            this.saveLocale(dasherizedLocale);
           });
         })
         .catch(() => {
           this.setState({ fetching: false, normalizedLocale: null }, () => {
-            IntlStartup.saveLocale(DEFAULT_LANGUAGE);
+            this.saveLocale(DEFAULT_LANGUAGE);
           });
         });
     });
   }
 
+  saveLocale(localeName) {
+    Settings.application.locale = localeName;
+    if (RTL_LANGUAGES.includes(localeName.substring(0, 2))) {
+      document.body.parentNode.setAttribute('dir', 'rtl');
+      Settings.application.isRTL = true;
+    } else {
+      document.body.parentNode.setAttribute('dir', 'ltr');
+      Settings.application.isRTL = false;
+    }
+    Settings.save();
+  }
+
   render() {
     const { fetching, normalizedLocale, messages } = this.state;
     const { children } = this.props;
-    const test = normalizedLocale;
+
     return fetching ? <LoadingScreen /> : (
-      <IntlProvider locale={test} messages={messages}>
+      <IntlProvider locale={normalizedLocale} messages={messages}>
         {children}
       </IntlProvider>
     );
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 629402ecac0d348faaccab82d5c23c4ebe7b50bb..c89a30a828c511e4b3cbe7dd71302c731300d6e2 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
@@ -91,9 +91,9 @@ class ActionsDropdown extends PureComponent {
     this.makePresentationItems = this.makePresentationItems.bind(this);
   }
 
-  componentDidUpdate(prevProps) {
-    const { amIPresenter: wasPresenter } = prevProps;
-    const { amIPresenter: isPresenter, mountModal } = this.props;
+  componentWillUpdate(nextProps) {
+    const { amIPresenter: isPresenter } = nextProps;
+    const { amIPresenter: wasPresenter, mountModal } = this.props;
     if (wasPresenter && !isPresenter) {
       mountModal(null);
     }
diff --git a/bigbluebutton-html5/imports/ui/components/actions-bar/component.jsx b/bigbluebutton-html5/imports/ui/components/actions-bar/component.jsx
index a39cebecff08f47773d1a95caa1efa631a25f1df..cbb2f601bf9e0956fce298540ad1e1a447e0f429 100755
--- a/bigbluebutton-html5/imports/ui/components/actions-bar/component.jsx
+++ b/bigbluebutton-html5/imports/ui/components/actions-bar/component.jsx
@@ -7,38 +7,8 @@ import AudioControlsContainer from '../audio/audio-controls/container';
 import JoinVideoOptionsContainer from '../video-provider/video-button/container';
 import CaptionsButtonContainer from '/imports/ui/components/actions-bar/captions/container';
 import PresentationOptionsContainer from './presentation-options/component';
-import Button from '/imports/ui/components/button/component';
-import Storage from '/imports/ui/services/storage/session';
-import { ACTIONSBAR_HEIGHT } from '/imports/ui/components/layout/layout-manager';
-import { withLayoutConsumer } from '/imports/ui/components/layout/context';
 
 class ActionsBar extends PureComponent {
-  constructor(props) {
-    super(props);
-
-    this.autoArrangeToggle = this.autoArrangeToggle.bind(this);
-  }
-
-  componentDidUpdate(prevProps) {
-    const { layoutContextState } = this.props;
-    const { layoutContextState: prevLayoutContextState } = prevProps;
-    const { autoArrangeLayout } = layoutContextState;
-    const { autoArrangeLayout: prevAutoArrangeLayout } = prevLayoutContextState;
-    if (autoArrangeLayout !== prevAutoArrangeLayout) this.forceUpdate();
-  }
-
-  autoArrangeToggle() {
-    const { layoutContextDispatch } = this.props;
-    const autoArrangeLayout = Storage.getItem('autoArrangeLayout');
-    layoutContextDispatch(
-      {
-        type: 'setAutoArrangeLayout',
-        value: !autoArrangeLayout,
-      },
-    );
-    window.dispatchEvent(new Event('autoArrangeChanged'));
-  }
-
   render() {
     const {
       amIPresenter,
@@ -69,19 +39,13 @@ class ActionsBar extends PureComponent {
     } = this.props;
 
     const actionBarClasses = {};
-    const autoArrangeLayout = Storage.getItem('autoArrangeLayout');
 
     actionBarClasses[styles.centerWithActions] = amIPresenter;
     actionBarClasses[styles.center] = true;
     actionBarClasses[styles.mobileLayoutSwapped] = isLayoutSwapped && amIPresenter;
 
     return (
-      <div
-        className={styles.actionsbar}
-        style={{
-          height: ACTIONSBAR_HEIGHT,
-        }}
-      >
+      <div className={styles.actionsbar}>
         <div className={styles.left}>
           <ActionsDropdown {...{
             amIPresenter,
@@ -123,18 +87,6 @@ class ActionsBar extends PureComponent {
             screenshareDataSavingSetting,
           }}
           />
-          <Button
-            className={cx(styles.button, autoArrangeLayout || styles.btn)}
-            icon={autoArrangeLayout ? 'lock' : 'unlock'}
-            color={autoArrangeLayout ? 'primary' : 'default'}
-            ghost={!autoArrangeLayout}
-            onClick={this.autoArrangeToggle}
-            label={autoArrangeLayout ? 'Disable Auto Arrange' : 'Enable Auto Arrange'}
-            aria-label="Auto Arrange test"
-            hideLabel
-            circle
-            size="lg"
-          />
         </div>
         <div className={styles.right}>
           {isLayoutSwapped
@@ -152,4 +104,4 @@ class ActionsBar extends PureComponent {
   }
 }
 
-export default withLayoutConsumer(ActionsBar);
+export default ActionsBar;
diff --git a/bigbluebutton-html5/imports/ui/components/app/component.jsx b/bigbluebutton-html5/imports/ui/components/app/component.jsx
index a2772cb1687e292d97a3ba1ac13c8a27c8705498..b087b4447822f6e2719c4a18d9d7a6dd46b89e35 100755
--- a/bigbluebutton-html5/imports/ui/components/app/component.jsx
+++ b/bigbluebutton-html5/imports/ui/components/app/component.jsx
@@ -23,7 +23,6 @@ import MediaService from '/imports/ui/components/media/service';
 import ManyWebcamsNotifier from '/imports/ui/components/video-provider/many-users-notify/container';
 import { withDraggableContext } from '../media/webcam-draggable-overlay/context';
 import { styles } from './styles';
-import { NAVBAR_HEIGHT } from '/imports/ui/components/layout/layout-manager';
 
 const MOBILE_MEDIA = 'only screen and (max-width: 40em)';
 const APP_CONFIG = Meteor.settings.public.app;
@@ -230,12 +229,7 @@ class App extends Component {
     if (!navbar) return null;
 
     return (
-      <header
-        className={styles.navbar}
-        style={{
-          height: NAVBAR_HEIGHT,
-        }}
-      >
+      <header className={styles.navbar}>
         {navbar}
       </header>
     );
diff --git a/bigbluebutton-html5/imports/ui/components/app/styles.scss b/bigbluebutton-html5/imports/ui/components/app/styles.scss
index 4e09d202f3bbf93ec53c4500946563b57940c284..0d03f0eba183c776db7a25b3910ff4078da5d792 100755
--- a/bigbluebutton-html5/imports/ui/components/app/styles.scss
+++ b/bigbluebutton-html5/imports/ui/components/app/styles.scss
@@ -155,12 +155,12 @@
   }
 
   @include mq($medium-up) {
-    // flex: 0 25vw;
+    flex: 0 25vw;
     order: 1;
   }
 
   @include mq($xlarge-up) {
-    // flex-basis: 20vw;
+    flex-basis: 20vw;
   }
 }
 
@@ -172,10 +172,10 @@
 
 .breakoutRoom {
   height: 100%;
-  // width: 20vw;
+  width: 20vw;
   background-color: var(--color-white);
   @include mq($small-only) {
-    // width: auto;
+    width: auto;
     height: auto;
   }
 }
diff --git a/bigbluebutton-html5/imports/ui/components/breakout-room/service.js b/bigbluebutton-html5/imports/ui/components/breakout-room/service.js
index 9fe60517c9b3cb73a8d9d9d132aa8e11b1b3ecfb..28d549d134b134eb3dd261ec8bf1c0b26746dfa1 100644
--- a/bigbluebutton-html5/imports/ui/components/breakout-room/service.js
+++ b/bigbluebutton-html5/imports/ui/components/breakout-room/service.js
@@ -27,10 +27,7 @@ const breakoutRoomUser = (breakoutId) => {
   return breakoutUser;
 };
 
-const closeBreakoutPanel = () => {
-  Session.set('openPanel', 'userlist');
-  window.dispatchEvent(new Event('panelChanged'));
-};
+const closeBreakoutPanel = () => Session.set('openPanel', 'userlist');
 
 const endAllBreakouts = () => {
   makeCall('endAllBreakouts');
diff --git a/bigbluebutton-html5/imports/ui/components/chat/alert/push-alert/component.jsx b/bigbluebutton-html5/imports/ui/components/chat/alert/push-alert/component.jsx
index db51df75ae5971c6d95a30f5664f06c16123dd21..31a3a02ed888311402c7373cb6d01f6d11c960a4 100755
--- a/bigbluebutton-html5/imports/ui/components/chat/alert/push-alert/component.jsx
+++ b/bigbluebutton-html5/imports/ui/components/chat/alert/push-alert/component.jsx
@@ -1,5 +1,6 @@
 import React, { PureComponent } from 'react';
 import PropTypes from 'prop-types';
+import _ from 'lodash';
 import injectNotify from '/imports/ui/components/toast/inject-notify/component';
 import { Session } from 'meteor/session';
 
@@ -30,7 +31,6 @@ class ChatPushAlert extends PureComponent {
         onClick={() => {
           Session.set('openPanel', 'chat');
           Session.set('idChatOpen', chat);
-          window.dispatchEvent(new Event('panelChanged'));
         }}
         onKeyPress={() => null}
       >
diff --git a/bigbluebutton-html5/imports/ui/components/chat/component.jsx b/bigbluebutton-html5/imports/ui/components/chat/component.jsx
index 44a2f0c6c66909301972225434f652e8e01bd940..e19380a741ebb8d2065d2ac6f9fa7ae76652a8f7 100755
--- a/bigbluebutton-html5/imports/ui/components/chat/component.jsx
+++ b/bigbluebutton-html5/imports/ui/components/chat/component.jsx
@@ -60,7 +60,6 @@ const Chat = (props) => {
             onClick={() => {
               Session.set('idChatOpen', '');
               Session.set('openPanel', 'userlist');
-              window.dispatchEvent(new Event('panelChanged'));
             }}
             aria-label={intl.formatMessage(intlMessages.hideChatLabel, { 0: title })}
             accessKey={HIDE_CHAT_AK}
@@ -82,7 +81,6 @@ const Chat = (props) => {
                   actions.handleClosePrivateChat(chatID);
                   Session.set('idChatOpen', '');
                   Session.set('openPanel', 'userlist');
-                  window.dispatchEvent(new Event('panelChanged'));
                 }}
                 aria-label={intl.formatMessage(intlMessages.closeChatLabel, { 0: title })}
                 label={intl.formatMessage(intlMessages.closeChatLabel, { 0: title })}
diff --git a/bigbluebutton-html5/imports/ui/components/dropdown/component.jsx b/bigbluebutton-html5/imports/ui/components/dropdown/component.jsx
index db7be924cbf08ba2f5a3f45a758ebf005fd442ae..4183ae38cff6697594836c9583fea2feff0acafd 100644
--- a/bigbluebutton-html5/imports/ui/components/dropdown/component.jsx
+++ b/bigbluebutton-html5/imports/ui/components/dropdown/component.jsx
@@ -72,6 +72,10 @@ class Dropdown extends Component {
     this.handleWindowClick = this.handleWindowClick.bind(this);
   }
 
+  componentWillUpdate(nextProps, nextState) {
+    return nextState.isOpen ? screenreaderTrap.trap(this.dropdown) : screenreaderTrap.untrap();
+  }
+
   componentDidUpdate(prevProps, prevState) {
     const {
       onShow,
@@ -81,11 +85,7 @@ class Dropdown extends Component {
 
     const { isOpen } = this.state;
 
-    if (isOpen) {
-      screenreaderTrap.trap(this.dropdown);
-    } else {
-      screenreaderTrap.untrap();
-    }
+
     if (isOpen && !prevState.isOpen) { onShow(); }
 
     if (!isOpen && prevState.isOpen) { onHide(); }
diff --git a/bigbluebutton-html5/imports/ui/components/layout/context.jsx b/bigbluebutton-html5/imports/ui/components/layout/context.jsx
deleted file mode 100644
index 4a83c894f3d5725452d866a9a88020af95dd621d..0000000000000000000000000000000000000000
--- a/bigbluebutton-html5/imports/ui/components/layout/context.jsx
+++ /dev/null
@@ -1,290 +0,0 @@
-import React, { createContext, useReducer, useEffect } from 'react';
-import Storage from '/imports/ui/services/storage/session';
-
-const { webcamsDefaultPlacement } = Meteor.settings.public.layout;
-
-export const LayoutContext = createContext();
-
-const initialState = {
-  autoArrangeLayout: true,
-  webcamsAreaResizing: false,
-  numUsersVideo: null,
-  windowSize: {
-    width: 0,
-    height: 0,
-  },
-  mediaBounds: {
-    width: 0,
-    height: 0,
-    top: 0,
-    left: 0,
-  },
-  userListSize: {
-    width: 0,
-  },
-  chatSize: {
-    width: 0,
-  },
-  noteSize: {
-    width: 0,
-  },
-  captionsSize: {
-    width: 0,
-  },
-  pollSize: {
-    width: 0,
-  },
-  waitingSize: {
-    width: 0,
-  },
-  breakoutRoomSize: {
-    width: 0,
-  },
-  webcamsAreaSize: {
-    width: 0,
-    height: 0,
-  },
-  tempWebcamsAreaSize: {
-    width: 0,
-    height: 0,
-  },
-  webcamsAreaUserSetsHeight: 0,
-  webcamsAreaUserSetsWidth: 0,
-  webcamsPlacement: webcamsDefaultPlacement || 'top',
-  presentationAreaSize: {
-    width: 0,
-    height: 0,
-  },
-  presentationSlideSize: {
-    width: 0,
-    height: 0,
-  },
-  presentationIsFullscreen: null,
-  presentationOrientation: null,
-};
-
-const reducer = (state, action) => {
-  switch (action.type) {
-    case 'setAutoArrangeLayout': {
-      return {
-        ...state,
-        autoArrangeLayout: action.value,
-      };
-    }
-    case 'setWebcamsAreaResizing': {
-      return {
-        ...state,
-        webcamsAreaResizing: action.value,
-      };
-    }
-    case 'setUsersVideo': {
-      return {
-        ...state,
-        numUsersVideo: action.value,
-      };
-    }
-    case 'setWindowSize': {
-      return {
-        ...state,
-        windowSize: {
-          width: action.value.width,
-          height: action.value.height,
-        },
-      };
-    }
-    case 'setMediaBounds': {
-      return {
-        ...state,
-        mediaBounds: {
-          width: action.value.width,
-          height: action.value.height,
-          top: action.value.top,
-          left: action.value.left,
-        },
-      };
-    }
-    case 'setUserListSize': {
-      return {
-        ...state,
-        userListSize: {
-          width: action.value.width,
-        },
-      };
-    }
-    case 'setChatSize': {
-      return {
-        ...state,
-        chatSize: {
-          width: action.value.width,
-        },
-      };
-    }
-    case 'setNoteSize': {
-      return {
-        ...state,
-        noteSize: {
-          width: action.value.width,
-        },
-      };
-    }
-    case 'setCaptionsSize': {
-      return {
-        ...state,
-        captionsSize: {
-          width: action.value.width,
-        },
-      };
-    }
-    case 'setPollSize': {
-      return {
-        ...state,
-        pollSize: {
-          width: action.value.width,
-        },
-      };
-    }
-    case 'setWaitingUsersPanelSize': {
-      return {
-        ...state,
-        waitingSize: {
-          width: action.value.width,
-        },
-      };
-    }
-    case 'setBreakoutRoomSize': {
-      return {
-        ...state,
-        breakoutRoomSize: {
-          width: action.value.width,
-        },
-      };
-    }
-    case 'setWebcamsPlacement': {
-      // webcamsPlacement: ('top' | 'right' | 'bottom' | 'left') string
-      return {
-        ...state,
-        webcamsPlacement: action.value,
-      };
-    }
-    case 'setWebcamsAreaSize': {
-      return {
-        ...state,
-        webcamsAreaSize: {
-          width: action.value.width,
-          height: action.value.height,
-        },
-      };
-    }
-    case 'setTempWebcamsAreaSize': {
-      return {
-        ...state,
-        tempWebcamsAreaSize: {
-          width: action.value.width,
-          height: action.value.height,
-        },
-      };
-    }
-    case 'setWebcamsAreaUserSetsHeight': {
-      return {
-        ...state,
-        webcamsAreaUserSetsHeight: action.value,
-      };
-    }
-    case 'setWebcamsAreaUserSetsWidth': {
-      return {
-        ...state,
-        webcamsAreaUserSetsWidth: action.value,
-      };
-    }
-    case 'setPresentationAreaSize': {
-      return {
-        ...state,
-        presentationAreaSize: {
-          width: action.value.width,
-          height: action.value.height,
-        },
-      };
-    }
-    case 'setPresentationSlideSize': {
-      return {
-        ...state,
-        presentationSlideSize: {
-          width: action.value.width,
-          height: action.value.height,
-        },
-      };
-    }
-    case 'setPresentationFullscreen': {
-      // presentationIsFullscreen: (true | false) boolean
-      return {
-        ...state,
-        presentationIsFullscreen: action.value,
-      };
-    }
-    case 'setPresentationOrientation': {
-      // presentationOrientation: ('portrait' | 'landscape') string
-      return {
-        ...state,
-        presentationOrientation: action.value,
-      };
-    }
-    default: {
-      throw new Error('Unexpected action');
-    }
-  }
-};
-
-const ContextProvider = (props) => {
-  const [layoutContextState, layoutContextDispatch] = useReducer(reducer, initialState);
-  const {
-    webcamsPlacement,
-    webcamsAreaUserSetsHeight,
-    webcamsAreaUserSetsWidth,
-    autoArrangeLayout,
-  } = layoutContextState;
-  const { children } = props;
-
-  useEffect(() => {
-    Storage.setItem('webcamsPlacement', webcamsPlacement);
-    Storage.setItem('webcamsAreaUserSetsHeight', webcamsAreaUserSetsHeight);
-    Storage.setItem('webcamsAreaUserSetsWidth', webcamsAreaUserSetsWidth);
-    Storage.setItem('autoArrangeLayout', autoArrangeLayout);
-  }, [
-    webcamsPlacement,
-    webcamsAreaUserSetsHeight,
-    webcamsAreaUserSetsWidth,
-    autoArrangeLayout,
-  ]);
-
-  return (
-    <LayoutContext.Provider value={{
-      layoutContextState,
-      layoutContextDispatch,
-      ...props,
-    }}
-    >
-      {children}
-    </LayoutContext.Provider>
-  );
-};
-
-const withProvider = Component => props => (
-  <ContextProvider {...props}>
-    <Component />
-  </ContextProvider>
-);
-
-const ContextConsumer = Component => props => (
-  <LayoutContext.Consumer>
-    {contexts => <Component {...props} {...contexts} />}
-  </LayoutContext.Consumer>
-);
-
-const withLayoutConsumer = Component => ContextConsumer(Component);
-const withLayoutContext = Component => withProvider(withLayoutConsumer(Component));
-
-export {
-  withProvider,
-  withLayoutConsumer,
-  withLayoutContext,
-};
diff --git a/bigbluebutton-html5/imports/ui/components/layout/layout-manager.jsx b/bigbluebutton-html5/imports/ui/components/layout/layout-manager.jsx
deleted file mode 100644
index 5a27d6fe807e793b63b75851d021e3f325c78b20..0000000000000000000000000000000000000000
--- a/bigbluebutton-html5/imports/ui/components/layout/layout-manager.jsx
+++ /dev/null
@@ -1,547 +0,0 @@
-import React, { Component, Fragment } from 'react';
-import Storage from '/imports/ui/services/storage/session';
-import { Session } from 'meteor/session';
-import { withLayoutConsumer } from '/imports/ui/components/layout/context';
-import { isVideoBroadcasting } from '/imports/ui/components/screenshare/service';
-import _ from 'lodash';
-
-const windowWidth = () => window.innerWidth;
-const windowHeight = () => window.innerHeight;
-const min = (value1, value2) => (value1 <= value2 ? value1 : value2);
-const max = (value1, value2) => (value1 >= value2 ? value1 : value2);
-
-// values based on sass file
-const USERLIST_MIN_WIDTH = 150;
-const USERLIST_MAX_WIDTH = 240;
-const CHAT_MIN_WIDTH = 150;
-const CHAT_MAX_WIDTH = 335;
-const NAVBAR_HEIGHT = 85;
-const ACTIONSBAR_HEIGHT = 42;
-
-const WEBCAMSAREA_MIN_PERCENT = 0.2;
-const WEBCAMSAREA_MAX_PERCENT = 0.8;
-// const PRESENTATIONAREA_MIN_PERCENT = 0.2;
-const PRESENTATIONAREA_MIN_WIDTH = 385; // Value based on presentation toolbar
-// const PRESENTATIONAREA_MAX_PERCENT = 0.8;
-
-const storageLayoutData = () => Storage.getItem('layoutData');
-
-class LayoutManager extends Component {
-  static calculatesPresentationSize(
-    mediaAreaWidth, mediaAreaHeight, presentationSlideWidth, presentationSlideHeight,
-  ) {
-    let presentationWidth;
-    let presentationHeight;
-    if (presentationSlideWidth > presentationSlideHeight
-      || presentationSlideWidth === presentationSlideHeight) {
-      presentationWidth = mediaAreaWidth;
-      presentationHeight = (mediaAreaWidth * presentationSlideHeight)
-        / presentationSlideWidth;
-      // if overflow
-      if (presentationHeight > mediaAreaHeight) {
-        presentationWidth = (mediaAreaHeight * presentationWidth) / presentationHeight;
-        presentationHeight = mediaAreaHeight;
-      }
-    }
-    if (presentationSlideHeight > presentationSlideWidth) {
-      presentationWidth = (mediaAreaHeight * presentationSlideWidth)
-        / presentationSlideHeight;
-      presentationHeight = mediaAreaHeight;
-      // if overflow
-      if (presentationWidth > mediaAreaWidth) {
-        presentationHeight = (mediaAreaWidth * presentationWidth) / presentationHeight;
-        presentationWidth = mediaAreaWidth;
-      }
-    }
-
-    return {
-      presentationWidth,
-      presentationHeight,
-    };
-  }
-
-  constructor(props) {
-    super(props);
-
-    this.setLayoutSizes = this.setLayoutSizes.bind(this);
-    this.calculatesLayout = this.calculatesLayout.bind(this);
-  }
-
-  componentDidMount() {
-    this.setLayoutSizes();
-    window.addEventListener('resize', _.throttle(() => this.setLayoutSizes(), 200));
-
-    window.addEventListener('panelChanged', () => {
-      this.setLayoutSizes(true);
-    });
-
-    window.addEventListener('autoArrangeChanged', () => {
-      setTimeout(() => this.setLayoutSizes(false, true), 200);
-    });
-
-    window.addEventListener('slideChanged', () => {
-      setTimeout(() => this.setLayoutSizes(), 200);
-    });
-
-    window.addEventListener('togglePresentationHide', () => {
-      setTimeout(() => this.setLayoutSizes(), 200);
-    });
-
-    window.addEventListener('webcamAreaResize', () => {
-      this.setLayoutSizes();
-    });
-
-    window.addEventListener('webcamPlacementChange', () => {
-      this.setLayoutSizes(false, false, true);
-    });
-  }
-
-  componentDidUpdate(prevProps) {
-    const { layoutContextState } = this.props;
-    const { layoutContextState: prevLayoutContextState } = prevProps;
-    const {
-      numUsersVideo,
-    } = layoutContextState;
-    const {
-      numUsersVideo: prevNumUsersVideo,
-    } = prevLayoutContextState;
-
-    if (numUsersVideo !== prevNumUsersVideo) {
-      setTimeout(() => this.setLayoutSizes(), 500);
-    }
-  }
-
-  setLayoutSizes(panelChanged = false, autoarrangeChanged = false, placementChanged = false) {
-    const { layoutContextDispatch, layoutContextState } = this.props;
-    const { autoArrangeLayout } = layoutContextState;
-
-    if (autoarrangeChanged && !autoArrangeLayout && !placementChanged) return;
-
-    const layoutSizes = this.calculatesLayout(panelChanged);
-
-    layoutContextDispatch(
-      {
-        type: 'setWindowSize',
-        value: {
-          width: windowWidth(),
-          height: windowHeight(),
-        },
-      },
-    );
-    layoutContextDispatch(
-      {
-        type: 'setMediaBounds',
-        value: {
-          width: layoutSizes.mediaBounds.width,
-          height: layoutSizes.mediaBounds.height,
-          top: layoutSizes.mediaBounds.top,
-          left: layoutSizes.mediaBounds.left,
-        },
-      },
-    );
-    layoutContextDispatch(
-      {
-        type: 'setUserListSize',
-        value: {
-          width: layoutSizes.userListSize.width,
-        },
-      },
-    );
-    layoutContextDispatch(
-      {
-        type: 'setChatSize',
-        value: {
-          width: layoutSizes.chatSize.width,
-        },
-      },
-    );
-    layoutContextDispatch(
-      {
-        type: 'setBreakoutRoomSize',
-        value: {
-          width: layoutSizes.breakoutRoomSize.width,
-        },
-      },
-    );
-    layoutContextDispatch(
-      {
-        type: 'setWebcamsAreaSize',
-        value: {
-          width: layoutSizes.webcamsAreaSize.width,
-          height: layoutSizes.webcamsAreaSize.height,
-        },
-      },
-    );
-    layoutContextDispatch(
-      {
-        type: 'setPresentationAreaSize',
-        value: {
-          width: layoutSizes.presentationAreaSize.width,
-          height: layoutSizes.presentationAreaSize.height,
-        },
-      },
-    );
-
-    const newLayoutData = {
-      windowSize: {
-        width: windowWidth(),
-        height: windowHeight(),
-      },
-      mediaBounds: {
-        width: layoutSizes.mediaBounds.width,
-        height: layoutSizes.mediaBounds.height,
-        top: layoutSizes.mediaBounds.top,
-        left: layoutSizes.mediaBounds.left,
-      },
-      userListSize: {
-        width: layoutSizes.userListSize.width,
-      },
-      chatSize: {
-        width: layoutSizes.chatSize.width,
-      },
-      breakoutRoomSize: {
-        width: layoutSizes.breakoutRoomSize.width,
-      },
-      webcamsAreaSize: {
-        width: layoutSizes.webcamsAreaSize.width,
-        height: layoutSizes.webcamsAreaSize.height,
-      },
-      presentationAreaSize: {
-        width: layoutSizes.presentationAreaSize.width,
-        height: layoutSizes.presentationAreaSize.height,
-      },
-    };
-
-    Storage.setItem('layoutData', newLayoutData);
-    window.dispatchEvent(new Event('layoutSizesSets'));
-  }
-
-  defineWebcamPlacement(mediaAreaWidth, mediaAreaHeight, presentationWidth, presentationHeight) {
-    const { layoutContextDispatch, layoutContextState } = this.props;
-    const { autoArrangeLayout } = layoutContextState;
-    const isScreenShare = isVideoBroadcasting();
-
-    if (!autoArrangeLayout) return;
-
-    if (isScreenShare) {
-      layoutContextDispatch(
-        {
-          type: 'setWebcamsPlacement',
-          value: 'top',
-        },
-      );
-      Storage.setItem('webcamsPlacement', 'top');
-      return;
-    }
-
-    if ((mediaAreaWidth - presentationWidth) > (mediaAreaHeight - presentationHeight)) {
-      layoutContextDispatch(
-        {
-          type: 'setWebcamsPlacement',
-          value: 'left',
-        },
-      );
-      Storage.setItem('webcamsPlacement', 'left');
-    } else {
-      layoutContextDispatch(
-        {
-          type: 'setWebcamsPlacement',
-          value: 'top',
-        },
-      );
-      Storage.setItem('webcamsPlacement', 'top');
-    }
-  }
-
-  calculatesPanelsSize(panelChanged) {
-    const { layoutContextState } = this.props;
-    const {
-      userListSize: userListSizeContext,
-      chatSize: chatSizeContext,
-      breakoutRoomSize: breakoutRoomSizeContext,
-    } = layoutContextState;
-    const openPanel = Session.get('openPanel');
-    const storageLData = storageLayoutData();
-
-    let storageUserListWidth;
-    let storageChatWidth;
-    let storageBreakoutRoomWidth;
-    if (storageLData) {
-      storageUserListWidth = storageLData.userListSize.width;
-      storageChatWidth = storageLData.chatSize.width;
-      storageBreakoutRoomWidth = storageLData.breakoutRoomSize.width;
-    }
-
-    let newUserListSize;
-    let newChatSize;
-    let newBreakoutRoomSize;
-
-    if (panelChanged && userListSizeContext.width !== 0) {
-      newUserListSize = userListSizeContext;
-    } else if (!storageUserListWidth) {
-      newUserListSize = {
-        width: min(max((windowWidth() * 0.1), USERLIST_MIN_WIDTH), USERLIST_MAX_WIDTH),
-      };
-    } else {
-      newUserListSize = {
-        width: storageUserListWidth,
-      };
-    }
-
-    if (panelChanged && chatSizeContext.width !== 0) {
-      newChatSize = chatSizeContext;
-    } else if (!storageChatWidth) {
-      newChatSize = {
-        width: min(max((windowWidth() * 0.2), CHAT_MIN_WIDTH), CHAT_MAX_WIDTH),
-      };
-    } else {
-      newChatSize = {
-        width: storageChatWidth,
-      };
-    }
-
-    if (panelChanged && breakoutRoomSizeContext.width !== 0) {
-      newBreakoutRoomSize = breakoutRoomSizeContext;
-    } else if (!storageBreakoutRoomWidth) {
-      newBreakoutRoomSize = {
-        width: min(max((windowWidth() * 0.2), CHAT_MIN_WIDTH), CHAT_MAX_WIDTH),
-      };
-    } else {
-      newBreakoutRoomSize = {
-        width: storageBreakoutRoomWidth,
-      };
-    }
-
-    if (openPanel === 'userlist') {
-      newChatSize = {
-        width: 0,
-      };
-      newBreakoutRoomSize = {
-        width: 0,
-      };
-    }
-
-    if (openPanel === '') {
-      newUserListSize = {
-        width: 0,
-      };
-      newChatSize = {
-        width: 0,
-      };
-      newBreakoutRoomSize = {
-        width: 0,
-      };
-    }
-
-    return {
-      newUserListSize,
-      newChatSize,
-      newBreakoutRoomSize,
-    };
-  }
-
-  calculatesWebcamsAreaSize(
-    mediaAreaWidth, mediaAreaHeight, presentationWidth, presentationHeight,
-  ) {
-    const {
-      layoutContextState,
-    } = this.props;
-    const { webcamsPlacement, numUsersVideo } = layoutContextState;
-
-    const autoArrangeLayout = Storage.getItem('autoArrangeLayout');
-    const webcamsAreaUserSetsHeight = Storage.getItem('webcamsAreaUserSetsHeight');
-    const webcamsAreaUserSetsWidth = Storage.getItem('webcamsAreaUserSetsWidth');
-
-    let webcamsAreaWidth;
-    let webcamsAreaHeight;
-
-    if (numUsersVideo < 1) {
-      return {
-        webcamsAreaWidth: 0,
-        webcamsAreaHeight: 0,
-      };
-    }
-
-    if (autoArrangeLayout) {
-      if (webcamsPlacement === 'left' || webcamsPlacement === 'right') {
-        webcamsAreaWidth = (mediaAreaWidth - presentationWidth)
-          < (mediaAreaWidth * WEBCAMSAREA_MIN_PERCENT)
-          ? mediaAreaWidth * WEBCAMSAREA_MIN_PERCENT
-          : mediaAreaWidth - presentationWidth;
-        webcamsAreaHeight = mediaAreaHeight;
-      } else {
-        webcamsAreaWidth = mediaAreaWidth;
-        webcamsAreaHeight = (mediaAreaHeight - presentationHeight)
-          < (mediaAreaHeight * WEBCAMSAREA_MIN_PERCENT)
-          ? mediaAreaHeight * WEBCAMSAREA_MIN_PERCENT
-          : mediaAreaHeight - presentationHeight;
-      }
-    } else if (webcamsPlacement === 'left' || webcamsPlacement === 'right') {
-      webcamsAreaWidth = min(
-        max(
-          webcamsAreaUserSetsWidth
-          || mediaAreaWidth * WEBCAMSAREA_MIN_PERCENT,
-          mediaAreaWidth * WEBCAMSAREA_MIN_PERCENT,
-        ),
-        mediaAreaWidth * WEBCAMSAREA_MAX_PERCENT,
-      );
-      webcamsAreaHeight = mediaAreaHeight;
-    } else {
-      webcamsAreaWidth = mediaAreaWidth;
-      webcamsAreaHeight = min(
-        max(
-          webcamsAreaUserSetsHeight
-          || mediaAreaHeight * WEBCAMSAREA_MIN_PERCENT,
-          mediaAreaHeight * WEBCAMSAREA_MIN_PERCENT,
-        ),
-        mediaAreaHeight * WEBCAMSAREA_MAX_PERCENT,
-      );
-    }
-
-    if ((webcamsPlacement === 'left' || webcamsPlacement === 'right') && (mediaAreaWidth - webcamsAreaWidth) < PRESENTATIONAREA_MIN_WIDTH) {
-      webcamsAreaWidth = mediaAreaWidth - PRESENTATIONAREA_MIN_WIDTH;
-    }
-
-    return {
-      webcamsAreaWidth,
-      webcamsAreaHeight,
-    };
-  }
-
-  calculatesPresentationAreaSize(
-    mediaAreaWidth, mediaAreaHeight, webcamAreaWidth, webcamAreaHeight,
-  ) {
-    const {
-      layoutContextState,
-    } = this.props;
-    const {
-      webcamsPlacement,
-      numUsersVideo,
-    } = layoutContextState;
-
-    if (numUsersVideo < 1) {
-      return {
-        presentationAreaWidth: mediaAreaWidth,
-        presentationAreaHeight: mediaAreaHeight - 20,
-      };
-    }
-
-    let presentationAreaWidth;
-    let presentationAreaHeight;
-
-    if (webcamsPlacement === 'left' || webcamsPlacement === 'right') {
-      presentationAreaWidth = mediaAreaWidth - webcamAreaWidth - 20;
-      presentationAreaHeight = mediaAreaHeight - 20;
-    } else {
-      presentationAreaWidth = mediaAreaWidth;
-      presentationAreaHeight = mediaAreaHeight - webcamAreaHeight - 30;
-    }
-
-    return {
-      presentationAreaWidth,
-      presentationAreaHeight,
-    };
-  }
-
-  calculatesLayout(panelChanged = false) {
-    const {
-      layoutContextState,
-    } = this.props;
-    const {
-      presentationIsFullscreen,
-      presentationSlideSize,
-    } = layoutContextState;
-
-    const {
-      width: presentationSlideWidth,
-      height: presentationSlideHeight,
-    } = presentationSlideSize;
-
-    const panelsSize = this.calculatesPanelsSize(panelChanged);
-
-    const {
-      newUserListSize,
-      newChatSize,
-      newBreakoutRoomSize,
-    } = panelsSize;
-
-    const firstPanel = newUserListSize;
-    let secondPanel = {
-      width: 0,
-    };
-    if (newChatSize.width > 0) {
-      secondPanel = newChatSize;
-    } else if (newBreakoutRoomSize.width > 0) {
-      secondPanel = newBreakoutRoomSize;
-    }
-
-    const mediaAreaHeight = windowHeight() - (NAVBAR_HEIGHT + ACTIONSBAR_HEIGHT) - 10;
-    const mediaAreaWidth = windowWidth() - (firstPanel.width + secondPanel.width);
-    const newMediaBounds = {
-      width: mediaAreaWidth,
-      height: mediaAreaHeight,
-      top: NAVBAR_HEIGHT,
-      left: firstPanel.width + secondPanel.width,
-    };
-
-    const { presentationWidth, presentationHeight } = LayoutManager.calculatesPresentationSize(
-      mediaAreaWidth, mediaAreaHeight, presentationSlideWidth, presentationSlideHeight,
-    );
-
-    this.defineWebcamPlacement(
-      mediaAreaWidth, mediaAreaHeight, presentationWidth, presentationHeight,
-    );
-
-    const { webcamsAreaWidth, webcamsAreaHeight } = this.calculatesWebcamsAreaSize(
-      mediaAreaWidth, mediaAreaHeight, presentationWidth, presentationHeight,
-    );
-
-    const newWebcamsAreaSize = {
-      width: webcamsAreaWidth,
-      height: webcamsAreaHeight,
-    };
-    let newPresentationAreaSize;
-    let newScreenShareAreaSize;
-    const { presentationAreaWidth, presentationAreaHeight } = this.calculatesPresentationAreaSize(
-      mediaAreaWidth, mediaAreaHeight, webcamsAreaWidth, webcamsAreaHeight,
-    );
-    if (!presentationIsFullscreen) {
-      newPresentationAreaSize = {
-        width: presentationAreaWidth || 0,
-        height: presentationAreaHeight || 0,
-      };
-    } else {
-      newPresentationAreaSize = {
-        width: windowWidth(),
-        height: windowHeight(),
-      };
-    }
-
-    return {
-      mediaBounds: newMediaBounds,
-      userListSize: newUserListSize,
-      chatSize: newChatSize,
-      breakoutRoomSize: newBreakoutRoomSize,
-      webcamsAreaSize: newWebcamsAreaSize,
-      presentationAreaSize: newPresentationAreaSize,
-      screenShareAreaSize: newScreenShareAreaSize,
-    };
-  }
-
-  render() {
-    return <Fragment />;
-  }
-}
-
-export default withLayoutConsumer(LayoutManager);
-export {
-  USERLIST_MIN_WIDTH,
-  USERLIST_MAX_WIDTH,
-  CHAT_MIN_WIDTH,
-  CHAT_MAX_WIDTH,
-  NAVBAR_HEIGHT,
-  ACTIONSBAR_HEIGHT,
-  WEBCAMSAREA_MIN_PERCENT,
-  WEBCAMSAREA_MAX_PERCENT,
-  PRESENTATIONAREA_MIN_WIDTH,
-};
diff --git a/bigbluebutton-html5/imports/ui/components/media/component.jsx b/bigbluebutton-html5/imports/ui/components/media/component.jsx
index 9f7c18c054cb076a97eb2bd1b29468b05e865182..36c17f52feb71b662ac0f8d1cdd798e8a00d1694 100644
--- a/bigbluebutton-html5/imports/ui/components/media/component.jsx
+++ b/bigbluebutton-html5/imports/ui/components/media/component.jsx
@@ -16,7 +16,7 @@ const propTypes = {
   swapLayout: PropTypes.bool,
   disableVideo: PropTypes.bool,
   audioModalIsOpen: PropTypes.bool,
-  layoutContextState: PropTypes.instanceOf(Object).isRequired,
+  webcamDraggableState: PropTypes.instanceOf(Object).isRequired,
 };
 
 const defaultProps = {
@@ -43,12 +43,12 @@ export default class Media extends Component {
       children,
       audioModalIsOpen,
       usersVideo,
-      layoutContextState,
+      webcamDraggableState,
     } = this.props;
 
-    const { webcamsPlacement: placement } = layoutContextState;
-    const placementStorage = Storage.getItem('webcamsPlacement');
-    const webcamsPlacement = placement || placementStorage;
+    const { placement } = webcamDraggableState;
+    const placementStorage = Storage.getItem('webcamPlacement');
+    const webcamPlacement = placement || placementStorage;
 
     const contentClassName = cx({
       [styles.content]: true,
@@ -57,13 +57,13 @@ export default class Media extends Component {
     const overlayClassName = cx({
       [styles.overlay]: true,
       [styles.hideOverlay]: hideOverlay,
-      [styles.floatingOverlay]: (webcamsPlacement === 'floating'),
+      [styles.floatingOverlay]: (webcamPlacement === 'floating'),
     });
 
     const containerClassName = cx({
       [styles.container]: true,
-      [styles.containerV]: webcamsPlacement === 'top' || webcamsPlacement === 'bottom' || webcamsPlacement === 'floating',
-      [styles.containerH]: webcamsPlacement === 'left' || webcamsPlacement === 'right',
+      [styles.containerV]: webcamPlacement === 'top' || webcamPlacement === 'bottom' || webcamPlacement === 'floating',
+      [styles.containerH]: webcamPlacement === 'left' || webcamPlacement === 'right',
     });
 
     return (
@@ -77,24 +77,24 @@ export default class Media extends Component {
           style={{
             maxHeight: usersVideo.length > 0
             && (
-              webcamsPlacement !== 'left'
-              || webcamsPlacement !== 'right'
+              webcamPlacement !== 'left'
+              || webcamPlacement !== 'right'
             )
             && (
-              webcamsPlacement === 'top'
-              || webcamsPlacement === 'bottom'
+              webcamPlacement === 'top'
+              || webcamPlacement === 'bottom'
             )
               ? '80%'
               : '100%',
             minHeight: BROWSER_ISMOBILE && window.innerWidth > window.innerHeight ? '50%' : '20%',
             maxWidth: usersVideo.length > 0
             && (
-              webcamsPlacement !== 'top'
-              || webcamsPlacement !== 'bottom'
+              webcamPlacement !== 'top'
+              || webcamPlacement !== 'bottom'
             )
             && (
-              webcamsPlacement === 'left'
-              || webcamsPlacement === 'right'
+              webcamPlacement === 'left'
+              || webcamPlacement === 'right'
             )
               ? '80%'
               : '100%',
diff --git a/bigbluebutton-html5/imports/ui/components/media/container.jsx b/bigbluebutton-html5/imports/ui/components/media/container.jsx
index 0e6466180a14d6fbcdc8803bcfb60789dd52dcd4..4efa85853f0a7c769154664a565761f96cf7f4c7 100755
--- a/bigbluebutton-html5/imports/ui/components/media/container.jsx
+++ b/bigbluebutton-html5/imports/ui/components/media/container.jsx
@@ -9,13 +9,13 @@ import VideoService from '/imports/ui/components/video-provider/service';
 import getFromUserSettings from '/imports/ui/services/users-settings';
 import { withModalMounter } from '/imports/ui/components/modal/service';
 import Media from './component';
-import MediaService, { getSwapLayout, shouldEnableSwapLayout } from '/imports/ui/components/media/service';
+import MediaService, { getSwapLayout, shouldEnableSwapLayout } from './service';
 import PresentationPodsContainer from '../presentation-pod/container';
 import ScreenshareContainer from '../screenshare/container';
 import DefaultContent from '../presentation/default-content/component';
 import ExternalVideoContainer from '../external-video-player/container';
 import Storage from '../../services/storage/session';
-import { withLayoutConsumer } from '/imports/ui/components/layout/context';
+import { withDraggableConsumer } from './webcam-draggable-overlay/context';
 
 const LAYOUT_CONFIG = Meteor.settings.public.layout;
 const KURENTO_CONFIG = Meteor.settings.public.kurento;
@@ -49,22 +49,19 @@ const intlMessages = defineMessages({
 });
 
 class MediaContainer extends Component {
-  componentDidMount() {
+  componentWillMount() {
     document.addEventListener('installChromeExtension', this.installChromeExtension.bind(this));
     document.addEventListener('screenshareNotSupported', this.screenshareNotSupported.bind(this));
   }
 
-  componentDidUpdate(prevProps) {
+  componentWillReceiveProps(nextProps) {
     const {
       isScreensharing,
       intl,
     } = this.props;
-    const {
-      isScreensharing: wasScreenSharing,
-    } = prevProps;
 
-    if (isScreensharing !== wasScreenSharing) {
-      if (isScreensharing) {
+    if (isScreensharing !== nextProps.isScreensharing) {
+      if (nextProps.isScreensharing) {
         notify(intl.formatMessage(intlMessages.screenshareStarted), 'info', 'desktop');
       } else {
         notify(intl.formatMessage(intlMessages.screenshareEnded), 'info', 'desktop');
@@ -106,7 +103,7 @@ class MediaContainer extends Component {
   }
 }
 
-export default withLayoutConsumer(withModalMounter(withTracker(() => {
+export default withDraggableConsumer(withModalMounter(withTracker(() => {
   const { dataSaving } = Settings;
   const { viewParticipantsWebcams, viewScreenshare } = dataSaving;
   const hidePresentation = getFromUserSettings('bbb_hide_presentation', LAYOUT_CONFIG.hidePresentation);
@@ -153,7 +150,7 @@ export default withLayoutConsumer(withModalMounter(withTracker(() => {
     );
   }
 
-  data.webcamsPlacement = Storage.getItem('webcamsPlacement');
+  data.webcamPlacement = Storage.getItem('webcamPlacement');
 
   MediaContainer.propTypes = propTypes;
   return data;
diff --git a/bigbluebutton-html5/imports/ui/components/media/service.js b/bigbluebutton-html5/imports/ui/components/media/service.js
index 4a0670f0443b7daa0ad09d926ca4887fcf0714ed..9811280c05f5fa8669a9789b462962daa23a7818 100755
--- a/bigbluebutton-html5/imports/ui/components/media/service.js
+++ b/bigbluebutton-html5/imports/ui/components/media/service.js
@@ -4,6 +4,7 @@ import { getVideoUrl } from '/imports/ui/components/external-video-player/servic
 import Auth from '/imports/ui/services/auth';
 import Users from '/imports/api/users';
 import Settings from '/imports/ui/services/settings';
+import PollingService from '/imports/ui/components/polling/service';
 import getFromUserSettings from '/imports/ui/services/users-settings';
 
 const LAYOUT_CONFIG = Meteor.settings.public.layout;
@@ -52,7 +53,6 @@ const setSwapLayout = () => {
 };
 
 const toggleSwapLayout = () => {
-  window.dispatchEvent(new Event('togglePresentationHide'));
   swapLayout.value = !swapLayout.value;
   swapLayout.tracker.changed();
 };
diff --git a/bigbluebutton-html5/imports/ui/components/media/webcam-draggable-overlay/component.jsx b/bigbluebutton-html5/imports/ui/components/media/webcam-draggable-overlay/component.jsx
index a47575fcd678a49504151ef6deb05c5d0257c27b..40f82c4ee666aae39e75a241924f05665530a9e4 100644
--- a/bigbluebutton-html5/imports/ui/components/media/webcam-draggable-overlay/component.jsx
+++ b/bigbluebutton-html5/imports/ui/components/media/webcam-draggable-overlay/component.jsx
@@ -1,6 +1,7 @@
 import React, { PureComponent, Fragment } from 'react';
 import Draggable from 'react-draggable';
 import cx from 'classnames';
+import _ from 'lodash';
 import PropTypes from 'prop-types';
 import Resizable from 're-resizable';
 import { isMobile, isIPad13 } from 'react-device-detect';
@@ -8,8 +9,6 @@ import { withDraggableConsumer } from './context';
 import VideoProviderContainer from '/imports/ui/components/video-provider/container';
 import { styles } from '../styles.scss';
 import Storage from '../../../services/storage/session';
-import { withLayoutConsumer } from '/imports/ui/components/layout/context';
-import { WEBCAMSAREA_MIN_PERCENT, PRESENTATIONAREA_MIN_WIDTH } from '/imports/ui/components/layout/layout-manager';
 
 const BROWSER_ISMOBILE = isMobile || isIPad13;
 
@@ -21,8 +20,7 @@ const propTypes = {
   webcamDraggableState: PropTypes.objectOf(Object).isRequired,
   webcamDraggableDispatch: PropTypes.func.isRequired,
   refMediaContainer: PropTypes.shape({ current: PropTypes.instanceOf(Element) }),
-  layoutContextState: PropTypes.objectOf(Object).isRequired,
-  layoutContextDispatch: PropTypes.func.isRequired,
+  usersVideoLenght: PropTypes.number.isRequired,
 };
 
 const defaultProps = {
@@ -32,38 +30,78 @@ const defaultProps = {
   audioModalIsOpen: false,
   refMediaContainer: null,
 };
+const dispatchResizeEvent = () => window.dispatchEvent(new Event('resize'));
 
 class WebcamDraggable extends PureComponent {
   constructor(props) {
     super(props);
 
-    const { layoutContextState } = props;
-    const { webcamsPlacement, mediaBounds } = layoutContextState;
-    this.state = {
-      webcamsAreaResizable: {
-        width: webcamsPlacement === 'top' || webcamsPlacement === 'bottom' ? mediaBounds.width : mediaBounds.width * WEBCAMSAREA_MIN_PERCENT,
-        height: webcamsPlacement === 'left' || webcamsPlacement === 'right' ? mediaBounds.height : mediaBounds.height * WEBCAMSAREA_MIN_PERCENT,
-      },
-      resizing: false,
-      hideWebcams: false,
-    };
-
     this.handleWebcamDragStart = this.handleWebcamDragStart.bind(this);
     this.handleWebcamDragStop = this.handleWebcamDragStop.bind(this);
     this.onFullscreenChange = this.onFullscreenChange.bind(this);
+    this.debouncedOnResize = _.debounce(this.onWindowResize.bind(this), 500);
     this.onResizeStop = this.onResizeStop.bind(this);
     this.onResizeStart = this.onResizeStart.bind(this);
-    this.handleLayoutSizesSets = this.handleLayoutSizesSets.bind(this);
+    this.setPlacementPercent = this.setPlacementPercent.bind(this);
+    this.recalculateAreaSize = this.recalculateAreaSize.bind(this);
+
+    this.state = {
+      resizing: false,
+      placementPercent: 0,
+    };
   }
 
   componentDidMount() {
+    dispatchResizeEvent();
+    window.addEventListener('resize', this.debouncedOnResize);
     document.addEventListener('fullscreenchange', this.onFullscreenChange);
-    window.addEventListener('layoutSizesSets', this.handleLayoutSizesSets);
+    window.addEventListener('orientationchange', () => setTimeout(this.recalculateAreaSize, 500));
+  }
+
+  componentDidUpdate(prevProps) {
+    const {
+      swapLayout,
+      webcamDraggableState,
+      webcamDraggableDispatch,
+      usersVideoLenght,
+    } = this.props;
+    const {
+      placement: statePlacement,
+      orientation,
+      lastPlacementLandscape,
+      lastPlacementPortrait,
+    } = webcamDraggableState;
+    const { webcamDraggableState: prevWebcamDraggableState } = prevProps;
+    const { placement: prevPlacement, orientation: prevOrientation } = prevWebcamDraggableState;
+    if (prevProps.swapLayout !== swapLayout) {
+      setTimeout(() => this.forceUpdate(), 500);
+    }
+    if (prevPlacement !== statePlacement) {
+      setTimeout(() => this.forceUpdate(), 200);
+      setTimeout(() => window.dispatchEvent(new Event('resize')), 500);
+    }
+
+    if (prevProps.usersVideoLenght !== usersVideoLenght) {
+      dispatchResizeEvent();
+    }
+
+    if (prevOrientation !== orientation) {
+      const storagePlacement = Storage.getItem('webcamPlacement');
+      if ((prevOrientation == null || prevOrientation === 'portrait') && orientation === 'landscape') {
+        if (storagePlacement !== lastPlacementLandscape && lastPlacementLandscape === 'top') webcamDraggableDispatch({ type: 'setplacementToTop' });
+        if (storagePlacement !== lastPlacementLandscape && lastPlacementLandscape === 'bottom') webcamDraggableDispatch({ type: 'setplacementToBottom' });
+      }
+      if ((prevOrientation == null || prevOrientation === 'landscape') && orientation === 'portrait') {
+        if (storagePlacement !== lastPlacementPortrait && lastPlacementPortrait === 'left') webcamDraggableDispatch({ type: 'setplacementToLeft' });
+        if (storagePlacement !== lastPlacementPortrait && lastPlacementPortrait === 'right') webcamDraggableDispatch({ type: 'setplacementToRight' });
+      }
+    }
   }
 
   componentWillUnmount() {
+    window.removeEventListener('resize', this.debouncedOnResize);
     document.removeEventListener('fullscreenchange', this.onFullscreenChange);
-    window.removeEventListener('layoutSizesSets', this.handleLayoutSizesSets);
+    dispatchResizeEvent();
   }
 
   onFullscreenChange() {
@@ -71,115 +109,97 @@ class WebcamDraggable extends PureComponent {
   }
 
   onResizeStart() {
-    const { layoutContextDispatch } = this.props;
-
     this.setState({ resizing: true });
-    layoutContextDispatch(
-      {
-        type: 'setWebcamsAreaResizing',
-        value: true,
-      },
-    );
   }
 
-  onResizeHandle(resizableWidth, resizableHeight) {
-    const { webcamsAreaResizable } = this.state;
-    const { layoutContextState, layoutContextDispatch } = this.props;
-    const { webcamsPlacement, webcamsAreaSize } = layoutContextState;
-
-    layoutContextDispatch(
-      {
-        type: 'setAutoArrangeLayout',
-        value: false,
-      },
-    );
-
-    const newWebcamsAreaResizable = {
-      width: Math.trunc(webcamsAreaResizable.width) + resizableWidth,
-      height: Math.trunc(webcamsAreaResizable.height) + resizableHeight,
-    };
-
-    const newWidth = webcamsPlacement === 'top' || webcamsPlacement === 'bottom' ? webcamsAreaSize.width : newWebcamsAreaResizable.width;
-    const newHeight = webcamsPlacement === 'left' || webcamsPlacement === 'right' ? webcamsAreaSize.height : newWebcamsAreaResizable.height;
+  onWindowResize() {
+    const { webcamDraggableState, webcamDraggableDispatch } = this.props;
+    const { mediaSize } = webcamDraggableState;
+    const { width: stateWidth, height: stateHeight } = mediaSize;
+    const { width, height } = this.getMediaBounds();
 
-    layoutContextDispatch(
-      {
-        type: 'setTempWebcamsAreaSize',
-        value: {
-          width: newWidth,
-          height: newHeight,
-        },
-      },
-    );
-
-    window.dispatchEvent(new Event('webcamAreaResize'));
-  }
-
-  onResizeStop(resizableWidth, resizableHeight) {
-    const { webcamsAreaResizable } = this.state;
-    const { layoutContextState, layoutContextDispatch } = this.props;
-    const { webcamsPlacement, webcamsAreaSize } = layoutContextState;
-
-    layoutContextDispatch(
-      {
-        type: 'setWebcamsAreaResizing',
-        value: false,
-      },
-    );
-
-    const newWebcamsAreaResizable = {
-      width: Math.trunc(webcamsAreaResizable.width) + resizableWidth,
-      height: Math.trunc(webcamsAreaResizable.height) + resizableHeight,
-    };
-
-    if (webcamsPlacement === 'top' || webcamsPlacement === 'bottom') {
-      layoutContextDispatch(
+    if (stateWidth !== width || stateHeight !== height) {
+      webcamDraggableDispatch(
         {
-          type: 'setWebcamsAreaUserSetsHeight',
-          value: newWebcamsAreaResizable.height,
+          type: 'setMediaSize',
+          value: {
+            width,
+            height,
+          },
         },
       );
+      setTimeout(() => window.dispatchEvent(new Event('resize')), 300);
     }
+  }
+
+  onResize() {
+    this.setPlacementPercent();
+  }
 
-    if (webcamsPlacement === 'right' || webcamsPlacement === 'left') {
-      layoutContextDispatch(
+  onResizeStop() {
+    const { webcamDraggableState, webcamDraggableDispatch } = this.props;
+    const { optimalGrid } = webcamDraggableState;
+    if (optimalGrid) {
+      webcamDraggableDispatch(
         {
-          type: 'setWebcamsAreaUserSetsWidth',
-          value: newWebcamsAreaResizable.width,
+          type: 'setVideoListSize',
+          value: {
+            width: optimalGrid.width,
+            height: optimalGrid.height,
+          },
         },
       );
     }
-
-    const newWidth = webcamsPlacement === 'top' || webcamsPlacement === 'bottom'
-      ? webcamsAreaSize.width
-      : newWebcamsAreaResizable.width;
-    const newHeight = webcamsPlacement === 'left' || webcamsPlacement === 'right'
-      ? webcamsAreaSize.height
-      : newWebcamsAreaResizable.height;
-
-    layoutContextDispatch(
-      {
-        type: 'setWebcamsAreaSize',
-        value: {
-          width: newWidth,
-          height: newHeight,
-        },
-      },
-    );
-
-    this.setWebcamsAreaResizable(newWidth, newHeight);
-
+    this.setPlacementPercent();
+    window.dispatchEvent(new Event('resize'));
     setTimeout(() => this.setState({ resizing: false }), 500);
-    window.dispatchEvent(new Event('webcamAreaResize'));
   }
 
-  setWebcamsAreaResizable(width, height) {
-    this.setState({
-      webcamsAreaResizable: { width, height },
-    });
+  setPlacementPercent() {
+    const { webcamDraggableState } = this.props;
+    const { optimalGrid, placement } = webcamDraggableState;
+    if (placement === 'top' || placement === 'bottom') {
+      const mediaSelection = document.querySelector('section[class^=media]');
+      const mediaHeight = mediaSelection ? mediaSelection.offsetHeight : 0;
+      this.setState({ placementPercent: (optimalGrid.height * 100) / mediaHeight });
+    }
+    if (placement === 'left' || placement === 'right') {
+      const mediaSelection = document.querySelector('section[class^=media]');
+      const mediaWidth = mediaSelection ? mediaSelection.offsetWidth : 0;
+      this.setState({ placementPercent: (optimalGrid.width * 100) / mediaWidth });
+    }
   }
 
-  setHideWebcams(hideWebcams) { this.setState({ hideWebcams }); }
+  getMediaBounds() {
+    const { refMediaContainer, webcamDraggableState, webcamDraggableDispatch } = this.props;
+    const { mediaSize: mediaState } = webcamDraggableState;
+    const { current: mediaContainer } = refMediaContainer;
+    if (mediaContainer) {
+      const mediaContainerRect = mediaContainer.getBoundingClientRect();
+      const {
+        top, left, width: newWidth, height: newHeight,
+      } = mediaContainerRect;
+      if ((mediaState.width === 0 || mediaState.height === 0) && (newWidth > 0 && newHeight > 0)) {
+        webcamDraggableDispatch(
+          {
+            type: 'setMediaSize',
+            value: {
+              newWidth,
+              newHeight,
+            },
+          },
+        );
+      }
+
+      return {
+        top,
+        left,
+        width: newWidth,
+        height: newHeight,
+      };
+    }
+    return false;
+  }
 
   getWebcamsListBounds() {
     const { webcamDraggableState } = this.props;
@@ -190,20 +210,22 @@ class WebcamDraggable extends PureComponent {
         top, left, width, height,
       } = videoListRefRect;
       return {
-        top,
-        left,
-        width,
-        height,
+        top, // 10 = margin
+        left, // 10 = margin
+        width, // 20 = margin
+        height, // 20 = margin
       };
     }
     return false;
   }
 
-  calculatePosition() {
-    const { layoutContextState } = this.props;
-    const { mediaBounds } = layoutContextState;
+  recalculateAreaSize() {
+    this.onResizeStart();
+    this.onResizeStop();
+  }
 
-    const { top: mediaTop, left: mediaLeft } = mediaBounds;
+  calculatePosition() {
+    const { top: mediaTop, left: mediaLeft } = this.getMediaBounds();
     const { top: webcamsListTop, left: webcamsListLeft } = this.getWebcamsListBounds();
     const x = webcamsListLeft - mediaLeft;
     const y = webcamsListTop - mediaTop;
@@ -213,14 +235,6 @@ class WebcamDraggable extends PureComponent {
     };
   }
 
-  handleLayoutSizesSets() {
-    const { layoutContextState } = this.props;
-    const { webcamsAreaSize } = layoutContextState;
-
-    this.setWebcamsAreaResizable(webcamsAreaSize.width, webcamsAreaSize.height);
-    this.setHideWebcams(false);
-  }
-
   handleWebcamDragStart() {
     const { webcamDraggableDispatch } = this.props;
     const { x, y } = this.calculatePosition();
@@ -237,69 +251,52 @@ class WebcamDraggable extends PureComponent {
   }
 
   handleWebcamDragStop(e) {
-    const { webcamDraggableDispatch, layoutContextDispatch } = this.props;
+    const { webcamDraggableDispatch } = this.props;
     const targetClassname = JSON.stringify(e.target.className);
 
-    this.setHideWebcams(true);
-
-    layoutContextDispatch(
-      {
-        type: 'setAutoArrangeLayout',
-        value: false,
-      },
-    );
-
     if (targetClassname) {
       if (targetClassname.includes('Top')) {
-        layoutContextDispatch({
-          type: 'setWebcamsPlacement',
-          value: 'top',
-        });
+        webcamDraggableDispatch({ type: 'setplacementToTop' });
+        webcamDraggableDispatch({ type: 'setLastPlacementLandscapeToTop' });
       } else if (targetClassname.includes('Right')) {
-        layoutContextDispatch({
-          type: 'setWebcamsPlacement',
-          value: 'right',
-        });
+        webcamDraggableDispatch({ type: 'setplacementToRight' });
+        webcamDraggableDispatch({ type: 'setLastPlacementPortraitToRight' });
       } else if (targetClassname.includes('Bottom')) {
-        layoutContextDispatch({
-          type: 'setWebcamsPlacement',
-          value: 'bottom',
-        });
+        webcamDraggableDispatch({ type: 'setplacementToBottom' });
+        webcamDraggableDispatch({ type: 'setLastPlacementLandscapeToBottom' });
       } else if (targetClassname.includes('Left')) {
-        layoutContextDispatch({
-          type: 'setWebcamsPlacement',
-          value: 'left',
-        });
+        webcamDraggableDispatch({ type: 'setplacementToLeft' });
+        webcamDraggableDispatch({ type: 'setLastPlacementPortraitToLeft' });
       }
     }
     webcamDraggableDispatch({ type: 'dragEnd' });
-    window.dispatchEvent(new Event('webcamPlacementChange'));
+    window.dispatchEvent(new Event('resize'));
+    setTimeout(this.recalculateAreaSize, 500);
   }
 
   render() {
     const {
-      layoutContextState,
       webcamDraggableState,
       swapLayout,
       hideOverlay,
       disableVideo,
       audioModalIsOpen,
+      refMediaContainer,
     } = this.props;
 
-    const { resizing, webcamsAreaResizable, hideWebcams } = this.state;
-
     const {
-      mediaBounds,
-      webcamsAreaSize,
-    } = layoutContextState;
+      resizing,
+      placementPercent,
+    } = this.state;
 
     const {
       dragging,
       isCameraFullscreen,
+      videoListSize,
       optimalGrid,
     } = webcamDraggableState;
 
-    const webcamsPlacement = Storage.getItem('webcamsPlacement');
+    const placement = Storage.getItem('webcamPlacement');
 
     const lastPosition = Storage.getItem('webcamLastPosition') || { x: 0, y: 0 };
 
@@ -326,7 +323,7 @@ class WebcamDraggable extends PureComponent {
     const {
       width: mediaWidth,
       height: mediaHeight,
-    } = mediaBounds;
+    } = this.getMediaBounds();
 
     const {
       width: webcamsWidth,
@@ -349,16 +346,57 @@ class WebcamDraggable extends PureComponent {
       [styles.fullHeight]: swapLayout,
     });
 
+    const { current: mediaContainer } = refMediaContainer;
+    let layout = 'vertical';
+    if (mediaContainer) {
+      const classNameMediaContainer = mediaContainer.className;
+      if (classNameMediaContainer.includes('containerH')) {
+        layout = 'horizontal';
+      } else {
+        layout = 'vertical';
+      }
+    }
+
     const overlayClassName = cx({
       [styles.overlay]: true,
       [styles.hideOverlay]: hideOverlay,
       [styles.floatingOverlay]: dragging,
       [styles.autoWidth]: dragging,
-      [styles.overlayToTop]: webcamsPlacement === 'top' && !dragging,
-      [styles.overlayToRight]: webcamsPlacement === 'right' && !dragging,
-      [styles.overlayToBottom]: webcamsPlacement === 'bottom' && !dragging,
-      [styles.overlayToLeft]: webcamsPlacement === 'left' && !dragging,
+      [styles.fullWidth]: (
+        (
+          placement === 'top'
+          || placement === 'bottom'
+        )
+        || swapLayout
+      )
+        && !dragging,
+      [styles.fullHeight]: (
+        (
+          placement === 'left'
+          && placement === 'right'
+        )
+        || swapLayout
+      )
+        && !dragging,
+      [styles.overlayToTop]: placement === 'top' && !dragging,
+      [styles.overlayToRight]: placement === 'right' && !dragging,
+      [styles.overlayToBottom]: placement === 'bottom' && !dragging,
+      [styles.overlayToLeft]: placement === 'left' && !dragging,
       [styles.dragging]: dragging,
+      [styles.hide]: (
+        (
+          placement === 'left'
+          || placement === 'right'
+        )
+        && layout === 'vertical'
+      )
+        || (
+          (
+            placement === 'top'
+            || placement === 'bottom'
+          )
+          && layout === 'horizontal'
+        ),
     });
 
     const dropZoneTopClassName = cx({
@@ -405,17 +443,33 @@ class WebcamDraggable extends PureComponent {
       [styles.dropZoneBgRight]: true,
     });
 
-    let sizeHeight;
-    let sizeWidth;
+    const mediaSelection = document.querySelector('section[class^=media]');
+    const mHeight = mediaSelection ? mediaSelection.offsetHeight : 0;
+    const mWidth = mediaSelection ? mediaSelection.offsetWidth : 0;
+
+    let resizeWidth;
+    let resizeHeight;
+    if (resizing && (placement === 'top' || placement === 'bottom') && !dragging) {
+      resizeWidth = '100%';
+      resizeHeight = videoListSize.height;
+    }
+    if (!resizing && (placement === 'top' || placement === 'bottom') && !dragging) {
+      resizeWidth = '100%';
+      resizeHeight = mHeight * (placementPercent / 100);
+    }
+
+    if (resizing && (placement === 'left' || placement === 'right') && !dragging) {
+      resizeWidth = videoListSize.width;
+      resizeHeight = '100%';
+    }
+    if (!resizing && (placement === 'left' || placement === 'right') && !dragging) {
+      resizeWidth = mWidth * (placementPercent / 100);
+      resizeHeight = '100%';
+    }
+
     if (dragging) {
-      sizeWidth = optimalGrid.width;
-      sizeHeight = optimalGrid.height;
-    } else if (resizing) {
-      sizeWidth = webcamsAreaResizable.width;
-      sizeHeight = webcamsAreaResizable.height;
-    } else {
-      sizeWidth = webcamsAreaSize.width;
-      sizeHeight = webcamsAreaSize.height;
+      resizeHeight = optimalGrid.height;
+      resizeWidth = optimalGrid.width;
     }
 
     return (
@@ -445,37 +499,26 @@ class WebcamDraggable extends PureComponent {
           onStart={this.handleWebcamDragStart}
           onStop={this.handleWebcamDragStop}
           onMouseDown={e => e.preventDefault()}
-          disabled={swapLayout || isCameraFullscreen || BROWSER_ISMOBILE || resizing}
+          disabled={swapLayout || isCameraFullscreen || BROWSER_ISMOBILE}
           position={position}
         >
           <Resizable
-            minWidth={mediaWidth * WEBCAMSAREA_MIN_PERCENT}
-            minHeight={mediaHeight * WEBCAMSAREA_MIN_PERCENT}
-            maxWidth={
-              webcamsPlacement === 'left' || webcamsPlacement === 'right'
-                ? mediaWidth - PRESENTATIONAREA_MIN_WIDTH
-                : undefined
-            }
             size={
               {
-                width: sizeWidth,
-                height: sizeHeight,
+                height: resizeHeight,
+                width: resizeWidth,
               }
             }
-            // lockAspectRatio
+            lockAspectRatio
             handleWrapperClass="resizeWrapper"
             onResizeStart={this.onResizeStart}
-            onResize={(e, direction, ref, d) => {
-              this.onResizeHandle(d.width, d.height);
-            }}
-            onResizeStop={(e, direction, ref, d) => {
-              this.onResizeStop(d.width, d.height);
-            }}
+            onResize={dispatchResizeEvent}
+            onResizeStop={this.onResizeStop}
             enable={{
-              top: (webcamsPlacement === 'bottom') && !swapLayout,
-              bottom: (webcamsPlacement === 'top') && !swapLayout,
-              left: (webcamsPlacement === 'right') && !swapLayout,
-              right: (webcamsPlacement === 'left') && !swapLayout,
+              top: (placement === 'bottom') && !swapLayout,
+              bottom: (placement === 'top') && !swapLayout,
+              left: (placement === 'right') && !swapLayout,
+              right: (placement === 'left') && !swapLayout,
               topLeft: false,
               topRight: false,
               bottomLeft: false,
@@ -484,12 +527,10 @@ class WebcamDraggable extends PureComponent {
             className={
               !swapLayout
                 ? overlayClassName
-                : contentClassName
-            }
+                : contentClassName}
             style={{
               marginLeft: 0,
               marginRight: 0,
-              display: hideWebcams ? 'none' : undefined,
             }}
           >
             {
@@ -531,4 +572,4 @@ class WebcamDraggable extends PureComponent {
 WebcamDraggable.propTypes = propTypes;
 WebcamDraggable.defaultProps = defaultProps;
 
-export default withDraggableConsumer(withLayoutConsumer(WebcamDraggable));
+export default withDraggableConsumer(WebcamDraggable);
diff --git a/bigbluebutton-html5/imports/ui/components/media/webcam-draggable-overlay/context.jsx b/bigbluebutton-html5/imports/ui/components/media/webcam-draggable-overlay/context.jsx
index f86b406f7701f5250ebf8f463fb482c1bbdda712..a0981de896110c2c1fc8fc6a6ac613bf2a7c6a73 100644
--- a/bigbluebutton-html5/imports/ui/components/media/webcam-draggable-overlay/context.jsx
+++ b/bigbluebutton-html5/imports/ui/components/media/webcam-draggable-overlay/context.jsx
@@ -1,9 +1,19 @@
 import React, { createContext, useReducer, useEffect } from 'react';
-import Storage from '/imports/ui/services/storage/session';
+import Storage from '../../../services/storage/session';
+
+const { webcamsDefaultPlacement } = Meteor.settings.public.layout;
 
 export const WebcamDraggableContext = createContext();
 
 const initialState = {
+  placement: webcamsDefaultPlacement || 'top',
+  lastPlacementLandscape: 'top',
+  lastPlacementPortrait: 'left',
+  orientation: null,
+  mediaSize: {
+    width: 0,
+    height: 0,
+  },
   videoListSize: {
     width: 0,
     height: 0,
@@ -29,6 +39,81 @@ const initialState = {
 
 const reducer = (state, action) => {
   switch (action.type) {
+    case 'setplacementToTop': {
+      return {
+        ...state,
+        placement: 'top',
+      };
+    }
+    case 'setplacementToRight': {
+      return {
+        ...state,
+        placement: 'right',
+      };
+    }
+    case 'setplacementToBottom': {
+      return {
+        ...state,
+        placement: 'bottom',
+      };
+    }
+    case 'setplacementToLeft': {
+      return {
+        ...state,
+        placement: 'left',
+      };
+    }
+    case 'setLastPlacementPortraitToLeft': {
+      return {
+        ...state,
+        lastPlacementPortrait: 'left',
+      };
+    }
+    case 'setLastPlacementPortraitToRight': {
+      return {
+        ...state,
+        lastPlacementPortrait: 'right',
+      };
+    }
+    case 'setLastPlacementLandscapeToTop': {
+      return {
+        ...state,
+        lastPlacementLandscape: 'top',
+      };
+    }
+    case 'setLastPlacementLandscapeToBottom': {
+      return {
+        ...state,
+        lastPlacementLandscape: 'bottom',
+      };
+    }
+    case 'setplacementToFloating': {
+      return {
+        ...state,
+        placement: 'floating',
+      };
+    }
+    case 'setOrientationToLandscape': {
+      return {
+        ...state,
+        orientation: 'landscape',
+      };
+    }
+    case 'setOrientationToPortrait': {
+      return {
+        ...state,
+        orientation: 'portrait',
+      };
+    }
+    case 'setMediaSize': {
+      return {
+        ...state,
+        mediaSize: {
+          width: action.value.width,
+          height: action.value.height,
+        },
+      };
+    }
     case 'setVideoListSize': {
       return {
         ...state,
@@ -62,6 +147,15 @@ const reducer = (state, action) => {
         },
       };
     }
+    case 'setLastPosition': {
+      return {
+        ...state,
+        lastPosition: {
+          x: action.value.x,
+          y: action.value.y,
+        },
+      };
+    }
     case 'setVideoRef': {
       return {
         ...state,
@@ -120,6 +214,7 @@ const ContextProvider = (props) => {
   } = webcamDraggableState;
   const { children } = props;
   useEffect(() => {
+    Storage.setItem('webcamPlacement', placement);
     Storage.setItem('webcamLastPlacementLandscape', lastPlacementLandscape);
     Storage.setItem('webcamlastPlacementPortrait', lastPlacementPortrait);
     Storage.setItem('webcamLastPosition', lastPosition);
diff --git a/bigbluebutton-html5/imports/ui/components/nav-bar/component.jsx b/bigbluebutton-html5/imports/ui/components/nav-bar/component.jsx
index 5acf94dcb45050cb205054522f92e77d0cd8d66e..0a23e1e2c2bd5aadb3661c68b5f9ae7ee66fabbd 100755
--- a/bigbluebutton-html5/imports/ui/components/nav-bar/component.jsx
+++ b/bigbluebutton-html5/imports/ui/components/nav-bar/component.jsx
@@ -1,4 +1,4 @@
-import React, { Component } from 'react';
+import React, { PureComponent } from 'react';
 import PropTypes from 'prop-types';
 import { Session } from 'meteor/session';
 import cx from 'classnames';
@@ -8,11 +8,12 @@ import getFromUserSettings from '/imports/ui/services/users-settings';
 import { defineMessages, injectIntl } from 'react-intl';
 import Icon from '../icon/component';
 import { styles } from './styles.scss';
-import Button from '/imports/ui/components/button/component';
+import Button from '../button/component';
 import RecordingIndicator from './recording-indicator/container';
 import TalkingIndicatorContainer from '/imports/ui/components/nav-bar/talking-indicator/container';
 import SettingsDropdownContainer from './settings-dropdown/container';
 
+
 const intlMessages = defineMessages({
   toggleUserListLabel: {
     id: 'app.navBar.userListToggleBtnLabel',
@@ -40,7 +41,7 @@ const defaultProps = {
   shortcuts: '',
 };
 
-class NavBar extends Component {
+class NavBar extends PureComponent {
   static handleToggleUserList() {
     Session.set(
       'openPanel',
@@ -49,8 +50,6 @@ class NavBar extends Component {
         : 'userlist',
     );
     Session.set('idChatOpen', '');
-
-    window.dispatchEvent(new Event('panelChanged'));
   }
 
   componentDidMount() {
@@ -90,9 +89,7 @@ class NavBar extends Component {
     ariaLabel += hasUnreadMessages ? (` ${intl.formatMessage(intlMessages.newMessages)}`) : '';
 
     return (
-      <div
-        className={styles.navbar}
-      >
+      <div className={styles.navbar}>
         <div className={styles.top}>
           <div className={styles.left}>
             {!isExpanded ? null
diff --git a/bigbluebutton-html5/imports/ui/components/nav-bar/styles.scss b/bigbluebutton-html5/imports/ui/components/nav-bar/styles.scss
index 0040f6a63ae7eb6696ac4eb7b6a9798aa3685dae..b3e4205a4709f36587341f26c2b1991f16968a83 100755
--- a/bigbluebutton-html5/imports/ui/components/nav-bar/styles.scss
+++ b/bigbluebutton-html5/imports/ui/components/nav-bar/styles.scss
@@ -11,6 +11,7 @@
 .navbar {
   display: flex;
   flex-direction: column;
+  height:var(--mobile-nav-height);
 }
 
 .top,
diff --git a/bigbluebutton-html5/imports/ui/components/panel-manager/component.jsx b/bigbluebutton-html5/imports/ui/components/panel-manager/component.jsx
index a77d4b43ed0fa48fc4307f2966bd0f085390aad3..1c867fa59c7648afbbed209ac61ee123c22c53e6 100755
--- a/bigbluebutton-html5/imports/ui/components/panel-manager/component.jsx
+++ b/bigbluebutton-html5/imports/ui/components/panel-manager/component.jsx
@@ -1,4 +1,4 @@
-import React, { Component } from 'react';
+import React, { PureComponent } from 'react';
 import PropTypes from 'prop-types';
 import BreakoutRoomContainer from '/imports/ui/components/breakout-room/container';
 import UserListContainer from '/imports/ui/components/user-list/container';
@@ -11,13 +11,6 @@ import { defineMessages, injectIntl } from 'react-intl';
 import Resizable from 're-resizable';
 import { styles } from '/imports/ui/components/app/styles';
 import _ from 'lodash';
-import { withLayoutConsumer } from '/imports/ui/components/layout/context';
-import {
-  USERLIST_MIN_WIDTH,
-  USERLIST_MAX_WIDTH,
-  CHAT_MIN_WIDTH,
-  CHAT_MAX_WIDTH,
-} from '/imports/ui/components/layout/layout-manager';
 
 const intlMessages = defineMessages({
   chatLabel: {
@@ -50,8 +43,12 @@ const propTypes = {
 const DEFAULT_PANEL_WIDTH = 340;
 
 // Variables for resizing user-list.
-const USERLIST_MIN_WIDTH_PX = USERLIST_MIN_WIDTH;
-const USERLIST_MAX_WIDTH_PX = USERLIST_MAX_WIDTH;
+const USERLIST_MIN_WIDTH_PX = 150;
+const USERLIST_MAX_WIDTH_PX = 240;
+
+// Variables for resizing chat.
+const CHAT_MIN_WIDTH = 150;
+const CHAT_MAX_WIDTH = 350;
 
 // Variables for resizing poll.
 const POLL_MIN_WIDTH = 320;
@@ -69,9 +66,11 @@ const CAPTIONS_MAX_WIDTH = 400;
 const WAITING_MIN_WIDTH = DEFAULT_PANEL_WIDTH;
 const WAITING_MAX_WIDTH = 800;
 
-class PanelManager extends Component {
-  constructor(props) {
-    super(props);
+const dispatchResizeEvent = () => window.dispatchEvent(new Event('resize'));
+
+class PanelManager extends PureComponent {
+  constructor() {
+    super();
 
     this.padKey = _.uniqueId('resize-pad-');
     this.userlistKey = _.uniqueId('userlist-');
@@ -82,232 +81,23 @@ class PanelManager extends Component {
     this.captionsKey = _.uniqueId('captions-');
     this.waitingUsers = _.uniqueId('waitingUsers-');
 
-    const { layoutContextState } = props;
-    const { userListSize, chatSize } = layoutContextState;
-
     this.state = {
-      userlistWidth: userListSize.width,
-      chatWidth: chatSize.width,
+      chatWidth: DEFAULT_PANEL_WIDTH,
+      pollWidth: DEFAULT_PANEL_WIDTH,
+      userlistWidth: 180,
       noteWidth: DEFAULT_PANEL_WIDTH,
       captionsWidth: DEFAULT_PANEL_WIDTH,
-      pollWidth: DEFAULT_PANEL_WIDTH,
       waitingWidth: DEFAULT_PANEL_WIDTH,
-      breakoutRoomWidth: 0,
     };
-
-    this.setUserListWidth = this.setUserListWidth.bind(this);
-  }
-
-  shouldComponentUpdate(prevProps) {
-    const { layoutContextState } = this.props;
-    const { layoutContextState: prevLayoutContextState } = prevProps;
-    const {
-      userListSize,
-      chatSize,
-      breakoutRoomSize,
-    } = layoutContextState;
-    const {
-      userListSize: prevUserListSize,
-      chatSize: prevChatSize,
-      breakoutRoomSize: prevBreakoutRoomSize,
-    } = prevLayoutContextState;
-
-    if ((layoutContextState !== prevLayoutContextState)
-      && (userListSize.width === prevUserListSize.width
-        && chatSize.width === prevChatSize.width
-        && breakoutRoomSize.width === prevBreakoutRoomSize.width)) return false;
-    return true;
   }
 
   componentDidUpdate(prevProps) {
-    const {
-      userlistWidth,
-      chatWidth,
-      noteWidth,
-      captionsWidth,
-      pollWidth,
-      waitingWidth,
-      breakoutRoomWidth,
-    } = this.state;
-    const { layoutContextState } = this.props;
-    const {
-      userListSize,
-      chatSize,
-      noteSize,
-      captionsSize,
-      pollSize,
-      waitingSize,
-      breakoutRoomSize,
-    } = layoutContextState;
-    const { layoutContextState: oldLayoutContextState } = prevProps;
-    const {
-      userListSize: oldUserListSize,
-      chatSize: oldChatSize,
-      noteSize: oldNoteSize,
-      captionsSize: oldCaptionsSize,
-      pollSize: oldPollSize,
-      waitingSize: oldWaitingSize,
-      breakoutRoomSize: oldBreakoutRoomSize,
-    } = oldLayoutContextState;
-
-    if (userListSize.width !== oldUserListSize.width && userListSize.width !== userlistWidth) {
-      this.setUserListWidth(userListSize.width);
-    }
-    if (chatSize.width !== oldChatSize.width && chatSize.width !== chatWidth) {
-      this.setChatWidth(chatSize.width);
-    }
-    if (noteSize.width !== oldNoteSize.width && noteSize.width !== noteWidth) {
-      this.setNoteWidth(noteSize.width);
-    }
-    if (captionsSize.width !== oldCaptionsSize.width && captionsSize.width !== captionsWidth) {
-      this.setCaptionsWidth(captionsSize.width);
-    }
-    if (pollSize.width !== oldPollSize.width && pollSize.width !== pollWidth) {
-      this.setPollWidth(pollSize.width);
-    }
-    if (waitingSize.width !== oldWaitingSize.width && waitingSize.width !== waitingWidth) {
-      this.setWaitingWidth(waitingSize.width);
-    }
-    if (breakoutRoomSize.width !== oldBreakoutRoomSize.width
-      && breakoutRoomSize.width !== breakoutRoomWidth) {
-      this.setBreakoutRoomWidth(breakoutRoomSize.width);
-    }
-  }
-
-  setUserListWidth(userlistWidth) {
-    this.setState({ userlistWidth });
-  }
-
-  setChatWidth(chatWidth) {
-    this.setState({ chatWidth });
-  }
-
-  setNoteWidth(noteWidth) {
-    this.setState({ noteWidth });
-  }
-
-  setCaptionsWidth(captionsWidth) {
-    this.setState({ captionsWidth });
-  }
-
-  setPollWidth(pollWidth) {
-    this.setState({ pollWidth });
-  }
-
-  setWaitingWidth(waitingWidth) {
-    this.setState({ waitingWidth });
-  }
-
-  setBreakoutRoomWidth(breakoutRoomWidth) {
-    this.setState({ breakoutRoomWidth });
-  }
-
-  userListResizeStop(addvalue) {
-    const { userlistWidth } = this.state;
-    const { layoutContextDispatch } = this.props;
-
-    this.setUserListWidth(userlistWidth + addvalue);
-
-    layoutContextDispatch(
-      {
-        type: 'setUserListSize',
-        value: {
-          width: userlistWidth + addvalue,
-        },
-      },
-    );
+    const { openPanel } = this.props;
+    const { openPanel: oldOpenPanel } = prevProps;
 
-    window.dispatchEvent(new Event('panelChanged'));
-  }
-
-  chatResizeStop(addvalue) {
-    const { chatWidth } = this.state;
-    const { layoutContextDispatch } = this.props;
-
-    this.setChatWidth(chatWidth + addvalue);
-
-    layoutContextDispatch(
-      {
-        type: 'setChatSize',
-        value: {
-          width: chatWidth + addvalue,
-        },
-      },
-    );
-
-    window.dispatchEvent(new Event('panelChanged'));
-  }
-
-  noteResizeStop(addvalue) {
-    const { noteWidth } = this.state;
-    const { layoutContextDispatch } = this.props;
-
-    this.setNoteWidth(noteWidth + addvalue);
-
-    layoutContextDispatch(
-      {
-        type: 'setNoteSize',
-        value: {
-          width: noteWidth + addvalue,
-        },
-      },
-    );
-
-    window.dispatchEvent(new Event('panelChanged'));
-  }
-
-  captionsResizeStop(addvalue) {
-    const { captionsWidth } = this.state;
-    const { layoutContextDispatch } = this.props;
-
-    this.setCaptionsWidth(captionsWidth + addvalue);
-
-    layoutContextDispatch(
-      {
-        type: 'setCaptionsSize',
-        value: {
-          width: captionsWidth + addvalue,
-        },
-      },
-    );
-
-    window.dispatchEvent(new Event('panelChanged'));
-  }
-
-  pollResizeStop(addvalue) {
-    const { pollWidth } = this.state;
-    const { layoutContextDispatch } = this.props;
-
-    this.setPollWidth(pollWidth + addvalue);
-
-    layoutContextDispatch(
-      {
-        type: 'setPollSize',
-        value: {
-          width: pollWidth + addvalue,
-        },
-      },
-    );
-
-    window.dispatchEvent(new Event('panelChanged'));
-  }
-
-  waitingResizeStop(addvalue) {
-    const { waitingWidth } = this.state;
-    const { layoutContextDispatch } = this.props;
-
-    this.setWaitingWidth(waitingWidth + addvalue);
-
-    layoutContextDispatch(
-      {
-        type: 'setWaitingUsersPanelSize',
-        value: {
-          width: waitingWidth + addvalue,
-        },
-      },
-    );
-
-    window.dispatchEvent(new Event('panelChanged'));
+    if (openPanel !== oldOpenPanel) {
+      window.dispatchEvent(new Event('resize'));
+    }
   }
 
   renderUserList() {
@@ -355,8 +145,11 @@ class PanelManager extends Component {
         enable={resizableEnableOptions}
         key={this.userlistKey}
         size={{ width: userlistWidth }}
+        onResize={dispatchResizeEvent}
         onResizeStop={(e, direction, ref, d) => {
-          this.userListResizeStop(d.width);
+          this.setState({
+            userlistWidth: userlistWidth + d.width,
+          });
         }}
       >
         {this.renderUserList()}
@@ -401,8 +194,11 @@ class PanelManager extends Component {
         enable={resizableEnableOptions}
         key={this.chatKey}
         size={{ width: chatWidth }}
+        onResize={dispatchResizeEvent}
         onResizeStop={(e, direction, ref, d) => {
-          this.chatResizeStop(d.width);
+          this.setState({
+            chatWidth: chatWidth + d.width,
+          });
         }}
       >
         {this.renderChat()}
@@ -447,8 +243,11 @@ class PanelManager extends Component {
         enable={resizableEnableOptions}
         key={this.noteKey}
         size={{ width: noteWidth }}
+        onResize={dispatchResizeEvent}
         onResizeStop={(e, direction, ref, d) => {
-          this.noteResizeStop(d.width);
+          this.setState({
+            noteWidth: noteWidth + d.width,
+          });
         }}
       >
         {this.renderNote()}
@@ -493,8 +292,11 @@ class PanelManager extends Component {
         enable={resizableEnableOptions}
         key={this.captionsKey}
         size={{ width: captionsWidth }}
+        onResize={dispatchResizeEvent}
         onResizeStop={(e, direction, ref, d) => {
-          this.captionsResizeStop(captionsWidth + d.width);
+          this.setState({
+            captionsWidth: captionsWidth + d.width,
+          });
         }}
       >
         {this.renderCaptions()}
@@ -539,8 +341,11 @@ class PanelManager extends Component {
         enable={resizableEnableOptions}
         key={this.waitingUsers}
         size={{ width: waitingWidth }}
+        onResize={dispatchResizeEvent}
         onResizeStop={(e, direction, ref, d) => {
-          this.waitingResizeStop(waitingWidth + d.width);
+          this.setState({
+            waitingWidth: waitingWidth + d.width,
+          });
         }}
       >
         {this.renderWaitingUsersPanel()}
@@ -549,15 +354,8 @@ class PanelManager extends Component {
   }
 
   renderBreakoutRoom() {
-    const { breakoutRoomWidth } = this.state;
     return (
-      <div
-        className={styles.breakoutRoom}
-        key={this.breakoutroomKey}
-        style={{
-          width: breakoutRoomWidth,
-        }}
-      >
+      <div className={styles.breakoutRoom} key={this.breakoutroomKey}>
         <BreakoutRoomContainer />
       </div>
     );
@@ -595,8 +393,10 @@ class PanelManager extends Component {
         key={this.pollKey}
         size={{ width: pollWidth }}
         onResizeStop={(e, direction, ref, d) => {
-          // window.dispatchEvent(new Event('resize'));
-          this.pollResizeStop(pollWidth + d.width);
+          window.dispatchEvent(new Event('resize'));
+          this.setState({
+            pollWidth: pollWidth + d.width,
+          });
         }}
       >
         {this.renderPoll()}
@@ -608,7 +408,6 @@ class PanelManager extends Component {
     const { enableResize, openPanel } = this.props;
     if (openPanel === '') return null;
     const panels = [];
-
     if (enableResize) {
       panels.push(
         this.renderUserListResizable(),
@@ -670,6 +469,6 @@ class PanelManager extends Component {
   }
 }
 
-export default injectIntl(withLayoutConsumer(PanelManager));
+export default injectIntl(PanelManager);
 
 PanelManager.propTypes = propTypes;
diff --git a/bigbluebutton-html5/imports/ui/components/presentation/component.jsx b/bigbluebutton-html5/imports/ui/components/presentation/component.jsx
index aa07839e398c8a194f55466c4bd5979e76f5a5f5..adf8f4e09b09004bc5cc9359c3ffe263abdc90fd 100755
--- a/bigbluebutton-html5/imports/ui/components/presentation/component.jsx
+++ b/bigbluebutton-html5/imports/ui/components/presentation/component.jsx
@@ -4,6 +4,7 @@ import WhiteboardOverlayContainer from '/imports/ui/components/whiteboard/whiteb
 import WhiteboardToolbarContainer from '/imports/ui/components/whiteboard/whiteboard-toolbar/container';
 import { HUNDRED_PERCENT, MAX_PERCENT } from '/imports/utils/slideCalcUtils';
 import { defineMessages, injectIntl, intlShape } from 'react-intl';
+import { toast } from 'react-toastify';
 import PresentationToolbarContainer from './presentation-toolbar/container';
 import CursorWrapperContainer from './cursor/cursor-wrapper-container/container';
 import AnnotationGroupContainer from '../whiteboard/annotation-group/container';
@@ -16,7 +17,6 @@ import DownloadPresentationButton from './download-presentation-button/component
 import FullscreenService from '../fullscreen-button/service';
 import FullscreenButtonContainer from '../fullscreen-button/container';
 import { withDraggableConsumer } from '../media/webcam-draggable-overlay/context';
-import { withLayoutConsumer } from '/imports/ui/components/layout/context';
 import Icon from '/imports/ui/components/icon/component';
 
 const intlMessages = defineMessages({
@@ -54,10 +54,6 @@ class PresentationArea extends PureComponent {
     this.panAndZoomChanger = this.panAndZoomChanger.bind(this);
     this.fitToWidthHandler = this.fitToWidthHandler.bind(this);
     this.onFullscreenChange = this.onFullscreenChange.bind(this);
-    this.getPresentationSizesAvailable = this.getPresentationSizesAvailable.bind(this);
-    this.handleResize = this.handleResize.bind(this);
-
-
     this.onResize = () => setTimeout(this.handleResize.bind(this), 0);
     this.renderCurrentPresentationToast = this.renderCurrentPresentationToast.bind(this);
   }
@@ -87,101 +83,59 @@ class PresentationArea extends PureComponent {
   }
 
   componentDidMount() {
+    // adding an event listener to scale the whiteboard on 'resize' events sent by chat/userlist etc
+    window.addEventListener('resize', this.onResize);
     this.getInitialPresentationSizes();
     this.refPresentationContainer.addEventListener('fullscreenchange', this.onFullscreenChange);
-    window.addEventListener('resize', this.onResize, false);
-    window.addEventListener('layoutSizesSets', this.onResize, false);
-    window.addEventListener('webcamAreaResize', this.handleResize, false);
 
-    const { slidePosition, layoutContextDispatch } = this.props;
+    const { slidePosition, webcamDraggableDispatch } = this.props;
     const { width: currWidth, height: currHeight } = slidePosition;
-
-    layoutContextDispatch({
-      type: 'setPresentationSlideSize',
-      value: {
-        width: currWidth,
-        height: currHeight,
-      },
-    });
-
     if (currWidth > currHeight || currWidth === currHeight) {
-      layoutContextDispatch({
-        type: 'setPresentationOrientation',
-        value: 'landscape',
-      });
+      webcamDraggableDispatch({ type: 'setOrientationToLandscape' });
     }
     if (currHeight > currWidth) {
-      layoutContextDispatch({
-        type: 'setPresentationOrientation',
-        value: 'portrait',
-      });
+      webcamDraggableDispatch({ type: 'setOrientationToPortrait' });
     }
   }
 
   componentDidUpdate(prevProps) {
     const {
       currentPresentation,
-      notify,
-      intl,
       slidePosition,
-      layoutContextDispatch,
-      layoutContextState,
+      webcamDraggableDispatch,
       layoutSwapped,
       currentSlide,
       publishedPoll,
       isViewer,
       toggleSwapLayout,
       restoreOnUpdate,
-      currentPresentationId,
     } = this.props;
 
-    const { numUsersVideo } = layoutContextState;
-    const { layoutContextState: prevLayoutContextState } = prevProps;
-    const {
-      numUsersVideo: prevNumUsersVideo,
-    } = prevLayoutContextState;
-
-    if (numUsersVideo !== prevNumUsersVideo) {
-      this.onResize();
-    }
-    if (prevProps.slidePosition.id !== slidePosition.id) {
-      window.dispatchEvent(new Event('slideChanged'));
-    }
     const { width: prevWidth, height: prevHeight } = prevProps.slidePosition;
     const { width: currWidth, height: currHeight } = slidePosition;
 
     if (prevWidth !== currWidth || prevHeight !== currHeight) {
-      layoutContextDispatch({
-        type: 'setPresentationSlideSize',
-        value: {
-          width: currWidth,
-          height: currHeight,
-        },
-      });
       if (currWidth > currHeight || currWidth === currHeight) {
-        layoutContextDispatch({
-          type: 'setPresentationOrientation',
-          value: 'landscape',
-        });
+        webcamDraggableDispatch({ type: 'setOrientationToLandscape' });
       }
       if (currHeight > currWidth) {
-        layoutContextDispatch({
-          type: 'setPresentationOrientation',
-          value: 'portrait',
-        });
+        webcamDraggableDispatch({ type: 'setOrientationToPortrait' });
       }
     }
 
-    const presentationChanged = currentPresentationId !== currentPresentation._id;
+    if (prevProps.currentPresentation.name !== currentPresentation.name) {
+      if (this.currentPresentationToastId) {
+        return toast.update(this.currentPresentationToastId, {
+          render: this.renderCurrentPresentationToast(),
+        });
+      }
 
-    if (presentationChanged) {
-      Session.set('currentPresentationId', currentPresentation._id);
-      notify(
-        `${intl.formatMessage(intlMessages.changeNotification)} ${currentPresentation.name}`,
-        'info',
-        'presentation',
-      );
+      this.currentPresentationToastId = toast(this.renderCurrentPresentationToast(), {
+        onClose: () => { this.currentPresentationToastId = null; },
+        autoClose: true,
+      });
     }
+
     if (layoutSwapped && restoreOnUpdate && isViewer && currentSlide) {
       const slideChanged = currentSlide.id !== prevProps.currentSlide.id;
       const positionChanged = slidePosition.viewBoxHeight !== prevProps.slidePosition.viewBoxHeight
@@ -194,18 +148,16 @@ class PresentationArea extends PureComponent {
   }
 
   componentWillUnmount() {
-    window.removeEventListener('resize', this.onResize, false);
-    window.removeEventListener('layoutSizesSets', this.onResize, false);
+    window.removeEventListener('resize', this.onResize);
     this.refPresentationContainer.removeEventListener('fullscreenchange', this.onFullscreenChange);
   }
 
   onFullscreenChange() {
-    const { layoutContextDispatch } = this.props;
     const { isFullscreen } = this.state;
     const newIsFullscreen = FullscreenService.isFullScreen(this.refPresentationContainer);
     if (isFullscreen !== newIsFullscreen) {
       this.setState({ isFullscreen: newIsFullscreen });
-      layoutContextDispatch({ type: 'setPresentationFullscreen', value: newIsFullscreen });
+      window.dispatchEvent(new Event('resize'));
     }
   }
 
@@ -226,25 +178,25 @@ class PresentationArea extends PureComponent {
   }
 
   getPresentationSizesAvailable() {
-    const { layoutContextState } = this.props;
-    const {
-      presentationAreaSize,
-      webcamsAreaResizing,
-      mediaBounds,
-      tempWebcamsAreaSize,
-      webcamsPlacement,
-    } = layoutContextState;
-    const presentationSizes = {
-      presentationAreaWidth: 0,
-      presentationAreaHeight: 0,
-    };
+    const { userIsPresenter, multiUser } = this.props;
+    const { refPresentationArea, refWhiteboardArea } = this;
+    const presentationSizes = {};
+
+    if (refPresentationArea && refWhiteboardArea) {
+      // By default presentation sizes are equal to the sizes of the refPresentationArea
+      // direct parent of the svg wrapper
+      let { clientWidth, clientHeight } = refPresentationArea;
+
+      // if a user is a presenter - this means there is a whiteboard toolbar on the right
+      // and we have to get the width/height of the refWhiteboardArea
+      // (inner hidden div with absolute position)
+      if (userIsPresenter || multiUser) {
+        ({ clientWidth, clientHeight } = refWhiteboardArea);
+      }
 
-    presentationSizes.presentationAreaWidth = webcamsAreaResizing && (webcamsPlacement === 'left' || webcamsPlacement === 'right')
-      ? mediaBounds.width - tempWebcamsAreaSize.width
-      : presentationAreaSize.width;
-    presentationSizes.presentationAreaHeight = webcamsAreaResizing && (webcamsPlacement === 'top' || webcamsPlacement === 'bottom')
-      ? mediaBounds.height - tempWebcamsAreaSize.height - (this.getToolbarHeight() || 0) - 30
-      : presentationAreaSize.height - (this.getToolbarHeight() || 0);
+      presentationSizes.presentationAreaHeight = clientHeight - this.getToolbarHeight();
+      presentationSizes.presentationAreaWidth = clientWidth;
+    }
     return presentationSizes;
   }
 
@@ -693,8 +645,8 @@ class PresentationArea extends PureComponent {
 
     const {
       showSlide,
-      // fitToWidth,
-      // presentationAreaWidth,
+      fitToWidth,
+      presentationAreaWidth,
       localPosition,
     } = this.state;
 
@@ -725,7 +677,16 @@ class PresentationArea extends PureComponent {
 
     let toolbarWidth = 0;
     if (this.refWhiteboardArea) {
-      toolbarWidth = svgWidth;
+      if (svgWidth === presentationAreaWidth
+        || presentationAreaWidth <= 400
+        || fitToWidth === true) {
+        toolbarWidth = '100%';
+      } else if (svgWidth <= 400
+        && presentationAreaWidth > 400) {
+        toolbarWidth = '400px';
+      } else {
+        toolbarWidth = svgWidth;
+      }
     }
 
     return (
@@ -775,7 +736,7 @@ class PresentationArea extends PureComponent {
   }
 }
 
-export default injectIntl(withDraggableConsumer(withLayoutConsumer(PresentationArea)));
+export default injectIntl(withDraggableConsumer(PresentationArea));
 
 PresentationArea.propTypes = {
   intl: intlShape.isRequired,
diff --git a/bigbluebutton-html5/imports/ui/components/presentation/cursor/component.jsx b/bigbluebutton-html5/imports/ui/components/presentation/cursor/component.jsx
index 61d1b86e2b955ef5ea84081826903fe3515fd71b..85a74fee86897c799d7c29d24f804f48933dcdc6 100755
--- a/bigbluebutton-html5/imports/ui/components/presentation/cursor/component.jsx
+++ b/bigbluebutton-html5/imports/ui/components/presentation/cursor/component.jsx
@@ -38,7 +38,7 @@ export default class Cursor extends Component {
     return obj;
   }
 
-  static getScaledSizes(props, state) {
+  static getScaledSizes(props) {
     // TODO: This might need to change for the use case of fit-to-width portrait
     //       slides in non-presenter view. Some elements are still shrinking.
     const scaleFactor = props.widthRatio / props.physicalWidthRatio;
@@ -58,25 +58,14 @@ export default class Cursor extends Component {
         yOffset: props.cursorLabelBox.yOffset * scaleFactor,
         // making width and height a little bit larger than the size of the text
         // received from BBox, so that the text didn't touch the border
-        width: (state.labelBoxWidth + 3) * scaleFactor,
-        height: (state.labelBoxHeight + 3) * scaleFactor,
+        width: (props.labelBoxWidth + 3) * scaleFactor,
+        height: (props.labelBoxHeight + 3) * scaleFactor,
         strokeWidth: props.cursorLabelBox.labelBoxStrokeWidth * scaleFactor,
       },
     };
   }
 
-  constructor(props) {
-    super(props);
-    this.state = {
-      scaledSizes: null,
-      labelBoxWidth: 0,
-      labelBoxHeight: 0,
-    };
-
-    this.setLabelBoxDimensions = this.setLabelBoxDimensions.bind(this);
-  }
-
-  componentDidMount() {
+  componentWillMount() {
     const {
       cursorX,
       cursorY,
@@ -86,9 +75,8 @@ export default class Cursor extends Component {
       isMultiUser,
     } = this.props;
 
-    this.setState({
-      scaledSizes: Cursor.getScaledSizes(this.props, this.state),
-    });
+    // setting the initial cursor info
+    this.scaledSizes = Cursor.getScaledSizes(this.props);
     this.cursorCoordinate = Cursor.getCursorCoordinates(
       cursorX,
       cursorY,
@@ -99,83 +87,66 @@ export default class Cursor extends Component {
     const { fill, displayLabel } = Cursor.getFillAndLabel(presenter, isMultiUser);
     this.fill = fill;
     this.displayLabel = displayLabel;
-    // we need to find the BBox of the text, so that we could set a proper border box arount it
   }
 
-  componentDidUpdate(prevProps, prevState) {
-    const {
-      scaledSizes,
-    } = this.state;
-    if (!prevState.scaledSizes && scaledSizes) {      
-      this.calculateCursorLabelBoxDimensions();
-    }
+  componentDidMount() {
+    // we need to find the BBox of the text, so that we could set a proper border box arount it
+    this.calculateCursorLabelBoxDimensions();
+  }
 
+  componentWillReceiveProps(nextProps) {
     const {
       presenter,
       isMultiUser,
       widthRatio,
       physicalWidthRatio,
+      labelBoxWidth,
+      labelBoxHeight,
       cursorX,
       cursorY,
-      slideWidth,
-      slideHeight,
     } = this.props;
-    const {
-      labelBoxWidth,
-      labelBoxHeight,
-    } = this.state;
-
-    const {
-      labelBoxWidth: prevLabelBoxWidth,
-      labelBoxHeight: prevLabelBoxHeight,
-    } = prevState;
 
-    if (presenter !== prevProps.presenter || isMultiUser !== prevProps.isMultiUser) {
+    if (presenter !== nextProps.presenter || isMultiUser !== nextProps.isMultiUser) {
       const { fill, displayLabel } = Cursor.getFillAndLabel(
-        presenter,
-        isMultiUser,
+        nextProps.presenter,
+        nextProps.isMultiUser,
       );
       this.displayLabel = displayLabel;
       this.fill = fill;
     }
 
-    if ((widthRatio !== prevProps.widthRatio
-          || physicalWidthRatio !== prevProps.physicalWidthRatio)
-      || (labelBoxWidth !== prevLabelBoxWidth
-          || labelBoxHeight !== prevLabelBoxHeight)) {
-            this.setState({
-              scaledSizes: Cursor.getScaledSizes(this.props, this.state),
-            });
+    if ((widthRatio !== nextProps.widthRatio
+          || physicalWidthRatio !== nextProps.physicalWidthRatio)
+      || (labelBoxWidth !== nextProps.labelBoxWidth
+          || labelBoxHeight !== nextProps.labelBoxHeight)) {
+      this.scaledSizes = Cursor.getScaledSizes(nextProps);
     }
 
-    if (cursorX !== prevProps.cursorX || cursorY !== prevProps.cursorY) {
+    if (cursorX !== nextProps.cursorX || cursorY !== nextProps.cursorY) {
       const cursorCoordinate = Cursor.getCursorCoordinates(
-        cursorX,
-        cursorY,
-        slideWidth,
-        slideHeight,
+        nextProps.cursorX,
+        nextProps.cursorY,
+        nextProps.slideWidth,
+        nextProps.slideHeight,
       );
       this.cursorCoordinate = cursorCoordinate;
     }
   }
 
-  setLabelBoxDimensions(labelBoxWidth, labelBoxHeight) {    
-    this.setState({
-      labelBoxWidth,
-      labelBoxHeight,
-    });
-  }
-
   // this function retrieves the text node, measures its BBox and sets the size for the outer box
   calculateCursorLabelBoxDimensions() {
+    const {
+      setLabelBoxDimensions,
+    } = this.props;
+
     let labelBoxWidth = 0;
     let labelBoxHeight = 0;
-
     if (this.cursorLabelRef) {
       const { width, height } = this.cursorLabelRef.getBBox();
       const { widthRatio, physicalWidthRatio, cursorLabelBox } = this.props;
       labelBoxWidth = Cursor.invertScale(width, widthRatio, physicalWidthRatio);
       labelBoxHeight = Cursor.invertScale(height, widthRatio, physicalWidthRatio);
+
       // if the width of the text node is bigger than the maxSize - set the width to maxWidth
       if (labelBoxWidth > cursorLabelBox.maxWidth) {
         labelBoxWidth = cursorLabelBox.maxWidth;
@@ -183,30 +154,26 @@ export default class Cursor extends Component {
     }
 
     // updating labelBoxWidth and labelBoxHeight in the container, which then passes it down here
-    this.setLabelBoxDimensions(labelBoxWidth, labelBoxHeight);
+    setLabelBoxDimensions(labelBoxWidth, labelBoxHeight);
   }
 
   render() {
-    const {
-      scaledSizes,
-    } = this.state;
     const {
       cursorId,
       userName,
       isRTL,
     } = this.props;
-    
+
     const {
       cursorCoordinate,
       fill,
     } = this;
-    
-    if (!scaledSizes) return null;
+
     const {
       cursorLabelBox,
       cursorLabelText,
       finalRadius,
-    } = scaledSizes;
+    } = this.scaledSizes;
 
     const {
       x,
@@ -325,6 +292,17 @@ Cursor.propTypes = {
     fontSize: PropTypes.number.isRequired,
   }),
 
+  // Defines the width of the label box
+  labelBoxWidth: PropTypes.number.isRequired,
+  // Defines the height of the label box
+  labelBoxHeight: PropTypes.number.isRequired,
+
+  // Defines the function, which sets the state for the label box and passes it back down
+  // we need it, since we need to render the text first -> measure its dimensions ->
+  // set proper width and height of the border box -> pass it down ->
+  // catch in the 'componentWillReceiveProps' -> apply new values
+  setLabelBoxDimensions: PropTypes.func.isRequired,
+
   // Defines the direction the client text should be displayed
   isRTL: PropTypes.bool.isRequired,
 };
diff --git a/bigbluebutton-html5/imports/ui/components/presentation/cursor/container.jsx b/bigbluebutton-html5/imports/ui/components/presentation/cursor/container.jsx
index 1279973447f7ad6169fe73dad688d5c407e8376d..4938b194f66975db5376fa0b28fbb4e1a32a973a 100644
--- a/bigbluebutton-html5/imports/ui/components/presentation/cursor/container.jsx
+++ b/bigbluebutton-html5/imports/ui/components/presentation/cursor/container.jsx
@@ -6,16 +6,33 @@ import Cursor from './component';
 
 
 class CursorContainer extends Component {
+  constructor() {
+    super();
+    this.state = {
+      labelBoxWidth: 0,
+      labelBoxHeight: 0,
+    };
+    this.setLabelBoxDimensions = this.setLabelBoxDimensions.bind(this);
+  }
 
+  setLabelBoxDimensions(labelBoxWidth, labelBoxHeight) {
+    this.setState({
+      labelBoxWidth,
+      labelBoxHeight,
+    });
+  }
 
   render() {
     const { cursorX, cursorY } = this.props;
+    const { labelBoxWidth, labelBoxHeight } = this.state;
 
     if (cursorX > 0 && cursorY > 0) {
       return (
         <Cursor
           cursorX={cursorX}
           cursorY={cursorY}
+          labelBoxWidth={labelBoxWidth}
+          labelBoxHeight={labelBoxHeight}
           setLabelBoxDimensions={this.setLabelBoxDimensions}
           {...this.props}
         />
diff --git a/bigbluebutton-html5/imports/ui/components/screenshare/component.jsx b/bigbluebutton-html5/imports/ui/components/screenshare/component.jsx
index e733b16d05a57b5c49655db2a35aaeb10730a7fc..5d7753857a8e750ff9e3c10b11a7b43ebd3e38f1 100755
--- a/bigbluebutton-html5/imports/ui/components/screenshare/component.jsx
+++ b/bigbluebutton-html5/imports/ui/components/screenshare/component.jsx
@@ -48,11 +48,11 @@ class ScreenshareComponent extends React.Component {
     window.addEventListener('screensharePlayFailed', this.handlePlayElementFailed);
   }
 
-  componentDidUpdate(prevProps) {
+  componentWillReceiveProps(nextProps) {
     const {
       isPresenter, unshareScreen,
     } = this.props;
-    if (prevProps.isPresenter && !isPresenter) {
+    if (isPresenter && !nextProps.isPresenter) {
       unshareScreen();
     }
   }
@@ -176,10 +176,7 @@ class ScreenshareComponent extends React.Component {
           <video
             id="screenshareVideo"
             key="screenshareVideo"
-            style={{
-              maxHeight: '100%',
-              width: '100%',
-            }}
+            style={{ maxHeight: '100%', width: '100%' }}
             playsInline
             onLoadedData={this.onVideoLoad}
             ref={(ref) => { this.videoTag = ref; }}
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 0f993dd5bfe4447e8d8be6a869fb00a02d726671..6420327c623cf7eed982f22a47b8b131acaf6850 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
@@ -56,7 +56,6 @@ const handleClickToggleChat = (id) => {
   } else {
     Session.set('idChatOpen', '');
   }
-  window.dispatchEvent(new Event('panelChanged'));
 };
 
 const ChatListItem = (props) => {
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 ef853eea3a020a5df7e1b9fe892db46481903b51..2371966dcebd2ea19ec83c19668894aafb260a61 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
@@ -18,7 +18,6 @@ const toggleBreakoutPanel = () => {
       ? 'userlist'
       : 'breakoutroom',
   );
-  window.dispatchEvent(new Event('panelChanged'));
 };
 
 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 03dbddaf7df1ff5887af3af2e6f4eac535678de1..58117844d04d4df62380694285a2de82b1be3689 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
@@ -160,8 +160,6 @@ class UserDropdown extends PureComponent {
       showNestedOptions: false,
     };
 
-    this.title = _.uniqueId('dropdown-title-');
-    this.seperator = _.uniqueId('action-separator-');
     this.audio = new Audio(`${Meteor.settings.public.app.cdn + Meteor.settings.public.app.basename}/resources/sounds/bbb-handRaise.mp3`);
 
     this.handleScroll = this.handleScroll.bind(this);
@@ -173,6 +171,11 @@ class UserDropdown extends PureComponent {
     this.makeDropdownItem = this.makeDropdownItem.bind(this);
   }
 
+  componentWillMount() {
+    this.title = _.uniqueId('dropdown-title-');
+    this.seperator = _.uniqueId('action-separator-');
+  }
+
   componentDidUpdate() {
     this.checkDropdownDirection();
   }
diff --git a/bigbluebutton-html5/imports/ui/components/video-provider/component.jsx b/bigbluebutton-html5/imports/ui/components/video-provider/component.jsx
index 8ebb5fee311a23b26a8d205645a719f49d82ab6f..910f89894aeedad10b7c019a322d9685cce3c13f 100755
--- a/bigbluebutton-html5/imports/ui/components/video-provider/component.jsx
+++ b/bigbluebutton-html5/imports/ui/components/video-provider/component.jsx
@@ -77,10 +77,6 @@ const propTypes = {
 };
 
 class VideoProvider extends Component {
-  static onBeforeUnload() {
-    VideoService.onBeforeUnload();
-  }
-
   constructor(props) {
     super(props);
 
@@ -105,6 +101,8 @@ class VideoProvider extends Component {
     this.onWsClose = this.onWsClose.bind(this);
     this.onWsMessage = this.onWsMessage.bind(this);
 
+    this.onBeforeUnload = this.onBeforeUnload.bind(this);
+
     this.updateStreams = this.updateStreams.bind(this);
   }
 
@@ -117,7 +115,7 @@ class VideoProvider extends Component {
 
     this.ws.onmessage = this.onWsMessage;
 
-    window.addEventListener('beforeunload', VideoProvider.onBeforeUnload);
+    window.addEventListener('beforeunload', this.onBeforeUnload);
   }
 
   componentDidUpdate(prevProps) {
@@ -136,7 +134,7 @@ class VideoProvider extends Component {
     window.removeEventListener('online', this.openWs);
     window.removeEventListener('offline', this.onWsClose);
 
-    window.removeEventListener('beforeunload', VideoProvider.onBeforeUnload);
+    window.removeEventListener('beforeunload', this.onBeforeUnload);
 
     VideoService.exitVideo();
 
@@ -207,41 +205,17 @@ class VideoProvider extends Component {
     this.setState({ socketOpen: true });
   }
 
-  setReconnectionTimeout(cameraId, isLocal) {
-    const peer = this.webRtcPeers[cameraId];
-    const peerHasStarted = peer && peer.started === true;
-    const shouldSetReconnectionTimeout = !this.restartTimeout[cameraId] && !peerHasStarted;
-
-    if (shouldSetReconnectionTimeout) {
-      const newReconnectTimer = this.restartTimer[cameraId] || CAMERA_SHARE_FAILED_WAIT_TIME;
-      this.restartTimer[cameraId] = newReconnectTimer;
-
-      logger.info({
-        logCode: 'video_provider_setup_reconnect',
-        extraInfo: {
-          cameraId,
-          reconnectTimer: newReconnectTimer,
-        },
-      }, `Camera has a new reconnect timer of ${newReconnectTimer} ms for ${cameraId}`);
-
-      this.restartTimeout[cameraId] = setTimeout(
-        this._getWebRTCStartTimeout(cameraId, isLocal),
-        this.restartTimer[cameraId],
-      );
-    }
+  onBeforeUnload() {
+    VideoService.onBeforeUnload();
   }
 
   updateStreams(streams) {
     const streamsCameraIds = streams.map(s => s.cameraId);
     const streamsConnected = Object.keys(this.webRtcPeers);
 
-    const streamsToConnect = streamsCameraIds.filter(
-      cameraId => !streamsConnected.includes(cameraId),
-    );
+    const streamsToConnect = streamsCameraIds.filter(cameraId => !streamsConnected.includes(cameraId));
 
-    const streamsToDisconnect = streamsConnected.filter(
-      cameraId => !streamsCameraIds.includes(cameraId),
-    );
+    const streamsToDisconnect = streamsConnected.filter(cameraId => !streamsCameraIds.includes(cameraId));
 
     streamsToConnect.forEach((cameraId) => {
       const isLocal = VideoService.isLocalStream(cameraId);
@@ -531,8 +505,7 @@ class VideoProvider extends Component {
       const peer = this.webRtcPeers[cameraId];
       if (peer && peer.peerConnection) {
         const conn = peer.peerConnection;
-        conn.oniceconnectionstatechange = this
-          ._getOnIceConnectionStateChangeCallback(cameraId, isLocal);
+        conn.oniceconnectionstatechange = this._getOnIceConnectionStateChangeCallback(cameraId, isLocal);
         VideoService.monitor(conn);
       }
     }
@@ -622,6 +595,30 @@ class VideoProvider extends Component {
     }, `Camera peer creation failed for ${cameraId} due to ${error.message}`);
   }
 
+  setReconnectionTimeout(cameraId, isLocal) {
+    const peer = this.webRtcPeers[cameraId];
+    const peerHasStarted = peer && peer.started === true;
+    const shouldSetReconnectionTimeout = !this.restartTimeout[cameraId] && !peerHasStarted;
+
+    if (shouldSetReconnectionTimeout) {
+      const newReconnectTimer = this.restartTimer[cameraId] || CAMERA_SHARE_FAILED_WAIT_TIME;
+      this.restartTimer[cameraId] = newReconnectTimer;
+
+      logger.info({
+        logCode: 'video_provider_setup_reconnect',
+        extraInfo: {
+          cameraId,
+          reconnectTimer: newReconnectTimer,
+        },
+      }, `Camera has a new reconnect timer of ${newReconnectTimer} ms for ${cameraId}`);
+
+      this.restartTimeout[cameraId] = setTimeout(
+        this._getWebRTCStartTimeout(cameraId, isLocal),
+        this.restartTimer[cameraId],
+      );
+    }
+  }
+
   _getOnIceCandidateCallback(cameraId, isLocal) {
     return (candidate) => {
       const peer = this.webRtcPeers[cameraId];
diff --git a/bigbluebutton-html5/imports/ui/components/video-provider/container.jsx b/bigbluebutton-html5/imports/ui/components/video-provider/container.jsx
index 214027c24aa8c72088035cb2ccefe490efeb5f68..6e3e491010fe05d77f308e49b388d1085f32c2bb 100755
--- a/bigbluebutton-html5/imports/ui/components/video-provider/container.jsx
+++ b/bigbluebutton-html5/imports/ui/components/video-provider/container.jsx
@@ -2,7 +2,6 @@ import React from 'react';
 import { withTracker } from 'meteor/react-meteor-data';
 import VideoProvider from './component';
 import VideoService from './service';
-import { withLayoutContext } from '/imports/ui/components/layout/context';
 
 const VideoProviderContainer = ({ children, ...props }) => {
   const { streams } = props;
@@ -13,4 +12,4 @@ export default withTracker(props => ({
   swapLayout: props.swapLayout,
   streams: VideoService.getVideoStreams(),
   isUserLocked: VideoService.isUserLocked(),
-}))(withLayoutContext(VideoProviderContainer));
+}))(VideoProviderContainer);
diff --git a/bigbluebutton-html5/imports/ui/components/video-provider/video-list/component.jsx b/bigbluebutton-html5/imports/ui/components/video-provider/video-list/component.jsx
index 210bfec8f9af83e1f0c03d0a960e46f3713e4c1c..bfe4b551bc8d658fd1605b061c08e6699594c0d1 100755
--- a/bigbluebutton-html5/imports/ui/components/video-provider/video-list/component.jsx
+++ b/bigbluebutton-html5/imports/ui/components/video-provider/video-list/component.jsx
@@ -102,13 +102,11 @@ class VideoList extends Component {
 
     this.handleCanvasResize();
     window.addEventListener('resize', this.handleCanvasResize, false);
-    window.addEventListener('layoutSizesSets', this.handleCanvasResize, false);
     window.addEventListener('videoPlayFailed', this.handlePlayElementFailed);
   }
 
   componentWillUnmount() {
     window.removeEventListener('resize', this.handleCanvasResize, false);
-    window.removeEventListener('layoutSizesSets', this.handleCanvasResize, false);
     window.removeEventListener('videoPlayFailed', this.handlePlayElementFailed);
   }
 
diff --git a/bigbluebutton-html5/imports/ui/components/whiteboard/annotations/pencil/component.jsx b/bigbluebutton-html5/imports/ui/components/whiteboard/annotations/pencil/component.jsx
index 7dbbf745e9bc634c322bce2e0833ad6120f096cb..3a808503eae96addbe86d87827d5220f57649657 100755
--- a/bigbluebutton-html5/imports/ui/components/whiteboard/annotations/pencil/component.jsx
+++ b/bigbluebutton-html5/imports/ui/components/whiteboard/annotations/pencil/component.jsx
@@ -89,26 +89,16 @@ export default class PencilDrawComponent extends Component {
     return version !== nextProps.version;
   }
 
-  componentDidUpdate(prevProps) {
-    const { annotation: prevAnnotation } = prevProps;
-    const { points: prevPoints } = prevAnnotation;
-    const { annotation, slideWidth, slideHeight } = this.props;
+  componentWillUpdate(nextProps) {
+    const { annotation: nextAnnotation, slideWidth, slideHeight } = nextProps;
+    const { points: nextPoints } = nextAnnotation;
+    const { annotation } = this.props;
     const { points } = annotation;
-    if (prevPoints.length !== points.length) {
-      this.path = this.getCoordinates(annotation, slideWidth, slideHeight);
+    if (nextPoints.length !== points.length) {
+      this.path = this.getCoordinates(nextAnnotation, slideWidth, slideHeight);
     }
   }
 
-  // componentWillUpdate(nextProps) {
-  //   const { annotation: nextAnnotation, slideWidth, slideHeight } = nextProps;
-  //   const { points: nextPoints } = nextAnnotation;
-  //   const { annotation } = this.props;
-  //   const { points } = annotation;
-  //   if (nextPoints.length !== points.length) {
-  //     this.path = this.getCoordinates(nextAnnotation, slideWidth, slideHeight);
-  //   }
-  // }
-
   getCoordinates(annotation, slideWidth, slideHeight) {
     if ((!annotation || annotation.points.length === 0)
         || (annotation.status === 'DRAW_END' && !annotation.commands)) {
diff --git a/bigbluebutton-html5/imports/ui/components/whiteboard/annotations/poll/component.jsx b/bigbluebutton-html5/imports/ui/components/whiteboard/annotations/poll/component.jsx
index ca4c93824e19caf227fed1c965bf558dc3312818..c6382af1ef3f2d93f758617505d43f7055d784fd 100644
--- a/bigbluebutton-html5/imports/ui/components/whiteboard/annotations/poll/component.jsx
+++ b/bigbluebutton-html5/imports/ui/components/whiteboard/annotations/poll/component.jsx
@@ -9,11 +9,10 @@ class PollDrawComponent extends Component {
     super(props);
 
     this.state = {
-      // We did it because it was calculated in the componentWillMount
-      calculated: false,
       // flag indicating whether we need to continue calculating the sizes or display the annotation
       prepareToDisplay: true,
-      // outer (white) rectangle's coordinates and sizes (calculated in componentDidMount)
+
+      // outer (white) rectangle's coordinates and sizes (calculated in componentWillMount)
       outerRect: {
         x: 0,
         y: 0,
@@ -51,12 +50,131 @@ class PollDrawComponent extends Component {
       lineToMeasure: [],
       fontSizeDirection: 1,
     };
+  }
+
+  componentWillMount() {
+    // in this part we retrieve the props and perform initial calculations for the state
+    // calculating only the parts which have to be done just once and don't require
+    // rendering / rerendering the text objects
+
+    const { annotation } = this.props;
+    const { points, result } = annotation;
+    const { slideWidth, slideHeight, intl } = this.props;
+
+    // x1 and y1 - coordinates of the top left corner of the annotation
+    // initial width and height are the width and height of the annotation
+    // all the points are given as percentages of the slide
+    const x1 = points[0];
+    const y1 = points[1];
+    const initialWidth = points[2];
+    const initialHeight = points[3];
+
+    // calculating the data for the outer rectangle
+    // 0.001 is needed to accomodate bottom and right borders of the annotation
+    const x = (x1 / 100) * slideWidth;
+    const y = (y1 / 100) * slideHeight;
+    const width = ((initialWidth - 0.001) / 100) * slideWidth;
+    const height = ((initialHeight - 0.001) / 100) * slideHeight;
+
+    let votesTotal = 0;
+    let maxNumVotes = 0;
+    const textArray = [];
+
+    // counting the total number of votes, finding the biggest number of votes
+    result.reduce((previousValue, currentValue) => {
+      votesTotal = previousValue + currentValue.numVotes;
+      if (maxNumVotes < currentValue.numVotes) {
+        maxNumVotes = currentValue.numVotes;
+      }
+
+      return votesTotal;
+    }, 0);
+
+    // filling the textArray with data to display
+    // adding value of the iterator to each line needed to create unique
+    // keys while rendering at the end
+    const arrayLength = result.length;
+    for (let i = 0; i < arrayLength; i += 1) {
+      const _tempArray = [];
+      const _result = result[i];
+      let isDefaultPoll;
+      switch (_result.key.toLowerCase()) {
+        case 'true':
+        case 'false':
+        case 'yes':
+        case 'no':
+        case 'a':
+        case 'b':
+        case 'c':
+        case 'd':
+        case 'e':
+          isDefaultPoll = true;
+          break;
+        default:
+          isDefaultPoll = false;
+          break;
+      }
+
+      if (isDefaultPoll) {
+        _result.key = intl.formatMessage({ id: `app.poll.answer.${_result.key.toLowerCase()}` });
+      }
+
+      _tempArray.push(_result.key, `${_result.numVotes}`);
+      if (votesTotal === 0) {
+        _tempArray.push('0%');
+        _tempArray.push(i);
+      } else {
+        const percResult = (_result.numVotes / votesTotal) * 100;
+        _tempArray.push(`${Math.round(percResult)}%`);
+        _tempArray.push(i);
+      }
+
+      textArray.push(_tempArray);
+    }
+
+    // calculating the data for the inner rectangle
+    const innerWidth = width * 0.95;
+    const innerHeight = height - (width * 0.05);
+    const innerX = x + (width * 0.025);
+    const innerY = y + (width * 0.025);
+    const thickness = (width - innerWidth) / 10;
+
+    // calculating the maximum possible width and height of the each line
+    // 25% of the height goes to the padding
+    const maxLineWidth = innerWidth / 3;
+    const maxLineHeight = (innerHeight * 0.75) / textArray.length;
 
-    this.pollInitialCalculation = this.pollInitialCalculation.bind(this);
+    const lineToMeasure = textArray[0];
+    const { pollAnswerIds } = PollService;
+    const messageIndex = lineToMeasure[0].toLowerCase();
+    if (pollAnswerIds[messageIndex]) {
+      lineToMeasure[0] = intl.formatMessage(pollAnswerIds[messageIndex]);
+    }
+
+    // saving all the initial calculations in the state
+    this.setState({
+      outerRect: {
+        x,
+        y,
+        width,
+        height,
+      },
+      innerRect: {
+        x: innerX,
+        y: innerY,
+        width: innerWidth,
+        height: innerHeight,
+      },
+      thickness,
+      maxNumVotes,
+      textArray,
+      maxLineWidth,
+      maxLineHeight,
+      lineToMeasure,
+    });
   }
 
   componentDidMount() {
-    this.pollInitialCalculation();
     this.checkSizes();
   }
 
@@ -82,11 +200,9 @@ class PollDrawComponent extends Component {
       fontSizeDirection,
       calcFontSize,
       textArray,
-      calculated,
     } = this.state;
 
     const { annotation } = this.props;
-    if (!calculated) return null;
     // increment the font size by 2 to prevent Maximum update depth exceeded
     const fontSizeIncrement = 2;
 
@@ -186,129 +302,6 @@ class PollDrawComponent extends Component {
     });
   }
 
-  pollInitialCalculation() {
-    // in this part we retrieve the props and perform initial calculations for the state
-    // calculating only the parts which have to be done just once and don't require
-    // rendering / rerendering the text objects
-
-    // if (!state.initialState) return;
-    const { annotation } = this.props;
-    const { points, result } = annotation;
-    const { slideWidth, slideHeight, intl } = this.props;
-
-    // x1 and y1 - coordinates of the top left corner of the annotation
-    // initial width and height are the width and height of the annotation
-    // all the points are given as percentages of the slide
-    const x1 = points[0];
-    const y1 = points[1];
-    const initialWidth = points[2];
-    const initialHeight = points[3];
-
-    // calculating the data for the outer rectangle
-    // 0.001 is needed to accomodate bottom and right borders of the annotation
-    const x = (x1 / 100) * slideWidth;
-    const y = (y1 / 100) * slideHeight;
-    const width = ((initialWidth - 0.001) / 100) * slideWidth;
-    const height = ((initialHeight - 0.001) / 100) * slideHeight;
-
-    let votesTotal = 0;
-    let maxNumVotes = 0;
-    const textArray = [];
-
-    // counting the total number of votes, finding the biggest number of votes
-    result.reduce((previousValue, currentValue) => {
-      votesTotal = previousValue + currentValue.numVotes;
-      if (maxNumVotes < currentValue.numVotes) {
-        maxNumVotes = currentValue.numVotes;
-      }
-
-      return votesTotal;
-    }, 0);
-
-    // filling the textArray with data to display
-    // adding value of the iterator to each line needed to create unique
-    // keys while rendering at the end
-    const arrayLength = result.length;
-    for (let i = 0; i < arrayLength; i += 1) {
-      const _tempArray = [];
-      const _result = result[i];
-      let isDefaultPoll;
-      switch (_result.key.toLowerCase()) {
-        case 'true':
-        case 'false':
-        case 'yes':
-        case 'no':
-        case 'a':
-        case 'b':
-        case 'c':
-        case 'd':
-        case 'e':
-          isDefaultPoll = true;
-          break;
-        default:
-          isDefaultPoll = false;
-          break;
-      }
-
-      if (isDefaultPoll) {
-        _result.key = intl.formatMessage({ id: `app.poll.answer.${_result.key.toLowerCase()}` });
-      }
-
-      _tempArray.push(_result.key, `${_result.numVotes}`);
-      if (votesTotal === 0) {
-        _tempArray.push('0%');
-        _tempArray.push(i);
-      } else {
-        const percResult = (_result.numVotes / votesTotal) * 100;
-        _tempArray.push(`${Math.round(percResult)}%`);
-        _tempArray.push(i);
-      }
-
-      textArray.push(_tempArray);
-    }
-
-    // calculating the data for the inner rectangle
-    const innerWidth = width * 0.95;
-    const innerHeight = height - (width * 0.05);
-    const innerX = x + (width * 0.025);
-    const innerY = y + (width * 0.025);
-    const thickness = (width - innerWidth) / 10;
-
-    // calculating the maximum possible width and height of the each line
-    // 25% of the height goes to the padding
-    const maxLineWidth = innerWidth / 3;
-    const maxLineHeight = (innerHeight * 0.75) / textArray.length;
-
-    const lineToMeasure = textArray[0];
-    const { pollAnswerIds } = PollService;
-    const messageIndex = lineToMeasure[0].toLowerCase();
-    if (pollAnswerIds[messageIndex]) {
-      lineToMeasure[0] = intl.formatMessage(pollAnswerIds[messageIndex]);
-    }
-
-    this.setState({
-      outerRect: {
-        x,
-        y,
-        width,
-        height,
-      },
-      innerRect: {
-        x: innerX,
-        y: innerY,
-        width: innerWidth,
-        height: innerHeight,
-      },
-      thickness,
-      maxNumVotes,
-      textArray,
-      maxLineWidth,
-      maxLineHeight,
-      lineToMeasure,
-      calculated: true,
-    });
-  }
-
   renderPoll() {
     const {
       backgroundColor,
diff --git a/bigbluebutton-html5/imports/ui/components/whiteboard/whiteboard-overlay/text-draw-listener/component.jsx b/bigbluebutton-html5/imports/ui/components/whiteboard/whiteboard-overlay/text-draw-listener/component.jsx
index f0dfc7e7416bb821c9f667594a1271d9e6dc4a8f..056d81ce9772d52b506feffbb74d36af8f7f0eff 100755
--- a/bigbluebutton-html5/imports/ui/components/whiteboard/whiteboard-overlay/text-draw-listener/component.jsx
+++ b/bigbluebutton-html5/imports/ui/components/whiteboard/whiteboard-overlay/text-draw-listener/component.jsx
@@ -65,6 +65,17 @@ export default class TextDrawListener extends Component {
   }
 
 
+  // If the activeId suddenly became empty - this means the shape was deleted
+  // While the user was drawing it. So we are resetting the state.
+  componentWillReceiveProps(nextProps) {
+    const { drawSettings } = this.props;
+    const nextDrawsettings = nextProps.drawSettings;
+
+    if (drawSettings.textShapeActiveId !== '' && nextDrawsettings.textShapeActiveId === '') {
+      this.resetState();
+    }
+  }
+
   componentDidUpdate(prevProps) {
     const {
       drawSettings,
@@ -74,12 +85,6 @@ export default class TextDrawListener extends Component {
     const prevDrawsettings = prevProps.drawSettings;
     const prevTextShapeValue = prevProps.drawSettings.textShapeValue;
 
-    // If the activeId suddenly became empty - this means the shape was deleted
-    // While the user was drawing it. So we are resetting the state.
-    if (prevDrawsettings.textShapeActiveId !== '' && drawSettings.textShapeActiveId === '') {
-      this.resetState();
-    }
-
     // Updating the component in cases when:
     // Either color / font-size or text value has changed
     // and excluding the case when the textShapeActiveId changed to ''
diff --git a/bigbluebutton-html5/imports/ui/components/whiteboard/whiteboard-toolbar/toolbar-menu-item/component.jsx b/bigbluebutton-html5/imports/ui/components/whiteboard/whiteboard-toolbar/toolbar-menu-item/component.jsx
index 443321242e5543979d0ab57b8ad8fd111ada851a..db141bbfada4b3ae731975884a1810b590b4a34e 100755
--- a/bigbluebutton-html5/imports/ui/components/whiteboard/whiteboard-toolbar/toolbar-menu-item/component.jsx
+++ b/bigbluebutton-html5/imports/ui/components/whiteboard/whiteboard-toolbar/toolbar-menu-item/component.jsx
@@ -11,13 +11,18 @@ export default class ToolbarMenuItem extends Component {
 
     // a flag to keep track of whether the menu item was actually clicked
     this.clicked = false;
-    this.uniqueRef = _.uniqueId('toolbar-menu-item');
+
     this.handleTouchStart = this.handleTouchStart.bind(this);
     this.handleOnMouseUp = this.handleOnMouseUp.bind(this);
     this.handleOnMouseDown = this.handleOnMouseDown.bind(this);
     this.setRef = this.setRef.bind(this);
   }
 
+  // generating a unique ref string for the toolbar-item
+  componentWillMount() {
+    this.uniqueRef = _.uniqueId('toolbar-menu-item');
+  }
+
   componentDidMount() {
     // adding and removing touchstart events can be done via standard React way
     // by passing onTouchStart={this.funcName} once they stop triggering mousedown events
diff --git a/bigbluebutton-html5/imports/ui/components/whiteboard/whiteboard-toolbar/toolbar-submenu-item/component.jsx b/bigbluebutton-html5/imports/ui/components/whiteboard/whiteboard-toolbar/toolbar-submenu-item/component.jsx
index bc04ac5025b8bc92e052a6f03ed05485575c4715..a54768658132512abee61a671539739257e5fcb4 100644
--- a/bigbluebutton-html5/imports/ui/components/whiteboard/whiteboard-toolbar/toolbar-submenu-item/component.jsx
+++ b/bigbluebutton-html5/imports/ui/components/whiteboard/whiteboard-toolbar/toolbar-submenu-item/component.jsx
@@ -7,12 +7,17 @@ import { styles } from '../styles';
 export default class ToolbarSubmenuItem extends Component {
   constructor() {
     super();
-    this.uniqueRef = _.uniqueId('toolbar-submenu-item');
+
     this.handleTouchStart = this.handleTouchStart.bind(this);
     this.handleOnMouseUp = this.handleOnMouseUp.bind(this);
     this.setRef = this.setRef.bind(this);
   }
 
+  // generating a unique ref string for the toolbar-item
+  componentWillMount() {
+    this.uniqueRef = _.uniqueId('toolbar-submenu-item');
+  }
+
   componentDidMount() {
     // adding and removing touchstart events can be done via standard React way
     // by passing onTouchStart={this.funcName} once they stop triggering mousedown events