WiP(invite-ui): Initial move of invite UI to invite button (#1950)

* WiP(invite-ui): Initial move of invite UI to invite button

* Adjusts styling to fit both horizontal and vertical filmstrip

* Removes comment and functions not needed

* [squash] Addressing various review comments

* [squash] Move invite options to a separate config

* [squash] Adjust invite button styles until we fix the whole UI theme

* [squash] Fix the remote videos scroll

* [squash]:Do not show popup menu when 1 option is available

* [squash]: Disable the invite button in filmstrip mode

* feat(connection-indicator): implement automatic hiding on good connection (#2009)

* ref(connection-stats): use PropTypes package

* feat(connection-stats): display a summary of the connection quality

* feat(connection-indicator): show empty bars for interrupted connection

* feat(connection-indicator): change background color based on status

* feat(connection-indicator): implement automatic hiding on good connection

* fix(connection-indicator): explicitly set font size

Currently non-react code will set an icon size on ConnectionIndicator.
This doesn't work on initial call join in vertical filmstrip after
some changes to support hiding the indicator. The chosen fix is
passing in the icon size to mirror what would happe with full
filmstrip reactification.

* ref(connection-stats): rename statuses

* feat(connection-indicator): make hiding behavior configurable

The original implementation made the auto hiding of the indicator
configured in interfaceConfig.

* fix(connection-indicator): readd class expected by torture tests

* fix(connection-indicator): change connection quality display styling

Bold the connection summary in the stats popover so it stands out.
Change the summaries so there are only three--strong, nonoptimal,
poor.

* fix(connection-indicator): gray background on lost connection

* feat(icons): add new gsm bars icon

* feat(connection-indicator): use new 3-bar icon

* ref(icons): remove icon-connection and icon-connection-lost

Both have been replaced by icon-gsm-bars so they are not
being referenced anymore. Mobile looks to have connect-lost
as a separate icon in font-icons/jitsi.json.

* fix(defaultToolbarButtons): Fixes unresolved InfoDialogButton component problem

* [squash]: Makes invite button fit the container

* [squash]:Addressing invite truncate, remote menu position and comment

* [squash]:Fix z-index in horizontal mode, z-index in lonely call

* [squash]: Fix filmstripOnly property, remove important from css
This commit is contained in:
yanas 2017-10-03 11:30:42 -05:00 committed by GitHub
parent dfebd692f3
commit 86fcfcc535
26 changed files with 766 additions and 533 deletions

View File

@ -895,13 +895,6 @@ export default {
let user = room.getParticipantById(id);
return user && user.isModerator();
},
/**
* Check if SIP is supported.
* @returns {boolean}
*/
sipGatewayEnabled() {
return room.isSIPCallingSupported();
},
get membersCount() {
return room.getParticipants().length + 1;
},

View File

@ -48,21 +48,66 @@
&__videos {
@extend %align-right;
position:relative;
height:196px;
padding: 0;
/* The filmstrip should not be covered by the left toolbar. */
bottom: 0;
width:auto;
transition: bottom 2s;
overflow: visible !important;
/*!!! Removes the gap between the local video container and the remote
videos. */
font-size: 0pt;
&#remoteVideos {
border: $thumbnailsBorder solid transparent;
padding-left: $defaultToolbarSize + 5;
}
}
/**
* The local video identifier.
*/
&#filmstripLocalVideo {
bottom: 32px;
flex-direction: column;
/**
* The invite button style.
*/
.filmstrip__invite {
padding-bottom: 5px;
margin-left: 2px;
}
/**
* The invite button group style.
* TOFIX: use AtlasKit.ButtonGroup if it starts supporting different
* flex grow options for the buttons.
*/
.invite-button-group {
display: inline-flex;
justify-content: space-between;
width: 100%;
& button {
background: $toolbarBackground;
flex-grow: 1;
flex-shrink: 1;
overflow: hidden;
}
& > * {
color: $toolbarButtonColor;
flex-grow: 0;
flex-shrink: 0;
margin-left: 2px;
}
/**
* Making sure any svg-s in an invite button group will be
* colored the way we want.
*/
& path {
fill: $toolbarButtonColor;
}
}
}
&.hidden {
bottom: -196px;

View File

@ -24,10 +24,6 @@
*/
z-index: #{$tooltipsZ + 1};
&.hide-videos {
z-index: #{$tooltipsZ - 1};
}
/**
* Hide videos by making them slight to the right.
*/
@ -50,9 +46,21 @@
}
}
/**
* Re-styles the local Video and invite button to better fit the
* vertical filmstrip layout.
*/
#filmstripLocalVideo {
bottom: 5px;
flex-direction: column-reverse;
height: auto;
justify-content: flex-end;
justify-content: flex-start;
.filmstrip__invite {
padding-bottom: 0px;
padding-top: 5px;
z-index: $dropdownZ;
}
}
/**
@ -69,10 +77,11 @@
flex: 1;
flex-direction: column;
height: auto;
overflow-x: hidden !important;
justify-content: flex-end;
.remote-videos-container {
flex-direction: column;
#filmstripRemoteVideosContainer {
flex-direction: column-reverse;
overflow-x: hidden;
}
}
@ -101,7 +110,7 @@
}
#remoteVideos {
flex-direction: column-reverse;
flex-direction: column;
flex-grow: 1;
}
@ -153,15 +162,11 @@
* be hidden.
* The class opening is for when the filmstrip is transitioning from hidden
* to visible.
* The class with-remote-videos is for when the filmstrip has remote videos
* displayed, as opposed to 1-on-1 mode where they might be hidden.
* The class without-remote-videos is for when the filmstrip is visible
* but it has no videos to display.
*/
.video-state-indicator.moveToCorner {
transition: right 0.5s;
&.with-filmstrip.with-remote-videos {
&.with-filmstrip {
&#recordingLabel {
right: 200px;
}
@ -171,11 +176,7 @@
}
}
&.with-filmstrip.without-remote-videos {
transition-delay: 0.5s;
}
&.with-filmstrip.with-remote-videos.opening {
&.with-filmstrip.opening {
transition: 0.9s;
transition-timing-function: ease-in-out;
}

View File

@ -35,13 +35,14 @@ var interfaceConfig = { // eslint-disable-line no-unused-vars
//main toolbar
'microphone', 'camera', 'desktop', 'invite', 'fullscreen', 'fodeviceselection', 'hangup', // jshint ignore:line
//extended toolbar
'profile', 'addtocall', 'contacts', 'info', 'chat', 'recording', 'etherpad', 'sharedvideo', 'dialout', 'settings', 'raisehand', 'videoquality', 'filmstrip'], // jshint ignore:line
'profile', 'contacts', 'info', 'chat', 'recording', 'etherpad', 'sharedvideo', 'settings', 'raisehand', 'videoquality', 'filmstrip'], // jshint ignore:line
/**
* Main Toolbar Buttons
* All of them should be in TOOLBAR_BUTTONS
*/
MAIN_TOOLBAR_BUTTONS: ['microphone', 'camera', 'desktop', 'invite', 'fullscreen', 'fodeviceselection', 'hangup'], // jshint ignore:line
SETTINGS_SECTIONS: ['language', 'devices', 'moderator'],
INVITE_OPTIONS: ['invite', 'dialout', 'addtocall'],
// Determines how the video would fit the screen. 'both' would fit the whole
// screen, 'height' would fit the original video height to the height of the
// screen, 'width' would fit the original video width to the width of the
@ -124,4 +125,9 @@ var interfaceConfig = { // eslint-disable-line no-unused-vars
* @type {number}
*/
CONNECTION_INDICATOR_AUTO_HIDE_TIMEOUT: 5000
/**
* The name of the application connected to the "Add people" search service.
*/
// ADD_PEOPLE_APP_NAME: ""
};

