feat(toolbox): axe the old toolbox (#2731)
This PR takes The Bulldozer Approach (R): removes the old toolbox and lots of associated code, though not all of it. Subsequent cleanups will follow.
This commit is contained in:
parent
0cd32c8155
commit
b73b51f1f4
|
@ -106,10 +106,7 @@ import {
|
|||
suspendDetected
|
||||
} from './react/features/overlay';
|
||||
import { setSharedVideoStatus } from './react/features/shared-video';
|
||||
import {
|
||||
isButtonEnabled,
|
||||
showDesktopSharingButton
|
||||
} from './react/features/toolbox';
|
||||
import { isButtonEnabled } from './react/features/toolbox';
|
||||
|
||||
const logger = require('jitsi-meet-logger').getLogger(__filename);
|
||||
|
||||
|
@ -768,7 +765,6 @@ export default {
|
|||
|
||||
APP.store.dispatch(
|
||||
setDesktopSharingEnabled(this.isDesktopSharingEnabled));
|
||||
APP.store.dispatch(showDesktopSharingButton());
|
||||
|
||||
this._createRoom(tracks);
|
||||
APP.remoteControl.init();
|
||||
|
@ -1359,7 +1355,6 @@ export default {
|
|||
this.isSharingScreen = newStream && newStream.videoType === 'desktop';
|
||||
|
||||
if (wasSharingScreen !== this.isSharingScreen) {
|
||||
APP.UI.updateDesktopSharingButtons();
|
||||
APP.API.notifyScreenSharingStatusChanged(this.isSharingScreen);
|
||||
}
|
||||
},
|
||||
|
|
|
@ -153,19 +153,7 @@ var interfaceConfig = {
|
|||
*
|
||||
* @type {boolean}
|
||||
*/
|
||||
VIDEO_QUALITY_LABEL_DISABLED: false,
|
||||
|
||||
/**
|
||||
* This is a temporary feature flag used to gate access to the toolbox so it
|
||||
* can be developed through smaller changesets and set to false if bad bugs
|
||||
* are found. This feature flag will be removed at some point, as well as
|
||||
* the old toolbox. This new toolbox will be horizontal and the previous
|
||||
* feature of supporting menu button ordering through interfaceConfig will
|
||||
* be removed. Support for configuring which buttons display will remain.
|
||||
*
|
||||
* @type {boolean}
|
||||
*/
|
||||
_USE_NEW_TOOLBOX: true
|
||||
VIDEO_QUALITY_LABEL_DISABLED: false
|
||||
|
||||
/**
|
||||
* Specify custom URL for downloading android mobile app.
|
||||
|
|
|
@ -36,14 +36,7 @@ import {
|
|||
showWarningNotification
|
||||
} from '../../react/features/notifications';
|
||||
import {
|
||||
checkAutoEnableDesktopSharing,
|
||||
clearButtonPopup,
|
||||
dockToolbox,
|
||||
setButtonPopupTimeout,
|
||||
setToolbarButton,
|
||||
showDialPadButton,
|
||||
showEtherpadButton,
|
||||
showSharedVideoButton,
|
||||
showToolbox
|
||||
} from '../../react/features/toolbox';
|
||||
|
||||
|
@ -278,7 +271,7 @@ UI.setLocalRaisedHandStatus
|
|||
* Initialize conference UI.
|
||||
*/
|
||||
UI.initConference = function() {
|
||||
const { dispatch, getState } = APP.store;
|
||||
const { getState } = APP.store;
|
||||
const { email, id, name } = getLocalParticipant(getState);
|
||||
|
||||
// Update default button states before showing the toolbar
|
||||
|
@ -298,8 +291,6 @@ UI.initConference = function() {
|
|||
UI.setUserEmail(id, email);
|
||||
}
|
||||
|
||||
dispatch(checkAutoEnableDesktopSharing());
|
||||
|
||||
// FollowMe attempts to copy certain aspects of the moderator's UI into the
|
||||
// other participants' UI. Consequently, it needs (1) read and write access
|
||||
// to the UI (depending on the moderator role of the local participant) and
|
||||
|
@ -372,11 +363,10 @@ UI.start = function() {
|
|||
$('body').addClass('vertical-filmstrip');
|
||||
}
|
||||
|
||||
|
||||
// TODO: remove this class once the old toolbar has been removed. This class
|
||||
// is set so that any CSS changes needed to adjust elements outside of the
|
||||
// new toolbar can be scoped to just the app with the new toolbar enabled.
|
||||
if (interfaceConfig._USE_NEW_TOOLBOX && !interfaceConfig.filmStripOnly) {
|
||||
if (!interfaceConfig.filmStripOnly) {
|
||||
$('body').addClass('use-new-toolbox');
|
||||
}
|
||||
|
||||
|
@ -476,7 +466,6 @@ UI.initEtherpad = name => {
|
|||
= new EtherpadManager(config.etherpad_base, name, eventEmitter);
|
||||
|
||||
APP.store.dispatch(setEtherpadHasInitialzied());
|
||||
APP.store.dispatch(showEtherpadButton());
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -544,8 +533,6 @@ UI.onPeerVideoTypeChanged
|
|||
UI.updateLocalRole = isModerator => {
|
||||
VideoLayout.showModeratorIndicator();
|
||||
|
||||
APP.store.dispatch(showSharedVideoButton());
|
||||
|
||||
Recording.showRecordingButton(isModerator);
|
||||
|
||||
if (isModerator) {
|
||||
|
@ -672,14 +659,9 @@ UI.inputDisplayNameHandler = function(newDisplayName) {
|
|||
* @param {number} timeout - The time to show the popup
|
||||
* @returns {void}
|
||||
*/
|
||||
// eslint-disable-next-line max-params
|
||||
// eslint-disable-next-line max-params, no-unused-vars
|
||||
UI.showCustomToolbarPopup = function(buttonName, popupID, show, timeout) {
|
||||
const action
|
||||
= show
|
||||
? setButtonPopupTimeout(buttonName, popupID, timeout)
|
||||
: clearButtonPopup(buttonName);
|
||||
|
||||
APP.store.dispatch(action);
|
||||
// TODO: this is no longer implemented as of Toolbox v2. Remove?
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -904,17 +886,6 @@ UI.promptDisplayName = () => {
|
|||
*/
|
||||
UI.setAudioLevel = (id, lvl) => VideoLayout.setAudioLevel(id, lvl);
|
||||
|
||||
/**
|
||||
* Update state of desktop sharing buttons.
|
||||
*
|
||||
* @returns {void}
|
||||
*/
|
||||
UI.updateDesktopSharingButtons
|
||||
= () =>
|
||||
APP.store.dispatch(setToolbarButton('desktop', {
|
||||
toggled: APP.conference.isSharingScreen
|
||||
}));
|
||||
|
||||
/**
|
||||
* Hide connection quality statistics from UI.
|
||||
*/
|
||||
|
@ -946,8 +917,9 @@ UI.addMessage = function(from, displayName, message, stamp) {
|
|||
Chat.updateChatConversation(from, displayName, message, stamp);
|
||||
};
|
||||
|
||||
UI.updateDTMFSupport
|
||||
= isDTMFSupported => APP.store.dispatch(showDialPadButton(isDTMFSupported));
|
||||
// TODO: With Toolbox v2 this got scrapped. Remove?
|
||||
// eslint-disable-next-line no-empty-function
|
||||
UI.updateDTMFSupport = () => { };
|
||||
|
||||
UI.updateRecordingState = function(state) {
|
||||
Recording.updateRecordingState(state);
|
||||
|
|
|
@ -129,8 +129,7 @@ class Etherpad extends LargeContainer {
|
|||
let height, width;
|
||||
|
||||
if (interfaceConfig.VERTICAL_FILMSTRIP) {
|
||||
height = interfaceConfig._USE_NEW_TOOLBOX
|
||||
? containerHeight - getToolboxHeight() : containerHeight;
|
||||
height = containerHeight - getToolboxHeight();
|
||||
width = containerWidth - Filmstrip.getFilmstripWidth();
|
||||
} else {
|
||||
height = containerHeight - Filmstrip.getFilmstripHeight();
|
||||
|
|
|
@ -699,8 +699,7 @@ class SharedVideoContainer extends LargeContainer {
|
|||
let height, width;
|
||||
|
||||
if (interfaceConfig.VERTICAL_FILMSTRIP) {
|
||||
height = interfaceConfig._USE_NEW_TOOLBOX
|
||||
? containerHeight - getToolboxHeight() : containerHeight;
|
||||
height = containerHeight - getToolboxHeight();
|
||||
width = containerWidth - Filmstrip.getFilmstripWidth();
|
||||
} else {
|
||||
height = containerHeight - Filmstrip.getFilmstripHeight();
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/* global APP, $, interfaceConfig */
|
||||
/* global APP, $ */
|
||||
|
||||
import { processReplacements, linkify } from './Replacement';
|
||||
import CommandsProcessor from './Commands';
|
||||
|
@ -181,15 +181,11 @@ function resizeChatConversation() {
|
|||
$('#smileysContainer').css('bottom', msgareaHeight);
|
||||
chat.width(width - 10);
|
||||
|
||||
if (interfaceConfig._USE_NEW_TOOLBOX) {
|
||||
const maybeAMagicNumberForPaddingAndMargin = 100;
|
||||
const offset = maybeAMagicNumberForPaddingAndMargin
|
||||
+ msgareaHeight + getToolboxHeight();
|
||||
const maybeAMagicNumberForPaddingAndMargin = 100;
|
||||
const offset = maybeAMagicNumberForPaddingAndMargin
|
||||
+ msgareaHeight + getToolboxHeight();
|
||||
|
||||
chat.height(window.innerHeight - offset);
|
||||
} else {
|
||||
chat.height(window.innerHeight - 15 - msgareaHeight);
|
||||
}
|
||||
chat.height(window.innerHeight - offset);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -14,7 +14,6 @@ import { NotificationsContainer } from '../../notifications';
|
|||
import { SidePanel } from '../../side-panel';
|
||||
import {
|
||||
Toolbox,
|
||||
ToolboxV2,
|
||||
fullScreenChanged,
|
||||
setToolboxAlwaysVisible,
|
||||
showToolbox
|
||||
|
@ -139,7 +138,6 @@ class Conference extends Component<Props> {
|
|||
*/
|
||||
render() {
|
||||
const {
|
||||
_USE_NEW_TOOLBOX,
|
||||
VIDEO_QUALITY_LABEL_DISABLED,
|
||||
filmStripOnly
|
||||
} = interfaceConfig;
|
||||
|
@ -148,16 +146,6 @@ class Conference extends Component<Props> {
|
|||
|| VIDEO_QUALITY_LABEL_DISABLED
|
||||
|| this.props._iAmRecorder;
|
||||
|
||||
let ToolboxToUse;
|
||||
|
||||
if (filmStripOnly) {
|
||||
ToolboxToUse = null;
|
||||
} else if (interfaceConfig._USE_NEW_TOOLBOX) {
|
||||
ToolboxToUse = ToolboxV2;
|
||||
} else {
|
||||
ToolboxToUse = Toolbox;
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
id = 'videoconference_page'
|
||||
|
@ -168,10 +156,8 @@ class Conference extends Component<Props> {
|
|||
<Filmstrip filmstripOnly = { filmStripOnly } />
|
||||
</div>
|
||||
|
||||
{ ToolboxToUse && <ToolboxToUse /> }
|
||||
|
||||
{ _USE_NEW_TOOLBOX && !filmStripOnly
|
||||
&& <SidePanel /> }
|
||||
{ !filmStripOnly && <Toolbox /> }
|
||||
{ !filmStripOnly && <SidePanel /> }
|
||||
|
||||
<DialogContainer />
|
||||
<NotificationsContainer />
|
||||
|
|
|
@ -5,9 +5,7 @@ import PropTypes from 'prop-types';
|
|||
import React, { Component } from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
|
||||
import { getLocalParticipant, PARTICIPANT_ROLE } from '../../base/participants';
|
||||
import { InviteButton } from '../../invite';
|
||||
import { Toolbox, ToolboxFilmstrip, dockToolbox } from '../../toolbox';
|
||||
import { ToolboxFilmstrip, dockToolbox } from '../../toolbox';
|
||||
|
||||
import { setFilmstripHovered } from '../actions';
|
||||
import { shouldRemoteVideosBeVisible } from '../functions';
|
||||
|
@ -35,29 +33,11 @@ class Filmstrip extends Component<*> {
|
|||
* @static
|
||||
*/
|
||||
static propTypes = {
|
||||
/**
|
||||
* Whether invite button rendering should be skipped,
|
||||
* by default it is. false
|
||||
*/
|
||||
_hideInviteButton: PropTypes.bool,
|
||||
|
||||
/**
|
||||
* Whether or not remote videos are currently being hovered over.
|
||||
*/
|
||||
_hovered: PropTypes.bool,
|
||||
|
||||
/**
|
||||
* Whether or not the feature to directly invite people into the
|
||||
* conference is available.
|
||||
*/
|
||||
_isAddToCallAvailable: PropTypes.bool,
|
||||
|
||||
/**
|
||||
* Whether or not the feature to dial out to number to join the
|
||||
* conference is available.
|
||||
*/
|
||||
_isDialOutAvailable: PropTypes.bool,
|
||||
|
||||
/**
|
||||
* Whether or not the remote videos should be visible. Will toggle
|
||||
* a class for hiding the videos.
|
||||
|
@ -115,9 +95,6 @@ class Filmstrip extends Component<*> {
|
|||
*/
|
||||
render() {
|
||||
const {
|
||||
_hideInviteButton,
|
||||
_isAddToCallAvailable,
|
||||
_isDialOutAvailable,
|
||||
_remoteVideosVisible,
|
||||
_toolboxVisible,
|
||||
filmstripOnly
|
||||
|
@ -136,12 +113,9 @@ class Filmstrip extends Component<*> {
|
|||
const filmstripClassNames = `filmstrip ${_remoteVideosVisible
|
||||
? '' : 'hide-videos'} ${reduceHeight ? 'reduce-height' : ''}`;
|
||||
|
||||
const ToolboxToUse = interfaceConfig._USE_NEW_TOOLBOX
|
||||
? ToolboxFilmstrip : Toolbox;
|
||||
|
||||
return (
|
||||
<div className = { filmstripClassNames }>
|
||||
{ filmstripOnly ? <ToolboxToUse /> : null }
|
||||
{ filmstripOnly && <ToolboxFilmstrip /> }
|
||||
<div
|
||||
className = 'filmstrip__videos'
|
||||
id = 'remoteVideos'>
|
||||
|
@ -150,11 +124,6 @@ class Filmstrip extends Component<*> {
|
|||
id = 'filmstripLocalVideo'
|
||||
onMouseOut = { this._onMouseOut }
|
||||
onMouseOver = { this._onMouseOver }>
|
||||
{ filmstripOnly || _hideInviteButton
|
||||
? null
|
||||
: <InviteButton
|
||||
enableAddPeople = { _isAddToCallAvailable }
|
||||
enableDialOut = { _isDialOutAvailable } /> }
|
||||
<div id = 'filmstripLocalVideoThumbnail' />
|
||||
</div>
|
||||
<div
|
||||
|
@ -185,9 +154,7 @@ class Filmstrip extends Component<*> {
|
|||
*/
|
||||
_notifyOfHoveredStateUpdate() {
|
||||
if (this.props._hovered !== this._isHovered) {
|
||||
if (interfaceConfig._USE_NEW_TOOLBOX) {
|
||||
this.props.dispatch(dockToolbox(this._isHovered));
|
||||
}
|
||||
this.props.dispatch(dockToolbox(this._isHovered));
|
||||
this.props.dispatch(setFilmstripHovered(this._isHovered));
|
||||
}
|
||||
}
|
||||
|
@ -223,36 +190,16 @@ class Filmstrip extends Component<*> {
|
|||
* @param {Object} state - The Redux state.
|
||||
* @private
|
||||
* @returns {{
|
||||
* _hideInviteButton: boolean,
|
||||
* _hovered: boolean,
|
||||
* _isAddToCallAvailable: boolean,
|
||||
* _isDialOutAvailable: boolean,
|
||||
* _remoteVideosVisible: boolean,
|
||||
* _toolboxVisible: boolean
|
||||
* }}
|
||||
*/
|
||||
function _mapStateToProps(state) {
|
||||
const { conference } = state['features/base/conference'];
|
||||
const {
|
||||
enableUserRolesBasedOnToken,
|
||||
iAmRecorder
|
||||
} = state['features/base/config'];
|
||||
const { isGuest } = state['features/base/jwt'];
|
||||
const { hovered } = state['features/filmstrip'];
|
||||
|
||||
const isAddToCallAvailable = !isGuest;
|
||||
const isDialOutAvailable
|
||||
= getLocalParticipant(state).role === PARTICIPANT_ROLE.MODERATOR
|
||||
&& conference && conference.isSIPCallingSupported()
|
||||
&& (!enableUserRolesBasedOnToken || !isGuest);
|
||||
|
||||
return {
|
||||
_hideInviteButton: iAmRecorder
|
||||
|| (!isAddToCallAvailable && !isDialOutAvailable)
|
||||
|| interfaceConfig._USE_NEW_TOOLBOX,
|
||||
_hovered: hovered,
|
||||
_isAddToCallAvailable: isAddToCallAvailable,
|
||||
_isDialOutAvailable: isDialOutAvailable,
|
||||
_remoteVideosVisible: shouldRemoteVideosBeVisible(state),
|
||||
_toolboxVisible: state['features/toolbox'].visible
|
||||
};
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
/* global interfaceConfig */
|
||||
|
||||
import InlineDialog from '@atlaskit/inline-dialog';
|
||||
import PropTypes from 'prop-types';
|
||||
import React, { Component } from 'react';
|
||||
|
@ -8,30 +6,12 @@ import { connect } from 'react-redux';
|
|||
import { createToolbarEvent, sendAnalytics } from '../../analytics';
|
||||
import { translate } from '../../base/i18n';
|
||||
import { getParticipantCount } from '../../base/participants';
|
||||
import {
|
||||
ToolbarButton,
|
||||
ToolbarButtonV2,
|
||||
TOOLTIP_TO_POPUP_POSITION
|
||||
} from '../../toolbox';
|
||||
import { ToolbarButtonV2 } from '../../toolbox';
|
||||
|
||||
import { updateDialInNumbers } from '../actions';
|
||||
|
||||
import { InfoDialog } from './info-dialog';
|
||||
|
||||
/**
|
||||
* A configuration object to describe how {@code ToolbarButton} should render
|
||||
* the button.
|
||||
*
|
||||
* @type {object}
|
||||
*/
|
||||
const DEFAULT_BUTTON_CONFIGURATION = {
|
||||
buttonName: 'info',
|
||||
classNames: [ 'button', 'icon-info' ],
|
||||
enabled: true,
|
||||
id: 'toolbar_button_info',
|
||||
tooltipKey: 'info.tooltip'
|
||||
};
|
||||
|
||||
/**
|
||||
* The amount of time, in milliseconds, to wait until automatically showing
|
||||
* the {@code InfoDialog}. This is essentially a hack as automatic showing
|
||||
|
@ -170,9 +150,26 @@ class InfoDialogButton extends Component {
|
|||
* @returns {ReactElement}
|
||||
*/
|
||||
render() {
|
||||
return interfaceConfig._USE_NEW_TOOLBOX
|
||||
? this._renderNewToolbarButton()
|
||||
: this._renderOldToolbarButton();
|
||||
const { t } = this.props;
|
||||
const { showDialog } = this.state;
|
||||
const iconClass = `icon-info ${showDialog ? 'toggled' : ''}`;
|
||||
|
||||
return (
|
||||
<div className = 'toolbox-button-wth-dialog'>
|
||||
<InlineDialog
|
||||
content = {
|
||||
<InfoDialog onClose = { this._onDialogClose } /> }
|
||||
isOpen = { showDialog }
|
||||
onClose = { this._onDialogClose }
|
||||
position = { 'top right' }>
|
||||
<ToolbarButtonV2
|
||||
accessibilityLabel = 'Info'
|
||||
iconName = { iconClass }
|
||||
onClick = { this._onDialogToggle }
|
||||
tooltip = { t('info.tooltip') } />
|
||||
</InlineDialog>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -209,69 +206,6 @@ class InfoDialogButton extends Component {
|
|||
|
||||
this.setState({ showDialog: !this.state.showDialog });
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders a React Element for the {@code InfoDialog} using legacy
|
||||
* {@code ToolbarButton}.
|
||||
*
|
||||
* @private
|
||||
* @returns {ReactElement}
|
||||
*/
|
||||
_renderOldToolbarButton() {
|
||||
const { tooltipPosition } = this.props;
|
||||
const { showDialog } = this.state;
|
||||
|
||||
const buttonConfiguration = {
|
||||
...DEFAULT_BUTTON_CONFIGURATION,
|
||||
classNames: [
|
||||
...DEFAULT_BUTTON_CONFIGURATION.classNames,
|
||||
showDialog ? 'toggled button-active' : ''
|
||||
]
|
||||
};
|
||||
|
||||
return (
|
||||
<InlineDialog
|
||||
content = { <InfoDialog onClose = { this._onDialogClose } /> }
|
||||
isOpen = { showDialog }
|
||||
onClose = { this._onDialogClose }
|
||||
position = { TOOLTIP_TO_POPUP_POSITION[tooltipPosition] }>
|
||||
<ToolbarButton
|
||||
button = { buttonConfiguration }
|
||||
onClick = { this._onDialogToggle }
|
||||
tooltipPosition = { tooltipPosition } />
|
||||
</InlineDialog>
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders a React Element for the {@code InfoDialog} using the newer
|
||||
* {@code ToolbarButtonV2}.
|
||||
*
|
||||
* @private
|
||||
* @returns {ReactElement}
|
||||
*/
|
||||
_renderNewToolbarButton() {
|
||||
const { t } = this.props;
|
||||
const { showDialog } = this.state;
|
||||
const iconClass = `icon-info ${showDialog ? 'toggled' : ''}`;
|
||||
|
||||
return (
|
||||
<div className = 'toolbox-button-wth-dialog'>
|
||||
<InlineDialog
|
||||
content = {
|
||||
<InfoDialog onClose = { this._onDialogClose } /> }
|
||||
isOpen = { showDialog }
|
||||
onClose = { this._onDialogClose }
|
||||
position = { 'top right' }>
|
||||
<ToolbarButtonV2
|
||||
accessibilityLabel = 'Info'
|
||||
iconName = { iconClass }
|
||||
onClick = { this._onDialogToggle }
|
||||
tooltip = { t('info.tooltip') } />
|
||||
</InlineDialog>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -61,17 +61,6 @@ export const SET_SUBJECT = Symbol('SET_SUBJECT');
|
|||
*/
|
||||
export const SET_SUBJECT_SLIDE_IN = Symbol('SET_SUBJECT_SLIDE_IN');
|
||||
|
||||
/**
|
||||
* The type of the action which sets the descriptor of a toolbar button.
|
||||
*
|
||||
* {
|
||||
* type: SET_TOOLBAR_BUTTON,
|
||||
* button: Object,
|
||||
* key: string
|
||||
* }
|
||||
*/
|
||||
export const SET_TOOLBAR_BUTTON = Symbol('SET_TOOLBAR_BUTTON');
|
||||
|
||||
/**
|
||||
* The type of the action which sets the indicator which determiens whether a
|
||||
* fToolbar in the Toolbox is hovered.
|
||||
|
|
|
@ -1,12 +1,9 @@
|
|||
/* @flow */
|
||||
|
||||
import type { Dispatch } from 'redux-thunk';
|
||||
|
||||
import {
|
||||
CLEAR_TOOLBOX_TIMEOUT,
|
||||
SET_SUBJECT,
|
||||
SET_SUBJECT_SLIDE_IN,
|
||||
SET_TOOLBAR_BUTTON,
|
||||
SET_TOOLBAR_HOVERED,
|
||||
SET_TOOLBOX_ALWAYS_VISIBLE,
|
||||
SET_TOOLBOX_ENABLED,
|
||||
|
@ -15,28 +12,6 @@ import {
|
|||
SET_TOOLBOX_VISIBLE
|
||||
} from './actionTypes';
|
||||
|
||||
/**
|
||||
* FIXME: We should make sure all common functions for native and web are
|
||||
* merged in a global functions file.
|
||||
*/
|
||||
import { getButton } from './functions.native';
|
||||
|
||||
/**
|
||||
* Event handler for local raise hand changed event.
|
||||
*
|
||||
* @param {boolean} handRaised - Flag showing whether hand is raised.
|
||||
* @returns {Function}
|
||||
*/
|
||||
export function changeLocalRaiseHand(handRaised: boolean): Function {
|
||||
return (dispatch: Dispatch<*>, getState: Function) => {
|
||||
const buttonName = 'raisehand';
|
||||
const button = getButton(buttonName, getState());
|
||||
|
||||
button.toggled = handRaised;
|
||||
|
||||
dispatch(setToolbarButton(buttonName, button));
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Signals that toolbox timeout should be cleared.
|
||||
|
@ -81,25 +56,6 @@ export function setSubjectSlideIn(subjectSlideIn: boolean): Object {
|
|||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Signals that value of the button specified by key should be changed.
|
||||
*
|
||||
* @param {string} buttonName - Button key.
|
||||
* @param {Object} button - Button object.
|
||||
* @returns {{
|
||||
* type: SET_TOOLBAR_BUTTON,
|
||||
* button: Object,
|
||||
* buttonName: string
|
||||
* }}
|
||||
*/
|
||||
export function setToolbarButton(buttonName: string, button: Object): Object {
|
||||
return {
|
||||
type: SET_TOOLBAR_BUTTON,
|
||||
button,
|
||||
buttonName
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Signals that toolbar is hovered value should be changed.
|
||||
*
|
||||
|
@ -203,51 +159,3 @@ export function setToolboxVisible(visible: boolean): Object {
|
|||
visible
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows etherpad button if it's not shown.
|
||||
*
|
||||
* @returns {Function}
|
||||
*/
|
||||
export function showEtherpadButton(): Function {
|
||||
return (dispatch: Dispatch<*>) => {
|
||||
dispatch(setToolbarButton('etherpad', {
|
||||
hidden: false
|
||||
}));
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Event handler for full screen toggled event.
|
||||
*
|
||||
* @param {boolean} isFullScreen - Flag showing whether app in full
|
||||
* screen mode.
|
||||
* @returns {Function}
|
||||
*/
|
||||
export function toggleFullScreen(isFullScreen: boolean): Function {
|
||||
return (dispatch: Dispatch<*>, getState: Function) => {
|
||||
const buttonName = 'fullscreen';
|
||||
const button = getButton(buttonName, getState());
|
||||
|
||||
if (button) {
|
||||
button.toggled = isFullScreen;
|
||||
dispatch(setToolbarButton(buttonName, button));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets negation of button's toggle property.
|
||||
*
|
||||
* @param {string} buttonName - Button key.
|
||||
* @returns {Function}
|
||||
*/
|
||||
export function toggleToolbarButton(buttonName: string): Function {
|
||||
return (dispatch: Dispatch, getState: Function) => {
|
||||
const button = getButton(buttonName, getState());
|
||||
|
||||
dispatch(setToolbarButton(buttonName, {
|
||||
toggled: !button.toggled
|
||||
}));
|
||||
};
|
||||
}
|
||||
|
|
|
@ -1,73 +1,24 @@
|
|||
/* @flow */
|
||||
|
||||
import Recording from '../../../modules/UI/recording/Recording';
|
||||
import SideContainerToggler
|
||||
from '../../../modules/UI/side_pannels/SideContainerToggler';
|
||||
|
||||
import UIEvents from '../../../service/UI/UIEvents';
|
||||
|
||||
import {
|
||||
changeLocalRaiseHand,
|
||||
clearToolboxTimeout,
|
||||
setSubjectSlideIn,
|
||||
setToolbarButton,
|
||||
setToolboxTimeout,
|
||||
setToolboxTimeoutMS,
|
||||
setToolboxVisible,
|
||||
toggleToolbarButton
|
||||
setToolboxVisible
|
||||
} from './actions.native';
|
||||
import {
|
||||
FULL_SCREEN_CHANGED,
|
||||
SET_DEFAULT_TOOLBOX_BUTTONS,
|
||||
SET_FULL_SCREEN
|
||||
} from './actionTypes';
|
||||
import {
|
||||
getButton,
|
||||
getDefaultToolboxButtons,
|
||||
isButtonEnabled
|
||||
} from './functions';
|
||||
|
||||
declare var $: Function;
|
||||
declare var APP: Object;
|
||||
declare var config: Object;
|
||||
declare var interfaceConfig: Object;
|
||||
|
||||
export * from './actions.native';
|
||||
|
||||
/**
|
||||
* Checks whether desktop sharing is enabled and whether
|
||||
* we have params to start automatically sharing.
|
||||
*
|
||||
* @returns {Function}
|
||||
*/
|
||||
export function checkAutoEnableDesktopSharing(): Function {
|
||||
return () => {
|
||||
// XXX Should use dispatcher to toggle screensharing but screensharing
|
||||
// hasn't been React-ified yet.
|
||||
if (isButtonEnabled('desktop')
|
||||
&& config.autoEnableDesktopSharing) {
|
||||
APP.UI.eventEmitter.emit(UIEvents.TOGGLE_SCREENSHARING);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Dispatches an action to hide any popups displayed by the associated button.
|
||||
*
|
||||
* @param {string} buttonName - The name of the button as specified in the
|
||||
* button configurations for the toolbar.
|
||||
* @returns {Function}
|
||||
*/
|
||||
export function clearButtonPopup(buttonName) {
|
||||
return (dispatch, getState) => {
|
||||
_clearPopupTimeout(buttonName, getState());
|
||||
|
||||
dispatch(setToolbarButton(buttonName, {
|
||||
popupDisplay: null
|
||||
}));
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Docks/undocks the Toolbox.
|
||||
*
|
||||
|
@ -115,56 +66,6 @@ export function fullScreenChanged(fullScreen: boolean) {
|
|||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns button on mount/unmount handlers with dispatch function stored in
|
||||
* closure.
|
||||
*
|
||||
* @param {Function} dispatch - Redux action dispatcher.
|
||||
* @returns {Object} Button on mount/unmount handlers.
|
||||
* @private
|
||||
*/
|
||||
function _getButtonHandlers(dispatch) {
|
||||
const localRaiseHandHandler
|
||||
= (...args) => dispatch(changeLocalRaiseHand(...args));
|
||||
|
||||
return {
|
||||
/**
|
||||
* Mount handler for desktop button.
|
||||
*
|
||||
* @type {Object}
|
||||
*/
|
||||
desktop: {
|
||||
onMount: () => dispatch(showDesktopSharingButton())
|
||||
},
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
|
@ -202,97 +103,6 @@ export function hideToolbox(force: boolean = false): Function {
|
|||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Dispatches an action to show the popup associated with a button. Sets a
|
||||
* timeout to be fired which will dismiss the popup.
|
||||
*
|
||||
* @param {string} buttonName - The name of the button as specified in the
|
||||
* button configurations for the toolbar.
|
||||
* @param {string} popupName - The id of the popup to show as specified in
|
||||
* the button configurations for the toolbar.
|
||||
* @param {number} timeout - The time in milliseconds to show the popup.
|
||||
* @returns {Function}
|
||||
*/
|
||||
export function setButtonPopupTimeout(buttonName, popupName, timeout) {
|
||||
return (dispatch, getState) => {
|
||||
_clearPopupTimeout(buttonName, getState());
|
||||
|
||||
const newTimeoutId = setTimeout(() => {
|
||||
dispatch(clearButtonPopup(buttonName));
|
||||
}, timeout);
|
||||
|
||||
dispatch(setToolbarButton(buttonName, {
|
||||
popupDisplay: {
|
||||
popupID: popupName,
|
||||
timeoutID: newTimeoutId
|
||||
}
|
||||
}));
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the default toolbar buttons of the Toolbox.
|
||||
*
|
||||
* @returns {Function}
|
||||
*/
|
||||
export function setDefaultToolboxButtons(): Function {
|
||||
return (dispatch: Dispatch) => {
|
||||
// Save dispatch function in closure.
|
||||
const buttonHandlers = _getButtonHandlers(dispatch);
|
||||
const toolboxButtons = getDefaultToolboxButtons(buttonHandlers);
|
||||
|
||||
dispatch({
|
||||
type: SET_DEFAULT_TOOLBOX_BUTTONS,
|
||||
...toolboxButtons
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows desktop sharing button.
|
||||
*
|
||||
* @returns {Function}
|
||||
*/
|
||||
export function showDesktopSharingButton(): Function {
|
||||
return (dispatch: Dispatch<*>) => {
|
||||
const buttonName = 'desktop';
|
||||
const disabledTooltipText
|
||||
= APP.conference.desktopSharingDisabledTooltip;
|
||||
const showTooltip
|
||||
= disabledTooltipText
|
||||
&& APP.conference.isDesktopSharingDisabledByConfig;
|
||||
const visible
|
||||
= isButtonEnabled(buttonName)
|
||||
&& (APP.conference.isDesktopSharingEnabled || showTooltip);
|
||||
|
||||
const newState = {
|
||||
enabled: APP.conference.isDesktopSharingEnabled,
|
||||
hidden: !visible,
|
||||
tooltipText: showTooltip ? disabledTooltipText : undefined
|
||||
};
|
||||
|
||||
dispatch(setToolbarButton(buttonName, newState));
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows or hides the dialpad button.
|
||||
*
|
||||
* @param {boolean} show - Flag showing whether to show button or not.
|
||||
* @returns {Function}
|
||||
*/
|
||||
export function showDialPadButton(show: boolean): Function {
|
||||
return (dispatch: Dispatch<*>) => {
|
||||
const buttonName = 'dialpad';
|
||||
|
||||
if (show && isButtonEnabled(buttonName)) {
|
||||
dispatch(setToolbarButton(buttonName, {
|
||||
hidden: false
|
||||
}));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Signals a request to enter or exit full screen mode.
|
||||
*
|
||||
|
@ -309,39 +119,6 @@ export function setFullScreen(fullScreen: boolean) {
|
|||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows recording button.
|
||||
*
|
||||
* @returns {Function}
|
||||
*/
|
||||
export function showRecordingButton(): Function {
|
||||
return (dispatch: Dispatch<*>) => {
|
||||
dispatch(setToolbarButton('recording', {
|
||||
hidden: false
|
||||
}));
|
||||
|
||||
Recording.initRecordingButton();
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows or hides the 'shared video' button.
|
||||
*
|
||||
* @returns {Function}
|
||||
*/
|
||||
export function showSharedVideoButton(): Function {
|
||||
return (dispatch: Dispatch<*>) => {
|
||||
const buttonName = 'sharedvideo';
|
||||
|
||||
if (isButtonEnabled(buttonName)
|
||||
&& !config.disableThirdPartyRequests) {
|
||||
dispatch(setToolbarButton(buttonName, {
|
||||
hidden: false
|
||||
}));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows the toolbox for specified timeout.
|
||||
*
|
||||
|
@ -374,43 +151,3 @@ export function showToolbox(timeout: number = 0): Object {
|
|||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Event handler for side toolbar container toggled event.
|
||||
*
|
||||
* @param {string} containerId - ID of the container.
|
||||
* @returns {Function}
|
||||
*/
|
||||
export function toggleSideToolbarContainer(containerId: string): Function {
|
||||
return (dispatch: Dispatch, getState: Function) => {
|
||||
const { secondaryToolbarButtons } = getState()['features/toolbox'];
|
||||
|
||||
for (const key of secondaryToolbarButtons.keys()) {
|
||||
const button = secondaryToolbarButtons.get(key);
|
||||
|
||||
if (isButtonEnabled(key)
|
||||
&& button.sideContainerId
|
||||
&& button.sideContainerId === containerId) {
|
||||
dispatch(toggleToolbarButton(key));
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears the timeout set for hiding a button popup.
|
||||
*
|
||||
* @param {string} buttonName - The name of the button as specified in the
|
||||
* button configurations for the toolbar.
|
||||
* @param {Object} state - The redux state in which the button is expected to
|
||||
* be defined.
|
||||
* @private
|
||||
* @returns {void}
|
||||
*/
|
||||
function _clearPopupTimeout(buttonName, state) {
|
||||
const { popupDisplay } = getButton(buttonName, state);
|
||||
const { timeoutID } = popupDisplay || {};
|
||||
|
||||
clearTimeout(timeoutID);
|
||||
}
|
||||
|
|
|
@ -1,97 +0,0 @@
|
|||
/* @flow */
|
||||
|
||||
import PropTypes from 'prop-types';
|
||||
import React, { Component } from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
|
||||
import { getToolbarClassNames } from '../functions';
|
||||
import Toolbar from './Toolbar';
|
||||
|
||||
declare var interfaceConfig: Object;
|
||||
|
||||
/**
|
||||
* Implementation of PrimaryToolbar React Component.
|
||||
*
|
||||
* @class PrimaryToolbar
|
||||
* @extends Component
|
||||
*/
|
||||
class PrimaryToolbar extends Component<*, *> {
|
||||
static propTypes = {
|
||||
|
||||
/**
|
||||
* Contains toolbar buttons for primary toolbar.
|
||||
*/
|
||||
_primaryToolbarButtons: PropTypes.instanceOf(Map),
|
||||
|
||||
/**
|
||||
* Shows whether toolbox is visible.
|
||||
*/
|
||||
_visible: PropTypes.bool
|
||||
};
|
||||
|
||||
state: Object;
|
||||
|
||||
/**
|
||||
* Renders primary toolbar component.
|
||||
*
|
||||
* @returns {ReactElement}
|
||||
*/
|
||||
render(): React$Element<*> | null {
|
||||
const { _primaryToolbarButtons } = this.props;
|
||||
|
||||
// The number of buttons to show in the toolbar isn't fixed, it depends
|
||||
// on the availability of features and configuration parameters. So
|
||||
// there may be nothing to render.
|
||||
if (_primaryToolbarButtons.size === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const { primaryToolbarClassName } = getToolbarClassNames(this.props);
|
||||
const tooltipPosition
|
||||
= interfaceConfig.filmStripOnly ? 'left' : 'bottom';
|
||||
|
||||
return (
|
||||
<Toolbar
|
||||
className = { primaryToolbarClassName }
|
||||
toolbarButtons = { _primaryToolbarButtons }
|
||||
tooltipPosition = { tooltipPosition } />
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Maps part of Redux store to React component props.
|
||||
*
|
||||
* @param {Object} state - Snapshot of Redux store.
|
||||
* @returns {{
|
||||
* _primaryToolbarButtons: Map,
|
||||
* _visible: boolean
|
||||
* }}
|
||||
* @private
|
||||
*/
|
||||
function _mapStateToProps(state: Object): Object {
|
||||
const {
|
||||
primaryToolbarButtons,
|
||||
visible
|
||||
} = state['features/toolbox'];
|
||||
|
||||
return {
|
||||
/**
|
||||
* Default toolbar buttons for primary toolbar.
|
||||
*
|
||||
* @private
|
||||
* @type {Map}
|
||||
*/
|
||||
_primaryToolbarButtons: primaryToolbarButtons,
|
||||
|
||||
/**
|
||||
* Shows whether toolbox is visible.
|
||||
*
|
||||
* @private
|
||||
* @type {boolean}
|
||||
*/
|
||||
_visible: visible
|
||||
};
|
||||
}
|
||||
|
||||
export default connect(_mapStateToProps)(PrimaryToolbar);
|
|
@ -1,188 +0,0 @@
|
|||
/* @flow */
|
||||
|
||||
import PropTypes from 'prop-types';
|
||||
import React, { Component } from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
|
||||
import { FeedbackButton } from '../../feedback';
|
||||
import UIEvents from '../../../../service/UI/UIEvents';
|
||||
|
||||
import {
|
||||
toggleSideToolbarContainer
|
||||
} from '../actions';
|
||||
import { getToolbarClassNames } from '../functions';
|
||||
import Toolbar from './Toolbar';
|
||||
|
||||
declare var APP: Object;
|
||||
declare var config: Object;
|
||||
|
||||
/**
|
||||
* Implementation of secondary toolbar React component.
|
||||
*
|
||||
* @class SecondaryToolbar
|
||||
* @extends Component
|
||||
*/
|
||||
class SecondaryToolbar extends Component<*, *> {
|
||||
state: Object;
|
||||
|
||||
/**
|
||||
* Secondary toolbar property types.
|
||||
*
|
||||
* @static
|
||||
*/
|
||||
static propTypes = {
|
||||
/**
|
||||
* Application ID for callstats.io API. The {@code FeedbackButton} will
|
||||
* display if defined.
|
||||
*/
|
||||
_callStatsID: PropTypes.string,
|
||||
|
||||
/**
|
||||
* The indicator which determines whether the local participant is a
|
||||
* guest in the conference.
|
||||
*/
|
||||
_isGuest: PropTypes.bool,
|
||||
|
||||
/**
|
||||
* Handler dispatching toggle toolbar container.
|
||||
*/
|
||||
_onSideToolbarContainerToggled: PropTypes.func,
|
||||
|
||||
/**
|
||||
* Contains map of secondary toolbar buttons.
|
||||
*/
|
||||
_secondaryToolbarButtons: PropTypes.instanceOf(Map),
|
||||
|
||||
/**
|
||||
* Shows whether toolbox is visible.
|
||||
*/
|
||||
_visible: PropTypes.bool
|
||||
};
|
||||
|
||||
/**
|
||||
* Register legacy UI listener.
|
||||
*
|
||||
* @returns {void}
|
||||
*/
|
||||
componentDidMount(): void {
|
||||
APP.UI.addListener(
|
||||
UIEvents.SIDE_TOOLBAR_CONTAINER_TOGGLED,
|
||||
this.props._onSideToolbarContainerToggled);
|
||||
}
|
||||
|
||||
/**
|
||||
* Unregisters legacy UI listener.
|
||||
*
|
||||
* @returns {void}
|
||||
*/
|
||||
componentWillUnmount(): void {
|
||||
APP.UI.removeListener(
|
||||
UIEvents.SIDE_TOOLBAR_CONTAINER_TOGGLED,
|
||||
this.props._onSideToolbarContainerToggled);
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders secondary toolbar component.
|
||||
*
|
||||
* @returns {ReactElement}
|
||||
*/
|
||||
render(): React$Element<*> | null {
|
||||
const { _callStatsID, _secondaryToolbarButtons } = this.props;
|
||||
|
||||
// The number of buttons to show in the toolbar isn't fixed, it depends
|
||||
// on the availability of features and configuration parameters. So
|
||||
// there may be nothing to render.
|
||||
if (_secondaryToolbarButtons.size === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const { secondaryToolbarClassName } = getToolbarClassNames(this.props);
|
||||
|
||||
return (
|
||||
<Toolbar
|
||||
className = { secondaryToolbarClassName }
|
||||
toolbarButtons = { _secondaryToolbarButtons }
|
||||
tooltipPosition = 'right'>
|
||||
{ _callStatsID
|
||||
? <FeedbackButton tooltipPosition = 'right' /> : null }
|
||||
</Toolbar>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Maps some of Redux actions to component's props.
|
||||
*
|
||||
* @param {Function} dispatch - Redux action dispatcher.
|
||||
* @returns {{
|
||||
* _onSideToolbarContainerToggled
|
||||
* }}
|
||||
* @private
|
||||
*/
|
||||
function _mapDispatchToProps(dispatch: Function): Object {
|
||||
return {
|
||||
|
||||
/**
|
||||
* Dispatches an action signalling that side toolbar container is
|
||||
* toggled.
|
||||
*
|
||||
* @param {string} containerId - Id of side toolbar container.
|
||||
* @returns {Object} Dispatched action.
|
||||
*/
|
||||
_onSideToolbarContainerToggled(containerId: string) {
|
||||
dispatch(toggleSideToolbarContainer(containerId));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Maps part of Redux state to component's props.
|
||||
*
|
||||
* @param {Object} state - Snapshot of Redux store.
|
||||
* @returns {{
|
||||
* _isGuest: boolean,
|
||||
* _secondaryToolbarButtons: Map,
|
||||
* _visible: boolean
|
||||
* }}
|
||||
* @private
|
||||
*/
|
||||
function _mapStateToProps(state: Object): Object {
|
||||
const { isGuest } = state['features/base/jwt'];
|
||||
const { secondaryToolbarButtons, visible } = state['features/toolbox'];
|
||||
const { callStatsID } = state['features/base/config'];
|
||||
|
||||
return {
|
||||
/**
|
||||
* Application ID for callstats.io API.
|
||||
*/
|
||||
_callStatsID: callStatsID,
|
||||
|
||||
/**
|
||||
* The indicator which determines whether the local participant is a
|
||||
* guest in the conference.
|
||||
*
|
||||
* @private
|
||||
* @type {boolean}
|
||||
*/
|
||||
_isGuest: isGuest,
|
||||
|
||||
/**
|
||||
* Default toolbar buttons for secondary toolbar.
|
||||
*
|
||||
* @private
|
||||
* @type {Map}
|
||||
*/
|
||||
_secondaryToolbarButtons: secondaryToolbarButtons,
|
||||
|
||||
/**
|
||||
* The indicator which determines whether the {@code SecondaryToolbar}
|
||||
* is visible.
|
||||
*
|
||||
* @private
|
||||
* @type {boolean}
|
||||
*/
|
||||
_visible: visible
|
||||
};
|
||||
}
|
||||
|
||||
export default connect(_mapStateToProps, _mapDispatchToProps)(SecondaryToolbar);
|
|
@ -1,181 +0,0 @@
|
|||
// @flow
|
||||
|
||||
import PropTypes from 'prop-types';
|
||||
import React, { Component } from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
|
||||
import { setToolbarHovered } from '../actions';
|
||||
|
||||
import StatelessToolbar from './StatelessToolbar';
|
||||
import ToolbarButton from './ToolbarButton';
|
||||
|
||||
/**
|
||||
* Implements a toolbar in React/Web. It is a strip that contains a set of
|
||||
* toolbar items such as buttons. Toolbar is commonly placed inside of a
|
||||
* Toolbox.
|
||||
*
|
||||
* @class Toolbar
|
||||
* @extends Component
|
||||
*/
|
||||
class Toolbar extends Component<*> {
|
||||
/**
|
||||
* Base toolbar component's property types.
|
||||
*
|
||||
* @static
|
||||
*/
|
||||
static propTypes = {
|
||||
/**
|
||||
* Children of current React component.
|
||||
*/
|
||||
children: PropTypes.element,
|
||||
|
||||
/**
|
||||
* Toolbar's class name.
|
||||
*/
|
||||
className: PropTypes.string,
|
||||
|
||||
/**
|
||||
* Used to dispatch an action when a button is clicked or on mouse
|
||||
* out/in event.
|
||||
*/
|
||||
dispatch: PropTypes.func,
|
||||
|
||||
/**
|
||||
* Map with toolbar buttons.
|
||||
*/
|
||||
toolbarButtons: PropTypes.instanceOf(Map),
|
||||
|
||||
/**
|
||||
* Indicates the position of the tooltip.
|
||||
*/
|
||||
tooltipPosition: PropTypes.oneOf([ 'bottom', 'left', 'right', 'top' ])
|
||||
};
|
||||
|
||||
/**
|
||||
* Constructor of Primary toolbar class.
|
||||
*
|
||||
* @param {Object} props - Object containing React component properties.
|
||||
*/
|
||||
constructor(props: Object) {
|
||||
super(props);
|
||||
|
||||
// Bind callbacks to preverse this.
|
||||
this._onMouseOut = this._onMouseOut.bind(this);
|
||||
this._onMouseOver = this._onMouseOver.bind(this);
|
||||
this._renderToolbarButton = this._renderToolbarButton.bind(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements React's {@link Component#render()}.
|
||||
*
|
||||
* @inheritdoc
|
||||
* @returns {ReactElement}
|
||||
*/
|
||||
render(): React$Element<*> {
|
||||
const props = {
|
||||
className: this.props.className,
|
||||
onMouseOut: this._onMouseOut,
|
||||
onMouseOver: this._onMouseOver
|
||||
};
|
||||
|
||||
return (
|
||||
<StatelessToolbar { ...props }>
|
||||
{
|
||||
[ ...this.props.toolbarButtons.entries() ]
|
||||
.map(this._renderToolbarButton)
|
||||
}
|
||||
{
|
||||
this.props.children
|
||||
}
|
||||
</StatelessToolbar>
|
||||
);
|
||||
}
|
||||
|
||||
_onMouseOut: () => void;
|
||||
|
||||
/**
|
||||
* Dispatches an action signalling that toolbar is no being hovered.
|
||||
*
|
||||
* @protected
|
||||
* @returns {void}
|
||||
*/
|
||||
_onMouseOut() {
|
||||
this.props.dispatch(setToolbarHovered(false));
|
||||
}
|
||||
|
||||
_onMouseOver: () => void;
|
||||
|
||||
/**
|
||||
* Dispatches an action signalling that toolbar is now being hovered.
|
||||
*
|
||||
* @protected
|
||||
* @returns {void}
|
||||
*/
|
||||
_onMouseOver() {
|
||||
this.props.dispatch(setToolbarHovered(true));
|
||||
}
|
||||
|
||||
_renderToolbarButton: (Array<*>) => React$Element<*>;
|
||||
|
||||
/**
|
||||
* Renders toolbar button. Method is passed to map function.
|
||||
*
|
||||
* @param {Array} keyValuePair - Key value pair containing button and its
|
||||
* key.
|
||||
* @private
|
||||
* @returns {ReactElement} A toolbar button.
|
||||
*/
|
||||
_renderToolbarButton([ key, button ]): React$Element<*> {
|
||||
const { tooltipPosition } = this.props;
|
||||
|
||||
if (button.component) {
|
||||
return (
|
||||
<button.component
|
||||
key = { key }
|
||||
toggled = { button.toggled }
|
||||
tooltipPosition = { tooltipPosition } />
|
||||
);
|
||||
}
|
||||
|
||||
const {
|
||||
childComponent: ChildComponent,
|
||||
onClick,
|
||||
onMount,
|
||||
onUnmount
|
||||
} = button;
|
||||
const onClickWithDispatch = (...args) =>
|
||||
onClick && onClick(this.props.dispatch, ...args);
|
||||
|
||||
return (
|
||||
<ToolbarButton
|
||||
button = { button }
|
||||
key = { key }
|
||||
|
||||
// TODO The following disables an eslint error alerting about a
|
||||
// known potential/theoretical performance pernalty:
|
||||
//
|
||||
// A bind call or arrow function in a JSX prop will create a
|
||||
// brand new function on every single render. This is bad for
|
||||
// performance, as it will result in the garbage collector being
|
||||
// invoked way more than is necessary. It may also cause
|
||||
// unnecessary re-renders if a brand new function is passed as a
|
||||
// prop to a component that uses reference equality check on the
|
||||
// prop to determine if it should update.
|
||||
//
|
||||
// I'm not addressing the potential/theoretical performance
|
||||
// penalty at the time of this writing because I don't know for
|
||||
// a fact that it's a practical performance penalty in the case.
|
||||
//
|
||||
// eslint-disable-next-line react/jsx-no-bind
|
||||
onClick = { onClickWithDispatch }
|
||||
onMount = { onMount }
|
||||
onUnmount = { onUnmount }
|
||||
tooltipPosition = { tooltipPosition }>
|
||||
{ button.html || null }
|
||||
{ ChildComponent ? <ChildComponent /> : null }
|
||||
</ToolbarButton>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default connect()(Toolbar);
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -2,6 +2,5 @@ export { default as ToolbarButton } from './ToolbarButton';
|
|||
export { default as ToolbarButtonV2 } from './ToolbarButtonV2';
|
||||
export { default as ToolbarButtonWithDialog }
|
||||
from './ToolbarButtonWithDialog';
|
||||
export { default as Toolbox } from './Toolbox';
|
||||
export { default as ToolboxFilmstrip } from './ToolboxFilmstrip';
|
||||
export { default as ToolboxV2 } from './ToolboxV2';
|
||||
export { default as Toolbox } from './Toolbox';
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
export default undefined;
|
|
@ -1,532 +0,0 @@
|
|||
// @flow
|
||||
|
||||
import React from 'react';
|
||||
|
||||
import { setFullScreen } from '../toolbox';
|
||||
import {
|
||||
ACTION_SHORTCUT_TRIGGERED as TRIGGERED,
|
||||
AUDIO_MUTE,
|
||||
VIDEO_MUTE,
|
||||
createShortcutEvent,
|
||||
createToolbarEvent,
|
||||
sendAnalytics
|
||||
} from '../analytics';
|
||||
import {
|
||||
getLocalParticipant,
|
||||
participantUpdated
|
||||
} from '../base/participants';
|
||||
import { ParticipantCounter } from '../contact-list';
|
||||
import { openDeviceSelectionDialog } from '../device-selection';
|
||||
import { InfoDialogButton } from '../invite';
|
||||
import UIEvents from '../../../service/UI/UIEvents';
|
||||
import { VideoQualityButton } from '../video-quality';
|
||||
|
||||
import ProfileButton from './components/ProfileButton';
|
||||
|
||||
declare var APP: Object;
|
||||
declare var interfaceConfig: Object;
|
||||
|
||||
/**
|
||||
* The cache of {@link getDefaultButtons()}.
|
||||
*/
|
||||
let defaultButtons: Object;
|
||||
|
||||
/**
|
||||
* Returns a map of all button descriptors and according properties.
|
||||
*
|
||||
* @returns {Object} - The maps of default button descriptors.
|
||||
*/
|
||||
export default function getDefaultButtons() {
|
||||
if (defaultButtons) {
|
||||
return defaultButtons;
|
||||
}
|
||||
|
||||
defaultButtons = {
|
||||
/**
|
||||
* The descriptor of the camera toolbar button.
|
||||
*/
|
||||
camera: {
|
||||
classNames: [ 'button', 'icon-camera' ],
|
||||
enabled: true,
|
||||
isDisplayed: () => true,
|
||||
id: 'toolbar_button_camera',
|
||||
onClick() {
|
||||
// TODO: Why is this different from the code which handles
|
||||
// a keyboard shortcut?
|
||||
const newVideoMutedState = !APP.conference.isLocalVideoMuted();
|
||||
|
||||
// The 'enable' attribute in the event is set to true if the
|
||||
// button click triggered a mute action, and set to false if it
|
||||
// triggered an unmute action.
|
||||
sendAnalytics(createToolbarEvent(
|
||||
VIDEO_MUTE,
|
||||
{
|
||||
enable: newVideoMutedState
|
||||
}));
|
||||
APP.UI.emitEvent(UIEvents.VIDEO_MUTED, newVideoMutedState);
|
||||
},
|
||||
popups: [
|
||||
{
|
||||
dataAttr: 'audioOnly.featureToggleDisabled',
|
||||
dataInterpolate: { feature: 'video mute' },
|
||||
id: 'unmuteWhileAudioOnly'
|
||||
}
|
||||
],
|
||||
shortcut: 'V',
|
||||
shortcutAttr: 'toggleVideoPopover',
|
||||
shortcutFunc() {
|
||||
if (APP.conference.isAudioOnly()) {
|
||||
APP.UI.emitEvent(UIEvents.VIDEO_UNMUTING_WHILE_AUDIO_ONLY);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// The 'enable' attribute in the event is set to true if the
|
||||
// shortcut triggered a mute action, and set to false if it
|
||||
// triggered an unmute action.
|
||||
sendAnalytics(createShortcutEvent(
|
||||
VIDEO_MUTE,
|
||||
TRIGGERED,
|
||||
{ enable: !APP.conference.isLocalVideoMuted() }));
|
||||
APP.conference.toggleVideoMuted();
|
||||
},
|
||||
shortcutDescription: 'keyboardShortcuts.videoMute',
|
||||
tooltipKey: 'toolbar.videomute'
|
||||
},
|
||||
|
||||
/**
|
||||
* The descriptor of the chat toolbar button.
|
||||
*/
|
||||
chat: {
|
||||
classNames: [ 'button', 'icon-chat' ],
|
||||
enabled: true,
|
||||
html: <span className = 'badge-round'>
|
||||
<span id = 'unreadMessages' /></span>,
|
||||
id: 'toolbar_button_chat',
|
||||
onClick() {
|
||||
// The 'enable' attribute is set to true if the click resulted
|
||||
// in the chat panel being shown, and to false if it was hidden.
|
||||
sendAnalytics(createToolbarEvent(
|
||||
'toggle.chat',
|
||||
{
|
||||
enable: !APP.UI.isChatVisible()
|
||||
}));
|
||||
APP.UI.emitEvent(UIEvents.TOGGLE_CHAT);
|
||||
},
|
||||
shortcut: 'C',
|
||||
shortcutAttr: 'toggleChatPopover',
|
||||
shortcutFunc() {
|
||||
// The 'enable' attribute is set to true if the shortcut
|
||||
// resulted in the chat panel being shown, and to false if it
|
||||
// was hidden.
|
||||
sendAnalytics(createShortcutEvent(
|
||||
'toggle.chat',
|
||||
{
|
||||
enable: !APP.UI.isChatVisible()
|
||||
}));
|
||||
APP.UI.toggleChat();
|
||||
},
|
||||
shortcutDescription: 'keyboardShortcuts.toggleChat',
|
||||
sideContainerId: 'chat_container',
|
||||
tooltipKey: 'toolbar.chat'
|
||||
},
|
||||
|
||||
/**
|
||||
* The descriptor of the contact list toolbar button.
|
||||
*/
|
||||
contacts: {
|
||||
childComponent: ParticipantCounter,
|
||||
classNames: [ 'button', 'icon-contactList' ],
|
||||
enabled: true,
|
||||
id: 'toolbar_contact_list',
|
||||
onClick() {
|
||||
// TODO: Include an 'enable' attribute which specifies whether
|
||||
// the contacts panel was shown or hidden.
|
||||
sendAnalytics(createToolbarEvent('contacts'));
|
||||
APP.UI.emitEvent(UIEvents.TOGGLE_CONTACT_LIST);
|
||||
},
|
||||
sideContainerId: 'contacts_container',
|
||||
tooltipKey: 'bottomtoolbar.contactlist'
|
||||
},
|
||||
|
||||
/**
|
||||
* The descriptor of the desktop sharing toolbar button.
|
||||
*/
|
||||
desktop: {
|
||||
classNames: [ 'button', 'icon-share-desktop' ],
|
||||
enabled: true,
|
||||
id: 'toolbar_button_desktopsharing',
|
||||
onClick() {
|
||||
// TODO: Why is the button clicked handled differently that
|
||||
// a keyboard shortcut press (firing a TOGGLE_SCREENSHARING
|
||||
// event vs. directly calling toggleScreenSharing())?
|
||||
sendAnalytics(createToolbarEvent(
|
||||
'screen.sharing',
|
||||
{
|
||||
enable: !APP.conference.isSharingScreen
|
||||
}));
|
||||
APP.UI.emitEvent(UIEvents.TOGGLE_SCREENSHARING);
|
||||
},
|
||||
popups: [
|
||||
{
|
||||
dataAttr: 'audioOnly.featureToggleDisabled',
|
||||
dataInterpolate: { feature: 'screen sharing' },
|
||||
id: 'screenshareWhileAudioOnly'
|
||||
}
|
||||
],
|
||||
shortcut: 'D',
|
||||
shortcutAttr: 'toggleDesktopSharingPopover',
|
||||
shortcutFunc() {
|
||||
// The 'enable' attribute is set to true if pressing the
|
||||
// shortcut resulted in screen sharing being enabled, and false
|
||||
// if it resulted in screen sharing being disabled.
|
||||
sendAnalytics(createShortcutEvent(
|
||||
'toggle.screen.sharing',
|
||||
TRIGGERED,
|
||||
{ enable: !APP.conference.isSharingScreen }));
|
||||
|
||||
// eslint-disable-next-line no-empty-function
|
||||
APP.conference.toggleScreenSharing().catch(() => {});
|
||||
},
|
||||
shortcutDescription: 'keyboardShortcuts.toggleScreensharing',
|
||||
tooltipKey: 'toolbar.sharescreen'
|
||||
},
|
||||
|
||||
/**
|
||||
* The descriptor of the device selection toolbar button.
|
||||
*/
|
||||
fodeviceselection: {
|
||||
classNames: [ 'button', 'icon-settings' ],
|
||||
enabled: true,
|
||||
isDisplayed() {
|
||||
return interfaceConfig.filmStripOnly;
|
||||
},
|
||||
id: 'toolbar_button_fodeviceselection',
|
||||
onClick(dispatch: Function) {
|
||||
sendAnalytics(
|
||||
createToolbarEvent('filmstrip.only.device.selection'));
|
||||
|
||||
dispatch(openDeviceSelectionDialog());
|
||||
},
|
||||
sideContainerId: 'settings_container',
|
||||
tooltipKey: 'toolbar.Settings'
|
||||
},
|
||||
|
||||
/**
|
||||
* The descriptor of the dialpad toolbar button.
|
||||
*/
|
||||
dialpad: {
|
||||
classNames: [ 'button', 'icon-dialpad' ],
|
||||
enabled: true,
|
||||
|
||||
// TODO: remove it after UI.updateDTMFSupport fix
|
||||
hidden: true,
|
||||
id: 'toolbar_button_dialpad',
|
||||
onClick() {
|
||||
sendAnalytics(createToolbarEvent('dialpad'));
|
||||
},
|
||||
tooltipKey: 'toolbar.dialpad'
|
||||
},
|
||||
|
||||
/**
|
||||
* The descriptor of the etherpad toolbar button.
|
||||
*/
|
||||
etherpad: {
|
||||
classNames: [ 'button', 'icon-share-doc' ],
|
||||
enabled: true,
|
||||
hidden: true,
|
||||
id: 'toolbar_button_etherpad',
|
||||
onClick() {
|
||||
// The 'enable' attribute is set to true if the click resulted
|
||||
// in the etherpad panel being shown, or false it it was hidden.
|
||||
sendAnalytics(createToolbarEvent(
|
||||
'toggle.etherpad',
|
||||
{
|
||||
enable: !APP.UI.isEtherpadVisible()
|
||||
}));
|
||||
APP.UI.emitEvent(UIEvents.ETHERPAD_CLICKED);
|
||||
},
|
||||
tooltipKey: 'toolbar.etherpad'
|
||||
},
|
||||
|
||||
/**
|
||||
* The descriptor of the toolbar button which toggles full-screen mode.
|
||||
*/
|
||||
fullscreen: {
|
||||
classNames: [ 'button', 'icon-full-screen' ],
|
||||
enabled: true,
|
||||
id: 'toolbar_button_fullScreen',
|
||||
onClick() {
|
||||
const state = APP.store.getState();
|
||||
const isFullScreen = Boolean(
|
||||
state['features/toolbox'].fullScreen);
|
||||
|
||||
// The 'enable' attribute is set to true if the action resulted
|
||||
// in fullscreen mode being enabled.
|
||||
sendAnalytics(createToolbarEvent(
|
||||
'toggle.fullscreen',
|
||||
{
|
||||
enable: !isFullScreen
|
||||
}));
|
||||
|
||||
APP.store.dispatch(setFullScreen(!isFullScreen));
|
||||
},
|
||||
shortcut: 'S',
|
||||
shortcutAttr: 'toggleFullscreenPopover',
|
||||
shortcutDescription: 'keyboardShortcuts.fullScreen',
|
||||
shortcutFunc() {
|
||||
const state = APP.store.getState();
|
||||
const isFullScreen = Boolean(
|
||||
state['features/toolbox'].fullScreen);
|
||||
|
||||
// The 'enable' attribute is set to true if the action resulted
|
||||
// in fullscreen mode being enabled.
|
||||
sendAnalytics(createShortcutEvent(
|
||||
'toggle.fullscreen',
|
||||
{
|
||||
enable: !isFullScreen
|
||||
}));
|
||||
|
||||
APP.store.dispatch(setFullScreen(!isFullScreen));
|
||||
},
|
||||
tooltipKey: 'toolbar.fullscreen'
|
||||
},
|
||||
|
||||
/**
|
||||
* The descriptor of the toolbar button which hangs up the
|
||||
* call/conference.
|
||||
*/
|
||||
hangup: {
|
||||
classNames: [ 'button', 'icon-hangup', 'button_hangup' ],
|
||||
enabled: true,
|
||||
isDisplayed: () => true,
|
||||
id: 'toolbar_button_hangup',
|
||||
onClick() {
|
||||
sendAnalytics(createToolbarEvent('hangup'));
|
||||
APP.UI.emitEvent(UIEvents.HANGUP);
|
||||
},
|
||||
tooltipKey: 'toolbar.hangup'
|
||||
},
|
||||
|
||||
/**
|
||||
* The descriptor of the toolbar button which opens a dialog for the
|
||||
* conference URL and inviting others.
|
||||
*/
|
||||
info: {
|
||||
component: InfoDialogButton
|
||||
},
|
||||
|
||||
/**
|
||||
* The descriptor of the microphone toolbar button.
|
||||
*/
|
||||
microphone: {
|
||||
classNames: [ 'button', 'icon-microphone' ],
|
||||
enabled: true,
|
||||
isDisplayed: () => true,
|
||||
id: 'toolbar_button_mute',
|
||||
onClick() {
|
||||
const sharedVideoManager = APP.UI.getSharedVideoManager();
|
||||
|
||||
// TODO: Clicking the mute button and pressing the mute shortcut
|
||||
// could be handled in a uniform manner. The code below checks
|
||||
// the mute status and fires the appropriate event (MUTED or
|
||||
// UNMUTED), while the code which handles the keyboard shortcut
|
||||
// calls toggleAudioMuted(). Also strangely the the user is
|
||||
// only warned if they click the button (and not if they use
|
||||
// the shortcut).
|
||||
if (APP.conference.isLocalAudioMuted()) {
|
||||
// If there's a shared video with the volume "on" and we
|
||||
// aren't the video owner, we warn the user
|
||||
// that currently it's not possible to unmute.
|
||||
if (sharedVideoManager
|
||||
&& sharedVideoManager.isSharedVideoVolumeOn()
|
||||
&& !sharedVideoManager.isSharedVideoOwner()) {
|
||||
APP.UI.showCustomToolbarPopup(
|
||||
'microphone', 'unableToUnmutePopup', true, 5000);
|
||||
} else {
|
||||
sendAnalytics(createToolbarEvent(
|
||||
AUDIO_MUTE,
|
||||
{ enable: false }));
|
||||
APP.UI.emitEvent(UIEvents.AUDIO_MUTED, false, true);
|
||||
}
|
||||
} else {
|
||||
sendAnalytics(createToolbarEvent(
|
||||
AUDIO_MUTE,
|
||||
{ enable: true }));
|
||||
APP.UI.emitEvent(UIEvents.AUDIO_MUTED, true, true);
|
||||
}
|
||||
},
|
||||
popups: [
|
||||
{
|
||||
dataAttr: 'toolbar.micMutedPopup',
|
||||
id: 'micMutedPopup'
|
||||
},
|
||||
{
|
||||
dataAttr: 'toolbar.unableToUnmutePopup',
|
||||
id: 'unableToUnmutePopup'
|
||||
},
|
||||
{
|
||||
dataAttr: 'toolbar.talkWhileMutedPopup',
|
||||
id: 'talkWhileMutedPopup'
|
||||
}
|
||||
],
|
||||
shortcut: 'M',
|
||||
shortcutAttr: 'mutePopover',
|
||||
shortcutFunc() {
|
||||
// The 'enable' attribute in the event is set to true if the
|
||||
// shortcut triggered a mute action, and set to false if it
|
||||
// triggered an unmute action.
|
||||
sendAnalytics(createShortcutEvent(
|
||||
AUDIO_MUTE,
|
||||
TRIGGERED,
|
||||
{ enable: !APP.conference.isLocalAudioMuted() }));
|
||||
APP.conference.toggleAudioMuted();
|
||||
},
|
||||
shortcutDescription: 'keyboardShortcuts.mute',
|
||||
tooltipKey: 'toolbar.mute'
|
||||
},
|
||||
|
||||
/**
|
||||
* The descriptor of the profile toolbar button.
|
||||
*/
|
||||
profile: {
|
||||
component: ProfileButton,
|
||||
sideContainerId: 'profile_container'
|
||||
},
|
||||
|
||||
/**
|
||||
* The descriptor of the "Raise hand" toolbar button.
|
||||
*/
|
||||
raisehand: {
|
||||
classNames: [ 'button', 'icon-raised-hand' ],
|
||||
enabled: true,
|
||||
id: 'toolbar_button_raisehand',
|
||||
onClick() {
|
||||
// TODO: reduce duplication with shortcutFunc below.
|
||||
const localParticipant
|
||||
= getLocalParticipant(APP.store.getState());
|
||||
const currentRaisedHand = localParticipant.raisedHand;
|
||||
|
||||
// The 'enable' attribute is set to true if the pressing of the
|
||||
// shortcut resulted in the hand being raised, and to false
|
||||
// if it resulted in the hand being 'lowered'.
|
||||
sendAnalytics(createToolbarEvent(
|
||||
'raise.hand',
|
||||
{ enable: !currentRaisedHand }));
|
||||
|
||||
APP.store.dispatch(participantUpdated({
|
||||
id: localParticipant.id,
|
||||
local: true,
|
||||
raisedHand: !currentRaisedHand
|
||||
}));
|
||||
},
|
||||
shortcut: 'R',
|
||||
shortcutAttr: 'raiseHandPopover',
|
||||
shortcutDescription: 'keyboardShortcuts.raiseHand',
|
||||
shortcutFunc() {
|
||||
const localParticipant
|
||||
= getLocalParticipant(APP.store.getState());
|
||||
const currentRaisedHand = localParticipant.raisedHand;
|
||||
|
||||
// The 'enable' attribute is set to true if the pressing of the
|
||||
// shortcut resulted in the hand being raised, and to false
|
||||
// if it resulted in the hand being 'lowered'.
|
||||
sendAnalytics(createShortcutEvent(
|
||||
'toggle.raise.hand',
|
||||
TRIGGERED,
|
||||
{ enable: !currentRaisedHand }));
|
||||
|
||||
APP.store.dispatch(participantUpdated({
|
||||
id: localParticipant.id,
|
||||
local: true,
|
||||
raisedHand: !currentRaisedHand
|
||||
}));
|
||||
},
|
||||
tooltipKey: 'toolbar.raiseHand'
|
||||
},
|
||||
|
||||
/**
|
||||
* The descriptor of the recording toolbar button. Requires additional
|
||||
* initialization in the recording module.
|
||||
*/
|
||||
recording: {
|
||||
classNames: [ 'button' ],
|
||||
enabled: true,
|
||||
|
||||
// will be displayed once the recording functionality is detected
|
||||
hidden: true,
|
||||
id: 'toolbar_button_record',
|
||||
tooltipKey: 'liveStreaming.buttonTooltip'
|
||||
},
|
||||
|
||||
/**
|
||||
* The descriptor of the settings toolbar button.
|
||||
*/
|
||||
settings: {
|
||||
classNames: [ 'button', 'icon-settings' ],
|
||||
enabled: true,
|
||||
id: 'toolbar_button_settings',
|
||||
onClick() {
|
||||
// TODO: Include an 'enable' attribute which specifies whether
|
||||
// the settings panel was shown or hidden.
|
||||
sendAnalytics(createToolbarEvent('settings'));
|
||||
APP.UI.emitEvent(UIEvents.TOGGLE_SETTINGS);
|
||||
},
|
||||
sideContainerId: 'settings_container',
|
||||
tooltipKey: 'toolbar.Settings'
|
||||
},
|
||||
|
||||
/**
|
||||
* The descriptor of the "Share YouTube video" toolbar button.
|
||||
*/
|
||||
sharedvideo: {
|
||||
classNames: [ 'button', 'icon-shared-video' ],
|
||||
enabled: true,
|
||||
id: 'toolbar_button_sharedvideo',
|
||||
onClick() {
|
||||
// The 'enable' attribute is set to true if the click resulted
|
||||
// in the "start sharing video" dialog being shown, and false
|
||||
// if it resulted in the "stop sharing video" dialog being
|
||||
// shown.
|
||||
sendAnalytics(createToolbarEvent(
|
||||
'shared.video.toggled',
|
||||
{
|
||||
enable: !APP.UI.isSharedVideoShown()
|
||||
}));
|
||||
APP.UI.emitEvent(UIEvents.SHARED_VIDEO_CLICKED);
|
||||
},
|
||||
popups: [
|
||||
{
|
||||
dataAttr: 'toolbar.sharedVideoMutedPopup',
|
||||
id: 'sharedVideoMutedPopup'
|
||||
}
|
||||
],
|
||||
tooltipKey: 'toolbar.sharedvideo'
|
||||
},
|
||||
|
||||
videoquality: {
|
||||
component: VideoQualityButton
|
||||
}
|
||||
};
|
||||
|
||||
Object.keys(defaultButtons).forEach(name => {
|
||||
const button = defaultButtons[name];
|
||||
|
||||
if (!button.isDisplayed) {
|
||||
button.isDisplayed = _isDisplayed;
|
||||
}
|
||||
});
|
||||
|
||||
return defaultButtons;
|
||||
}
|
||||
|
||||
/**
|
||||
* The default implementation of the {@code isDisplayed} method of the toolbar
|
||||
* button definition returned by {@link getDefaultButtons()}.
|
||||
*
|
||||
* @returns {boolean} If the user intarface is full i.e. not filmstrip-only,
|
||||
* then {@code true}; otherwise, {@code false}.
|
||||
*/
|
||||
function _isDisplayed() {
|
||||
return !interfaceConfig.filmStripOnly;
|
||||
}
|
|
@ -1,10 +1,5 @@
|
|||
// @flow
|
||||
|
||||
import SideContainerToggler
|
||||
from '../../../modules/UI/side_pannels/SideContainerToggler';
|
||||
|
||||
import getDefaultButtons from './defaultToolbarButtons';
|
||||
|
||||
declare var interfaceConfig: Object;
|
||||
|
||||
export {
|
||||
|
@ -13,92 +8,6 @@ export {
|
|||
getButton
|
||||
} from './functions.native';
|
||||
|
||||
/**
|
||||
* Returns an object which contains the default buttons for the primary and
|
||||
* secondary toolbars.
|
||||
*
|
||||
* @param {Object} buttonHandlers - Contains additional toolbox button
|
||||
* handlers.
|
||||
* @returns {Object}
|
||||
*/
|
||||
export function getDefaultToolboxButtons(buttonHandlers: Object): Object {
|
||||
let toolbarButtons = {
|
||||
primaryToolbarButtons: new Map(),
|
||||
secondaryToolbarButtons: new Map()
|
||||
};
|
||||
|
||||
if (typeof interfaceConfig !== 'undefined'
|
||||
&& interfaceConfig.TOOLBAR_BUTTONS) {
|
||||
|
||||
toolbarButtons
|
||||
= interfaceConfig.TOOLBAR_BUTTONS.reduce(
|
||||
(acc, buttonName) => {
|
||||
const buttons = getDefaultButtons();
|
||||
let button = buttons ? buttons[buttonName] : null;
|
||||
const currentButtonHandlers = buttonHandlers[buttonName];
|
||||
|
||||
if (button) {
|
||||
const place = _getToolbarButtonPlace(buttonName);
|
||||
|
||||
button.buttonName = buttonName;
|
||||
|
||||
if (currentButtonHandlers) {
|
||||
button = {
|
||||
...button,
|
||||
...currentButtonHandlers
|
||||
};
|
||||
}
|
||||
|
||||
// If isDisplayed method is not defined, display the
|
||||
// button only for non-filmstripOnly mode
|
||||
if (button.isDisplayed()) {
|
||||
acc[place].set(buttonName, button);
|
||||
}
|
||||
}
|
||||
|
||||
return acc;
|
||||
},
|
||||
toolbarButtons);
|
||||
}
|
||||
|
||||
return toolbarButtons;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns toolbar class names to add while rendering.
|
||||
*
|
||||
* @param {Object} props - Props object pass to React component.
|
||||
* @returns {Object}
|
||||
* @private
|
||||
*/
|
||||
export function getToolbarClassNames(props: Object) {
|
||||
const primaryToolbarClassNames = [
|
||||
interfaceConfig.filmStripOnly
|
||||
? 'toolbar_filmstrip-only'
|
||||
: 'toolbar_primary'
|
||||
];
|
||||
const secondaryToolbarClassNames = [ 'toolbar_secondary' ];
|
||||
|
||||
if (props._visible) {
|
||||
const slideInAnimation
|
||||
= SideContainerToggler.isVisible ? 'slideInExtX' : 'slideInX';
|
||||
|
||||
primaryToolbarClassNames.push('fadeIn');
|
||||
secondaryToolbarClassNames.push(slideInAnimation);
|
||||
} else {
|
||||
const slideOutAnimation
|
||||
= SideContainerToggler.isVisible ? 'slideOutExtX' : 'slideOutX';
|
||||
|
||||
primaryToolbarClassNames.push('fadeOut');
|
||||
secondaryToolbarClassNames.push(slideOutAnimation);
|
||||
}
|
||||
|
||||
return {
|
||||
primaryToolbarClassName: primaryToolbarClassNames.join(' '),
|
||||
secondaryToolbarClassName: secondaryToolbarClassNames.join(' ')
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper for getting the height of the toolbox.
|
||||
*
|
||||
|
@ -122,18 +31,3 @@ export function isButtonEnabled(name: string) {
|
|||
return interfaceConfig.TOOLBAR_BUTTONS.indexOf(name) !== -1
|
||||
|| interfaceConfig.MAIN_TOOLBAR_BUTTONS.indexOf(name) !== -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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');
|
||||
}
|
||||
|
|
|
@ -1,17 +1,9 @@
|
|||
// @flow
|
||||
|
||||
import {
|
||||
MEDIA_TYPE,
|
||||
SET_AUDIO_AVAILABLE,
|
||||
SET_VIDEO_AVAILABLE
|
||||
} from '../base/media';
|
||||
import { MiddlewareRegistry } from '../base/redux';
|
||||
import { isLocalTrackMuted, TRACK_UPDATED } from '../base/tracks';
|
||||
|
||||
import { setToolbarButton, toggleFullScreen } from './actions';
|
||||
import {
|
||||
CLEAR_TOOLBOX_TIMEOUT,
|
||||
FULL_SCREEN_CHANGED,
|
||||
SET_TOOLBOX_TIMEOUT,
|
||||
SET_FULL_SCREEN
|
||||
} from './actionTypes';
|
||||
|
@ -34,12 +26,6 @@ MiddlewareRegistry.register(store => next => action => {
|
|||
break;
|
||||
}
|
||||
|
||||
case FULL_SCREEN_CHANGED:
|
||||
return _fullScreenChanged(store, next, action);
|
||||
|
||||
case SET_AUDIO_AVAILABLE:
|
||||
return _setMediaAvailableOrMuted(store, next, action);
|
||||
|
||||
case SET_FULL_SCREEN:
|
||||
return _setFullScreen(next, action);
|
||||
|
||||
|
@ -48,102 +34,15 @@ MiddlewareRegistry.register(store => next => action => {
|
|||
const { handler, timeoutMS } = action;
|
||||
|
||||
clearTimeout(timeoutID);
|
||||
const newTimeoutId = setTimeout(handler, timeoutMS);
|
||||
action.timeoutID = setTimeout(handler, timeoutMS);
|
||||
|
||||
action.timeoutID = newTimeoutId;
|
||||
break;
|
||||
}
|
||||
|
||||
case SET_VIDEO_AVAILABLE:
|
||||
return _setMediaAvailableOrMuted(store, next, action);
|
||||
|
||||
case TRACK_UPDATED:
|
||||
if (action.track.jitsiTrack.isLocal()) {
|
||||
return _setMediaAvailableOrMuted(store, next, action);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return next(action);
|
||||
});
|
||||
|
||||
/**
|
||||
* Updates the the redux state with the current known state of full screen.
|
||||
*
|
||||
* @param {Store} store - The redux store in which the specified action is being
|
||||
* dispatched.
|
||||
* @param {Dispatch} next - The redux dispatch function to dispatch the
|
||||
* specified action to the specified store.
|
||||
* @param {Action} action - The redux action FULL_SCREEN_CHANGED which is being
|
||||
* dispatched in the specified store.
|
||||
* @private
|
||||
* @returns {Object} The value returned by {@code next(action)}.
|
||||
*/
|
||||
function _fullScreenChanged({ dispatch }, next, action) {
|
||||
if (typeof APP === 'object') {
|
||||
dispatch(toggleFullScreen(action.fullScreen));
|
||||
}
|
||||
|
||||
return next(action);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adjusts the state of toolbar's microphone or camera button.
|
||||
*
|
||||
* @param {Store} store - The redux store.
|
||||
* @param {Function} next - The redux function to continue dispatching the
|
||||
* specified {@code action} in the specified {@code store}.
|
||||
* @param {Object} action - {@code SET_AUDIO_AVAILABLE},
|
||||
* {@code SET_VIDEO_AVAILABLE}, or {@code TRACK_UPDATED}.
|
||||
* @returns {*}
|
||||
*/
|
||||
function _setMediaAvailableOrMuted({ dispatch, getState }, next, action) {
|
||||
const result = next(action);
|
||||
|
||||
let mediaType;
|
||||
|
||||
switch (action.type) {
|
||||
case SET_AUDIO_AVAILABLE:
|
||||
mediaType = MEDIA_TYPE.AUDIO;
|
||||
break;
|
||||
|
||||
case SET_VIDEO_AVAILABLE:
|
||||
mediaType = MEDIA_TYPE.VIDEO;
|
||||
break;
|
||||
|
||||
case TRACK_UPDATED:
|
||||
mediaType
|
||||
= action.track.jitsiTrack.isAudioTrack()
|
||||
? MEDIA_TYPE.AUDIO
|
||||
: MEDIA_TYPE.VIDEO;
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new Error(`Unsupported action ${action}`);
|
||||
}
|
||||
|
||||
const state = getState();
|
||||
const { audio, video } = state['features/base/media'];
|
||||
const { available } = mediaType === MEDIA_TYPE.AUDIO ? audio : video;
|
||||
const i18nKey
|
||||
= mediaType === MEDIA_TYPE.AUDIO
|
||||
? available ? 'mute' : 'micDisabled'
|
||||
: available ? 'videomute' : 'cameraDisabled';
|
||||
const tracks = state['features/base/tracks'];
|
||||
const muted = isLocalTrackMuted(tracks, mediaType);
|
||||
|
||||
dispatch(
|
||||
setToolbarButton(
|
||||
mediaType === MEDIA_TYPE.AUDIO ? 'microphone' : 'camera',
|
||||
{
|
||||
enabled: available,
|
||||
i18n: `[content]toolbar.${i18nKey}`,
|
||||
toggled: available ? muted : true
|
||||
}));
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes an external request to enter or exit full screen mode.
|
||||
*
|
||||
|
|
|
@ -8,7 +8,6 @@ import {
|
|||
SET_DEFAULT_TOOLBOX_BUTTONS,
|
||||
SET_SUBJECT,
|
||||
SET_SUBJECT_SLIDE_IN,
|
||||
SET_TOOLBAR_BUTTON,
|
||||
SET_TOOLBAR_HOVERED,
|
||||
SET_TOOLBOX_ALWAYS_VISIBLE,
|
||||
SET_TOOLBOX_ENABLED,
|
||||
|
@ -16,7 +15,6 @@ import {
|
|||
SET_TOOLBOX_TIMEOUT_MS,
|
||||
SET_TOOLBOX_VISIBLE
|
||||
} from './actionTypes';
|
||||
import getDefaultButtons from './defaultToolbarButtons';
|
||||
|
||||
declare var interfaceConfig: Object;
|
||||
|
||||
|
@ -161,9 +159,6 @@ ReducerRegistry.register(
|
|||
subjectSlideIn: action.subjectSlideIn
|
||||
};
|
||||
|
||||
case SET_TOOLBAR_BUTTON:
|
||||
return _setToolbarButton(state, action);
|
||||
|
||||
case SET_TOOLBAR_HOVERED:
|
||||
return {
|
||||
...state,
|
||||
|
@ -204,46 +199,3 @@ ReducerRegistry.register(
|
|||
|
||||
return state;
|
||||
});
|
||||
|
||||
/**
|
||||
* Reduces the redux action {@code SET_TOOLBAR_BUTTON} in the feature toolbox.
|
||||
*
|
||||
* @param {Object} state - The redux state.
|
||||
* @param {Object} action - The redux action of type {@code SET_TOOLBAR_BUTTON}.
|
||||
* @param {Object} action.button - Object describing toolbar button.
|
||||
* @param {Object} action.buttonName - The name of the button.
|
||||
* @private
|
||||
* @returns {Object}
|
||||
*/
|
||||
function _setToolbarButton(state, { button, buttonName }): Object {
|
||||
// XXX getDefaultButtons, defaultToolbarButtons, SET_TOOLBAR_BUTTON are
|
||||
// abstractions fully implemented on Web only.
|
||||
const buttons = getDefaultButtons && getDefaultButtons();
|
||||
const buttonDefinition = buttons && buttons[buttonName];
|
||||
|
||||
// We don't need to update if the button shouldn't be displayed
|
||||
if (!buttonDefinition || !buttonDefinition.isDisplayed()) {
|
||||
return state;
|
||||
}
|
||||
|
||||
const { primaryToolbarButtons, secondaryToolbarButtons } = state;
|
||||
let selectedButton = primaryToolbarButtons.get(buttonName);
|
||||
let place = 'primaryToolbarButtons';
|
||||
|
||||
if (!selectedButton) {
|
||||
selectedButton = secondaryToolbarButtons.get(buttonName);
|
||||
place = 'secondaryToolbarButtons';
|
||||
}
|
||||
|
||||
selectedButton = {
|
||||
...selectedButton,
|
||||
...button
|
||||
};
|
||||
|
||||
const updatedToolbar = state[place].set(buttonName, selectedButton);
|
||||
|
||||
return {
|
||||
...state,
|
||||
[place]: new Map(updatedToolbar)
|
||||
};
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue