jiti-meet/react/features/toolbox/components/web/Toolbox.js

1111 lines
33 KiB
JavaScript
Raw Normal View History

// @flow
2017-02-16 23:02:40 +00:00
import React, { Component } from 'react';
import { connect } from 'react-redux';
import {
ACTION_SHORTCUT_TRIGGERED,
createShortcutEvent,
createToolbarEvent,
sendAnalytics
} from '../../../analytics';
import { openDialog } from '../../../base/dialog';
import { translate } from '../../../base/i18n';
2017-02-16 23:02:40 +00:00
import {
getLocalParticipant,
getParticipants,
participantUpdated
} from '../../../base/participants';
import { getLocalVideoTrack, toggleScreensharing } from '../../../base/tracks';
import { ChatCounter, toggleChat } from '../../../chat';
import { toggleDocument } from '../../../etherpad';
import { openFeedbackDialog } from '../../../feedback';
import {
beginAddPeople,
InfoDialogButton,
isAddPeopleEnabled,
isDialOutEnabled
} from '../../../invite';
import { openKeyboardShortcutsDialog } from '../../../keyboard-shortcuts';
import {
LocalRecordingButton,
LocalRecordingInfoDialog
} from '../../../local-recording';
feat(recording): frontend logic can support live streaming and recording (#2952) * feat(recording): frontend logic can support live streaming and recording Instead of either live streaming or recording, now both can live together. The changes to facilitate such include the following: - Killing the state storing in Recording.js. Instead state is stored in the lib and updated in redux for labels to display the necessary state updates. - Creating a new container, Labels, for recording labels. Previously labels were manually created and positioned. The container can create a reasonable number of labels and only the container itself needs to be positioned with CSS. The VideoQualityLabel has been shoved into the container as well because it moves along with the recording labels. - The action for updating recording state has been modified to enable updating an array of recording sessions to support having multiple sessions. - Confirmation dialogs for stopping and starting a file recording session have been created, as they previously were jquery modals opened by Recording.js. - Toolbox.web displays live streaming and recording buttons based on configuration instead of recording availability. - VideoQualityLabel and RecordingLabel have been simplified to remove any positioning logic, as the Labels container handles such. - Previous recording state update logic has been moved into the RecordingLabel component. Each RecordingLabel is in charge of displaying state for a recording session. The display UX has been left alone. - Sipgw availability is no longer broadcast so remove logic depending on its state. Some moving around of code was necessary to get around linting errors about the existing code being too deeply nested (even though I didn't touch it). * work around lib-jitsi-meet circular dependency issues * refactor labels to use html base * pass in translation keys to video quality label * add video quality classnames for torture tests * break up, rearrange recorder session update listener * add comment about disabling startup resize animation * rename session to sessionData * chore(deps): update to latest lib for recording changes
2018-05-16 14:00:16 +00:00
import {
2018-07-05 11:17:45 +00:00
LiveStreamButton,
RecordButton
feat(recording): frontend logic can support live streaming and recording (#2952) * feat(recording): frontend logic can support live streaming and recording Instead of either live streaming or recording, now both can live together. The changes to facilitate such include the following: - Killing the state storing in Recording.js. Instead state is stored in the lib and updated in redux for labels to display the necessary state updates. - Creating a new container, Labels, for recording labels. Previously labels were manually created and positioned. The container can create a reasonable number of labels and only the container itself needs to be positioned with CSS. The VideoQualityLabel has been shoved into the container as well because it moves along with the recording labels. - The action for updating recording state has been modified to enable updating an array of recording sessions to support having multiple sessions. - Confirmation dialogs for stopping and starting a file recording session have been created, as they previously were jquery modals opened by Recording.js. - Toolbox.web displays live streaming and recording buttons based on configuration instead of recording availability. - VideoQualityLabel and RecordingLabel have been simplified to remove any positioning logic, as the Labels container handles such. - Previous recording state update logic has been moved into the RecordingLabel component. Each RecordingLabel is in charge of displaying state for a recording session. The display UX has been left alone. - Sipgw availability is no longer broadcast so remove logic depending on its state. Some moving around of code was necessary to get around linting errors about the existing code being too deeply nested (even though I didn't touch it). * work around lib-jitsi-meet circular dependency issues * refactor labels to use html base * pass in translation keys to video quality label * add video quality classnames for torture tests * break up, rearrange recorder session update listener * add comment about disabling startup resize animation * rename session to sessionData * chore(deps): update to latest lib for recording changes
2018-05-16 14:00:16 +00:00
} from '../../../recording';
import {
SETTINGS_TABS,
SettingsButton,
openSettingsDialog
} from '../../../settings';
import { toggleSharedVideo } from '../../../shared-video';
import { SpeakerStats } from '../../../speaker-stats';
import { TileViewButton } from '../../../video-layout';
import {
OverflowMenuVideoQualityItem,
VideoQualityDialog
} from '../../../video-quality';
import {
setFullScreen,
setOverflowMenuVisible,
setToolbarHovered
} from '../../actions';
import AudioMuteButton from '../AudioMuteButton';
import HangupButton from '../HangupButton';
import OverflowMenuButton from './OverflowMenuButton';
import OverflowMenuItem from './OverflowMenuItem';
import OverflowMenuProfileItem from './OverflowMenuProfileItem';
import ToolbarButton from './ToolbarButton';
import VideoMuteButton from '../VideoMuteButton';
import {
ClosedCaptionButton
} from '../../../subtitles';
2018-05-11 02:10:26 +00:00
/**
* The type of the React {@code Component} props of {@link Toolbox}.
*/
type Props = {
/**
* Whether or not the chat feature is currently displayed.
*/
_chatOpen: boolean,
/**
* The {@code JitsiConference} for the current conference.
*/
_conference: Object,
/**
* The tooltip key to use when screensharing is disabled. Or undefined
* if non to be shown and the button to be hidden.
*/
_desktopSharingDisabledTooltipKey: boolean,
/**
* Whether or not screensharing is initialized.
*/
_desktopSharingEnabled: boolean,
/**
* Whether or not a dialog is displayed.
*/
_dialog: boolean,
/**
* Whether or not the local participant is currently editing a document.
*/
_editingDocument: boolean,
/**
* Whether or not collaborative document editing is enabled.
*/
_etherpadInitialized: boolean,
/**
* Whether or not call feedback can be sent.
*/
_feedbackConfigured: boolean,
/**
* Whether or not the app is currently in full screen.
*/
_fullScreen: boolean,
/**
* Whether or not invite should be hidden, regardless of feature
* availability.
*/
_hideInviteButton: boolean,
/**
* Whether or not the current user is logged in through a JWT.
*/
_isGuest: boolean,
/**
* The ID of the local participant.
*/
_localParticipantID: String,
/**
* The subsection of Redux state for local recording
*/
_localRecState: Object,
/**
* Whether or not the overflow menu is visible.
*/
_overflowMenuVisible: boolean,
/**
* Whether or not the local participant's hand is raised.
*/
_raisedHand: boolean,
/**
* Whether or not the local participant is screensharing.
*/
_screensharing: boolean,
/**
* Whether or not the local participant is sharing a YouTube video.
*/
_sharingVideo: boolean,
/**
* Flag showing whether toolbar is visible.
*/
_visible: boolean,
/**
* Set with the buttons which this Toolbox should display.
*/
_visibleButtons: Set<string>,
/**
* Invoked to active other features of the app.
*/
dispatch: Function,
/**
* Invoked to obtain translated strings.
*/
t: Function
2018-05-11 02:10:26 +00:00
};
2017-02-16 23:02:40 +00:00
declare var APP: Object;
declare var interfaceConfig: Object;
/**
2017-04-01 05:52:40 +00:00
* Implements the conference toolbox on React/Web.
*
* @extends Component
2017-02-16 23:02:40 +00:00
*/
class Toolbox extends Component<Props> {
2017-02-16 23:02:40 +00:00
/**
* Initializes a new {@code Toolbox} instance.
2017-02-16 23:02:40 +00:00
*
* @param {Props} props - The read-only React {@code Component} props with
* which the new instance is to be initialized.
2017-02-16 23:02:40 +00:00
*/
constructor(props: Props) {
super(props);
// Bind event handlers so they are only bound once per instance.
this._onMouseOut = this._onMouseOut.bind(this);
this._onMouseOver = this._onMouseOver.bind(this);
this._onSetOverflowVisible = this._onSetOverflowVisible.bind(this);
2017-02-16 23:02:40 +00:00
this._onShortcutToggleChat = this._onShortcutToggleChat.bind(this);
this._onShortcutToggleFullScreen
= this._onShortcutToggleFullScreen.bind(this);
this._onShortcutToggleRaiseHand
= this._onShortcutToggleRaiseHand.bind(this);
this._onShortcutToggleScreenshare
= this._onShortcutToggleScreenshare.bind(this);
2017-02-16 23:02:40 +00:00
this._onToolbarOpenFeedback
= this._onToolbarOpenFeedback.bind(this);
this._onToolbarOpenInvite = this._onToolbarOpenInvite.bind(this);
this._onToolbarOpenKeyboardShortcuts
= this._onToolbarOpenKeyboardShortcuts.bind(this);
this._onToolbarOpenSpeakerStats
= this._onToolbarOpenSpeakerStats.bind(this);
this._onToolbarOpenVideoQuality
= this._onToolbarOpenVideoQuality.bind(this);
this._onToolbarToggleChat = this._onToolbarToggleChat.bind(this);
this._onToolbarToggleEtherpad
= this._onToolbarToggleEtherpad.bind(this);
this._onToolbarToggleFullScreen
= this._onToolbarToggleFullScreen.bind(this);
this._onToolbarToggleProfile
= this._onToolbarToggleProfile.bind(this);
this._onToolbarToggleRaiseHand
= this._onToolbarToggleRaiseHand.bind(this);
this._onToolbarToggleScreenshare
= this._onToolbarToggleScreenshare.bind(this);
this._onToolbarToggleSharedVideo
= this._onToolbarToggleSharedVideo.bind(this);
2018-07-18 22:12:25 +00:00
this._onToolbarOpenLocalRecordingInfoDialog
= this._onToolbarOpenLocalRecordingInfoDialog.bind(this);
}
2017-02-16 23:02:40 +00:00
/**
* Sets keyboard shortcuts for to trigger ToolbarButtons actions.
2017-02-16 23:02:40 +00:00
*
* @inheritdoc
2017-02-16 23:02:40 +00:00
* @returns {void}
*/
componentDidMount() {
const KEYBOARD_SHORTCUTS = [
this._shouldShowButton('chat') && {
character: 'C',
exec: this._onShortcutToggleChat,
helpDescription: 'keyboardShortcuts.toggleChat'
},
this._shouldShowButton('desktop') && {
character: 'D',
exec: this._onShortcutToggleScreenshare,
helpDescription: 'keyboardShortcuts.toggleScreensharing'
},
this._shouldShowButton('raisehand') && {
character: 'R',
exec: this._onShortcutToggleRaiseHand,
helpDescription: 'keyboardShortcuts.raiseHand'
},
this._shouldShowButton('fullscreen') && {
character: 'S',
exec: this._onShortcutToggleFullScreen,
helpDescription: 'keyboardShortcuts.fullScreen'
}
];
KEYBOARD_SHORTCUTS.forEach(shortcut => {
if (typeof shortcut === 'object') {
APP.keyboardshortcut.registerShortcut(
shortcut.character,
null,
shortcut.exec,
shortcut.helpDescription);
}
});
}
/**
* Update the visibility of the {@code OverflowMenuButton}.
*
* @inheritdoc
*/
componentDidUpdate(prevProps) {
// Ensure the dialog is closed when the toolbox becomes hidden.
if (prevProps._overflowMenuVisible && !this.props._visible) {
this._onSetOverflowVisible(false);
}
if (prevProps._overflowMenuVisible
&& !prevProps._dialog
&& this.props._dialog) {
this._onSetOverflowVisible(false);
this.props.dispatch(setToolbarHovered(false));
}
}
/**
* Removes keyboard shortcuts registered by this component.
*
* @inheritdoc
* @returns {void}
*/
componentWillUnmount() {
[ 'C', 'D', 'R', 'S' ].forEach(letter =>
APP.keyboardshortcut.unregisterShortcut(letter));
2017-02-16 23:02:40 +00:00
}
/**
* Implements React's {@link Component#render()}.
*
* @inheritdoc
* @returns {ReactElement}
*/
render() {
2019-02-20 23:35:19 +00:00
const { _visible, _visibleButtons } = this.props;
const rootClassNames = `new-toolbox ${_visible ? 'visible' : ''} ${
_visibleButtons.size ? '' : 'no-buttons'}`;
2017-02-16 23:02:40 +00:00
return (
<div
className = { rootClassNames }
id = 'new-toolbox'
onMouseOut = { this._onMouseOut }
onMouseOver = { this._onMouseOver }>
2019-02-20 23:35:19 +00:00
<div className = 'toolbox-background' />
{ this._renderToolboxContent() }
2017-02-16 23:02:40 +00:00
</div>
);
}
/**
* Callback invoked to display {@code FeedbackDialog}.
2017-02-16 23:02:40 +00:00
*
* @private
* @returns {void}
2017-02-16 23:02:40 +00:00
*/
_doOpenFeedback() {
const { _conference } = this.props;
2017-02-16 23:02:40 +00:00
this.props.dispatch(openFeedbackDialog(_conference));
}
/**
* Dispatches an action to display {@code KeyboardShortcuts}.
*
* @private
* @returns {void}
*/
_doOpenKeyboardShorcuts() {
this.props.dispatch(openKeyboardShortcutsDialog());
}
/**
* Callback invoked to display {@code SpeakerStats}.
*
* @private
* @returns {void}
*/
_doOpenSpeakerStats() {
this.props.dispatch(openDialog(SpeakerStats, {
conference: this.props._conference
}));
}
/**
* Dispatches an action to toggle the video quality dialog.
*
* @private
* @returns {void}
*/
_doOpenVideoQuality() {
this.props.dispatch(openDialog(VideoQualityDialog));
}
/**
* Dispatches an action to toggle the display of chat.
*
* @private
* @returns {void}
*/
_doToggleChat() {
this.props.dispatch(toggleChat());
}
/**
* Dispatches an action to show or hide document editing.
*
* @private
* @returns {void}
*/
_doToggleEtherpad() {
this.props.dispatch(toggleDocument());
}
/**
* Dispatches an action to toggle screensharing.
*
* @private
* @returns {void}
*/
_doToggleFullScreen() {
const fullScreen = !this.props._fullScreen;
this.props.dispatch(setFullScreen(fullScreen));
}
/**
* Dispatches an action to show or hide the profile edit panel.
*
* @private
* @returns {void}
*/
_doToggleProfile() {
this.props.dispatch(openSettingsDialog(SETTINGS_TABS.PROFILE));
}
/**
* Dispatches an action to toggle the local participant's raised hand state.
*
* @private
* @returns {void}
*/
_doToggleRaiseHand() {
const { _localParticipantID, _raisedHand } = this.props;
this.props.dispatch(participantUpdated({
Associate remote participant w/ JitsiConference (_UPDATED) The commit message of "Associate remote participant w/ JitsiConference (_JOINED)" explains the motivation for this commit. Practically, _JOINED and _LEFT combined with "Remove remote participants who are no longer of interest" should alleviate the problem with multiplying remote participants to an acceptable level of annoyance. Technically though, a remote participant cannot be identified by an ID only. The ID is (somewhat) "unique" in the context of a single JitsiConference instance. So in order to not have to scratch our heads over an obscure corner, racing case, it's better to always identify remote participants by the pair id-conference. Unfortunately, that's a bit of a high order given the existing source code. So I've implemented the cases which are the easiest so that new source code written with participantUpdated is more likely to identify a remote participant with the pair id-conference. Additionally, the commit "Reduce direct read access to the features/base/participants redux state" brings more control back to the functions of the feature base/participants so that one day we can (if we choose to) do something like, for example: If getParticipants is called with a conference, it returns the participants from features/base/participants who are associated with the specified conference. If no conference is specified in the function call, then default to the conference which is the primary focus of the app at the time of the function call. Added to the above, this should allow us to further reduce the cases in which we're identifying remote participants by id only and get us even closer to a more "predictable" behavior in corner, racing cases.
2018-05-22 21:47:43 +00:00
// XXX Only the local participant is allowed to update without
// stating the JitsiConference instance (i.e. participant property
// `conference` for a remote participant) because the local
// participant is uniquely identified by the very fact that there is
// only one local participant.
id: _localParticipantID,
local: true,
raisedHand: !_raisedHand
}));
}
/**
* Dispatches an action to toggle screensharing.
*
* @private
* @returns {void}
*/
_doToggleScreenshare() {
if (this.props._desktopSharingEnabled) {
this.props.dispatch(toggleScreensharing());
2017-02-16 23:02:40 +00:00
}
}
2017-02-16 23:02:40 +00:00
/**
* Dispatches an action to toggle YouTube video sharing.
*
* @private
* @returns {void}
*/
_doToggleSharedVideo() {
this.props.dispatch(toggleSharedVideo());
}
2017-02-16 23:02:40 +00:00
_onMouseOut: () => void;
2017-02-16 23:02:40 +00:00
/**
* Dispatches an action signaling the toolbar is not being hovered.
*
* @private
* @returns {void}
*/
_onMouseOut() {
this.props.dispatch(setToolbarHovered(false));
2017-02-16 23:02:40 +00:00
}
_onMouseOver: () => void;
2017-02-16 23:02:40 +00:00
/**
* Dispatches an action signaling the toolbar is being hovered.
2017-02-16 23:02:40 +00:00
*
* @private
* @returns {void}
2017-02-16 23:02:40 +00:00
*/
_onMouseOver() {
this.props.dispatch(setToolbarHovered(true));
}
_onSetOverflowVisible: (boolean) => void;
/**
* Sets the visibility of the overflow menu.
*
* @param {boolean} visible - Whether or not the overflow menu should be
* displayed.
* @private
* @returns {void}
*/
_onSetOverflowVisible(visible) {
this.props.dispatch(setOverflowMenuVisible(visible));
}
_onShortcutToggleChat: () => void;
/**
* Creates an analytics keyboard shortcut event and dispatches an action for
* toggling the display of chat.
*
* @private
* @returns {void}
*/
_onShortcutToggleChat() {
sendAnalytics(createShortcutEvent(
'toggle.chat',
{
enable: !this.props._chatOpen
}));
this._doToggleChat();
}
_onShortcutToggleFullScreen: () => void;
/**
* Creates an analytics keyboard shortcut event and dispatches an action for
* toggling full screen mode.
*
* @private
* @returns {void}
*/
_onShortcutToggleFullScreen() {
sendAnalytics(createShortcutEvent(
'toggle.fullscreen',
{
enable: !this.props._fullScreen
}));
this._doToggleFullScreen();
}
_onShortcutToggleRaiseHand: () => void;
/**
* Creates an analytics keyboard shortcut event and dispatches an action for
* toggling raise hand.
*
* @private
* @returns {void}
*/
_onShortcutToggleRaiseHand() {
sendAnalytics(createShortcutEvent(
'toggle.raise.hand',
ACTION_SHORTCUT_TRIGGERED,
{ enable: !this.props._raisedHand }));
this._doToggleRaiseHand();
}
_onShortcutToggleScreenshare: () => void;
/**
* Creates an analytics keyboard shortcut event and dispatches an action for
* toggling screensharing.
*
* @private
* @returns {void}
*/
_onShortcutToggleScreenshare() {
sendAnalytics(createToolbarEvent(
'screen.sharing',
{
enable: !this.props._screensharing
}));
this._doToggleScreenshare();
}
_onToolbarOpenFeedback: () => void;
/**
* Creates an analytics toolbar event and dispatches an action for toggling
* display of feedback.
*
* @private
* @returns {void}
*/
_onToolbarOpenFeedback() {
sendAnalytics(createToolbarEvent('feedback'));
this._doOpenFeedback();
}
_onToolbarOpenInvite: () => void;
/**
* Creates an analytics toolbar event and dispatches an action for opening
* the modal for inviting people directly into the conference.
*
* @private
* @returns {void}
*/
_onToolbarOpenInvite() {
sendAnalytics(createToolbarEvent('invite'));
this.props.dispatch(beginAddPeople());
}
_onToolbarOpenKeyboardShortcuts: () => void;
/**
* Creates an analytics toolbar event and dispatches an action for opening
* the modal for showing available keyboard shortcuts.
*
* @private
* @returns {void}
*/
_onToolbarOpenKeyboardShortcuts() {
sendAnalytics(createToolbarEvent('shortcuts'));
this._doOpenKeyboardShorcuts();
}
_onToolbarOpenSpeakerStats: () => void;
/**
* Creates an analytics toolbar event and dispatches an action for opening
* the speaker stats modal.
*
* @private
* @returns {void}
*/
_onToolbarOpenSpeakerStats() {
sendAnalytics(createToolbarEvent('speaker.stats'));
this._doOpenSpeakerStats();
}
_onToolbarOpenVideoQuality: () => void;
/**
* Creates an analytics toolbar event and dispatches an action for toggling
* open the video quality dialog.
*
* @private
* @returns {void}
*/
_onToolbarOpenVideoQuality() {
sendAnalytics(createToolbarEvent('video.quality'));
this._doOpenVideoQuality();
}
_onToolbarToggleChat: () => void;
/**
* Creates an analytics toolbar event and dispatches an action for toggling
* the display of chat.
*
* @private
* @returns {void}
*/
_onToolbarToggleChat() {
sendAnalytics(createToolbarEvent(
'toggle.chat',
{
enable: !this.props._chatOpen
}));
this._doToggleChat();
}
_onToolbarToggleEtherpad: () => void;
/**
* Creates an analytics toolbar event and dispatches an action for toggling
* the display of document editing.
*
* @private
* @returns {void}
*/
_onToolbarToggleEtherpad() {
sendAnalytics(createToolbarEvent(
'toggle.etherpad',
{
enable: !this.props._editingDocument
}));
this._doToggleEtherpad();
}
_onToolbarToggleFullScreen: () => void;
/**
* Creates an analytics toolbar event and dispatches an action for toggling
* full screen mode.
*
* @private
* @returns {void}
*/
_onToolbarToggleFullScreen() {
sendAnalytics(createToolbarEvent(
'toggle.fullscreen',
{
enable: !this.props._fullScreen
}));
this._doToggleFullScreen();
}
_onToolbarToggleProfile: () => void;
/**
* Creates an analytics toolbar event and dispatches an action for showing
* or hiding the profile edit panel.
*
* @private
* @returns {void}
*/
_onToolbarToggleProfile() {
sendAnalytics(createToolbarEvent('profile'));
this._doToggleProfile();
}
_onToolbarToggleRaiseHand: () => void;
/**
* Creates an analytics toolbar event and dispatches an action for toggling
* raise hand.
*
* @private
* @returns {void}
*/
_onToolbarToggleRaiseHand() {
sendAnalytics(createToolbarEvent(
'raise.hand',
{ enable: !this.props._raisedHand }));
this._doToggleRaiseHand();
}
_onToolbarToggleScreenshare: () => void;
/**
* Creates an analytics toolbar event and dispatches an action for toggling
* screensharing.
*
* @private
* @returns {void}
*/
_onToolbarToggleScreenshare() {
if (!this.props._desktopSharingEnabled) {
return;
}
sendAnalytics(createShortcutEvent(
'toggle.screen.sharing',
ACTION_SHORTCUT_TRIGGERED,
{ enable: !this.props._screensharing }));
this._doToggleScreenshare();
}
_onToolbarToggleSharedVideo: () => void;
/**
* Creates an analytics toolbar event and dispatches an action for toggling
* the sharing of a YouTube video.
*
* @private
* @returns {void}
*/
_onToolbarToggleSharedVideo() {
sendAnalytics(createToolbarEvent('shared.video.toggled',
{
enable: !this.props._sharingVideo
}));
this._doToggleSharedVideo();
}
2018-07-18 22:12:25 +00:00
_onToolbarOpenLocalRecordingInfoDialog: () => void;
/**
2018-07-18 22:12:25 +00:00
* Opens the {@code LocalRecordingInfoDialog}.
*
* @private
* @returns {void}
*/
2018-07-18 22:12:25 +00:00
_onToolbarOpenLocalRecordingInfoDialog() {
sendAnalytics(createToolbarEvent('local.recording'));
this.props.dispatch(openDialog(LocalRecordingInfoDialog));
}
/**
* Renders a button for toggleing screen sharing.
*
* @private
* @returns {ReactElement|null}
*/
_renderDesktopSharingButton() {
const {
_desktopSharingEnabled,
_desktopSharingDisabledTooltipKey,
_screensharing,
t
} = this.props;
const visible
= _desktopSharingEnabled || _desktopSharingDisabledTooltipKey;
if (!visible) {
2017-02-16 23:02:40 +00:00
return null;
}
const classNames = `icon-share-desktop ${
_screensharing ? 'toggled' : ''} ${
_desktopSharingEnabled ? '' : 'disabled'}`;
const tooltip = t(
_desktopSharingEnabled
? 'dialog.shareYourScreen' : _desktopSharingDisabledTooltipKey);
2017-02-16 23:02:40 +00:00
return (
<ToolbarButton
fix(i18n) Accessiblity labels translations (#3071) * fix(toolbar): accessibilityLabel should be translatable. This commit adds a helper property to get the accessibilityLabel of an item, providing a translation if one is available. This mimics the behavior of label and tooltip. * fix(toolbar) 'hangup' button accessibilityLabel i18n * fix(toolbar) 'mute' button accessibilityLabel i18n * fix(toolbar) 'videomute' button accessibilityLabel i18n * fix(toolbar) 'moreActions' button accessibilityLabel i18n * fix(toolbar) 'shareRoom' button accessibilityLabel i18n * fix(toolbar) 'audioRoute' button accessibilityLabel i18n * fix(toolbar) 'toggleCamera' button accessibilityLabel i18n * fix(toolbar) 'audioOnly' button accessibilityLabel i18n * fix(toolbar) 'roomLock' button accessibilityLabel i18n * fix(toolbar) 'pip' button accessibilityLabel i18n * fix(toolbar) 'invite' button accessibilityLabel i18n * fix(toolbar) 'raiseHand' button accessibilityLabel i18n * fix(toolbar) 'chat' button accessibilityLabel i18n * fix(toolbar) 'shareYourScreen' button accessibilityLabel i18n * fix(toolbar) 'fullScreen' button accessibilityLabel i18n * fix(toolbar) 'sharedvideo' button accessibilityLabel i18n * fix(toolbar) 'document' button accessibilityLabel i18n * fix(toolbar) 'speakerStats' button accessibilityLabel i18n * fix(toolbar) 'feedback' button accessibilityLabel i18n * fix(toolbar) 'shortcuts' button accessibilityLabel i18n * fix(toolbar) 'recording' button accessibilityLabel i18n * fix(toolbar) 'settings' button accessibilityLabel i18n * fix(welcomepage) accessibilityLabels i18n * fix(toolbar) 'info' button accessibilityLabel i18n * fix(i18n): Add translation to various aria-label property values. * fix(i18n): Differentiate between overflow menu and button.
2018-06-07 20:32:18 +00:00
accessibilityLabel
= { t('toolbar.accessibilityLabel.shareYourScreen') }
iconName = { classNames }
onClick = { this._onToolbarToggleScreenshare }
tooltip = { tooltip } />
2017-02-16 23:02:40 +00:00
);
}
/**
* Renders the list elements of the overflow menu.
*
* @private
* @returns {Array<ReactElement>}
*/
_renderOverflowMenuContent() {
const {
_editingDocument,
_etherpadInitialized,
_feedbackConfigured,
_fullScreen,
_isGuest,
_sharingVideo,
t
} = this.props;
return [
_isGuest
&& this._shouldShowButton('profile')
&& <OverflowMenuProfileItem
key = 'profile'
onClick = { this._onToolbarToggleProfile } />,
this._shouldShowButton('videoquality')
&& <OverflowMenuVideoQualityItem
key = 'videoquality'
onClick = { this._onToolbarOpenVideoQuality } />,
this._shouldShowButton('fullscreen')
&& <OverflowMenuItem
fix(i18n) Accessiblity labels translations (#3071) * fix(toolbar): accessibilityLabel should be translatable. This commit adds a helper property to get the accessibilityLabel of an item, providing a translation if one is available. This mimics the behavior of label and tooltip. * fix(toolbar) 'hangup' button accessibilityLabel i18n * fix(toolbar) 'mute' button accessibilityLabel i18n * fix(toolbar) 'videomute' button accessibilityLabel i18n * fix(toolbar) 'moreActions' button accessibilityLabel i18n * fix(toolbar) 'shareRoom' button accessibilityLabel i18n * fix(toolbar) 'audioRoute' button accessibilityLabel i18n * fix(toolbar) 'toggleCamera' button accessibilityLabel i18n * fix(toolbar) 'audioOnly' button accessibilityLabel i18n * fix(toolbar) 'roomLock' button accessibilityLabel i18n * fix(toolbar) 'pip' button accessibilityLabel i18n * fix(toolbar) 'invite' button accessibilityLabel i18n * fix(toolbar) 'raiseHand' button accessibilityLabel i18n * fix(toolbar) 'chat' button accessibilityLabel i18n * fix(toolbar) 'shareYourScreen' button accessibilityLabel i18n * fix(toolbar) 'fullScreen' button accessibilityLabel i18n * fix(toolbar) 'sharedvideo' button accessibilityLabel i18n * fix(toolbar) 'document' button accessibilityLabel i18n * fix(toolbar) 'speakerStats' button accessibilityLabel i18n * fix(toolbar) 'feedback' button accessibilityLabel i18n * fix(toolbar) 'shortcuts' button accessibilityLabel i18n * fix(toolbar) 'recording' button accessibilityLabel i18n * fix(toolbar) 'settings' button accessibilityLabel i18n * fix(welcomepage) accessibilityLabels i18n * fix(toolbar) 'info' button accessibilityLabel i18n * fix(i18n): Add translation to various aria-label property values. * fix(i18n): Differentiate between overflow menu and button.
2018-06-07 20:32:18 +00:00
accessibilityLabel =
{ t('toolbar.accessibilityLabel.fullScreen') }
icon = { _fullScreen
? 'icon-exit-full-screen'
: 'icon-full-screen' }
key = 'fullscreen'
onClick = { this._onToolbarToggleFullScreen }
text = { _fullScreen
? t('toolbar.exitFullScreen')
: t('toolbar.enterFullScreen') } />,
2018-07-05 11:17:45 +00:00
<LiveStreamButton
key = 'livestreaming'
showLabel = { true } />,
<RecordButton
key = 'record'
showLabel = { true } />,
this._shouldShowButton('sharedvideo')
&& <OverflowMenuItem
fix(i18n) Accessiblity labels translations (#3071) * fix(toolbar): accessibilityLabel should be translatable. This commit adds a helper property to get the accessibilityLabel of an item, providing a translation if one is available. This mimics the behavior of label and tooltip. * fix(toolbar) 'hangup' button accessibilityLabel i18n * fix(toolbar) 'mute' button accessibilityLabel i18n * fix(toolbar) 'videomute' button accessibilityLabel i18n * fix(toolbar) 'moreActions' button accessibilityLabel i18n * fix(toolbar) 'shareRoom' button accessibilityLabel i18n * fix(toolbar) 'audioRoute' button accessibilityLabel i18n * fix(toolbar) 'toggleCamera' button accessibilityLabel i18n * fix(toolbar) 'audioOnly' button accessibilityLabel i18n * fix(toolbar) 'roomLock' button accessibilityLabel i18n * fix(toolbar) 'pip' button accessibilityLabel i18n * fix(toolbar) 'invite' button accessibilityLabel i18n * fix(toolbar) 'raiseHand' button accessibilityLabel i18n * fix(toolbar) 'chat' button accessibilityLabel i18n * fix(toolbar) 'shareYourScreen' button accessibilityLabel i18n * fix(toolbar) 'fullScreen' button accessibilityLabel i18n * fix(toolbar) 'sharedvideo' button accessibilityLabel i18n * fix(toolbar) 'document' button accessibilityLabel i18n * fix(toolbar) 'speakerStats' button accessibilityLabel i18n * fix(toolbar) 'feedback' button accessibilityLabel i18n * fix(toolbar) 'shortcuts' button accessibilityLabel i18n * fix(toolbar) 'recording' button accessibilityLabel i18n * fix(toolbar) 'settings' button accessibilityLabel i18n * fix(welcomepage) accessibilityLabels i18n * fix(toolbar) 'info' button accessibilityLabel i18n * fix(i18n): Add translation to various aria-label property values. * fix(i18n): Differentiate between overflow menu and button.
2018-06-07 20:32:18 +00:00
accessibilityLabel =
{ t('toolbar.accessibilityLabel.sharedvideo') }
icon = 'icon-shared-video'
key = 'sharedvideo'
onClick = { this._onToolbarToggleSharedVideo }
text = { _sharingVideo
? t('toolbar.stopSharedVideo')
: t('toolbar.sharedvideo') } />,
this._shouldShowButton('etherpad')
&& _etherpadInitialized
&& <OverflowMenuItem
fix(i18n) Accessiblity labels translations (#3071) * fix(toolbar): accessibilityLabel should be translatable. This commit adds a helper property to get the accessibilityLabel of an item, providing a translation if one is available. This mimics the behavior of label and tooltip. * fix(toolbar) 'hangup' button accessibilityLabel i18n * fix(toolbar) 'mute' button accessibilityLabel i18n * fix(toolbar) 'videomute' button accessibilityLabel i18n * fix(toolbar) 'moreActions' button accessibilityLabel i18n * fix(toolbar) 'shareRoom' button accessibilityLabel i18n * fix(toolbar) 'audioRoute' button accessibilityLabel i18n * fix(toolbar) 'toggleCamera' button accessibilityLabel i18n * fix(toolbar) 'audioOnly' button accessibilityLabel i18n * fix(toolbar) 'roomLock' button accessibilityLabel i18n * fix(toolbar) 'pip' button accessibilityLabel i18n * fix(toolbar) 'invite' button accessibilityLabel i18n * fix(toolbar) 'raiseHand' button accessibilityLabel i18n * fix(toolbar) 'chat' button accessibilityLabel i18n * fix(toolbar) 'shareYourScreen' button accessibilityLabel i18n * fix(toolbar) 'fullScreen' button accessibilityLabel i18n * fix(toolbar) 'sharedvideo' button accessibilityLabel i18n * fix(toolbar) 'document' button accessibilityLabel i18n * fix(toolbar) 'speakerStats' button accessibilityLabel i18n * fix(toolbar) 'feedback' button accessibilityLabel i18n * fix(toolbar) 'shortcuts' button accessibilityLabel i18n * fix(toolbar) 'recording' button accessibilityLabel i18n * fix(toolbar) 'settings' button accessibilityLabel i18n * fix(welcomepage) accessibilityLabels i18n * fix(toolbar) 'info' button accessibilityLabel i18n * fix(i18n): Add translation to various aria-label property values. * fix(i18n): Differentiate between overflow menu and button.
2018-06-07 20:32:18 +00:00
accessibilityLabel =
{ t('toolbar.accessibilityLabel.document') }
icon = 'icon-share-doc'
key = 'etherpad'
onClick = { this._onToolbarToggleEtherpad }
text = { _editingDocument
? t('toolbar.documentClose')
: t('toolbar.documentOpen') } />,
<SettingsButton
key = 'settings'
showLabel = { true }
visible = { this._shouldShowButton('settings') } />,
this._shouldShowButton('stats')
&& <OverflowMenuItem
fix(i18n) Accessiblity labels translations (#3071) * fix(toolbar): accessibilityLabel should be translatable. This commit adds a helper property to get the accessibilityLabel of an item, providing a translation if one is available. This mimics the behavior of label and tooltip. * fix(toolbar) 'hangup' button accessibilityLabel i18n * fix(toolbar) 'mute' button accessibilityLabel i18n * fix(toolbar) 'videomute' button accessibilityLabel i18n * fix(toolbar) 'moreActions' button accessibilityLabel i18n * fix(toolbar) 'shareRoom' button accessibilityLabel i18n * fix(toolbar) 'audioRoute' button accessibilityLabel i18n * fix(toolbar) 'toggleCamera' button accessibilityLabel i18n * fix(toolbar) 'audioOnly' button accessibilityLabel i18n * fix(toolbar) 'roomLock' button accessibilityLabel i18n * fix(toolbar) 'pip' button accessibilityLabel i18n * fix(toolbar) 'invite' button accessibilityLabel i18n * fix(toolbar) 'raiseHand' button accessibilityLabel i18n * fix(toolbar) 'chat' button accessibilityLabel i18n * fix(toolbar) 'shareYourScreen' button accessibilityLabel i18n * fix(toolbar) 'fullScreen' button accessibilityLabel i18n * fix(toolbar) 'sharedvideo' button accessibilityLabel i18n * fix(toolbar) 'document' button accessibilityLabel i18n * fix(toolbar) 'speakerStats' button accessibilityLabel i18n * fix(toolbar) 'feedback' button accessibilityLabel i18n * fix(toolbar) 'shortcuts' button accessibilityLabel i18n * fix(toolbar) 'recording' button accessibilityLabel i18n * fix(toolbar) 'settings' button accessibilityLabel i18n * fix(welcomepage) accessibilityLabels i18n * fix(toolbar) 'info' button accessibilityLabel i18n * fix(i18n): Add translation to various aria-label property values. * fix(i18n): Differentiate between overflow menu and button.
2018-06-07 20:32:18 +00:00
accessibilityLabel =
{ t('toolbar.accessibilityLabel.speakerStats') }
icon = 'icon-presentation'
key = 'stats'
onClick = { this._onToolbarOpenSpeakerStats }
text = { t('toolbar.speakerStats') } />,
this._shouldShowButton('feedback')
&& _feedbackConfigured
&& <OverflowMenuItem
fix(i18n) Accessiblity labels translations (#3071) * fix(toolbar): accessibilityLabel should be translatable. This commit adds a helper property to get the accessibilityLabel of an item, providing a translation if one is available. This mimics the behavior of label and tooltip. * fix(toolbar) 'hangup' button accessibilityLabel i18n * fix(toolbar) 'mute' button accessibilityLabel i18n * fix(toolbar) 'videomute' button accessibilityLabel i18n * fix(toolbar) 'moreActions' button accessibilityLabel i18n * fix(toolbar) 'shareRoom' button accessibilityLabel i18n * fix(toolbar) 'audioRoute' button accessibilityLabel i18n * fix(toolbar) 'toggleCamera' button accessibilityLabel i18n * fix(toolbar) 'audioOnly' button accessibilityLabel i18n * fix(toolbar) 'roomLock' button accessibilityLabel i18n * fix(toolbar) 'pip' button accessibilityLabel i18n * fix(toolbar) 'invite' button accessibilityLabel i18n * fix(toolbar) 'raiseHand' button accessibilityLabel i18n * fix(toolbar) 'chat' button accessibilityLabel i18n * fix(toolbar) 'shareYourScreen' button accessibilityLabel i18n * fix(toolbar) 'fullScreen' button accessibilityLabel i18n * fix(toolbar) 'sharedvideo' button accessibilityLabel i18n * fix(toolbar) 'document' button accessibilityLabel i18n * fix(toolbar) 'speakerStats' button accessibilityLabel i18n * fix(toolbar) 'feedback' button accessibilityLabel i18n * fix(toolbar) 'shortcuts' button accessibilityLabel i18n * fix(toolbar) 'recording' button accessibilityLabel i18n * fix(toolbar) 'settings' button accessibilityLabel i18n * fix(welcomepage) accessibilityLabels i18n * fix(toolbar) 'info' button accessibilityLabel i18n * fix(i18n): Add translation to various aria-label property values. * fix(i18n): Differentiate between overflow menu and button.
2018-06-07 20:32:18 +00:00
accessibilityLabel =
{ t('toolbar.accessibilityLabel.feedback') }
icon = 'icon-feedback'
key = 'feedback'
onClick = { this._onToolbarOpenFeedback }
text = { t('toolbar.feedback') } />,
this._shouldShowButton('shortcuts')
&& <OverflowMenuItem
fix(i18n) Accessiblity labels translations (#3071) * fix(toolbar): accessibilityLabel should be translatable. This commit adds a helper property to get the accessibilityLabel of an item, providing a translation if one is available. This mimics the behavior of label and tooltip. * fix(toolbar) 'hangup' button accessibilityLabel i18n * fix(toolbar) 'mute' button accessibilityLabel i18n * fix(toolbar) 'videomute' button accessibilityLabel i18n * fix(toolbar) 'moreActions' button accessibilityLabel i18n * fix(toolbar) 'shareRoom' button accessibilityLabel i18n * fix(toolbar) 'audioRoute' button accessibilityLabel i18n * fix(toolbar) 'toggleCamera' button accessibilityLabel i18n * fix(toolbar) 'audioOnly' button accessibilityLabel i18n * fix(toolbar) 'roomLock' button accessibilityLabel i18n * fix(toolbar) 'pip' button accessibilityLabel i18n * fix(toolbar) 'invite' button accessibilityLabel i18n * fix(toolbar) 'raiseHand' button accessibilityLabel i18n * fix(toolbar) 'chat' button accessibilityLabel i18n * fix(toolbar) 'shareYourScreen' button accessibilityLabel i18n * fix(toolbar) 'fullScreen' button accessibilityLabel i18n * fix(toolbar) 'sharedvideo' button accessibilityLabel i18n * fix(toolbar) 'document' button accessibilityLabel i18n * fix(toolbar) 'speakerStats' button accessibilityLabel i18n * fix(toolbar) 'feedback' button accessibilityLabel i18n * fix(toolbar) 'shortcuts' button accessibilityLabel i18n * fix(toolbar) 'recording' button accessibilityLabel i18n * fix(toolbar) 'settings' button accessibilityLabel i18n * fix(welcomepage) accessibilityLabels i18n * fix(toolbar) 'info' button accessibilityLabel i18n * fix(i18n): Add translation to various aria-label property values. * fix(i18n): Differentiate between overflow menu and button.
2018-06-07 20:32:18 +00:00
accessibilityLabel =
{ t('toolbar.accessibilityLabel.shortcuts') }
icon = 'icon-open_in_new'
key = 'shortcuts'
onClick = { this._onToolbarOpenKeyboardShortcuts }
text = { t('toolbar.shortcuts') } />
];
}
2019-02-20 23:35:19 +00:00
/**
* Renders the toolbox content.
*
* @returns {Array<ReactElement>}
*/
_renderToolboxContent() {
const {
_chatOpen,
_hideInviteButton,
_overflowMenuVisible,
_raisedHand,
t
} = this.props;
const overflowMenuContent = this._renderOverflowMenuContent();
const overflowHasItems = Boolean(overflowMenuContent.filter(
child => child).length);
const toolbarAccLabel = 'toolbar.accessibilityLabel.moreActionsMenu';
return (
<div className = 'toolbox-content'>
<div className = 'button-group-left'>
{ this._shouldShowButton('desktop')
&& this._renderDesktopSharingButton() }
{ this._shouldShowButton('raisehand')
&& <ToolbarButton
accessibilityLabel =
{
t('toolbar.accessibilityLabel.raiseHand')
}
iconName = { _raisedHand
? 'icon-raised-hand toggled'
: 'icon-raised-hand' }
onClick = { this._onToolbarToggleRaiseHand }
tooltip = { t('toolbar.raiseHand') } /> }
{ this._shouldShowButton('chat')
&& <div className = 'toolbar-button-with-badge'>
<ToolbarButton
accessibilityLabel =
{ t('toolbar.accessibilityLabel.chat') }
iconName = { _chatOpen
? 'icon-chat toggled'
: 'icon-chat' }
onClick = { this._onToolbarToggleChat }
tooltip = { t('toolbar.chat') } />
<ChatCounter />
</div> }
{
this._shouldShowButton('closedcaptions')
&& <ClosedCaptionButton />
}
</div>
<div className = 'button-group-center'>
<AudioMuteButton
visible = { this._shouldShowButton('microphone') } />
<HangupButton
visible = { this._shouldShowButton('hangup') } />
<VideoMuteButton
visible = { this._shouldShowButton('camera') } />
</div>
<div className = 'button-group-right'>
{ this._shouldShowButton('localrecording')
&& <LocalRecordingButton
onClick = {
this._onToolbarOpenLocalRecordingInfoDialog
} />
}
{ this._shouldShowButton('tileview')
&& <TileViewButton /> }
{ this._shouldShowButton('invite')
&& !_hideInviteButton
&& <ToolbarButton
accessibilityLabel =
{ t('toolbar.accessibilityLabel.invite') }
2019-02-21 01:12:21 +00:00
iconName = 'icon-invite'
2019-02-20 23:35:19 +00:00
onClick = { this._onToolbarOpenInvite }
tooltip = { t('toolbar.invite') } /> }
{ this._shouldShowButton('info') && <InfoDialogButton /> }
{ overflowHasItems
&& <OverflowMenuButton
isOpen = { _overflowMenuVisible }
onVisibilityChange = { this._onSetOverflowVisible }>
<ul
aria-label = { t(toolbarAccLabel) }
className = 'overflow-menu'>
{ overflowMenuContent }
</ul>
</OverflowMenuButton> }
</div>
</div>);
}
_shouldShowButton: (string) => boolean;
/**
* Returns if a button name has been explicitly configured to be displayed.
*
* @param {string} buttonName - The name of the button, as expected in
2018-08-08 20:35:40 +00:00
* {@link interfaceConfig}.
* @private
* @returns {boolean} True if the button should be displayed.
*/
_shouldShowButton(buttonName) {
return this.props._visibleButtons.has(buttonName);
}
2017-02-16 23:02:40 +00:00
}
/**
* Maps (parts of) the redux state to {@link Toolbox}'s React {@code Component}
* props.
2017-02-16 23:02:40 +00:00
*
* @param {Object} state - The redux store/state.
2017-02-16 23:02:40 +00:00
* @private
* @returns {{}}
2017-02-16 23:02:40 +00:00
*/
function _mapStateToProps(state) {
const { conference } = state['features/base/conference'];
let { desktopSharingEnabled } = state['features/base/conference'];
const {
callStatsID,
iAmRecorder
} = state['features/base/config'];
const sharedVideoStatus = state['features/shared-video'].status;
2017-02-16 23:02:40 +00:00
const {
alwaysVisible,
fullScreen,
overflowMenuVisible,
timeoutID,
visible
2017-04-01 05:52:40 +00:00
} = state['features/toolbox'];
const localParticipant = getLocalParticipant(state);
const localRecordingStates = state['features/local-recording'];
const localVideo = getLocalVideoTrack(state['features/base/tracks']);
const addPeopleEnabled = isAddPeopleEnabled(state);
const dialOutEnabled = isDialOutEnabled(state);
2017-02-16 23:02:40 +00:00
let desktopSharingDisabledTooltipKey;
if (state['features/base/config'].enableFeaturesBasedOnToken) {
// we enable desktop sharing if any participant already have this
// feature enabled
desktopSharingEnabled = getParticipants(state)
.find(({ features = {} }) =>
String(features['screen-sharing']) === 'true') !== undefined;
// we want to show button and tooltip
if (state['features/base/jwt'].isGuest) {
desktopSharingDisabledTooltipKey
= 'dialog.shareYourScreenDisabledForGuest';
} else {
desktopSharingDisabledTooltipKey
= 'dialog.shareYourScreenDisabled';
}
}
2017-02-16 23:02:40 +00:00
return {
_chatOpen: state['features/chat'].isOpen,
_conference: conference,
_desktopSharingEnabled: desktopSharingEnabled,
_desktopSharingDisabledTooltipKey: desktopSharingDisabledTooltipKey,
_dialog: Boolean(state['features/base/dialog'].component),
_editingDocument: Boolean(state['features/etherpad'].editing),
_etherpadInitialized: Boolean(state['features/etherpad'].initialized),
_feedbackConfigured: Boolean(callStatsID),
_hideInviteButton:
iAmRecorder || (!addPeopleEnabled && !dialOutEnabled),
_isGuest: state['features/base/jwt'].isGuest,
_fullScreen: fullScreen,
_localParticipantID: localParticipant.id,
_localRecState: localRecordingStates,
_overflowMenuVisible: overflowMenuVisible,
_raisedHand: localParticipant.raisedHand,
_screensharing: localVideo && localVideo.videoType === 'desktop',
_sharingVideo: sharedVideoStatus === 'playing'
|| sharedVideoStatus === 'start'
|| sharedVideoStatus === 'pause',
_visible: Boolean(timeoutID || visible || alwaysVisible),
// XXX: We are not currently using state here, but in the future, when
// interfaceConfig is part of redux we will.
_visibleButtons: new Set(interfaceConfig.TOOLBAR_BUTTONS)
2017-02-16 23:02:40 +00:00
};
}
export default translate(connect(_mapStateToProps)(Toolbox));