View File

@ -446,6 +446,7 @@
"hidePassword": "Hide password",
"inviteTo": "Invite people to __conferenceName__",
"invitedYouTo": "__userName__ has invited you to the __inviteURL__ conference",
"invitePeople": "Invite people",
"locked": "This call is locked. New callers must have the link and enter the password to join.",
"showPassword": "Show password",
"unlocked": "This call is unlocked. Any new caller with the link may join the call."
@ -467,7 +468,7 @@
},
"dialOut": {
"dial": "Dial",
"dialOut": "Call a phone number",
"dialOut": "Call a #",
"statusMessage": "is now __status__",
"enterPhone": "Enter phone number",
"phoneNotAllowed": "Oh, we don't support that destination yet! Sorry!"

View File

@ -37,7 +37,6 @@ import {
showDialPadButton,
showEtherpadButton,
showSharedVideoButton,
showDialOutButton,
showToolbox
} from '../../react/features/toolbox';
import {
@ -474,7 +473,6 @@ UI.onPeerVideoTypeChanged
UI.updateLocalRole = isModerator => {
VideoLayout.showModeratorIndicator();
APP.store.dispatch(showDialOutButton(isModerator));
APP.store.dispatch(showSharedVideoButton());
Recording.showRecordingButton(isModerator);

View File

@ -193,16 +193,6 @@ const Filmstrip = {
}
},
/**
* Returns the width of filmstip
* @returns {number} width
*/
getFilmstripWidth() {
return this.filmstrip.innerWidth()
- parseInt(this.filmstrip.css('paddingLeft'), 10)
- parseInt(this.filmstrip.css('paddingRight'), 10);
},
/**
* Calculates the size for thumbnails: local and remote one
* @returns {*|{localVideo, remoteVideo}}
@ -433,11 +423,14 @@ const Filmstrip = {
promises.push(new Promise((resolve) => {
// Let CSS take care of height in vertical filmstrip mode.
if (interfaceConfig.VERTICAL_FILMSTRIP) {
resolve();
$('#filmstripLocalVideo').animate({
// adds 4 px because of small video 2px border
width: local.thumbWidth + 4
}, this._getAnimateOptions(animate, resolve));
} else {
this.filmstrip.animate({
// adds 2 px because of small video 1px border
height: remote.thumbHeight + 2
// adds 4 px because of small video 2px border
height: remote.thumbHeight + 4
}, this._getAnimateOptions(animate, resolve));
}
}));

View File

@ -29,7 +29,7 @@ function LocalVideo(VideoLayout, emitter) {
this.isLocal = true;
this.emitter = emitter;
this.statsPopoverLocation = interfaceConfig.VERTICAL_FILMSTRIP
? 'left bottom' : 'top center';
? 'left top' : 'top center';
Object.defineProperty(this, 'id', {
get: function () {

View File

@ -43,7 +43,7 @@ function RemoteVideo(user, VideoLayout, emitter) {
this.hasRemoteVideoMenu = false;
this._supportsRemoteControl = false;
this.statsPopoverLocation = interfaceConfig.VERTICAL_FILMSTRIP
? 'left top' : 'top center';
? 'left bottom' : 'top center';
this.addRemoteVideoContainer();
this.updateIndicators();
this.setDisplayName();

View File

@ -151,3 +151,14 @@ export const SET_RECEIVE_VIDEO_QUALITY = Symbol('SET_RECEIVE_VIDEO_QUALITY');
* }
*/
export const SET_ROOM = Symbol('SET_ROOM');
/**
* The type of (redux) action, which indicates if a SIP gateway is enabled on
* the server.
*
* {
* type: SET_SIP_GATEWAY_ENABLED
* isSIPGatewayEnabled: boolean
* }
*/
export const SET_SIP_GATEWAY_ENABLED = Symbol('SET_SIP_GATEWAY_ENABLED');

View File

@ -14,7 +14,8 @@ import {
SET_AUDIO_ONLY,
SET_PASSWORD,
SET_RECEIVE_VIDEO_QUALITY,
SET_ROOM
SET_ROOM,
SET_SIP_GATEWAY_ENABLED
} from './actionTypes';
import { VIDEO_QUALITY_LEVELS } from './constants';
import { isRoomValid } from './functions';
@ -60,6 +61,9 @@ ReducerRegistry.register('features/base/conference', (state = {}, action) => {
case SET_ROOM:
return _setRoom(state, action);
case SET_SIP_GATEWAY_ENABLED:
return _setSIPGatewayEnabled(state, action);
}
return state;
@ -363,3 +367,17 @@ function _setRoom(state, action) {
*/
return set(state, 'room', room);
}
/**
* Reduces a specific Redux action SET_SIP_GATEWAY_ENABLED of the feature
* base/conference.
*
* @param {Object} state - The Redux state of the feature base/conference.
* @param {Action} action - The Redux action SET_SIP_GATEWAY_ENABLED to reduce.
* @private
* @returns {Object} The new state of the feature base/conference after the
* reduction of the specified action.
*/
function _setSIPGatewayEnabled(state, action) {
return set(state, 'isSIPGatewayEnabled', action.isSIPGatewayEnabled);
}

View File

@ -74,7 +74,7 @@ class Conference extends Component {
<div id = 'videoconference_page'>
<div id = 'videospace'>
<LargeVideo />
<Filmstrip displayToolbox = { filmStripOnly } />
<Filmstrip filmstripOnly = { filmStripOnly } />
</div>
{ filmStripOnly ? null : <Toolbox /> }

View File

@ -1,5 +1,3 @@
import { openDialog } from '../../features/base/dialog';
import {
DIAL_OUT_CANCELED,
DIAL_OUT_CODES_UPDATED,
@ -7,8 +5,6 @@ import {
PHONE_NUMBER_CHECKED
} from './actionTypes';
import { DialOutDialog } from './components';
declare var $: Function;
declare var config: Object;
@ -76,16 +72,6 @@ export function checkDialNumber(dialNumber) {
};
}
/**
* Opens the dial-out dialog.
*
* @returns {Function}
*/
export function openDialOutDialog() {
return openDialog(DialOutDialog);
}
/**
* Sends an ajax request for dial-out country codes.
*

View File

@ -5,6 +5,7 @@ import PropTypes from 'prop-types';
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { InviteButton } from '../../invite';
import { Toolbox } from '../../toolbox';
import { setFilmstripHovered } from '../actions';
@ -48,9 +49,9 @@ class Filmstrip extends Component {
dispatch: PropTypes.func,
/**
* Whether or not the toolbox should be displayed within the filmstrip.
* Whether or not the conference is in filmstripOnly mode.
*/
displayToolbox: PropTypes.bool
filmstripOnly: PropTypes.bool
};
/**
@ -100,7 +101,7 @@ class Filmstrip extends Component {
return (
<div className = { filmstripClassNames }>
{ this.props.displayToolbox ? <Toolbox /> : null }
{ this.props.filmstripOnly ? <Toolbox /> : null }
<div
className = 'filmstrip__videos'
id = 'remoteVideos'>
@ -108,7 +109,9 @@ class Filmstrip extends Component {
className = 'filmstrip__videos'
id = 'filmstripLocalVideo'
onMouseOut = { this._onMouseOut }
onMouseOver = { this._onMouseOver } />
onMouseOver = { this._onMouseOver }>
{ this.props.filmstripOnly ? null : <InviteButton /> }
</div>
<div
className = 'filmstrip__videos'
id = 'filmstripRemoteVideos'>

View File

@ -5,7 +5,7 @@ import {
UPDATE_DIAL_IN_NUMBERS_FAILED,
UPDATE_DIAL_IN_NUMBERS_SUCCESS
} from './actionTypes';
import { AddPeopleDialog, InviteDialog } from './components';
import { InviteDialog } from './components';
declare var $: Function;
@ -18,15 +18,6 @@ export function openInviteDialog() {
return openDialog(InviteDialog);
}
/**
* Opens the Add People Dialog.
*
* @returns {Function}
*/
export function openAddPeopleDialog() {
return openDialog(AddPeopleDialog);
}
/**
* Opens the inline conference info dialog.
*

View File

@ -0,0 +1 @@

View File

@ -0,0 +1,231 @@
import PropTypes from 'prop-types';
import React, { Component } from 'react';
import { connect } from 'react-redux';
import Button from '@atlaskit/button';
import DropdownMenu from '@atlaskit/dropdown-menu';
import { translate } from '../../base/i18n';
import { getLocalParticipant, PARTICIPANT_ROLE } from '../../base/participants';
import { openDialog } from '../../base/dialog';
import { AddPeopleDialog, InviteDialog } from '.';
import { DialOutDialog } from '../../dial-out';
import { isInviteOptionEnabled, getInviteOptionPosition } from '../functions';
declare var interfaceConfig: Object;
const SHARE_LINK_OPTION = 'invite';
const DIAL_OUT_OPTION = 'dialout';
const ADD_TO_CALL_OPTION = 'addtocall';
/**
* The button that provides different invite options.
*/
class InviteButton extends Component {
/**
* {@code InviteButton}'s property types.
*
* @static
*/
static propTypes = {
/**
* Indicates if the "Add to call" feature is available.
*/
_isAddToCallAvailable: PropTypes.bool,
/**
* Indicates if the "Dial out" feature is available.
*/
_isDialOutAvailable: PropTypes.bool,
/**
* The function opening the dialog.
*/
openDialog: PropTypes.func,
/**
* Invoked to obtain translated strings.
*/
t: PropTypes.func
};
/**
* Initializes a new {@code InviteButton} instance.
*
* @param {Object} props - The read-only properties with which the new
* instance is to be initialized.
*/
constructor(props) {
super(props);
this._onInviteClick = this._onInviteClick.bind(this);
this._onInviteOptionSelected = this._onInviteOptionSelected.bind(this);
this._updateInviteItems = this._updateInviteItems.bind(this);
this._updateInviteItems(this.props);
}
/**
* Implements React's {@link Component#componentWillReceiveProps()}.
*
* @inheritdoc
* @param {Object} nextProps - The read-only props which this Component will
* receive.
* @returns {void}
*/
componentWillReceiveProps(nextProps) {
if (this.props._isDialOutAvailable !== nextProps._isDialOutAvailable
|| this.props._isAddToCallAvailable
!== nextProps._isAddToCallAvailable) {
this._updateInviteItems(nextProps);
}
}
/**
* Renders the content of this component.
*
* @returns {ReactElement}
*/
render() {
const { t } = this.props;
const { VERTICAL_FILMSTRIP } = interfaceConfig;
return (
<div className = 'filmstrip__invite'>
<div className = 'invite-button-group'>
<Button
onClick = { this._onInviteClick }
shouldFitContainer = { true }>
{ t('invite.invitePeople') }
</Button>
{ this.props._isDialOutAvailable
|| this.props._isAddToCallAvailable
? <DropdownMenu
items = { this.state.inviteOptions }
onItemActivated = { this._onInviteOptionSelected }
position = { VERTICAL_FILMSTRIP
? 'bottom right'
: 'top right' }
shouldFlip = { true }
triggerType = 'button' />
: null }
</div>
</div>
);
}
/**
* Handles the click of the invite button.
*
* @private
* @returns {void}
*/
_onInviteClick() {
this.props.openDialog(InviteDialog);
}
/**
* Handles selection of the invite options.
*
* @param { Object } option - The invite option that has been selected from
* the dropdown menu.
* @private
* @returns {void}
*/
_onInviteOptionSelected(option) {
this.state.inviteOptions[0].items.forEach(item => {
if (item.content === option.item.content) {
item.action();
}
});
}
/**
* Updates the invite items list depending on the availability of the
* features.
*
* @param {Object} props - The read-only properties of the component.
* @private
* @returns {void}
*/
_updateInviteItems(props) {
const { t } = this.props;
const inviteItems = [];
inviteItems.splice(
getInviteOptionPosition(SHARE_LINK_OPTION),
0,
{
content: t('toolbar.invite'),
action: () => this.props.openDialog(InviteDialog)
}
);
if (props._isDialOutAvailable) {
inviteItems.splice(
getInviteOptionPosition(DIAL_OUT_OPTION),
0,
{
content: t('dialOut.dialOut'),
action: () => this.props.openDialog(DialOutDialog)
}
);
}
if (props._isAddToCallAvailable) {
inviteItems.splice(
getInviteOptionPosition(ADD_TO_CALL_OPTION),
0,
{
content: interfaceConfig.ADD_PEOPLE_APP_NAME,
action: () => this.props.openDialog(AddPeopleDialog)
}
);
}
this.state = {
/**
* The list of invite options.
*/
inviteOptions: [
{
items: inviteItems
}
]
};
}
}
/**
* Maps (parts of) the Redux state to the associated {@code InviteButton}'s
* props.
*
* @param {Object} state - The Redux state.
* @private
* @returns {{
* _isAddToCallAvailable: boolean,
* _isDialOutAvailable: boolean
* }}
*/
function _mapStateToProps(state) {
const { enableUserRolesBasedOnToken } = state['features/base/config'];
const { conference } = state['features/base/conference'];
const { isGuest } = state['features/jwt'];
return {
_isAddToCallAvailable: !isGuest
&& isInviteOptionEnabled(ADD_TO_CALL_OPTION),
_isDialOutAvailable:
getLocalParticipant(state).role === PARTICIPANT_ROLE.MODERATOR
&& conference && conference.isSIPCallingSupported()
&& isInviteOptionEnabled(DIAL_OUT_OPTION)
&& (!enableUserRolesBasedOnToken || !isGuest)
};
}
export default translate(connect(
_mapStateToProps, { openDialog })(InviteButton));

View File

@ -1,3 +1,4 @@
export { default as AddPeopleDialog } from './AddPeopleDialog';
export { default as InfoDialogButton } from './InfoDialogButton';
export { default as InviteButton } from './InviteButton';
export { default as InviteDialog } from './InviteDialog';

View File

@ -1,4 +1,5 @@
declare var $: Function;
declare var interfaceConfig: Object;
/**
* Sends an ajax request to a directory service.
@ -76,3 +77,27 @@ export function inviteRooms(conference, rooms) {
}
}
}
/**
* Indicates if an invite option is enabled in the configuration.
*
* @param {string} name - The name of the option defined in
* interfaceConfig.INVITE_OPTIONS.
* @returns {boolean} - True to indicate that the given invite option is
* enabled, false - otherwise.
*/
export function isInviteOptionEnabled(name) {
return interfaceConfig.INVITE_OPTIONS.indexOf(name) !== -1;
}
/**
* Get the position of the invite option in the interfaceConfig.INVITE_OPTIONS
* list.
*
* @param {string} optionName - The invite option name.
* @private
* @returns {number} - The position of the option in the list.
*/
export function getInviteOptionPosition(optionName) {
return interfaceConfig.INVITE_OPTIONS.indexOf(optionName);
}

View File

@ -3,7 +3,6 @@ import React, { Component } from 'react';
import { connect } from 'react-redux';
import { translate } from '../../base/i18n';
import { shouldRemoteVideosBeVisible } from '../../filmstrip';
/**
* Implements a React {@link Component} which displays the current state of
@ -38,14 +37,6 @@ class RecordingLabel extends Component {
*/
_labelDisplayConfiguration: PropTypes.object,
/**
* Whether or not remote videos within the filmstrip are currently
* visible. Depending on the visibility state, coupled with filmstrip
* visibility, CSS classes will be set to allow for adjusting of
* {@code RecordingLabel} positioning.
*/
_remoteVideosVisible: PropTypes.bool,
/**
* Invoked to obtain translated string.
*/
@ -106,9 +97,7 @@ class RecordingLabel extends Component {
centered ? '' : 'moveToCorner',
this.state.filmstripBecomingVisible ? 'opening' : '',
this.props._filmstripVisible
? 'with-filmstrip' : 'without-filmstrip',
this.props._remoteVideosVisible
? 'with-remote-videos' : 'without-remote-videos'
? 'with-filmstrip' : 'without-filmstrip'
].join(' ');
return (
@ -137,8 +126,7 @@ class RecordingLabel extends Component {
* @private
* @returns {{
* _filmstripVisible: boolean,
* _labelDisplayConfiguration: Object,
* _remoteVideosVisible: boolean,
* _labelDisplayConfiguration: Object
* }}
*/
function _mapStateToProps(state) {
@ -159,14 +147,7 @@ function _mapStateToProps(state) {
*
* @type {Object}
*/
_labelDisplayConfiguration: labelDisplayConfiguration,
/**
* Whether or not remote videos are displayed in the filmstrip.
*
* @type {boolean}
*/
_remoteVideosVisible: shouldRemoteVideosBeVisible(state)
_labelDisplayConfiguration: labelDisplayConfiguration
};
}

View File

@ -107,7 +107,7 @@ class RemoteVideoMenuTriggerButton extends Component {
content = { content }
onPopoverOpen = { this._onShowRemoteMenu }
position = { interfaceConfig.VERTICAL_FILMSTRIP
? 'left middle' : 'top center' }>
? 'left bottom' : 'top center' }>
<span
className = 'popover-trigger remote-video-menu-trigger'>
<i

View File

@ -324,29 +324,6 @@ export function showSharedVideoButton(): Function {
};
}
/**
* Shows the dial out button if it's required and appropriate
* flag is passed.
*
* @param {boolean} show - Flag showing whether to show button or not.
* @returns {Function}
*/
export function showDialOutButton(show: boolean): Function {
return (dispatch: Dispatch<*>, getState: Function) => {
const buttonName = 'dialout';
if (show
&& APP.conference.sipGatewayEnabled()
&& isButtonEnabled(buttonName)
&& (!config.enableUserRolesBasedOnToken
|| !getState()['features/jwt'].isGuest)) {
dispatch(setToolbarButton(buttonName, {
hidden: false
}));
}
};
}
/**
* Shows the toolbox for specified timeout.
*

View File

@ -1,16 +1,16 @@
/* @flow */
import React from 'react';
import _ from 'lodash';
import { ParticipantCounter } from '../contact-list';
import { openDeviceSelectionDialog } from '../device-selection';
import { openDialOutDialog } from '../dial-out';
import {
InfoDialogButton,
openAddPeopleDialog,
openInviteDialog
} from '../invite';
import { VideoQualityButton } from '../video-quality';
import UIEvents from '../../../service/UI/UIEvents';
@ -21,412 +21,394 @@ declare var APP: Object;
declare var interfaceConfig: Object;
declare var JitsiMeetJS: Object;
let buttons: Object = {};
/**
* All toolbar buttons' descriptors.
* Returns a map of all button descriptors and according properties.
*
* @returns {*} - The maps of default button descriptors.
*/
const buttons: Object = {
addtocall: {
classNames: [ 'button', 'icon-add' ],
enabled: true,
id: 'toolbar_button_add',
isDisplayed: () => !APP.store.getState()['features/jwt'].isGuest,
onClick(dispatch) {
JitsiMeetJS.analytics.sendEvent('toolbar.add.clicked');
function getDefaultButtons() {
if (!_.isEmpty(buttons)) {
return buttons;
}
dispatch(openAddPeopleDialog());
},
tooltipKey: 'toolbar.addPeople'
},
buttons = {
/**
* The descriptor of the camera toolbar button.
*/
camera: {
classNames: [ 'button', 'icon-camera' ],
enabled: true,
isDisplayed: () => true,
id: 'toolbar_button_camera',
onClick() {
const newVideoMutedState = !APP.conference.isLocalVideoMuted();
/**
* The descriptor of the camera toolbar button.
*/
camera: {
classNames: [ 'button', 'icon-camera' ],
enabled: true,
isDisplayed: () => true,
id: 'toolbar_button_camera',
onClick() {
const newVideoMutedState = !APP.conference.isLocalVideoMuted();
if (newVideoMutedState) {
JitsiMeetJS.analytics.sendEvent('toolbar.video.enabled');
} else {
JitsiMeetJS.analytics.sendEvent('toolbar.video.disabled');
}
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;
}
JitsiMeetJS.analytics.sendEvent('shortcut.videomute.toggled');
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() {
JitsiMeetJS.analytics.sendEvent('toolbar.chat.toggled');
APP.UI.emitEvent(UIEvents.TOGGLE_CHAT);
},
shortcut: 'C',
shortcutAttr: 'toggleChatPopover',
shortcutFunc() {
JitsiMeetJS.analytics.sendEvent('shortcut.chat.toggled');
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() {
JitsiMeetJS.analytics.sendEvent(
'toolbar.contacts.toggled');
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() {
if (APP.conference.isSharingScreen) {
JitsiMeetJS.analytics.sendEvent('toolbar.screen.disabled');
} else {
JitsiMeetJS.analytics.sendEvent('toolbar.screen.enabled');
}
APP.UI.emitEvent(UIEvents.TOGGLE_SCREENSHARING);
},
popups: [
{
dataAttr: 'audioOnly.featureToggleDisabled',
dataInterpolate: { feature: 'screen sharing' },
id: 'screenshareWhileAudioOnly'
}
],
shortcut: 'D',
shortcutAttr: 'toggleDesktopSharingPopover',
shortcutFunc() {
JitsiMeetJS.analytics.sendEvent('shortcut.screen.toggled');
// eslint-disable-next-line no-empty-function
APP.conference.toggleScreenSharing().catch(() => {});
},
shortcutDescription: 'keyboardShortcuts.toggleScreensharing',
tooltipKey: 'toolbar.sharescreen'
},
/**
* The descriptor of the dial out toolbar button.
*/
dialout: {
classNames: [ 'button', 'icon-telephone' ],
enabled: true,
// Will be displayed once the SIP calls functionality is detected.
hidden: true,
id: 'toolbar_button_dial_out',
onClick(dispatch) {
JitsiMeetJS.analytics.sendEvent('toolbar.sip.clicked');
dispatch(openDialOutDialog());
},
tooltipKey: 'dialOut.dialOut'
},
/**
* 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) {
JitsiMeetJS.analytics.sendEvent(
'toolbar.fodeviceselection.toggled');
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() {
JitsiMeetJS.analytics.sendEvent('toolbar.sip.dialpad.clicked');
},
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() {
JitsiMeetJS.analytics.sendEvent('toolbar.etherpad.clicked');
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() {
JitsiMeetJS.analytics.sendEvent('toolbar.fullscreen.enabled');
APP.UI.emitEvent(UIEvents.TOGGLE_FULLSCREEN);
},
shortcut: 'S',
shortcutAttr: 'toggleFullscreenPopover',
shortcutDescription: 'keyboardShortcuts.fullScreen',
shortcutFunc() {
JitsiMeetJS.analytics.sendEvent('shortcut.fullscreen.toggled');
APP.UI.toggleFullScreen();
},
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() {
JitsiMeetJS.analytics.sendEvent('toolbar.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 toolbar button which shows the invite user dialog.
*/
invite: {
classNames: [ 'button', 'icon-link' ],
enabled: true,
id: 'toolbar_button_link',
onClick(dispatch) {
JitsiMeetJS.analytics.sendEvent('toolbar.invite.clicked');
dispatch(openInviteDialog());
},
tooltipKey: 'toolbar.invite'
},
/**
* 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();
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(
'#unableToUnmutePopup', true, 5000);
if (newVideoMutedState) {
JitsiMeetJS.analytics.sendEvent('toolbar.video.enabled');
} else {
JitsiMeetJS.analytics.sendEvent('toolbar.audio.unmuted');
APP.UI.emitEvent(UIEvents.AUDIO_MUTED, false, true);
JitsiMeetJS.analytics.sendEvent('toolbar.video.disabled');
}
} else {
JitsiMeetJS.analytics.sendEvent('toolbar.audio.muted');
APP.UI.emitEvent(UIEvents.AUDIO_MUTED, true, true);
}
},
popups: [
{
dataAttr: 'toolbar.micMutedPopup',
id: 'micMutedPopup'
APP.UI.emitEvent(UIEvents.VIDEO_MUTED, newVideoMutedState);
},
{
dataAttr: 'toolbar.unableToUnmutePopup',
id: 'unableToUnmutePopup'
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;
}
JitsiMeetJS.analytics.sendEvent('shortcut.videomute.toggled');
APP.conference.toggleVideoMuted();
},
{
dataAttr: 'toolbar.talkWhileMutedPopup',
id: 'talkWhileMutedPopup'
}
],
shortcut: 'M',
shortcutAttr: 'mutePopover',
shortcutFunc() {
JitsiMeetJS.analytics.sendEvent('shortcut.audiomute.toggled');
APP.conference.toggleAudioMuted();
shortcutDescription: 'keyboardShortcuts.videoMute',
tooltipKey: 'toolbar.videomute'
},
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() {
JitsiMeetJS.analytics.sendEvent('toolbar.raiseHand.clicked');
APP.conference.maybeToggleRaisedHand();
/**
* 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() {
JitsiMeetJS.analytics.sendEvent('toolbar.chat.toggled');
APP.UI.emitEvent(UIEvents.TOGGLE_CHAT);
},
shortcut: 'C',
shortcutAttr: 'toggleChatPopover',
shortcutFunc() {
JitsiMeetJS.analytics.sendEvent('shortcut.chat.toggled');
APP.UI.toggleChat();
},
shortcutDescription: 'keyboardShortcuts.toggleChat',
sideContainerId: 'chat_container',
tooltipKey: 'toolbar.chat'
},
shortcut: 'R',
shortcutAttr: 'raiseHandPopover',
shortcutDescription: 'keyboardShortcuts.raiseHand',
shortcutFunc() {
JitsiMeetJS.analytics.sendEvent('shortcut.raisehand.clicked');
APP.conference.maybeToggleRaisedHand();
/**
* The descriptor of the contact list toolbar button.
*/
contacts: {
childComponent: ParticipantCounter,
classNames: [ 'button', 'icon-contactList' ],
enabled: true,
id: 'toolbar_contact_list',
onClick() {
JitsiMeetJS.analytics.sendEvent(
'toolbar.contacts.toggled');
APP.UI.emitEvent(UIEvents.TOGGLE_CONTACT_LIST);
},
sideContainerId: 'contacts_container',
tooltipKey: 'bottomtoolbar.contactlist'
},
tooltipKey: 'toolbar.raiseHand'
},
/**
* The descriptor of the recording toolbar button. Requires additional
* initialization in the recording module.
*/
recording: {
classNames: [ 'button' ],
enabled: true,
/**
* The descriptor of the desktop sharing toolbar button.
*/
desktop: {
classNames: [ 'button', 'icon-share-desktop' ],
enabled: true,
id: 'toolbar_button_desktopsharing',
onClick() {
if (APP.conference.isSharingScreen) {
JitsiMeetJS.analytics.sendEvent('toolbar.screen.disabled');
} else {
JitsiMeetJS.analytics.sendEvent('toolbar.screen.enabled');
}
APP.UI.emitEvent(UIEvents.TOGGLE_SCREENSHARING);
},
popups: [
{
dataAttr: 'audioOnly.featureToggleDisabled',
dataInterpolate: { feature: 'screen sharing' },
id: 'screenshareWhileAudioOnly'
}
],
shortcut: 'D',
shortcutAttr: 'toggleDesktopSharingPopover',
shortcutFunc() {
JitsiMeetJS.analytics.sendEvent('shortcut.screen.toggled');
// 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() {
JitsiMeetJS.analytics.sendEvent('toolbar.settings.toggled');
APP.UI.emitEvent(UIEvents.TOGGLE_SETTINGS);
// eslint-disable-next-line no-empty-function
APP.conference.toggleScreenSharing().catch(() => {});
},
shortcutDescription: 'keyboardShortcuts.toggleScreensharing',
tooltipKey: 'toolbar.sharescreen'
},
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() {
JitsiMeetJS.analytics.sendEvent('toolbar.sharedvideo.clicked');
APP.UI.emitEvent(UIEvents.SHARED_VIDEO_CLICKED);
/**
* 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) {
JitsiMeetJS.analytics.sendEvent(
'toolbar.fodeviceselection.toggled');
dispatch(openDeviceSelectionDialog());
},
sideContainerId: 'settings_container',
tooltipKey: 'toolbar.Settings'
},
popups: [
{
dataAttr: 'toolbar.sharedVideoMutedPopup',
id: 'sharedVideoMutedPopup'
}
],
tooltipKey: 'toolbar.sharedvideo'
},
videoquality: {
component: VideoQualityButton
}
};
/**
* 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() {
JitsiMeetJS.analytics.sendEvent('toolbar.sip.dialpad.clicked');
},
tooltipKey: 'toolbar.dialpad'
},
Object.keys(buttons).forEach(name => {
const button = buttons[name];
/**
* The descriptor of the etherpad toolbar button.
*/
etherpad: {
classNames: [ 'button', 'icon-share-doc' ],
enabled: true,
hidden: true,
id: 'toolbar_button_etherpad',
onClick() {
JitsiMeetJS.analytics.sendEvent('toolbar.etherpad.clicked');
APP.UI.emitEvent(UIEvents.ETHERPAD_CLICKED);
},
tooltipKey: 'toolbar.etherpad'
},
if (!button.isDisplayed) {
button.isDisplayed = () => !interfaceConfig.filmStripOnly;
}
});
/**
* The descriptor of the toolbar button which toggles full-screen mode.
*/
fullscreen: {
classNames: [ 'button', 'icon-full-screen' ],
enabled: true,
id: 'toolbar_button_fullScreen',
onClick() {
JitsiMeetJS.analytics.sendEvent('toolbar.fullscreen.enabled');
export default buttons;
APP.UI.emitEvent(UIEvents.TOGGLE_FULLSCREEN);
},
shortcut: 'S',
shortcutAttr: 'toggleFullscreenPopover',
shortcutDescription: 'keyboardShortcuts.fullScreen',
shortcutFunc() {
JitsiMeetJS.analytics.sendEvent('shortcut.fullscreen.toggled');
APP.UI.toggleFullScreen();
},
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() {
JitsiMeetJS.analytics.sendEvent('toolbar.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 toolbar button which shows the invite user
* dialog.
*/
invite: {
classNames: [ 'button', 'icon-link' ],
enabled: true,
id: 'toolbar_button_link',
onClick(dispatch: Function) {
JitsiMeetJS.analytics.sendEvent('toolbar.invite.clicked');
dispatch(openInviteDialog());
},
tooltipKey: 'toolbar.invite'
},
/**
* 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();
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(
'#unableToUnmutePopup', true, 5000);
} else {
JitsiMeetJS.analytics
.sendEvent('toolbar.audio.unmuted');
APP.UI.emitEvent(UIEvents.AUDIO_MUTED, false, true);
}
} else {
JitsiMeetJS.analytics.sendEvent('toolbar.audio.muted');
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() {
JitsiMeetJS.analytics.sendEvent('shortcut.audiomute.toggled');
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() {
JitsiMeetJS.analytics.sendEvent('toolbar.raiseHand.clicked');
APP.conference.maybeToggleRaisedHand();
},
shortcut: 'R',
shortcutAttr: 'raiseHandPopover',
shortcutDescription: 'keyboardShortcuts.raiseHand',
shortcutFunc() {
JitsiMeetJS.analytics.sendEvent('shortcut.raisehand.clicked');
APP.conference.maybeToggleRaisedHand();
},
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() {
JitsiMeetJS.analytics.sendEvent('toolbar.settings.toggled');
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() {
JitsiMeetJS.analytics.sendEvent('toolbar.sharedvideo.clicked');
APP.UI.emitEvent(UIEvents.SHARED_VIDEO_CLICKED);
},
popups: [
{
dataAttr: 'toolbar.sharedVideoMutedPopup',
id: 'sharedVideoMutedPopup'
}
],
tooltipKey: 'toolbar.sharedvideo'
},
videoquality: {
component: VideoQualityButton
}
};
Object.keys(buttons).forEach(name => {
const button = buttons[name];
if (!button.isDisplayed) {
button.isDisplayed = () => !interfaceConfig.filmStripOnly;
}
});
return buttons;
}
export default getDefaultButtons;

View File

@ -1,7 +1,7 @@
import SideContainerToggler
from '../../../modules/UI/side_pannels/SideContainerToggler';
import defaultToolbarButtons from './defaultToolbarButtons';
import getDefaultButtons from './defaultToolbarButtons';
declare var interfaceConfig: Object;
@ -27,7 +27,8 @@ export function getDefaultToolboxButtons(buttonHandlers: Object): Object {
toolbarButtons
= interfaceConfig.TOOLBAR_BUTTONS.reduce(
(acc, buttonName) => {
let button = defaultToolbarButtons[buttonName];
const buttons = getDefaultButtons();
let button = buttons ? buttons[buttonName] : null;
const currentButtonHandlers = buttonHandlers[buttonName];
if (button) {

View File

@ -15,7 +15,7 @@ import {
SET_TOOLBOX_TIMEOUT_MS,
SET_TOOLBOX_VISIBLE
} from './actionTypes';
import defaultToolbarButtons from './defaultToolbarButtons';
import getDefaultButtons from './defaultToolbarButtons';
declare var interfaceConfig: Object;
@ -209,7 +209,8 @@ ReducerRegistry.register(
* @returns {Object}
*/
function _setButton(state, { button, buttonName }): Object {
const buttonDefinition = defaultToolbarButtons[buttonName];
const buttons = getDefaultButtons();
const buttonDefinition = buttons ? buttons[buttonName] : null;
// We don't need to update if the button shouldn't be displayed
if (!buttonDefinition || !buttonDefinition.isDisplayed()) {

View File

@ -5,7 +5,6 @@ import { connect } from 'react-redux';
import { VIDEO_QUALITY_LEVELS } from '../../base/conference';
import { translate } from '../../base/i18n';
import { shouldRemoteVideosBeVisible } from '../../filmstrip';
const { HIGH, STANDARD, LOW } = VIDEO_QUALITY_LEVELS;
@ -59,12 +58,6 @@ export class VideoQualityLabel extends Component {
*/
_filmstripVisible: PropTypes.bool,
/**
* Whether or note remote videos are visible in the filmstrip,
* regardless of count. Used to determine display classes to set.
*/
_remoteVideosVisible: PropTypes.bool,
/**
* The current video resolution (height) to display a label for.
*/
@ -123,7 +116,6 @@ export class VideoQualityLabel extends Component {
_audioOnly,
_conferenceStarted,
_filmstripVisible,
_remoteVideosVisible,
_resolution,
t
} = this.props;
@ -140,12 +132,9 @@ export class VideoQualityLabel extends Component {
const baseClasses = 'video-state-indicator moveToCorner';
const filmstrip
= _filmstripVisible ? 'with-filmstrip' : 'without-filmstrip';
const remoteVideosVisible = _remoteVideosVisible
? 'with-remote-videos'
: 'without-remote-videos';
const opening = this.state.togglingToVisible ? 'opening' : '';
const classNames
= `${baseClasses} ${filmstrip} ${remoteVideosVisible} ${opening}`;
= `${baseClasses} ${filmstrip} ${opening}`;
const tooltipKey
= `videoStatus.labelTooltip${_audioOnly ? 'AudioOnly' : 'Video'}`;
@ -206,7 +195,6 @@ export class VideoQualityLabel extends Component {
* _audioOnly: boolean,
* _conferenceStarted: boolean,
* _filmstripVisible: true,
* _remoteVideosVisible: boolean,
* _resolution: number
* }}
*/
@ -219,7 +207,6 @@ function _mapStateToProps(state) {
_audioOnly: audioOnly,
_conferenceStarted: Boolean(conference),
_filmstripVisible: visible,
_remoteVideosVisible: shouldRemoteVideosBeVisible(state),
_resolution: resolution
};
}