feat(toolbox) allow any toolbox button to be displayed in main toolbar (#9488)
* feat(toolbox) allow any toolbox button to be displayed as main fixes the previous behaviour where only a certain set of buttons were whitelisted for being displayed in the main toolbar * code review * code review - fix avatar icon position
This commit is contained in:
parent
074a783bd9
commit
62c78950cd
|
@ -278,6 +278,10 @@
|
|||
}
|
||||
}
|
||||
|
||||
.profile-button-avatar {
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
/**
|
||||
* START of fade in animation for main toolbar
|
||||
*/
|
||||
|
|
|
@ -19,5 +19,5 @@ export const TOOLBAR_BUTTONS = [
|
|||
'livestreaming', 'etherpad', 'sharedvideo', 'shareaudio', 'settings', 'raisehand',
|
||||
'videoquality', 'filmstrip', 'participants-pane', 'feedback', 'stats', 'shortcuts',
|
||||
'tileview', 'select-background', 'download', 'help', 'mute-everyone', 'mute-video-everyone',
|
||||
'security', 'toggle-camera'
|
||||
'security'
|
||||
];
|
||||
|
|
|
@ -0,0 +1,109 @@
|
|||
// @flow
|
||||
|
||||
import React from 'react';
|
||||
|
||||
import { translate } from '../../../base/i18n';
|
||||
import { IconChat } from '../../../base/icons';
|
||||
import { connect } from '../../../base/redux';
|
||||
import { AbstractButton, type AbstractButtonProps } from '../../../base/toolbox/components';
|
||||
|
||||
import ChatCounter from './ChatCounter';
|
||||
|
||||
/**
|
||||
* The type of the React {@code Component} props of {@link ChatButton}.
|
||||
*/
|
||||
type Props = AbstractButtonProps & {
|
||||
|
||||
/**
|
||||
* Whether or not the chat feature is currently displayed.
|
||||
*/
|
||||
_chatOpen: boolean,
|
||||
|
||||
/**
|
||||
* External handler for click action.
|
||||
*/
|
||||
handleClick: Function
|
||||
};
|
||||
|
||||
/**
|
||||
* Implementation of a button for accessing chat pane.
|
||||
*/
|
||||
class ChatButton extends AbstractButton<Props, *> {
|
||||
accessibilityLabel = 'toolbar.accessibilityLabel.chat';
|
||||
icon = IconChat;
|
||||
label = 'toolbar.openChat';
|
||||
toggledLabel = 'toolbar.closeChat';
|
||||
|
||||
/**
|
||||
* Retrieves tooltip dynamically.
|
||||
*/
|
||||
get tooltip() {
|
||||
if (this._isToggled()) {
|
||||
return 'toolbar.closeChat';
|
||||
}
|
||||
|
||||
return 'toolbar.openChat';
|
||||
}
|
||||
|
||||
/**
|
||||
* Required by linter due to AbstractButton overwritten prop being writable.
|
||||
*
|
||||
* @param {string} value - The value.
|
||||
*/
|
||||
set tooltip(value) {
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles clicking / pressing the button, and opens the appropriate dialog.
|
||||
*
|
||||
* @protected
|
||||
* @returns {void}
|
||||
*/
|
||||
_handleClick() {
|
||||
this.props.handleClick();
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates whether this button is in toggled state or not.
|
||||
*
|
||||
* @override
|
||||
* @protected
|
||||
* @returns {boolean}
|
||||
*/
|
||||
_isToggled() {
|
||||
return this.props._chatOpen;
|
||||
}
|
||||
|
||||
/**
|
||||
* Overrides AbstractButton's {@link Component#render()}.
|
||||
*
|
||||
* @override
|
||||
* @protected
|
||||
* @returns {boReact$Nodeolean}
|
||||
*/
|
||||
render(): React$Node {
|
||||
return (
|
||||
<div
|
||||
className = 'toolbar-button-with-badge'
|
||||
key = 'chatcontainer'>
|
||||
{super.render()}
|
||||
<ChatCounter />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Function that maps parts of Redux state tree into component props.
|
||||
*
|
||||
* @param {Object} state - Redux state.
|
||||
* @returns {Object}
|
||||
*/
|
||||
const mapStateToProps = state => {
|
||||
return {
|
||||
_chatOpen: state['features/chat'].isOpen
|
||||
};
|
||||
};
|
||||
|
||||
export default translate(connect(mapStateToProps)(ChatButton));
|
|
@ -1,5 +1,6 @@
|
|||
// @flow
|
||||
|
||||
export { default as Chat } from './Chat';
|
||||
export { default as ChatButton } from './ChatButton';
|
||||
export { default as ChatCounter } from './ChatCounter';
|
||||
export { default as ChatPrivacyDialog } from './ChatPrivacyDialog';
|
||||
|
|
|
@ -0,0 +1,46 @@
|
|||
// @flow
|
||||
|
||||
import { createToolbarEvent, sendAnalytics } from '../../analytics';
|
||||
import { openDialog } from '../../base/dialog';
|
||||
import { translate } from '../../base/i18n';
|
||||
import { IconCodeBlock } from '../../base/icons';
|
||||
import { connect } from '../../base/redux';
|
||||
import { AbstractButton, type AbstractButtonProps } from '../../base/toolbox/components';
|
||||
|
||||
import EmbedMeetingDialog from './EmbedMeetingDialog';
|
||||
|
||||
/**
|
||||
* The type of the React {@code Component} props of {@link EmbedMeetingButton}.
|
||||
*/
|
||||
type Props = AbstractButtonProps & {
|
||||
|
||||
/**
|
||||
* The redux {@code dispatch} function.
|
||||
*/
|
||||
dispatch: Function
|
||||
};
|
||||
|
||||
/**
|
||||
* Implementation of a button for opening embed meeting dialog.
|
||||
*/
|
||||
class EmbedMeetingButton extends AbstractButton<Props, *> {
|
||||
accessibilityLabel = 'toolbar.accessibilityLabel.embedMeeting';
|
||||
icon = IconCodeBlock;
|
||||
label = 'toolbar.embedMeeting';
|
||||
tooltip = 'toolbar.embedMeeting';
|
||||
|
||||
/**
|
||||
* Handles clicking / pressing the button, and opens the appropriate dialog.
|
||||
*
|
||||
* @protected
|
||||
* @returns {void}
|
||||
*/
|
||||
_handleClick() {
|
||||
const { dispatch } = this.props;
|
||||
|
||||
sendAnalytics(createToolbarEvent('embed.meeting'));
|
||||
dispatch(openDialog(EmbedMeetingDialog));
|
||||
}
|
||||
}
|
||||
|
||||
export default translate(connect()(EmbedMeetingButton));
|
|
@ -1 +1,2 @@
|
|||
export { default as EmbedMeetingButton } from './EmbedMeetingButton';
|
||||
export { default as EmbedMeetingDialog } from './EmbedMeetingDialog';
|
||||
|
|
|
@ -32,6 +32,26 @@ class SharedDocumentButton extends AbstractButton<Props, *> {
|
|||
label = 'toolbar.documentOpen';
|
||||
toggledLabel = 'toolbar.documentClose';
|
||||
|
||||
/**
|
||||
* Dynamically retrieves tooltip based on sharing state.
|
||||
*/
|
||||
get tooltip() {
|
||||
if (this._isToggled()) {
|
||||
return 'toolbar.documentClose';
|
||||
}
|
||||
|
||||
return 'toolbar.documentOpen';
|
||||
}
|
||||
|
||||
/**
|
||||
* Required by linter due to AbstractButton overwritten prop being writable.
|
||||
*
|
||||
* @param {string} value - The value.
|
||||
*/
|
||||
set tooltip(value) {
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles clicking / pressing the button, and opens / closes the appropriate dialog.
|
||||
*
|
||||
|
|
|
@ -0,0 +1,56 @@
|
|||
|
||||
// @flow
|
||||
|
||||
import { createToolbarEvent, sendAnalytics } from '../../analytics';
|
||||
import { translate } from '../../base/i18n';
|
||||
import { IconFeedback } from '../../base/icons';
|
||||
import { connect } from '../../base/redux';
|
||||
import { AbstractButton, type AbstractButtonProps } from '../../base/toolbox/components';
|
||||
import { openFeedbackDialog } from '../actions';
|
||||
|
||||
/**
|
||||
* The type of the React {@code Component} props of {@link FeedbackButton}.
|
||||
*/
|
||||
type Props = AbstractButtonProps & {
|
||||
|
||||
/**
|
||||
* The {@code JitsiConference} for the current conference.
|
||||
*/
|
||||
_conference: Object,
|
||||
|
||||
/**
|
||||
* The redux {@code dispatch} function.
|
||||
*/
|
||||
dispatch: Function
|
||||
};
|
||||
|
||||
/**
|
||||
* Implementation of a button for opening feedback dialog.
|
||||
*/
|
||||
class FeedbackButton extends AbstractButton<Props, *> {
|
||||
accessibilityLabel = 'toolbar.accessibilityLabel.feedback';
|
||||
icon = IconFeedback;
|
||||
label = 'toolbar.feedback';
|
||||
tooltip = 'toolbar.feedback';
|
||||
|
||||
/**
|
||||
* Handles clicking / pressing the button, and opens the appropriate dialog.
|
||||
*
|
||||
* @protected
|
||||
* @returns {void}
|
||||
*/
|
||||
_handleClick() {
|
||||
const { _conference, dispatch } = this.props;
|
||||
|
||||
sendAnalytics(createToolbarEvent('feedback'));
|
||||
dispatch(openFeedbackDialog(_conference));
|
||||
}
|
||||
}
|
||||
|
||||
const mapStateToProps = state => {
|
||||
return {
|
||||
_conference: state['features/base/conference'].conference
|
||||
};
|
||||
};
|
||||
|
||||
export default translate(connect(mapStateToProps)(FeedbackButton));
|
|
@ -1 +1,2 @@
|
|||
export { default as FeedbackButton } from './FeedbackButton';
|
||||
export { default as FeedbackDialog } from './FeedbackDialog';
|
||||
|
|
|
@ -0,0 +1,44 @@
|
|||
// @flow
|
||||
|
||||
import { createToolbarEvent, sendAnalytics } from '../../analytics';
|
||||
import { translate } from '../../base/i18n';
|
||||
import { IconDeviceDocument } from '../../base/icons';
|
||||
import { connect } from '../../base/redux';
|
||||
import { AbstractButton, type AbstractButtonProps } from '../../base/toolbox/components';
|
||||
import { openKeyboardShortcutsDialog } from '../actions';
|
||||
|
||||
/**
|
||||
* The type of the React {@code Component} props of {@link KeyboardShortcutsButton}.
|
||||
*/
|
||||
type Props = AbstractButtonProps & {
|
||||
|
||||
/**
|
||||
* The redux {@code dispatch} function.
|
||||
*/
|
||||
dispatch: Function
|
||||
};
|
||||
|
||||
/**
|
||||
* Implementation of a button for opening keyboard shortcuts dialog.
|
||||
*/
|
||||
class KeyboardShortcutsButton extends AbstractButton<Props, *> {
|
||||
accessibilityLabel = 'toolbar.accessibilityLabel.shortcuts';
|
||||
icon = IconDeviceDocument;
|
||||
label = 'toolbar.shortcuts';
|
||||
tooltip = 'toolbar.shortcuts';
|
||||
|
||||
/**
|
||||
* Handles clicking / pressing the button, and opens the appropriate dialog.
|
||||
*
|
||||
* @protected
|
||||
* @returns {void}
|
||||
*/
|
||||
_handleClick() {
|
||||
const { dispatch } = this.props;
|
||||
|
||||
sendAnalytics(createToolbarEvent('shortcuts'));
|
||||
dispatch(openKeyboardShortcutsDialog());
|
||||
}
|
||||
}
|
||||
|
||||
export default translate(connect()(KeyboardShortcutsButton));
|
|
@ -1 +1,2 @@
|
|||
export { default as KeyboardShortcutsButton } from './KeyboardShortcutsButton';
|
||||
export { default as KeyboardShortcutsDialog } from './KeyboardShortcutsDialog';
|
||||
|
|
|
@ -1,85 +1,46 @@
|
|||
/* @flow */
|
||||
|
||||
import React, { Component } from 'react';
|
||||
// @flow
|
||||
|
||||
import { createToolbarEvent, sendAnalytics } from '../../analytics';
|
||||
import { openDialog } from '../../base/dialog';
|
||||
import { translate } from '../../base/i18n';
|
||||
import { IconRec } from '../../base/icons';
|
||||
import { ToolbarButton } from '../../toolbox/components/web';
|
||||
import { connect } from '../../base/redux';
|
||||
import { AbstractButton, type AbstractButtonProps } from '../../base/toolbox/components';
|
||||
|
||||
import LocalRecordingInfoDialog from './LocalRecordingInfoDialog';
|
||||
|
||||
/**
|
||||
* The type of the React {@code Component} state of
|
||||
* {@link LocalRecordingButton}.
|
||||
* The type of the React {@code Component} props of {@link LocalRecording}.
|
||||
*/
|
||||
type Props = {
|
||||
type Props = AbstractButtonProps & {
|
||||
|
||||
/**
|
||||
* Whether or not {@link LocalRecordingInfoDialog} should be displayed.
|
||||
* The redux {@code dispatch} function.
|
||||
*/
|
||||
isDialogShown: boolean,
|
||||
|
||||
/**
|
||||
* Callback function called when {@link LocalRecordingButton} is clicked.
|
||||
*/
|
||||
onClick: Function,
|
||||
|
||||
/**
|
||||
* Invoked to obtain translated strings.
|
||||
*/
|
||||
t: Function
|
||||
}
|
||||
dispatch: Function
|
||||
};
|
||||
|
||||
/**
|
||||
* A React {@code Component} for opening or closing the
|
||||
* {@code LocalRecordingInfoDialog}.
|
||||
*
|
||||
* @extends Component
|
||||
* Implementation of a button for opening local recording dialog.
|
||||
*/
|
||||
class LocalRecordingButton extends Component<Props> {
|
||||
class LocalRecording extends AbstractButton<Props, *> {
|
||||
accessibilityLabel = 'toolbar.accessibilityLabel.localRecording';
|
||||
icon = IconRec;
|
||||
label = 'localRecording.dialogTitle';
|
||||
tooltip = 'localRecording.dialogTitle';
|
||||
|
||||
/**
|
||||
* Initializes a new {@code LocalRecordingButton} instance.
|
||||
* Handles clicking / pressing the button, and opens the appropriate dialog.
|
||||
*
|
||||
* @param {Object} props - The read-only properties with which the new
|
||||
* instance is to be initialized.
|
||||
*/
|
||||
constructor(props: Props) {
|
||||
super(props);
|
||||
|
||||
// Bind event handlers so they are only bound once per instance.
|
||||
this._onClick = this._onClick.bind(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements React's {@link Component#render()}.
|
||||
*
|
||||
* @inheritdoc
|
||||
* @returns {ReactElement}
|
||||
*/
|
||||
render() {
|
||||
const { isDialogShown, t } = this.props;
|
||||
|
||||
return (
|
||||
<ToolbarButton
|
||||
accessibilityLabel
|
||||
= { t('toolbar.accessibilityLabel.localRecording') }
|
||||
icon = { IconRec }
|
||||
onClick = { this._onClick }
|
||||
toggled = { isDialogShown }
|
||||
tooltip = { t('localRecording.dialogTitle') } />
|
||||
);
|
||||
}
|
||||
|
||||
_onClick: () => void;
|
||||
|
||||
/**
|
||||
* Callback invoked when the Toolbar button is clicked.
|
||||
*
|
||||
* @private
|
||||
* @protected
|
||||
* @returns {void}
|
||||
*/
|
||||
_onClick() {
|
||||
this.props.onClick();
|
||||
_handleClick() {
|
||||
const { dispatch } = this.props;
|
||||
|
||||
sendAnalytics(createToolbarEvent('local.recording'));
|
||||
dispatch(openDialog(LocalRecordingInfoDialog));
|
||||
}
|
||||
}
|
||||
|
||||
export default translate(LocalRecordingButton);
|
||||
export default translate(connect()(LocalRecording));
|
||||
|
|
|
@ -0,0 +1,39 @@
|
|||
// @flow
|
||||
|
||||
import { translate } from '../../base/i18n';
|
||||
import { IconParticipants } from '../../base/icons';
|
||||
import { connect } from '../../base/redux';
|
||||
import { AbstractButton, type AbstractButtonProps } from '../../base/toolbox/components';
|
||||
|
||||
/**
|
||||
* The type of the React {@code Component} props of {@link ParticipantsPaneButton}.
|
||||
*/
|
||||
type Props = AbstractButtonProps & {
|
||||
|
||||
/**
|
||||
* External handler for click action.
|
||||
*/
|
||||
handleClick: Function
|
||||
};
|
||||
|
||||
/**
|
||||
* Implementation of a button for accessing participants pane.
|
||||
*/
|
||||
class ParticipantsPaneButton extends AbstractButton<Props, *> {
|
||||
accessibilityLabel = 'toolbar.accessibilityLabel.participants';
|
||||
icon = IconParticipants;
|
||||
label = 'toolbar.participants';
|
||||
tooltip = 'toolbar.participants';
|
||||
|
||||
/**
|
||||
* Handles clicking / pressing the button, and opens the appropriate dialog.
|
||||
*
|
||||
* @protected
|
||||
* @returns {void}
|
||||
*/
|
||||
_handleClick() {
|
||||
this.props.handleClick();
|
||||
}
|
||||
}
|
||||
|
||||
export default translate(connect()(ParticipantsPaneButton));
|
|
@ -6,4 +6,5 @@ export * from './MeetingParticipantItem';
|
|||
export * from './MeetingParticipantList';
|
||||
export * from './ParticipantItem';
|
||||
export * from './ParticipantsPane';
|
||||
export * from './ParticipantsPaneButton';
|
||||
export * from './RaisedHandIndicator';
|
||||
|
|
|
@ -39,6 +39,7 @@ class SecurityDialogButton extends AbstractButton<Props, *> {
|
|||
icon = IconSecurityOff;
|
||||
label = 'toolbar.security';
|
||||
toggledIcon = IconSecurityOn;
|
||||
tooltip = 'toolbar.security';
|
||||
|
||||
/**
|
||||
* Handles clicking / pressing the button, and opens / closes the appropriate dialog.
|
||||
|
|
|
@ -31,6 +31,7 @@ class SettingsButton extends AbstractButton<Props, *> {
|
|||
accessibilityLabel = 'toolbar.accessibilityLabel.Settings';
|
||||
icon = IconSettings;
|
||||
label = 'toolbar.Settings';
|
||||
tooltip = 'toolbar.Settings';
|
||||
|
||||
/**
|
||||
* Handles clicking / pressing the button, and opens the appropriate dialog.
|
||||
|
|
|
@ -37,9 +37,28 @@ class SharedVideoButton extends AbstractButton<Props, *> {
|
|||
accessibilityLabel = 'toolbar.accessibilityLabel.sharedvideo';
|
||||
icon = IconShareVideo;
|
||||
label = 'toolbar.sharedvideo';
|
||||
tooltip = 'toolbar.sharedvideo';
|
||||
toggledLabel = 'toolbar.stopSharedVideo';
|
||||
|
||||
/**
|
||||
* Dynamically retrieves tooltip based on sharing state.
|
||||
*/
|
||||
get tooltip() {
|
||||
if (this._isToggled()) {
|
||||
return 'toolbar.stopSharedVideo';
|
||||
}
|
||||
|
||||
return 'toolbar.sharedvideo';
|
||||
}
|
||||
|
||||
/**
|
||||
* Required by linter due to AbstractButton overwritten prop being writable.
|
||||
*
|
||||
* @param {string} value - The value.
|
||||
*/
|
||||
set tooltip(value) {
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles clicking / pressing the button, and opens a new dialog.
|
||||
*
|
||||
|
|
|
@ -0,0 +1,67 @@
|
|||
// @flow
|
||||
|
||||
import { createToolbarEvent, sendAnalytics } from '../../analytics';
|
||||
import { openDialog } from '../../base/dialog';
|
||||
import { translate } from '../../base/i18n';
|
||||
import { IconPresentation } from '../../base/icons';
|
||||
import { connect } from '../../base/redux';
|
||||
import { AbstractButton, type AbstractButtonProps } from '../../base/toolbox/components';
|
||||
|
||||
import SpeakerStats from './SpeakerStats';
|
||||
|
||||
/**
|
||||
* The type of the React {@code Component} props of {@link SpeakerStatsButton}.
|
||||
*/
|
||||
type Props = AbstractButtonProps & {
|
||||
|
||||
/**
|
||||
* The {@code JitsiConference} for the current conference.
|
||||
*/
|
||||
_conference: Object,
|
||||
|
||||
/**
|
||||
* The redux {@code dispatch} function.
|
||||
*/
|
||||
dispatch: Function
|
||||
};
|
||||
|
||||
/**
|
||||
* Implementation of a button for opening speaker stats dialog.
|
||||
*/
|
||||
class SpeakerStatsButton extends AbstractButton<Props, *> {
|
||||
accessibilityLabel = 'toolbar.accessibilityLabel.speakerStats';
|
||||
icon = IconPresentation;
|
||||
label = 'toolbar.speakerStats';
|
||||
tooltip = 'toolbar.speakerStats';
|
||||
|
||||
/**
|
||||
* Handles clicking / pressing the button, and opens the appropriate dialog.
|
||||
*
|
||||
* @protected
|
||||
* @returns {void}
|
||||
*/
|
||||
_handleClick() {
|
||||
const { _conference, dispatch } = this.props;
|
||||
|
||||
sendAnalytics(createToolbarEvent('speaker.stats'));
|
||||
dispatch(openDialog(SpeakerStats, {
|
||||
conference: _conference
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Maps (parts of) the Redux state to the associated
|
||||
* {@code SpeakerStatsButton} component's props.
|
||||
*
|
||||
* @param {Object} state - The Redux state.
|
||||
* @private
|
||||
* @returns {Object}
|
||||
*/
|
||||
function mapStateToProps(state) {
|
||||
return {
|
||||
_conference: state['features/base/conference'].conference
|
||||
};
|
||||
}
|
||||
|
||||
export default translate(connect(mapStateToProps)(SpeakerStatsButton));
|
|
@ -1 +1,2 @@
|
|||
export { default as SpeakerStatsButton } from './SpeakerStatsButton';
|
||||
export { default as SpeakerStats } from './SpeakerStats';
|
||||
|
|
|
@ -23,6 +23,7 @@ class DownloadButton extends AbstractButton<Props, *> {
|
|||
accessibilityLabel = 'toolbar.accessibilityLabel.download';
|
||||
icon = IconDownload;
|
||||
label = 'toolbar.download';
|
||||
tooltip = 'toolbar.download';
|
||||
|
||||
/**
|
||||
* Handles clicking / pressing the button, and opens a new window with the user documentation.
|
||||
|
|
|
@ -24,6 +24,7 @@ class HelpButton extends AbstractButton<Props, *> {
|
|||
accessibilityLabel = 'toolbar.accessibilityLabel.help';
|
||||
icon = IconHelp;
|
||||
label = 'toolbar.help';
|
||||
tooltip = 'toolbar.help';
|
||||
|
||||
/**
|
||||
* Handles clicking / pressing the button, and opens a new window with the user documentation.
|
||||
|
|
|
@ -30,6 +30,7 @@ class MuteEveryoneButton extends AbstractButton<Props, *> {
|
|||
accessibilityLabel = 'toolbar.accessibilityLabel.muteEveryone';
|
||||
icon = IconMuteEveryone;
|
||||
label = 'toolbar.muteEveryone';
|
||||
tooltip = 'toolbar.muteEveryone';
|
||||
|
||||
/**
|
||||
* Handles clicking / pressing the button, and opens a confirmation dialog.
|
||||
|
|
|
@ -30,6 +30,7 @@ class MuteEveryonesVideoButton extends AbstractButton<Props, *> {
|
|||
accessibilityLabel = 'toolbar.accessibilityLabel.muteEveryonesVideo';
|
||||
icon = IconMuteVideoEveryone;
|
||||
label = 'toolbar.muteEveryonesVideo';
|
||||
tooltip = 'toolbar.muteEveryonesVideo';
|
||||
|
||||
/**
|
||||
* Handles clicking / pressing the button, and opens a confirmation dialog.
|
||||
|
|
|
@ -0,0 +1,103 @@
|
|||
// @flow
|
||||
|
||||
import { translate } from '../../../base/i18n';
|
||||
import { IconExitFullScreen, IconFullScreen } from '../../../base/icons';
|
||||
import { connect } from '../../../base/redux';
|
||||
import { AbstractButton, type AbstractButtonProps } from '../../../base/toolbox/components';
|
||||
|
||||
type Props = AbstractButtonProps & {
|
||||
|
||||
/**
|
||||
* Whether or not the app is currently in full screen.
|
||||
*/
|
||||
_fullScreen: boolean,
|
||||
|
||||
/**
|
||||
* External handler for click action.
|
||||
*/
|
||||
handleClick: Function
|
||||
};
|
||||
|
||||
/**
|
||||
* Implementation of a button for toggling fullscreen state.
|
||||
*/
|
||||
class FullscreenButton extends AbstractButton<Props, *> {
|
||||
accessibilityLabel = 'toolbar.accessibilityLabel.fullScreen';
|
||||
label = 'toolbar.enterFullScreen';
|
||||
toggledLabel = 'toolbar.exitFullScreen'
|
||||
|
||||
/**
|
||||
* Retrieves icon dynamically.
|
||||
*/
|
||||
get icon() {
|
||||
if (this._isToggled()) {
|
||||
return IconExitFullScreen;
|
||||
}
|
||||
|
||||
return IconFullScreen;
|
||||
}
|
||||
|
||||
/**
|
||||
* Required by linter due to AbstractButton overwritten prop being writable.
|
||||
*
|
||||
* @param {string} value - The value.
|
||||
*/
|
||||
set icon(value) {
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves icon dynamically.
|
||||
*/
|
||||
get tooltip() {
|
||||
if (this._isToggled()) {
|
||||
return 'toolbar.exitFullScreen';
|
||||
}
|
||||
|
||||
return 'toolbar.enterFullScreen';
|
||||
}
|
||||
|
||||
/**
|
||||
* Required by linter due to AbstractButton overwritten prop being writable.
|
||||
*
|
||||
* @param {string} value - The value.
|
||||
*/
|
||||
set tooltip(value) {
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles clicking / pressing the button, and opens the appropriate dialog.
|
||||
*
|
||||
* @protected
|
||||
* @returns {void}
|
||||
*/
|
||||
_handleClick() {
|
||||
this.props.handleClick();
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates whether this button is in toggled state or not.
|
||||
*
|
||||
* @override
|
||||
* @protected
|
||||
* @returns {boolean}
|
||||
*/
|
||||
_isToggled() {
|
||||
return this.props._fullScreen;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Function that maps parts of Redux state tree into component props.
|
||||
*
|
||||
* @param {Object} state - Redux state.
|
||||
* @returns {Object}
|
||||
*/
|
||||
const mapStateToProps = state => {
|
||||
return {
|
||||
_fullScreen: state['features/toolbox'].fullScreen
|
||||
};
|
||||
};
|
||||
|
||||
export default translate(connect(mapStateToProps)(FullscreenButton));
|
|
@ -1,148 +0,0 @@
|
|||
// @flow
|
||||
|
||||
import React, { Component } from 'react';
|
||||
|
||||
import { Avatar } from '../../../base/avatar';
|
||||
import { translate } from '../../../base/i18n';
|
||||
import { getLocalParticipant } from '../../../base/participants';
|
||||
import { connect } from '../../../base/redux';
|
||||
|
||||
declare var interfaceConfig: Object;
|
||||
|
||||
/**
|
||||
* The type of the React {@code Component} props of
|
||||
* {@link OverflowMenuProfileItem}.
|
||||
*/
|
||||
type Props = {
|
||||
|
||||
/**
|
||||
* The redux representation of the local participant.
|
||||
*/
|
||||
_localParticipant: Object,
|
||||
|
||||
/**
|
||||
* Whether the button support clicking or not.
|
||||
*/
|
||||
_unclickable: boolean,
|
||||
|
||||
/**
|
||||
* The callback to invoke when {@code OverflowMenuProfileItem} is
|
||||
* clicked.
|
||||
*/
|
||||
onClick: Function,
|
||||
|
||||
/**
|
||||
* Invoked to obtain translated strings.
|
||||
*/
|
||||
t: Function
|
||||
};
|
||||
|
||||
/**
|
||||
* A React {@code Component} for displaying a link with a profile avatar as an
|
||||
* icon.
|
||||
*
|
||||
* @extends Component
|
||||
*/
|
||||
class OverflowMenuProfileItem extends Component<Props> {
|
||||
/**
|
||||
* Initializes a new {@code OverflowMenuProfileItem} instance.
|
||||
*
|
||||
* @param {Object} props - The read-only properties with which the new
|
||||
* instance is to be initialized.
|
||||
*/
|
||||
constructor(props: Props) {
|
||||
super(props);
|
||||
|
||||
// Bind event handler so it is only bound once for every instance.
|
||||
this._onClick = this._onClick.bind(this);
|
||||
this._onKeyPress = this._onKeyPress.bind(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements React's {@link Component#render()}.
|
||||
*
|
||||
* @inheritdoc
|
||||
* @returns {ReactElement}
|
||||
*/
|
||||
render() {
|
||||
const { _localParticipant, _unclickable, t } = this.props;
|
||||
const classNames = `overflow-menu-item ${
|
||||
_unclickable ? 'unclickable' : ''}`;
|
||||
let displayName;
|
||||
|
||||
if (_localParticipant && _localParticipant.name) {
|
||||
displayName = _localParticipant.name;
|
||||
} else {
|
||||
displayName = interfaceConfig.DEFAULT_LOCAL_DISPLAY_NAME;
|
||||
}
|
||||
|
||||
return (
|
||||
<li
|
||||
aria-label = { t('toolbar.accessibilityLabel.profile') }
|
||||
className = { classNames }
|
||||
onClick = { this._onClick }
|
||||
onKeyPress = { this._onKeyPress }
|
||||
role = 'menuitem'
|
||||
tabIndex = { 0 }>
|
||||
<span className = 'overflow-menu-item-icon'>
|
||||
<Avatar
|
||||
participantId = { _localParticipant.id }
|
||||
size = { 20 } />
|
||||
</span>
|
||||
<span className = 'profile-text'>
|
||||
{ displayName }
|
||||
</span>
|
||||
</li>
|
||||
);
|
||||
}
|
||||
|
||||
_onClick: () => void;
|
||||
|
||||
/**
|
||||
* Invokes an on click callback if clicking is allowed.
|
||||
*
|
||||
* @returns {void}
|
||||
*/
|
||||
_onClick() {
|
||||
if (!this.props._unclickable) {
|
||||
this.props.onClick();
|
||||
}
|
||||
}
|
||||
|
||||
_onKeyPress: (Object) => void;
|
||||
|
||||
/**
|
||||
* KeyPress handler for accessibility.
|
||||
*
|
||||
* @param {Object} e - The key event to handle.
|
||||
*
|
||||
* @returns {void}
|
||||
*/
|
||||
_onKeyPress(e) {
|
||||
if (!this.props._unclickable && (e.key === ' ' || e.key === 'Enter')) {
|
||||
e.preventDefault();
|
||||
this.props.onClick();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Maps (parts of) the Redux state to the associated
|
||||
* {@code OverflowMenuProfileItem} component's props.
|
||||
*
|
||||
* @param {Object} state - The Redux state.
|
||||
* @private
|
||||
* @returns {{
|
||||
* _localParticipant: Object,
|
||||
* _unclickable: boolean
|
||||
* }}
|
||||
*/
|
||||
function _mapStateToProps(state) {
|
||||
return {
|
||||
_localParticipant: getLocalParticipant(state),
|
||||
_unclickable: state['features/base/config'].disableProfile
|
||||
|| !interfaceConfig.SETTINGS_SECTIONS.includes('profile')
|
||||
};
|
||||
}
|
||||
|
||||
export default translate(connect(_mapStateToProps)(OverflowMenuProfileItem));
|
|
@ -0,0 +1,123 @@
|
|||
// @flow
|
||||
|
||||
import { createToolbarEvent, sendAnalytics } from '../../../analytics';
|
||||
import { translate } from '../../../base/i18n';
|
||||
import { getLocalParticipant } from '../../../base/participants';
|
||||
import { connect } from '../../../base/redux';
|
||||
import { AbstractButton, type AbstractButtonProps } from '../../../base/toolbox/components';
|
||||
import { openSettingsDialog, SETTINGS_TABS } from '../../../settings';
|
||||
|
||||
import ProfileButtonAvatar from './ProfileButtonAvatar';
|
||||
|
||||
/**
|
||||
* The type of the React {@code Component} props of {@link ProfileButton}.
|
||||
*/
|
||||
type Props = AbstractButtonProps & {
|
||||
|
||||
/**
|
||||
* The redux representation of the local participant.
|
||||
*/
|
||||
_localParticipant: Object,
|
||||
|
||||
/**
|
||||
* Whether the button support clicking or not.
|
||||
*/
|
||||
_unclickable: boolean,
|
||||
|
||||
/**
|
||||
* The redux {@code dispatch} function.
|
||||
*/
|
||||
dispatch: Function
|
||||
};
|
||||
|
||||
declare var interfaceConfig: Object;
|
||||
|
||||
/**
|
||||
* Implementation of a button for opening profile dialog.
|
||||
*/
|
||||
class ProfileButton extends AbstractButton<Props, *> {
|
||||
accessibilityLabel = 'toolbar.accessibilityLabel.profile';
|
||||
icon = ProfileButtonAvatar;
|
||||
|
||||
/**
|
||||
* Retrieves the label.
|
||||
*/
|
||||
get label() {
|
||||
const { _localParticipant } = this.props;
|
||||
let displayName;
|
||||
|
||||
if (_localParticipant && _localParticipant.name) {
|
||||
displayName = _localParticipant.name;
|
||||
} else {
|
||||
displayName = interfaceConfig.DEFAULT_LOCAL_DISPLAY_NAME;
|
||||
}
|
||||
|
||||
return displayName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Required by linter due to AbstractButton overwritten prop being writable.
|
||||
*
|
||||
* @param {string} value - The value.
|
||||
*/
|
||||
set label(value) {
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the tooltip.
|
||||
*/
|
||||
get tooltip() {
|
||||
return this.label;
|
||||
}
|
||||
|
||||
/**
|
||||
* Required by linter due to AbstractButton overwritten prop being writable.
|
||||
*
|
||||
* @param {string} value - The value.
|
||||
*/
|
||||
set tooltip(value) {
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles clicking / pressing the button, and opens the appropriate dialog.
|
||||
*
|
||||
* @protected
|
||||
* @returns {void}
|
||||
*/
|
||||
_handleClick() {
|
||||
const { dispatch, _unclickable } = this.props;
|
||||
|
||||
if (!_unclickable) {
|
||||
sendAnalytics(createToolbarEvent('profile'));
|
||||
dispatch(openSettingsDialog(SETTINGS_TABS.PROFILE));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates whether the button should be disabled or not.
|
||||
*
|
||||
* @protected
|
||||
* @returns {void}
|
||||
*/
|
||||
_isDisabled() {
|
||||
return this.props._unclickable;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Function that maps parts of Redux state tree into component props.
|
||||
*
|
||||
* @param {Object} state - Redux state.
|
||||
* @returns {Object}
|
||||
*/
|
||||
const mapStateToProps = state => {
|
||||
return {
|
||||
_localParticipant: getLocalParticipant(state),
|
||||
_unclickable: !interfaceConfig.SETTINGS_SECTIONS.includes('profile'),
|
||||
customClass: 'profile-button-avatar'
|
||||
};
|
||||
};
|
||||
|
||||
export default translate(connect(mapStateToProps)(ProfileButton));
|
|
@ -0,0 +1,63 @@
|
|||
// @flow
|
||||
|
||||
import React, { Component } from 'react';
|
||||
|
||||
import { Avatar } from '../../../base/avatar';
|
||||
import { translate } from '../../../base/i18n';
|
||||
import { getLocalParticipant } from '../../../base/participants';
|
||||
import { connect } from '../../../base/redux';
|
||||
|
||||
/**
|
||||
* The type of the React {@code Component} props of
|
||||
* {@link ProfileButtonAvatar}.
|
||||
*/
|
||||
type Props = {
|
||||
|
||||
/**
|
||||
* The redux representation of the local participant.
|
||||
*/
|
||||
_localParticipant: Object,
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* A React {@code Component} for displaying a profile avatar as an
|
||||
* icon.
|
||||
*
|
||||
* @extends Component
|
||||
*/
|
||||
class ProfileButtonAvatar extends Component<Props> {
|
||||
/**
|
||||
* Implements React's {@link Component#render()}.
|
||||
*
|
||||
* @inheritdoc
|
||||
* @returns {ReactElement}
|
||||
*/
|
||||
render() {
|
||||
const { _localParticipant } = this.props;
|
||||
|
||||
return (
|
||||
<Avatar
|
||||
participantId = { _localParticipant.id }
|
||||
size = { 20 } />
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Maps (parts of) the Redux state to the associated
|
||||
* {@code ProfileButtonAvatar} component's props.
|
||||
*
|
||||
* @param {Object} state - The Redux state.
|
||||
* @private
|
||||
* @returns {{
|
||||
* _localParticipant: Object,
|
||||
* }}
|
||||
*/
|
||||
function _mapStateToProps(state) {
|
||||
return {
|
||||
_localParticipant: getLocalParticipant(state)
|
||||
};
|
||||
}
|
||||
|
||||
export default translate(connect(_mapStateToProps)(ProfileButtonAvatar));
|
|
@ -0,0 +1,83 @@
|
|||
// @flow
|
||||
|
||||
import { translate } from '../../../base/i18n';
|
||||
import { IconRaisedHand } from '../../../base/icons';
|
||||
import { getLocalParticipant } from '../../../base/participants';
|
||||
import { connect } from '../../../base/redux';
|
||||
import { AbstractButton, type AbstractButtonProps } from '../../../base/toolbox/components';
|
||||
|
||||
type Props = AbstractButtonProps & {
|
||||
|
||||
/**
|
||||
* Whether or not the local participant's hand is raised.
|
||||
*/
|
||||
_raisedHand: boolean,
|
||||
|
||||
/**
|
||||
* External handler for click action.
|
||||
*/
|
||||
handleClick: Function
|
||||
};
|
||||
|
||||
/**
|
||||
* Implementation of a button for toggling raise hand functionality.
|
||||
*/
|
||||
class RaiseHandButton extends AbstractButton<Props, *> {
|
||||
accessibilityLabel = 'toolbar.accessibilityLabel.raiseHand';
|
||||
icon = IconRaisedHand
|
||||
label = 'toolbar.raiseYourHand';
|
||||
toggledLabel = 'toolbar.lowerYourHand'
|
||||
|
||||
/**
|
||||
* Retrieves tooltip dynamically.
|
||||
*/
|
||||
get tooltip() {
|
||||
return this.props._raisedHand ? 'toolbar.lowerYourHand' : 'toolbar.raiseYourHand';
|
||||
}
|
||||
|
||||
/**
|
||||
* Required by linter due to AbstractButton overwritten prop being writable.
|
||||
*
|
||||
* @param {string} value - The value.
|
||||
*/
|
||||
set tooltip(value) {
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles clicking / pressing the button, and opens the appropriate dialog.
|
||||
*
|
||||
* @protected
|
||||
* @returns {void}
|
||||
*/
|
||||
_handleClick() {
|
||||
this.props.handleClick();
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates whether this button is in toggled state or not.
|
||||
*
|
||||
* @override
|
||||
* @protected
|
||||
* @returns {boolean}
|
||||
*/
|
||||
_isToggled() {
|
||||
return this.props._raisedHand;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Function that maps parts of Redux state tree into component props.
|
||||
*
|
||||
* @param {Object} state - Redux state.
|
||||
* @returns {Object}
|
||||
*/
|
||||
const mapStateToProps = state => {
|
||||
const localParticipant = getLocalParticipant(state);
|
||||
|
||||
return {
|
||||
_raisedHand: localParticipant.raisedHand
|
||||
};
|
||||
};
|
||||
|
||||
export default translate(connect(mapStateToProps)(RaiseHandButton));
|
|
@ -0,0 +1,3 @@
|
|||
import React from 'react';
|
||||
|
||||
export default () => <hr className = 'overflow-menu-hr' />;
|
|
@ -0,0 +1,139 @@
|
|||
// @flow
|
||||
|
||||
import { translate } from '../../../base/i18n';
|
||||
import { IconShareDesktop } from '../../../base/icons';
|
||||
import JitsiMeetJS from '../../../base/lib-jitsi-meet/_';
|
||||
import { getParticipants } from '../../../base/participants';
|
||||
import { connect } from '../../../base/redux';
|
||||
import { AbstractButton, type AbstractButtonProps } from '../../../base/toolbox/components';
|
||||
import { getLocalVideoTrack } from '../../../base/tracks';
|
||||
import { isScreenAudioShared } from '../../../screen-share';
|
||||
|
||||
type Props = AbstractButtonProps & {
|
||||
|
||||
/**
|
||||
* Whether or not screensharing is initialized.
|
||||
*/
|
||||
_desktopSharingEnabled: boolean,
|
||||
|
||||
/**
|
||||
* The tooltip key to use when screensharing is disabled. Or undefined
|
||||
* if non to be shown and the button to be hidden.
|
||||
*/
|
||||
_desktopSharingDisabledTooltipKey: string,
|
||||
|
||||
/**
|
||||
* Whether or not the local participant is screensharing.
|
||||
*/
|
||||
_screensharing: boolean,
|
||||
|
||||
/**
|
||||
* The redux {@code dispatch} function.
|
||||
*/
|
||||
dispatch: Function,
|
||||
|
||||
/**
|
||||
* External handler for click action.
|
||||
*/
|
||||
handleClick: Function
|
||||
};
|
||||
|
||||
/**
|
||||
* Implementation of a button for sharing desktop / windows.
|
||||
*/
|
||||
class ShareDesktopButton extends AbstractButton<Props, *> {
|
||||
accessibilityLabel = 'toolbar.accessibilityLabel.shareYourScreen';
|
||||
label = 'toolbar.startScreenSharing';
|
||||
icon = IconShareDesktop;
|
||||
toggledLabel = 'toolbar.stopScreenSharing'
|
||||
tooltip = 'toolbar.accessibilityLabel.shareYourScreen';
|
||||
|
||||
/**
|
||||
* Retrieves tooltip dynamically.
|
||||
*/
|
||||
get tooltip() {
|
||||
const { _desktopSharingDisabledTooltipKey, _desktopSharingEnabled, _screensharing } = this.props;
|
||||
|
||||
if (_desktopSharingEnabled) {
|
||||
if (_screensharing) {
|
||||
return 'toolbar.stopScreenSharing';
|
||||
}
|
||||
|
||||
return 'toolbar.startScreenSharing';
|
||||
}
|
||||
|
||||
return _desktopSharingDisabledTooltipKey;
|
||||
}
|
||||
|
||||
/**
|
||||
* Required by linter due to AbstractButton overwritten prop being writable.
|
||||
*
|
||||
* @param {string} value - The icon value.
|
||||
*/
|
||||
set tooltip(value) {
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles clicking / pressing the button, and opens the appropriate dialog.
|
||||
*
|
||||
* @protected
|
||||
* @returns {void}
|
||||
*/
|
||||
_handleClick() {
|
||||
this.props.handleClick();
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates whether this button is in toggled state or not.
|
||||
*
|
||||
* @override
|
||||
* @protected
|
||||
* @returns {boolean}
|
||||
*/
|
||||
_isToggled() {
|
||||
return this.props._screensharing;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates whether this button is in disabled state or not.
|
||||
*
|
||||
* @override
|
||||
* @protected
|
||||
* @returns {boolean}
|
||||
*/
|
||||
_isDisabled() {
|
||||
return !this.props._desktopSharingEnabled;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Function that maps parts of Redux state tree into component props.
|
||||
*
|
||||
* @param {Object} state - Redux state.
|
||||
* @returns {Object}
|
||||
*/
|
||||
const mapStateToProps = state => {
|
||||
const localVideo = getLocalVideoTrack(state['features/base/tracks']);
|
||||
let desktopSharingEnabled = JitsiMeetJS.isDesktopSharingEnabled();
|
||||
const { enableFeaturesBasedOnToken } = state['features/base/config'];
|
||||
|
||||
let desktopSharingDisabledTooltipKey;
|
||||
|
||||
if (enableFeaturesBasedOnToken) {
|
||||
// we enable desktop sharing if any participant already have this
|
||||
// feature enabled
|
||||
desktopSharingEnabled = getParticipants(state)
|
||||
.find(({ features = {} }) =>
|
||||
String(features['screen-sharing']) === 'true') !== undefined;
|
||||
desktopSharingDisabledTooltipKey = 'dialog.shareYourScreenDisabled';
|
||||
}
|
||||
|
||||
return {
|
||||
_desktopSharingDisabledTooltipKey: desktopSharingDisabledTooltipKey,
|
||||
_desktopSharingEnabled: desktopSharingEnabled,
|
||||
_screensharing: (localVideo && localVideo.videoType === 'desktop') || isScreenAudioShared(state)
|
||||
};
|
||||
};
|
||||
|
||||
export default translate(connect(mapStateToProps)(ShareDesktopButton));
|
|
@ -1,75 +0,0 @@
|
|||
// @flow
|
||||
|
||||
import { translate } from '../../../base/i18n';
|
||||
import { IconCameraRefresh } from '../../../base/icons';
|
||||
import { connect } from '../../../base/redux';
|
||||
import { AbstractButton, type AbstractButtonProps } from '../../../base/toolbox/components';
|
||||
import { isLocalCameraTrackMuted, isToggleCameraEnabled, toggleCamera } from '../../../base/tracks';
|
||||
|
||||
/**
|
||||
* The type of the React {@code Component} props of {@link ToggleCameraButton}.
|
||||
*/
|
||||
type Props = AbstractButtonProps & {
|
||||
|
||||
/**
|
||||
* Whether the current conference is in audio only mode or not.
|
||||
*/
|
||||
_audioOnly: boolean,
|
||||
|
||||
/**
|
||||
* Whether video is currently muted or not.
|
||||
*/
|
||||
_videoMuted: boolean,
|
||||
|
||||
/**
|
||||
* The Redux dispatch function.
|
||||
*/
|
||||
dispatch: Function
|
||||
};
|
||||
|
||||
/**
|
||||
* An implementation of a button for toggling the camera facing mode.
|
||||
*/
|
||||
class ToggleCameraButton extends AbstractButton<Props, any> {
|
||||
accessibilityLabel = 'toolbar.accessibilityLabel.toggleCamera';
|
||||
icon = IconCameraRefresh;
|
||||
label = 'toolbar.toggleCamera';
|
||||
|
||||
/**
|
||||
* Handles clicking/pressing the button.
|
||||
*
|
||||
* @returns {void}
|
||||
*/
|
||||
_handleClick() {
|
||||
this.props.dispatch(toggleCamera());
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether this button is disabled or not.
|
||||
*
|
||||
* @returns {boolean}
|
||||
*/
|
||||
_isDisabled() {
|
||||
return this.props._audioOnly || this.props._videoMuted;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Maps (parts of) the redux state to the associated props for the
|
||||
* {@code ToggleCameraButton} component.
|
||||
*
|
||||
* @param {Object} state - The Redux state.
|
||||
* @returns {Props}
|
||||
*/
|
||||
function mapStateToProps(state): Object {
|
||||
const { enabled: audioOnly } = state['features/base/audio-only'];
|
||||
const tracks = state['features/base/tracks'];
|
||||
|
||||
return {
|
||||
_audioOnly: Boolean(audioOnly),
|
||||
_videoMuted: isLocalCameraTrackMuted(tracks),
|
||||
visible: isToggleCameraEnabled(state)
|
||||
};
|
||||
}
|
||||
|
||||
export default translate(connect(mapStateToProps)(ToggleCameraButton));
|
|
@ -11,7 +11,7 @@ import type { Props as AbstractToolbarButtonProps }
|
|||
/**
|
||||
* The type of the React {@code Component} props of {@link ToolbarButton}.
|
||||
*/
|
||||
type Props = AbstractToolbarButtonProps & {
|
||||
export type Props = AbstractToolbarButtonProps & {
|
||||
|
||||
/**
|
||||
* The text to display in the tooltip.
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,29 @@
|
|||
/**
|
||||
* Thresholds for displaying toolbox buttons
|
||||
*/
|
||||
export const THRESHOLDS = [
|
||||
{
|
||||
width: 520,
|
||||
order: [ 'microphone', 'camera', 'desktop', 'chat', 'raisehand', 'participants', 'tileview' ]
|
||||
},
|
||||
{
|
||||
width: 470,
|
||||
order: [ 'microphone', 'camera', 'desktop', 'chat', 'raisehand', 'participants' ]
|
||||
},
|
||||
{
|
||||
width: 420,
|
||||
order: [ 'microphone', 'camera', 'desktop', 'chat', 'participants' ]
|
||||
},
|
||||
{
|
||||
width: 370,
|
||||
order: [ 'microphone', 'camera', 'chat', 'participants' ]
|
||||
},
|
||||
{
|
||||
width: 320,
|
||||
order: [ 'microphone', 'camera', 'chat' ]
|
||||
},
|
||||
{
|
||||
width: 270,
|
||||
order: [ 'microphone', 'camera' ]
|
||||
}
|
||||
];
|
|
@ -3,70 +3,6 @@
|
|||
import { getToolbarButtons } from '../base/config';
|
||||
import { hasAvailableDevices } from '../base/devices';
|
||||
|
||||
const WIDTH = {
|
||||
FIT_9_ICONS: 520,
|
||||
FIT_8_ICONS: 470,
|
||||
FIT_7_ICONS: 420,
|
||||
FIT_6_ICONS: 370,
|
||||
FIT_5_ICONS: 320,
|
||||
FIT_4_ICONS: 280
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns a set of button names to be displayed in the toolbox, based on the screen width and platform.
|
||||
*
|
||||
* @param {number} width - The width of the screen.
|
||||
* @param {number} isMobile - The device is a mobile one.
|
||||
* @returns {Set} The button set.
|
||||
*/
|
||||
export function getToolbarAdditionalButtons(width: number, isMobile: boolean): Set<string> {
|
||||
let buttons = [];
|
||||
|
||||
switch (true) {
|
||||
case width >= WIDTH.FIT_9_ICONS: {
|
||||
buttons = isMobile
|
||||
? [ 'chat', 'raisehand', 'tileview', 'participants-pane', 'overflow' ]
|
||||
: [ 'desktop', 'chat', 'raisehand', 'tileview', 'participants-pane', 'overflow' ];
|
||||
break;
|
||||
}
|
||||
|
||||
case width >= WIDTH.FIT_8_ICONS: {
|
||||
buttons = [ 'desktop', 'chat', 'raisehand', 'participants-pane', 'overflow' ];
|
||||
break;
|
||||
}
|
||||
|
||||
case width >= WIDTH.FIT_7_ICONS: {
|
||||
buttons = [ 'desktop', 'chat', 'participants-pane', 'overflow' ];
|
||||
break;
|
||||
}
|
||||
|
||||
case width >= WIDTH.FIT_6_ICONS: {
|
||||
buttons = [ 'chat', 'participants-pane', 'overflow' ];
|
||||
break;
|
||||
}
|
||||
|
||||
case width >= WIDTH.FIT_5_ICONS: {
|
||||
buttons = [ 'chat', 'overflow' ];
|
||||
break;
|
||||
}
|
||||
|
||||
case width >= WIDTH.FIT_4_ICONS: {
|
||||
buttons = isMobile
|
||||
? [ 'chat', 'overflow' ]
|
||||
: [ 'overflow' ];
|
||||
break;
|
||||
}
|
||||
|
||||
default: {
|
||||
buttons = isMobile
|
||||
? [ 'chat', 'overflow' ]
|
||||
: [];
|
||||
}
|
||||
}
|
||||
|
||||
return new Set(buttons);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper for getting the height of the toolbox.
|
||||
*
|
||||
|
|
|
@ -52,13 +52,13 @@ class TileViewButton<P: Props> extends AbstractButton<P, *> {
|
|||
*/
|
||||
_handleClick() {
|
||||
const { _tileViewEnabled, dispatch } = this.props;
|
||||
const value = !_tileViewEnabled;
|
||||
|
||||
sendAnalytics(createToolbarEvent(
|
||||
'tileview.button',
|
||||
{
|
||||
'is_enabled': _tileViewEnabled
|
||||
'is_enabled': value
|
||||
}));
|
||||
const value = !_tileViewEnabled;
|
||||
|
||||
logger.debug(`Tile view ${value ? 'enable' : 'disable'}`);
|
||||
dispatch(setTileView(value));
|
||||
|
|
|
@ -1,16 +1,14 @@
|
|||
// @flow
|
||||
|
||||
import React, { Component } from 'react';
|
||||
|
||||
import { translate } from '../../base/i18n';
|
||||
import {
|
||||
Icon,
|
||||
IconVideoQualityAudioOnly,
|
||||
IconVideoQualityHD,
|
||||
IconVideoQualityLD,
|
||||
IconVideoQualitySD
|
||||
} from '../../base/icons';
|
||||
import { connect } from '../../base/redux';
|
||||
import { AbstractButton, type AbstractButtonProps } from '../../base/toolbox/components';
|
||||
import { VIDEO_QUALITY_LEVELS } from '../constants';
|
||||
import { findNearestQualityLevel } from '../functions';
|
||||
|
||||
|
@ -29,9 +27,9 @@ const VIDEO_QUALITY_TO_ICON = {
|
|||
|
||||
/**
|
||||
* The type of the React {@code Component} props of
|
||||
* {@link OverflowMenuVideoQualityItem}.
|
||||
* {@link VideoQualityButton}.
|
||||
*/
|
||||
type Props = {
|
||||
type Props = AbstractButtonProps & {
|
||||
|
||||
/**
|
||||
* Whether or not audio only mode is currently enabled.
|
||||
|
@ -45,9 +43,9 @@ type Props = {
|
|||
_videoQuality: number,
|
||||
|
||||
/**
|
||||
* Callback to invoke when {@link OverflowMenuVideoQualityItem} is clicked.
|
||||
* Callback to invoke when {@link VideoQualityButton} is clicked.
|
||||
*/
|
||||
onClick: Function,
|
||||
handleClick: Function,
|
||||
|
||||
/**
|
||||
* Invoked to obtain translated strings.
|
||||
|
@ -62,72 +60,50 @@ type Props = {
|
|||
*
|
||||
* @extends Component
|
||||
*/
|
||||
class OverflowMenuVideoQualityItem extends Component<Props> {
|
||||
class VideoQualityButton extends AbstractButton<Props, *> {
|
||||
accessibilityLabel = 'toolbar.accessibilityLabel.callQuality';
|
||||
label = 'toolbar.callQuality';
|
||||
tooltip = 'toolbar.callQuality';
|
||||
|
||||
/**
|
||||
* Initializes a new {@code OverflowMenuVideoQualityItem} instance.
|
||||
*
|
||||
* @param {*} props - The read-only properties with which the new instance
|
||||
* is to be initialized.
|
||||
* Dynamically retrieves the icon.
|
||||
*/
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
// Bind event handler so it is only bound once for every instance.
|
||||
this._onKeyPress = this._onKeyPress.bind(this);
|
||||
}
|
||||
|
||||
_onKeyPress: (Object) => void;
|
||||
|
||||
/**
|
||||
* KeyPress handler for accessibility.
|
||||
*
|
||||
* @param {Object} e - The key event to handle.
|
||||
*
|
||||
* @returns {void}
|
||||
*/
|
||||
_onKeyPress(e) {
|
||||
if (this.props.onClick && (e.key === ' ' || e.key === 'Enter')) {
|
||||
e.preventDefault();
|
||||
this.props.onClick();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements React's {@link Component#render()}.
|
||||
*
|
||||
* @inheritdoc
|
||||
* @returns {ReactElement}
|
||||
*/
|
||||
render() {
|
||||
get icon() {
|
||||
const { _audioOnly, _videoQuality } = this.props;
|
||||
|
||||
const videoQualityLevel = findNearestQualityLevel(_videoQuality);
|
||||
|
||||
const icon = _audioOnly || !videoQualityLevel
|
||||
? IconVideoQualityAudioOnly
|
||||
: VIDEO_QUALITY_TO_ICON[videoQualityLevel];
|
||||
|
||||
return (
|
||||
<li
|
||||
aria-label = { this.props.t('toolbar.accessibilityLabel.callQuality') }
|
||||
className = 'overflow-menu-item'
|
||||
onClick = { this.props.onClick }
|
||||
onKeyPress = { this._onKeyPress }
|
||||
role = 'menuitem'
|
||||
tabIndex = { 0 }>
|
||||
<span className = 'overflow-menu-item-icon'>
|
||||
<Icon src = { icon } />
|
||||
</span>
|
||||
<span className = 'profile-text'>
|
||||
{ this.props.t('toolbar.callQuality') }
|
||||
</span>
|
||||
</li>
|
||||
);
|
||||
return icon;
|
||||
}
|
||||
|
||||
/**
|
||||
* Required by linter due to AbstractButton overwritten prop being writable.
|
||||
*
|
||||
* @param {string} value - The icon value.
|
||||
*/
|
||||
set icon(value) {
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles clicking / pressing the button.
|
||||
*
|
||||
* @override
|
||||
* @protected
|
||||
* @returns {void}
|
||||
*/
|
||||
_handleClick() {
|
||||
this.props.handleClick();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Maps (parts of) the Redux state to the associated props for the
|
||||
* {@code OverflowMenuVideoQualityItem} component.
|
||||
* {@code VideoQualityButton} component.
|
||||
*
|
||||
* @param {Object} state - The Redux state.
|
||||
* @private
|
||||
|
@ -144,4 +120,4 @@ function _mapStateToProps(state) {
|
|||
}
|
||||
|
||||
export default translate(
|
||||
connect(_mapStateToProps)(OverflowMenuVideoQualityItem));
|
||||
connect(_mapStateToProps)(VideoQualityButton));
|
|
@ -1,6 +1,4 @@
|
|||
export {
|
||||
default as OverflowMenuVideoQualityItem
|
||||
} from './OverflowMenuVideoQualityItem';
|
||||
export { default as VideoQualityButton } from './VideoQualityButton.web';
|
||||
export { default as VideoQualityDialog } from './VideoQualityDialog';
|
||||
export { default as VideoQualityLabel } from './VideoQualityLabel';
|
||||
export {
|
||||
|
|
Loading…
Reference in New Issue