Fix raise hand toggled state

This commit is contained in:
yanas 2017-05-26 16:28:16 -05:00
parent ec2e6525ac
commit d1737745c2
7 changed files with 155 additions and 275 deletions

View File

@ -55,4 +55,8 @@ declare module 'redux' {
declare function compose<S, A>(...fns: Array<StoreEnhancer<S, A>>): Function; declare function compose<S, A>(...fns: Array<StoreEnhancer<S, A>>): Function;
// Utility function in Redux that can be used for function composition
// e.g. bar(foo(baz)) is equivalent to compose(bar, foo)(baz).
declare function compose(...fns: Array<Function>): Function;
} }

View File

@ -15,6 +15,13 @@ import {
SET_TOOLBOX_VISIBLE SET_TOOLBOX_VISIBLE
} from './actionTypes'; } from './actionTypes';
/**
* FIXME: We should make sure all common functions for native and web are
* separated in a global functions file, as well as all actions! Currently this
* file contains actions that are imported in actions.web.
*/
import { getButton } from './functions.web';
/** /**
* Event handler for local raise hand changed event. * Event handler for local raise hand changed event.
* *
@ -23,10 +30,8 @@ import {
*/ */
export function changeLocalRaiseHand(handRaised: boolean): Function { export function changeLocalRaiseHand(handRaised: boolean): Function {
return (dispatch: Dispatch<*>, getState: Function) => { return (dispatch: Dispatch<*>, getState: Function) => {
const state = getState();
const { secondaryToolbarButtons } = state['features/toolbox'];
const buttonName = 'raisehand'; const buttonName = 'raisehand';
const button = secondaryToolbarButtons.get(buttonName); const button = getButton(buttonName, getState());
button.toggled = handRaised; button.toggled = handRaised;
@ -264,10 +269,8 @@ export function showEtherpadButton(): Function {
*/ */
export function toggleFullScreen(isFullScreen: boolean): Function { export function toggleFullScreen(isFullScreen: boolean): Function {
return (dispatch: Dispatch<*>, getState: Function) => { return (dispatch: Dispatch<*>, getState: Function) => {
const state = getState();
const { primaryToolbarButtons } = state['features/toolbox'];
const buttonName = 'fullscreen'; const buttonName = 'fullscreen';
const button = primaryToolbarButtons.get(buttonName); const button = getButton(buttonName, getState());
button.toggled = isFullScreen; button.toggled = isFullScreen;
@ -283,14 +286,7 @@ export function toggleFullScreen(isFullScreen: boolean): Function {
*/ */
export function toggleToolbarButton(buttonName: string): Function { export function toggleToolbarButton(buttonName: string): Function {
return (dispatch: Dispatch, getState: Function) => { return (dispatch: Dispatch, getState: Function) => {
const state = getState(); const button = getButton(buttonName, getState());
const {
primaryToolbarButtons,
secondaryToolbarButtons
} = state['features/toolbox'];
const button
= primaryToolbarButtons.get(buttonName)
|| secondaryToolbarButtons.get(buttonName);
dispatch(setToolbarButton(buttonName, { dispatch(setToolbarButton(buttonName, {
toggled: !button.toggled toggled: !button.toggled

View File

@ -1,5 +1,7 @@
/* @flow */ /* @flow */
import { compose } from 'redux';
import Recording from '../../../modules/UI/recording/Recording'; import Recording from '../../../modules/UI/recording/Recording';
import SideContainerToggler import SideContainerToggler
from '../../../modules/UI/side_pannels/SideContainerToggler'; from '../../../modules/UI/side_pannels/SideContainerToggler';
@ -7,14 +9,17 @@ import UIEvents from '../../../service/UI/UIEvents';
import UIUtil from '../../../modules/UI/util/UIUtil'; import UIUtil from '../../../modules/UI/util/UIUtil';
import { import {
changeLocalRaiseHand,
clearToolboxTimeout, clearToolboxTimeout,
setSubjectSlideIn, setSubjectSlideIn,
setToolbarButton, setToolbarButton,
setToolboxTimeout, setToolboxTimeout,
setToolboxTimeoutMS, setToolboxTimeoutMS,
setToolboxVisible, setToolboxVisible,
toggleFullScreen,
toggleToolbarButton toggleToolbarButton
} from './actions.native'; } from './actions.native';
import { SET_DEFAULT_TOOLBOX_BUTTONS } from './actionTypes'; import { SET_DEFAULT_TOOLBOX_BUTTONS } from './actionTypes';
import { getDefaultToolboxButtons } from './functions'; import { getDefaultToolboxButtons } from './functions';
@ -74,6 +79,87 @@ export function dockToolbox(dock: boolean): Function {
}; };
} }
/**
* Returns button on mount/unmount handlers with dispatch function stored in
* closure.
*
* @param {Function} dispatch - Redux action dispatcher.
* @param {Function} getState - The function fetching the Redux state.
* @returns {Object} Button on mount/unmount handlers.
* @private
*/
function _getButtonHandlers(dispatch, getState) {
const { isGuest } = getState()['features/jwt'];
const localRaiseHandHandler = compose(dispatch, changeLocalRaiseHand);
const toggleFullScreenHandler = compose(dispatch, toggleFullScreen);
return {
/**
* Mount handler for desktop button.
*
* @type {Object}
*/
desktop: {
onMount: () => dispatch(showDesktopSharingButton())
},
/**
* Mount/Unmount handler for toggling fullscreen button.
*
* @type {Object}
*/
fullscreen: {
onMount: () =>
APP.UI.addListener(
UIEvents.FULLSCREEN_TOGGLED,
toggleFullScreenHandler),
onUnmount: () =>
APP.UI.removeListener(
UIEvents.FULLSCREEN_TOGGLED,
toggleFullScreenHandler)
},
/**
* Mount handler for profile button.
*
* @type {Object}
*/
profile: {
onMount: () =>
isGuest
|| dispatch(setProfileButtonUnclickable(true))
},
/**
* Mount/Unmount handlers for raisehand button.
*
* @type {button}
*/
raisehand: {
onMount: () =>
APP.UI.addListener(
UIEvents.LOCAL_RAISE_HAND_CHANGED,
localRaiseHandHandler),
onUnmount: () =>
APP.UI.removeListener(
UIEvents.LOCAL_RAISE_HAND_CHANGED,
localRaiseHandHandler)
},
/**
* Mount handler for recording button.
*
* @type {Object}
*/
recording: {
onMount: () =>
config.enableRecording
&& dispatch(showRecordingButton())
}
};
}
/** /**
* Hides the toolbox. * Hides the toolbox.
* *
@ -114,16 +200,18 @@ export function hideToolbox(force: boolean = false): Function {
/** /**
* Sets the default toolbar buttons of the Toolbox. * Sets the default toolbar buttons of the Toolbox.
* *
* @returns {{ * @returns {Function}
* type: SET_DEFAULT_TOOLBOX_BUTTONS,
* primaryToolbarButtons: Map,
* secondaryToolbarButtons: Map
* }}
*/ */
export function setDefaultToolboxButtons(): Object { export function setDefaultToolboxButtons(): Function {
return { return (dispatch: Dispatch, getState: Function) => {
// Save dispatch function in closure.
const buttonHandlers = _getButtonHandlers(dispatch, getState);
const toolboxButtons = getDefaultToolboxButtons(buttonHandlers);
dispatch({
type: SET_DEFAULT_TOOLBOX_BUTTONS, type: SET_DEFAULT_TOOLBOX_BUTTONS,
...getDefaultToolboxButtons() ...toolboxButtons
});
}; };
} }

View File

@ -3,13 +3,9 @@
import React, { Component } from 'react'; import React, { Component } from 'react';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import UIEvents from '../../../../service/UI/UIEvents';
import { showDesktopSharingButton, toggleFullScreen } from '../actions';
import { getToolbarClassNames } from '../functions'; import { getToolbarClassNames } from '../functions';
import Toolbar from './Toolbar'; import Toolbar from './Toolbar';
declare var APP: Object;
declare var interfaceConfig: Object; declare var interfaceConfig: Object;
/** /**
@ -20,15 +16,6 @@ declare var interfaceConfig: Object;
*/ */
class PrimaryToolbar extends Component { class PrimaryToolbar extends Component {
static propTypes = { static propTypes = {
/**
* Handler for toggling fullscreen mode.
*/
_onFullScreenToggled: React.PropTypes.func,
/**
* Handler for showing desktop sharing button.
*/
_onShowDesktopSharingButton: React.PropTypes.func,
/** /**
* Contains toolbar buttons for primary toolbar. * Contains toolbar buttons for primary toolbar.
@ -51,43 +38,10 @@ class PrimaryToolbar extends Component {
constructor(props) { constructor(props) {
super(props); super(props);
const buttonHandlers = {
/**
* Mount handler for desktop button.
*
* @type {Object}
*/
desktop: {
onMount: () => this.props._onShowDesktopSharingButton()
},
/**
* Mount/Unmount handler for toggling fullscreen button.
*
* @type {Object}
*/
fullscreen: {
onMount: () =>
APP.UI.addListener(
UIEvents.FULLSCREEN_TOGGLED,
this.props._onFullScreenToggled),
onUnmount: () =>
APP.UI.removeListener(
UIEvents.FULLSCREEN_TOGGLED,
this.props._onFullScreenToggled)
}
};
const splitterIndex = interfaceConfig.MAIN_TOOLBAR_SPLITTER_INDEX; const splitterIndex = interfaceConfig.MAIN_TOOLBAR_SPLITTER_INDEX;
this.state = { this.state = {
/**
* Object containing on mount/unmount handlers for toolbar buttons.
*
* @type {Object}
*/
buttonHandlers,
/** /**
* If deployment supports toolbar splitter this value contains its * If deployment supports toolbar splitter this value contains its
* index. * index.
@ -113,14 +67,13 @@ class PrimaryToolbar extends Component {
return null; return null;
} }
const { buttonHandlers, splitterIndex } = this.state; const { splitterIndex } = this.state;
const { primaryToolbarClassName } = getToolbarClassNames(this.props); const { primaryToolbarClassName } = getToolbarClassNames(this.props);
const tooltipPosition const tooltipPosition
= interfaceConfig.filmStripOnly ? 'left' : 'bottom'; = interfaceConfig.filmStripOnly ? 'left' : 'bottom';
return ( return (
<Toolbar <Toolbar
buttonHandlers = { buttonHandlers }
className = { primaryToolbarClassName } className = { primaryToolbarClassName }
splitterIndex = { splitterIndex } splitterIndex = { splitterIndex }
toolbarButtons = { _primaryToolbarButtons } toolbarButtons = { _primaryToolbarButtons }
@ -129,39 +82,6 @@ class PrimaryToolbar extends Component {
} }
} }
/**
* Maps some of the Redux actions to the component props.
*
* @param {Function} dispatch - Redux action dispatcher.
* @returns {{
* _onShowDesktopSharingButton: Function
* }}
* @private
*/
function _mapDispatchToProps(dispatch: Function): Object {
return {
/**
* Dispatches an action signalling that full screen mode is toggled.
*
* @param {boolean} isFullScreen - Show whether fullscreen mode is on.
* @returns {Object} Dispatched action.
*/
_onFullScreenToggled(isFullScreen: boolean) {
return dispatch(toggleFullScreen(isFullScreen));
},
/**
* Dispatches an action signalling that desktop sharing button
* should be shown.
*
* @returns {Object} Dispatched action.
*/
_onShowDesktopSharingButton() {
dispatch(showDesktopSharingButton());
}
};
}
/** /**
* Maps part of Redux store to React component props. * Maps part of Redux store to React component props.
* *
@ -197,4 +117,4 @@ function _mapStateToProps(state: Object): Object {
}; };
} }
export default connect(_mapStateToProps, _mapDispatchToProps)(PrimaryToolbar); export default connect(_mapStateToProps)(PrimaryToolbar);

View File

@ -7,16 +7,12 @@ import { FeedbackButton } from '../../feedback';
import UIEvents from '../../../../service/UI/UIEvents'; import UIEvents from '../../../../service/UI/UIEvents';
import { import {
changeLocalRaiseHand,
setProfileButtonUnclickable,
showRecordingButton,
toggleSideToolbarContainer toggleSideToolbarContainer
} from '../actions'; } from '../actions';
import { getToolbarClassNames } from '../functions'; import { getToolbarClassNames } from '../functions';
import Toolbar from './Toolbar'; import Toolbar from './Toolbar';
declare var APP: Object; declare var APP: Object;
declare var config: Object;
/** /**
* Implementation of secondary toolbar React component. * Implementation of secondary toolbar React component.
@ -39,21 +35,6 @@ class SecondaryToolbar extends Component {
*/ */
_isGuest: React.PropTypes.bool, _isGuest: React.PropTypes.bool,
/**
* Handler dispatching local "Raise hand".
*/
_onLocalRaiseHandChanged: React.PropTypes.func,
/**
* Handler setting profile button unclickable.
*/
_onSetProfileButtonUnclickable: React.PropTypes.func,
/**
* Handler for showing recording button.
*/
_onShowRecordingButton: React.PropTypes.func,
/** /**
* Handler dispatching toggle toolbar container. * Handler dispatching toggle toolbar container.
*/ */
@ -70,69 +51,6 @@ class SecondaryToolbar extends Component {
_visible: React.PropTypes.bool _visible: React.PropTypes.bool
}; };
/**
* Constructs instance of SecondaryToolbar component.
*
* @param {Object} props - React component properties.
*/
constructor(props) {
super(props);
const buttonHandlers = {
/**
* Mount handler for profile button.
*
* @type {Object}
*/
profile: {
onMount: () => {
const {
_isGuest,
_onSetProfileButtonUnclickable
} = this.props;
_isGuest || _onSetProfileButtonUnclickable(true);
}
},
/**
* Mount/Unmount handlers for raisehand button.
*
* @type {button}
*/
raisehand: {
onMount: () =>
APP.UI.addListener(
UIEvents.LOCAL_RAISE_HAND_CHANGED,
this.props._onLocalRaiseHandChanged),
onUnmount: () =>
APP.UI.removeListener(
UIEvents.LOCAL_RAISE_HAND_CHANGED,
this.props._onLocalRaiseHandChanged)
},
/**
* Mount handler for recording button.
*
* @type {Object}
*/
recording: {
onMount: () =>
config.enableRecording
&& this.props._onShowRecordingButton()
}
};
this.state = {
/**
* Object containing on mount/unmount handlers for toolbar buttons.
*
* @type {Object}
*/
buttonHandlers
};
}
/** /**
* Register legacy UI listener. * Register legacy UI listener.
* *
@ -170,12 +88,10 @@ class SecondaryToolbar extends Component {
return null; return null;
} }
const { buttonHandlers } = this.state;
const { secondaryToolbarClassName } = getToolbarClassNames(this.props); const { secondaryToolbarClassName } = getToolbarClassNames(this.props);
return ( return (
<Toolbar <Toolbar
buttonHandlers = { buttonHandlers }
className = { secondaryToolbarClassName } className = { secondaryToolbarClassName }
toolbarButtons = { _secondaryToolbarButtons } toolbarButtons = { _secondaryToolbarButtons }
tooltipPosition = { 'right' }> tooltipPosition = { 'right' }>
@ -190,45 +106,12 @@ class SecondaryToolbar extends Component {
* *
* @param {Function} dispatch - Redux action dispatcher. * @param {Function} dispatch - Redux action dispatcher.
* @returns {{ * @returns {{
* _onLocalRaiseHandChanged: Function,
* _onSetProfileButtonUnclickable: Function,
* _onShowRecordingButton: Function,
* _onSideToolbarContainerToggled * _onSideToolbarContainerToggled
* }} * }}
* @private * @private
*/ */
function _mapDispatchToProps(dispatch: Function): Object { function _mapDispatchToProps(dispatch: Function): Object {
return { return {
/**
* Dispatches an action that 'hand' is raised.
*
* @param {boolean} isRaisedHand - Show whether hand is raised.
* @returns {Object} Dispatched action.
*/
_onLocalRaiseHandChanged(isRaisedHand: boolean) {
return dispatch(changeLocalRaiseHand(isRaisedHand));
},
/**
* Dispatches an action signalling to set profile button unclickable.
*
* @param {boolean} unclickable - Flag showing whether unclickable
* property is true.
* @returns {Object} Dispatched action.
*/
_onSetProfileButtonUnclickable(unclickable: boolean) {
return dispatch(setProfileButtonUnclickable(unclickable));
},
/**
* Dispatches an action signalling that recording button should be
* shown.
*
* @returns {Object} Dispatched action.
*/
_onShowRecordingButton() {
return dispatch(showRecordingButton());
},
/** /**
* Dispatches an action signalling that side toolbar container is * Dispatches an action signalling that side toolbar container is

View File

@ -36,11 +36,6 @@ class Toolbar extends Component {
*/ */
_onMouseOver: React.PropTypes.func, _onMouseOver: React.PropTypes.func,
/**
* Contains button handlers.
*/
buttonHandlers: React.PropTypes.object,
/** /**
* Children of current React component. * Children of current React component.
*/ */
@ -77,8 +72,6 @@ class Toolbar extends Component {
constructor(props) { constructor(props) {
super(props); super(props);
this._setButtonHandlers();
// Bind callbacks to preverse this. // Bind callbacks to preverse this.
this._renderToolbarButton = this._renderToolbarButton.bind(this); this._renderToolbarButton = this._renderToolbarButton.bind(this);
} }
@ -154,35 +147,6 @@ class Toolbar extends Component {
return acc; return acc;
} }
/**
* Sets handlers for some of the buttons.
*
* @private
* @returns {void}
*/
_setButtonHandlers(): void {
const {
buttonHandlers,
toolbarButtons
} = this.props;
// Only a few buttons have buttonHandlers defined, so it may be
// undefined or empty depending on the buttons rendered.
// TODO Merge the buttonHandlers and onClick properties and come up with
// a consistent event handling property.
buttonHandlers && Object.keys(buttonHandlers).forEach(key => {
let button = toolbarButtons.get(key);
if (button) {
button = {
...button,
...buttonHandlers[key]
};
toolbarButtons.set(key, button);
}
});
}
} }
/** /**

View File

@ -13,6 +13,21 @@ export { abstractMapStateToProps } from './functions.native';
/* eslint-disable flowtype/space-before-type-colon */ /* eslint-disable flowtype/space-before-type-colon */
/**
* Returns the button object corresponding to the given buttonName.
*
* @param {string} buttonName - The name of the button.
* @param {Object} state - The current state.
* @returns {Object} - The button object.
*/
export function getButton(buttonName: string, state: Object) {
const { primaryToolbarButtons, secondaryToolbarButtons }
= state['features/toolbox'];
return primaryToolbarButtons.get(buttonName)
|| secondaryToolbarButtons.get(buttonName);
}
/** /**
* Takes toolbar button props and maps them to HTML attributes to set. * Takes toolbar button props and maps them to HTML attributes to set.
* *
@ -52,9 +67,11 @@ export function getButtonAttributesByProps(props: Object = {})
* Returns an object which contains the default buttons for the primary and * Returns an object which contains the default buttons for the primary and
* secondary toolbars. * secondary toolbars.
* *
* @param {Object} buttonHandlers - Contains additional toolbox button
* handlers.
* @returns {Object} * @returns {Object}
*/ */
export function getDefaultToolboxButtons(): Object { export function getDefaultToolboxButtons(buttonHandlers: Object): Object {
let toolbarButtons = { let toolbarButtons = {
primaryToolbarButtons: new Map(), primaryToolbarButtons: new Map(),
secondaryToolbarButtons: new Map() secondaryToolbarButtons: new Map()
@ -67,13 +84,21 @@ export function getDefaultToolboxButtons(): Object {
toolbarButtons toolbarButtons
= interfaceConfig.TOOLBAR_BUTTONS.reduce( = interfaceConfig.TOOLBAR_BUTTONS.reduce(
(acc, buttonName) => { (acc, buttonName) => {
const button = defaultToolbarButtons[buttonName]; let button = defaultToolbarButtons[buttonName];
const currentButtonHandlers = buttonHandlers[buttonName];
if (button) { if (button) {
const place = _getToolbarButtonPlace(buttonName); const place = _getToolbarButtonPlace(buttonName);
button.buttonName = buttonName; button.buttonName = buttonName;
if (currentButtonHandlers) {
button = {
...button,
...currentButtonHandlers
};
}
// In filmstrip-only mode we only add a button if it's // In filmstrip-only mode we only add a button if it's
// filmstrip-only enabled. // filmstrip-only enabled.
if (!filmStripOnly || button.filmstripOnlyEnabled) { if (!filmStripOnly || button.filmstripOnlyEnabled) {
@ -89,21 +114,6 @@ export function getDefaultToolboxButtons(): Object {
return toolbarButtons; return toolbarButtons;
} }
/**
* Get place for toolbar button. Now it can be in the primary Toolbar or in the
* secondary Toolbar.
*
* @param {string} btn - Button name.
* @private
* @returns {string}
*/
function _getToolbarButtonPlace(btn) {
return (
interfaceConfig.MAIN_TOOLBAR_BUTTONS.includes(btn)
? 'primaryToolbarButtons'
: 'secondaryToolbarButtons');
}
/** /**
* Returns toolbar class names to add while rendering. * Returns toolbar class names to add while rendering.
* *
@ -171,3 +181,18 @@ export function showCustomToolbarPopup(
AJS.$(popupSelectorID).tooltip('hide'); AJS.$(popupSelectorID).tooltip('hide');
} }
} }
/**
* Get place for toolbar button. Now it can be in the primary Toolbar or in the
* secondary Toolbar.
*
* @param {string} btn - Button name.
* @private
* @returns {string}
*/
function _getToolbarButtonPlace(btn) {
return (
interfaceConfig.MAIN_TOOLBAR_BUTTONS.includes(btn)
? 'primaryToolbarButtons'
: 'secondaryToolbarButtons');
}