feat(toolbar-button-clicked) Enhance toolbar buttons with notify click
- add possibility to allow execution of the button's routine besides triggering `toolbarButtonClicked` API event - keep backwards compatibility - get rid of `ToolbarButton`
This commit is contained in:
parent
847dc2a7bb
commit
197dbfbbcb
88
config.js
88
config.js
|
@ -615,41 +615,61 @@ var config = {
|
|||
// alwaysVisible: false
|
||||
// },
|
||||
|
||||
// Toolbar buttons which have their click event exposed through the API on
|
||||
// `toolbarButtonClicked` event instead of executing the normal click routine.
|
||||
// Toolbar buttons which have their click/tap event exposed through the API on
|
||||
// `toolbarButtonClicked`. Passing a string for the button key will
|
||||
// prevent execution of the click/tap routine; passing an object with `key` and
|
||||
// `preventExecution` flag on false will not prevent execution of the click/tap
|
||||
// routine. Below array with mixed mode for passing the buttons.
|
||||
// buttonsWithNotifyClick: [
|
||||
// 'camera',
|
||||
// 'chat',
|
||||
// 'closedcaptions',
|
||||
// 'desktop',
|
||||
// 'download',
|
||||
// 'embedmeeting',
|
||||
// 'etherpad',
|
||||
// 'feedback',
|
||||
// 'filmstrip',
|
||||
// 'fullscreen',
|
||||
// 'hangup',
|
||||
// 'help',
|
||||
// 'invite',
|
||||
// 'livestreaming',
|
||||
// 'microphone',
|
||||
// 'mute-everyone',
|
||||
// 'mute-video-everyone',
|
||||
// 'participants-pane',
|
||||
// 'profile',
|
||||
// 'raisehand',
|
||||
// 'recording',
|
||||
// 'security',
|
||||
// 'select-background',
|
||||
// 'settings',
|
||||
// 'shareaudio',
|
||||
// 'sharedvideo',
|
||||
// 'shortcuts',
|
||||
// 'stats',
|
||||
// 'tileview',
|
||||
// 'toggle-camera',
|
||||
// 'videoquality',
|
||||
// '__end'
|
||||
// 'camera',
|
||||
// {
|
||||
// key: 'chat',
|
||||
// preventExecution: false
|
||||
// },
|
||||
// {
|
||||
// key: 'closedcaptions',
|
||||
// preventExecution: true
|
||||
// },
|
||||
// 'desktop',
|
||||
// 'download',
|
||||
// 'embedmeeting',
|
||||
// 'etherpad',
|
||||
// 'feedback',
|
||||
// 'filmstrip',
|
||||
// 'fullscreen',
|
||||
// 'hangup',
|
||||
// 'help',
|
||||
// {
|
||||
// key: 'invite',
|
||||
// preventExecution: false
|
||||
// },
|
||||
// 'livestreaming',
|
||||
// 'microphone',
|
||||
// 'mute-everyone',
|
||||
// 'mute-video-everyone',
|
||||
// 'participants-pane',
|
||||
// 'profile',
|
||||
// {
|
||||
// key: 'raisehand',
|
||||
// preventExecution: true
|
||||
// },
|
||||
// 'recording',
|
||||
// 'security',
|
||||
// 'select-background',
|
||||
// 'settings',
|
||||
// 'shareaudio',
|
||||
// 'sharedvideo',
|
||||
// 'shortcuts',
|
||||
// 'stats',
|
||||
// 'tileview',
|
||||
// 'toggle-camera',
|
||||
// 'videoquality',
|
||||
// // The add passcode button from the security dialog.
|
||||
// {
|
||||
// key: 'add-passcode',
|
||||
// preventExecution: false
|
||||
// }
|
||||
// '__end'
|
||||
// ],
|
||||
|
||||
// List of pre meeting screens buttons to hide. The values must be one or more of the 5 allowed buttons:
|
||||
|
|
|
@ -1521,12 +1521,14 @@ class API {
|
|||
* Notify external application ( if API is enabled) that a toolbar button was clicked.
|
||||
*
|
||||
* @param {string} key - The key of the toolbar button.
|
||||
* @param {boolean} preventExecution - Whether execution of the button click was prevented or not.
|
||||
* @returns {void}
|
||||
*/
|
||||
notifyToolbarButtonClicked(key: string) {
|
||||
notifyToolbarButtonClicked(key: string, preventExecution: boolean) {
|
||||
this._sendEvent({
|
||||
name: 'toolbar-button-clicked',
|
||||
key
|
||||
key,
|
||||
preventExecution
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -23,14 +23,6 @@ export default class AbstractAudioMuteButton<P: Props, S: *>
|
|||
* @returns {void}
|
||||
*/
|
||||
_handleClick() {
|
||||
const { handleClick } = this.props;
|
||||
|
||||
if (handleClick) {
|
||||
handleClick();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
this._setAudioMuted(!this._isAudioMuted());
|
||||
}
|
||||
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
import React, { Component } from 'react';
|
||||
|
||||
import { NOTIFY_CLICK_MODE } from '../../../toolbox/constants';
|
||||
import { combineStyles } from '../../styles';
|
||||
|
||||
import type { Styles } from './AbstractToolboxItem';
|
||||
|
@ -14,6 +15,11 @@ export type Props = {
|
|||
*/
|
||||
afterClick: ?Function,
|
||||
|
||||
/**
|
||||
* The button's key.
|
||||
*/
|
||||
buttonKey?: string,
|
||||
|
||||
/**
|
||||
* Extra styles which will be applied in conjunction with `styles` or
|
||||
* `toggledStyles` when the button is disabled;.
|
||||
|
@ -25,6 +31,12 @@ export type Props = {
|
|||
*/
|
||||
handleClick?: Function,
|
||||
|
||||
/**
|
||||
* Notify mode for `toolbarButtonClicked` event -
|
||||
* whether to only notify or to also prevent button click routine.
|
||||
*/
|
||||
notifyMode?: string,
|
||||
|
||||
/**
|
||||
* Whether to show the label or not.
|
||||
*/
|
||||
|
@ -51,6 +63,8 @@ export type Props = {
|
|||
visible: boolean
|
||||
};
|
||||
|
||||
declare var APP: Object;
|
||||
|
||||
/**
|
||||
* Default style for disabled buttons.
|
||||
*/
|
||||
|
@ -134,6 +148,17 @@ export default class AbstractButton<P: Props, S: *> extends Component<P, S> {
|
|||
this._onClick = this._onClick.bind(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to be implemented by subclasses, which should be used
|
||||
* to handle a key being down.
|
||||
*
|
||||
* @protected
|
||||
* @returns {void}
|
||||
*/
|
||||
_onKeyDown() {
|
||||
// To be implemented by subclass.
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to be implemented by subclasses, which should be used
|
||||
* to handle the button being clicked / pressed.
|
||||
|
@ -248,17 +273,29 @@ export default class AbstractButton<P: Props, S: *> extends Component<P, S> {
|
|||
_onClick: (*) => void;
|
||||
|
||||
/**
|
||||
* Handles clicking / pressing the button, and toggles the audio mute state
|
||||
* accordingly.
|
||||
* Handles clicking / pressing the button.
|
||||
*
|
||||
* @param {Object} e - Event.
|
||||
* @private
|
||||
* @returns {void}
|
||||
*/
|
||||
_onClick(e) {
|
||||
const { afterClick } = this.props;
|
||||
const { afterClick, handleClick, notifyMode, buttonKey } = this.props;
|
||||
|
||||
if (typeof APP !== 'undefined' && notifyMode) {
|
||||
APP.API.notifyToolbarButtonClicked(
|
||||
buttonKey, notifyMode === NOTIFY_CLICK_MODE.PREVENT_AND_NOTIFY
|
||||
);
|
||||
}
|
||||
|
||||
if (notifyMode !== NOTIFY_CLICK_MODE.PREVENT_AND_NOTIFY) {
|
||||
if (handleClick) {
|
||||
handleClick();
|
||||
}
|
||||
|
||||
this._handleClick();
|
||||
}
|
||||
|
||||
this._handleClick();
|
||||
afterClick && afterClick(e);
|
||||
|
||||
// blur after click to release focus from button to allow PTT.
|
||||
|
@ -288,6 +325,7 @@ export default class AbstractButton<P: Props, S: *> extends Component<P, S> {
|
|||
<ToolboxItem
|
||||
disabled = { this._isDisabled() }
|
||||
onClick = { this._onClick }
|
||||
onKeyDown = { this._onKeyDown }
|
||||
{ ...props } />
|
||||
);
|
||||
}
|
||||
|
|
|
@ -22,14 +22,6 @@ export default class AbstractVideoMuteButton<P : Props, S : *>
|
|||
* @returns {void}
|
||||
*/
|
||||
_handleClick() {
|
||||
const { handleClick } = this.props;
|
||||
|
||||
if (handleClick) {
|
||||
handleClick();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
this._setVideoMuted(!this._isVideoMuted());
|
||||
}
|
||||
|
||||
|
|
|
@ -6,7 +6,15 @@ import { Icon } from '../../icons';
|
|||
import { Tooltip } from '../../tooltip';
|
||||
|
||||
import AbstractToolboxItem from './AbstractToolboxItem';
|
||||
import type { Props } from './AbstractToolboxItem';
|
||||
import type { Props as AbstractToolboxItemProps } from './AbstractToolboxItem';
|
||||
|
||||
type Props = AbstractToolboxItemProps & {
|
||||
|
||||
/**
|
||||
* On key down handler.
|
||||
*/
|
||||
onKeyDown: Function
|
||||
};
|
||||
|
||||
/**
|
||||
* Web implementation of {@code AbstractToolboxItem}.
|
||||
|
@ -53,6 +61,7 @@ export default class ToolboxItem extends AbstractToolboxItem<Props> {
|
|||
disabled,
|
||||
elementAfter,
|
||||
onClick,
|
||||
onKeyDown,
|
||||
showLabel,
|
||||
tooltipPosition,
|
||||
toggled
|
||||
|
@ -64,6 +73,7 @@ export default class ToolboxItem extends AbstractToolboxItem<Props> {
|
|||
'aria-label': this.accessibilityLabel,
|
||||
className: className + (disabled ? ' disabled' : ''),
|
||||
onClick: disabled ? undefined : onClick,
|
||||
onKeyDown: disabled ? undefined : onKeyDown,
|
||||
onKeyPress: this._onKeyPress,
|
||||
tabIndex: 0,
|
||||
role: showLabel ? 'menuitem' : 'button'
|
||||
|
|
|
@ -2,11 +2,17 @@
|
|||
|
||||
import React from 'react';
|
||||
|
||||
import { NOTIFY_CLICK_MODE } from '../../../../toolbox/constants';
|
||||
import { Icon } from '../../../icons';
|
||||
import { Tooltip } from '../../../tooltip';
|
||||
|
||||
type Props = {
|
||||
|
||||
/**
|
||||
* The button's key.
|
||||
*/
|
||||
buttonKey?: string,
|
||||
|
||||
/**
|
||||
* The decorated component (ToolboxButton).
|
||||
*/
|
||||
|
@ -22,6 +28,12 @@ type Props = {
|
|||
*/
|
||||
iconDisabled: boolean,
|
||||
|
||||
/**
|
||||
* Notify mode for `toolbarButtonClicked` event -
|
||||
* whether to only notify or to also prevent button click routine.
|
||||
*/
|
||||
notifyMode?: string,
|
||||
|
||||
/**
|
||||
* Click handler for the small icon.
|
||||
*/
|
||||
|
@ -68,6 +80,8 @@ type Props = {
|
|||
iconId: string
|
||||
};
|
||||
|
||||
declare var APP: Object;
|
||||
|
||||
/**
|
||||
* Displays the `ToolboxButtonWithIcon` component.
|
||||
*
|
||||
|
@ -80,6 +94,8 @@ export default function ToolboxButtonWithIcon(props: Props) {
|
|||
icon,
|
||||
iconDisabled,
|
||||
iconTooltip,
|
||||
buttonKey,
|
||||
notifyMode,
|
||||
onIconClick,
|
||||
onIconKeyDown,
|
||||
styles,
|
||||
|
@ -97,7 +113,17 @@ export default function ToolboxButtonWithIcon(props: Props) {
|
|||
= 'settings-button-small-icon settings-button-small-icon--disabled';
|
||||
} else {
|
||||
iconProps.className = 'settings-button-small-icon';
|
||||
iconProps.onClick = onIconClick;
|
||||
iconProps.onClick = () => {
|
||||
if (typeof APP !== 'undefined' && notifyMode) {
|
||||
APP.API.notifyToolbarButtonClicked(
|
||||
buttonKey, notifyMode === NOTIFY_CLICK_MODE.PREVENT_AND_NOTIFY
|
||||
);
|
||||
}
|
||||
|
||||
if (notifyMode !== NOTIFY_CLICK_MODE.PREVENT_AND_NOTIFY) {
|
||||
onIconClick();
|
||||
}
|
||||
};
|
||||
iconProps.onKeyDown = onIconKeyDown;
|
||||
iconProps.role = 'button';
|
||||
iconProps.tabIndex = 0;
|
||||
|
|
|
@ -49,22 +49,6 @@ class ChatButton extends AbstractButton<Props, *> {
|
|||
// Unused.
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles clicking / pressing the button, and opens the appropriate dialog.
|
||||
*
|
||||
* @protected
|
||||
* @returns {void}
|
||||
*/
|
||||
_handleClick() {
|
||||
const { handleClick } = this.props;
|
||||
|
||||
if (handleClick) {
|
||||
handleClick();
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates whether this button is in toggled state or not.
|
||||
*
|
||||
|
|
|
@ -36,13 +36,7 @@ class EmbedMeetingButton extends AbstractButton<Props, *> {
|
|||
* @returns {void}
|
||||
*/
|
||||
_handleClick() {
|
||||
const { dispatch, handleClick } = this.props;
|
||||
|
||||
if (handleClick) {
|
||||
handleClick();
|
||||
|
||||
return;
|
||||
}
|
||||
const { dispatch } = this.props;
|
||||
|
||||
sendAnalytics(createToolbarEvent('embed.meeting'));
|
||||
dispatch(openDialog(EmbedMeetingDialog));
|
||||
|
|
|
@ -9,7 +9,6 @@ import { connect } from '../../base/redux';
|
|||
import { AbstractButton, type AbstractButtonProps } from '../../base/toolbox/components';
|
||||
import { toggleDocument } from '../../etherpad/actions';
|
||||
|
||||
|
||||
type Props = AbstractButtonProps & {
|
||||
|
||||
/**
|
||||
|
@ -59,13 +58,7 @@ class SharedDocumentButton extends AbstractButton<Props, *> {
|
|||
* @returns {void}
|
||||
*/
|
||||
_handleClick() {
|
||||
const { _editing, dispatch, handleClick } = this.props;
|
||||
|
||||
if (handleClick) {
|
||||
handleClick();
|
||||
|
||||
return;
|
||||
}
|
||||
const { _editing, dispatch } = this.props;
|
||||
|
||||
sendAnalytics(createToolbarEvent(
|
||||
'toggle.etherpad',
|
||||
|
|
|
@ -40,13 +40,7 @@ class FeedbackButton extends AbstractButton<Props, *> {
|
|||
* @returns {void}
|
||||
*/
|
||||
_handleClick() {
|
||||
const { _conference, dispatch, handleClick } = this.props;
|
||||
|
||||
if (handleClick) {
|
||||
handleClick();
|
||||
|
||||
return;
|
||||
}
|
||||
const { _conference, dispatch } = this.props;
|
||||
|
||||
sendAnalytics(createToolbarEvent('feedback'));
|
||||
dispatch(openFeedbackDialog(_conference));
|
||||
|
|
|
@ -34,13 +34,7 @@ class InviteButton extends AbstractButton<Props, *> {
|
|||
* @returns {void}
|
||||
*/
|
||||
_handleClick() {
|
||||
const { dispatch, handleClick } = this.props;
|
||||
|
||||
if (handleClick) {
|
||||
handleClick();
|
||||
|
||||
return;
|
||||
}
|
||||
const { dispatch } = this.props;
|
||||
|
||||
sendAnalytics(createToolbarEvent('invite'));
|
||||
dispatch(beginAddPeople());
|
||||
|
|
|
@ -34,13 +34,7 @@ class KeyboardShortcutsButton extends AbstractButton<Props, *> {
|
|||
* @returns {void}
|
||||
*/
|
||||
_handleClick() {
|
||||
const { dispatch, handleClick } = this.props;
|
||||
|
||||
if (handleClick) {
|
||||
handleClick();
|
||||
|
||||
return;
|
||||
}
|
||||
const { dispatch } = this.props;
|
||||
|
||||
sendAnalytics(createToolbarEvent('shortcuts'));
|
||||
dispatch(openKeyboardShortcutsDialog());
|
||||
|
|
|
@ -36,13 +36,7 @@ class LocalRecording extends AbstractButton<Props, *> {
|
|||
* @returns {void}
|
||||
*/
|
||||
_handleClick() {
|
||||
const { dispatch, handleClick } = this.props;
|
||||
|
||||
if (handleClick) {
|
||||
handleClick();
|
||||
|
||||
return;
|
||||
}
|
||||
const { dispatch } = this.props;
|
||||
|
||||
sendAnalytics(createToolbarEvent('local.recording'));
|
||||
dispatch(openDialog(LocalRecordingInfoDialog));
|
||||
|
|
|
@ -25,22 +25,6 @@ class ParticipantsPaneButton extends AbstractButton<Props, *> {
|
|||
label = 'toolbar.participants';
|
||||
tooltip = 'toolbar.participants';
|
||||
|
||||
/**
|
||||
* Handles clicking / pressing the button, and opens the appropriate dialog.
|
||||
*
|
||||
* @protected
|
||||
* @returns {void}
|
||||
*/
|
||||
_handleClick() {
|
||||
const { handleClick } = this.props;
|
||||
|
||||
if (handleClick) {
|
||||
handleClick();
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates whether this button is in toggled state or not.
|
||||
*
|
||||
|
|
|
@ -0,0 +1,73 @@
|
|||
// @flow
|
||||
|
||||
import { translate } from '../../../base/i18n';
|
||||
import { IconRaisedHand } from '../../../base/icons';
|
||||
import { getLocalParticipant, hasRaisedHand } from '../../../base/participants';
|
||||
import { connect } from '../../../base/redux';
|
||||
import { AbstractButton, type AbstractButtonProps } from '../../../base/toolbox/components';
|
||||
|
||||
|
||||
/**
|
||||
* The type of the React {@code Component} props of {@link RaiseHandButton}.
|
||||
*/
|
||||
type Props = AbstractButtonProps & {
|
||||
|
||||
/**
|
||||
* Whether or not the hand is raised.
|
||||
*/
|
||||
raisedHand: boolean,
|
||||
};
|
||||
|
||||
/**
|
||||
* Implementation of a button for raising hand.
|
||||
*/
|
||||
class RaiseHandButton extends AbstractButton<Props, *> {
|
||||
accessibilityLabel = 'toolbar.accessibilityLabel.raiseHand';
|
||||
icon = IconRaisedHand;
|
||||
label = 'toolbar.raiseHand';
|
||||
toggledLabel = 'toolbar.raiseHand';
|
||||
|
||||
/**
|
||||
* Retrieves tooltip dynamically.
|
||||
*/
|
||||
get tooltip() {
|
||||
return 'toolbar.raiseHand';
|
||||
}
|
||||
|
||||
/**
|
||||
* Required by linter due to AbstractButton overwritten prop being writable.
|
||||
*
|
||||
* @param {string} _value - The value.
|
||||
*/
|
||||
set tooltip(_value) {
|
||||
// Unused.
|
||||
}
|
||||
|
||||
/**
|
||||
* 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: hasRaisedHand(localParticipant)
|
||||
};
|
||||
};
|
||||
|
||||
export default translate(connect(mapStateToProps)(RaiseHandButton));
|
|
@ -4,16 +4,15 @@ import React, { useCallback } from 'react';
|
|||
|
||||
import { isMobileBrowser } from '../../../base/environment/utils';
|
||||
import { translate } from '../../../base/i18n';
|
||||
import { IconArrowUp, IconRaisedHand } from '../../../base/icons';
|
||||
import { getLocalParticipant, hasRaisedHand } from '../../../base/participants';
|
||||
import { IconArrowUp } from '../../../base/icons';
|
||||
import { connect } from '../../../base/redux';
|
||||
import { ToolboxButtonWithIcon } from '../../../base/toolbox/components';
|
||||
import ToolbarButton from '../../../toolbox/components/web/ToolbarButton';
|
||||
import { toggleReactionsMenuVisibility } from '../../actions.web';
|
||||
import { type ReactionEmojiProps } from '../../constants';
|
||||
import { getReactionsQueue, isReactionsEnabled } from '../../functions.any';
|
||||
import { getReactionsMenuVisibility } from '../../functions.web';
|
||||
|
||||
import RaiseHandButton from './RaiseHandButton';
|
||||
import ReactionEmoji from './ReactionEmoji';
|
||||
import ReactionsMenuPopup from './ReactionsMenuPopup';
|
||||
|
||||
|
@ -24,6 +23,11 @@ type Props = {
|
|||
*/
|
||||
_reactionsEnabled: Boolean,
|
||||
|
||||
/**
|
||||
* The button's key.
|
||||
*/
|
||||
buttonKey?: string,
|
||||
|
||||
/**
|
||||
* Redux dispatch function.
|
||||
*/
|
||||
|
@ -45,9 +49,10 @@ type Props = {
|
|||
isMobile: boolean,
|
||||
|
||||
/**
|
||||
* Whether or not the local participant's hand is raised.
|
||||
* Notify mode for `toolbarButtonClicked` event -
|
||||
* whether to only notify or to also prevent button click routine.
|
||||
*/
|
||||
raisedHand: boolean,
|
||||
notifyMode?: string,
|
||||
|
||||
/**
|
||||
* The array of reactions to be displayed.
|
||||
|
@ -70,11 +75,12 @@ declare var APP: Object;
|
|||
*/
|
||||
function ReactionsMenuButton({
|
||||
_reactionsEnabled,
|
||||
buttonKey,
|
||||
dispatch,
|
||||
handleClick,
|
||||
isOpen,
|
||||
isMobile,
|
||||
raisedHand,
|
||||
notifyMode,
|
||||
reactionsQueue,
|
||||
t
|
||||
}: Props) {
|
||||
|
@ -82,30 +88,31 @@ function ReactionsMenuButton({
|
|||
dispatch(toggleReactionsMenuVisibility());
|
||||
}, [ dispatch ]);
|
||||
|
||||
const raiseHandButton = (<ToolbarButton
|
||||
accessibilityLabel = { t('toolbar.accessibilityLabel.raiseHand') }
|
||||
icon = { IconRaisedHand }
|
||||
key = 'raise-hand'
|
||||
onClick = { handleClick }
|
||||
toggled = { raisedHand }
|
||||
tooltip = { t('toolbar.raiseHand') } />);
|
||||
|
||||
return (
|
||||
<div className = 'reactions-menu-popup-container'>
|
||||
<ReactionsMenuPopup>
|
||||
{!_reactionsEnabled || isMobile ? raiseHandButton
|
||||
{!_reactionsEnabled || isMobile ? (
|
||||
<RaiseHandButton
|
||||
buttonKey = { buttonKey }
|
||||
handleClick = { handleClick }
|
||||
notifyMode = { notifyMode } />)
|
||||
: (
|
||||
<ToolboxButtonWithIcon
|
||||
ariaControls = 'reactions-menu-dialog'
|
||||
ariaExpanded = { isOpen }
|
||||
ariaHasPopup = { true }
|
||||
ariaLabel = { t('toolbar.accessibilityLabel.reactionsMenu') }
|
||||
buttonKey = { buttonKey }
|
||||
icon = { IconArrowUp }
|
||||
iconDisabled = { false }
|
||||
iconId = 'reactions-menu-button'
|
||||
iconTooltip = { t(`toolbar.${isOpen ? 'closeReactionsMenu' : 'openReactionsMenu'}`) }
|
||||
notifyMode = { notifyMode }
|
||||
onIconClick = { toggleReactionsMenu }>
|
||||
{raiseHandButton}
|
||||
<RaiseHandButton
|
||||
buttonKey = { buttonKey }
|
||||
handleClick = { handleClick }
|
||||
notifyMode = { notifyMode } />
|
||||
</ToolboxButtonWithIcon>
|
||||
)}
|
||||
</ReactionsMenuPopup>
|
||||
|
@ -125,14 +132,11 @@ function ReactionsMenuButton({
|
|||
* @returns {Object}
|
||||
*/
|
||||
function mapStateToProps(state) {
|
||||
const localParticipant = getLocalParticipant(state);
|
||||
|
||||
return {
|
||||
_reactionsEnabled: isReactionsEnabled(state),
|
||||
isOpen: getReactionsMenuVisibility(state),
|
||||
isMobile: isMobileBrowser(),
|
||||
reactionsQueue: getReactionsQueue(state),
|
||||
raisedHand: hasRaisedHand(localParticipant)
|
||||
reactionsQueue: getReactionsQueue(state)
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -77,13 +77,7 @@ export default class AbstractLiveStreamButton<P: Props> extends AbstractButton<P
|
|||
* @returns {void}
|
||||
*/
|
||||
async _handleClick() {
|
||||
const { _isLiveStreamRunning, dispatch, handleClick } = this.props;
|
||||
|
||||
if (handleClick) {
|
||||
handleClick();
|
||||
|
||||
return;
|
||||
}
|
||||
const { _isLiveStreamRunning, dispatch } = this.props;
|
||||
|
||||
const dialogShown = await dispatch(maybeShowPremiumFeatureDialog(FEATURES.RECORDING));
|
||||
|
||||
|
|
|
@ -78,13 +78,7 @@ export default class AbstractRecordButton<P: Props> extends AbstractButton<P, *>
|
|||
* @returns {void}
|
||||
*/
|
||||
async _handleClick() {
|
||||
const { _isRecordingRunning, dispatch, handleClick } = this.props;
|
||||
|
||||
if (handleClick) {
|
||||
handleClick();
|
||||
|
||||
return;
|
||||
}
|
||||
const { _isRecordingRunning, dispatch } = this.props;
|
||||
|
||||
sendAnalytics(createToolbarEvent(
|
||||
'recording.button',
|
||||
|
|
|
@ -47,13 +47,7 @@ class ShareAudioButton extends AbstractButton<Props, *> {
|
|||
* @returns {void}
|
||||
*/
|
||||
_handleClick() {
|
||||
const { dispatch, handleClick } = this.props;
|
||||
|
||||
if (handleClick) {
|
||||
handleClick();
|
||||
|
||||
return;
|
||||
}
|
||||
const { dispatch } = this.props;
|
||||
|
||||
dispatch(startAudioScreenShareFlow());
|
||||
dispatch(setOverflowMenuVisible(false));
|
||||
|
|
|
@ -56,13 +56,7 @@ export default class AbstractSecurityDialogButton<P: Props, S:*>
|
|||
* @returns {void}
|
||||
*/
|
||||
_handleClick() {
|
||||
const { _locked, handleClick } = this.props;
|
||||
|
||||
if (handleClick) {
|
||||
handleClick();
|
||||
|
||||
return;
|
||||
}
|
||||
const { _locked } = this.props;
|
||||
|
||||
sendAnalytics(createToolbarEvent('toggle.security', { enable: !_locked }));
|
||||
this._handleClickSecurityButton();
|
||||
|
|
|
@ -6,11 +6,19 @@ import React, { useRef } from 'react';
|
|||
|
||||
import { translate } from '../../../../base/i18n';
|
||||
import { copyText } from '../../../../base/util';
|
||||
import { NOTIFY_CLICK_MODE } from '../../../../toolbox/constants';
|
||||
|
||||
import PasswordForm from './PasswordForm';
|
||||
|
||||
const KEY = 'add-passcode';
|
||||
|
||||
type Props = {
|
||||
|
||||
/**
|
||||
* Toolbar buttons which have their click exposed through the API.
|
||||
*/
|
||||
buttonsWithNotifyClick: Array<string | Object>,
|
||||
|
||||
/**
|
||||
* Whether or not the current user can modify the current password.
|
||||
*/
|
||||
|
@ -59,12 +67,15 @@ type Props = {
|
|||
t: Function
|
||||
};
|
||||
|
||||
declare var APP: Object;
|
||||
|
||||
/**
|
||||
* Component that handles the password manipulation from the invite dialog.
|
||||
*
|
||||
* @returns {React$Element<any>}
|
||||
*/
|
||||
function PasswordSection({
|
||||
buttonsWithNotifyClick,
|
||||
canEditPassword,
|
||||
conference,
|
||||
locked,
|
||||
|
@ -97,7 +108,31 @@ function PasswordSection({
|
|||
* @returns {void}
|
||||
*/
|
||||
function onTogglePasswordEditState() {
|
||||
setPasswordEditEnabled(!passwordEditEnabled);
|
||||
if (typeof APP === 'undefined' || !buttonsWithNotifyClick?.length) {
|
||||
setPasswordEditEnabled(!passwordEditEnabled);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
let notifyMode;
|
||||
const notify = buttonsWithNotifyClick.find(
|
||||
(btn: string | Object) =>
|
||||
(typeof btn === 'string' && btn === KEY)
|
||||
|| (typeof btn === 'object' && btn.key === KEY)
|
||||
);
|
||||
|
||||
if (notify) {
|
||||
notifyMode = typeof notify === 'string' || notify.preventExecution
|
||||
? NOTIFY_CLICK_MODE.PREVENT_AND_NOTIFY
|
||||
: NOTIFY_CLICK_MODE.ONLY_NOTIFY;
|
||||
APP.API.notifyToolbarButtonClicked(
|
||||
KEY, notifyMode === NOTIFY_CLICK_MODE.PREVENT_AND_NOTIFY
|
||||
);
|
||||
}
|
||||
|
||||
if (notifyMode === NOTIFY_CLICK_MODE.ONLY_NOTIFY) {
|
||||
setPasswordEditEnabled(!passwordEditEnabled);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -13,6 +13,11 @@ import PasswordSection from './PasswordSection';
|
|||
|
||||
type Props = {
|
||||
|
||||
/**
|
||||
* Toolbar buttons which have their click exposed through the API.
|
||||
*/
|
||||
_buttonsWithNotifyClick: Array<string | Object>,
|
||||
|
||||
/**
|
||||
* Whether or not the current user can modify the current password.
|
||||
*/
|
||||
|
@ -57,6 +62,7 @@ type Props = {
|
|||
* @returns {React$Element<any>}
|
||||
*/
|
||||
function SecurityDialog({
|
||||
_buttonsWithNotifyClick,
|
||||
_canEditPassword,
|
||||
_conference,
|
||||
_locked,
|
||||
|
@ -82,6 +88,7 @@ function SecurityDialog({
|
|||
<div className = 'security-dialog'>
|
||||
<LobbySection />
|
||||
<PasswordSection
|
||||
buttonsWithNotifyClick = { _buttonsWithNotifyClick }
|
||||
canEditPassword = { _canEditPassword }
|
||||
conference = { _conference }
|
||||
locked = { _locked }
|
||||
|
@ -117,11 +124,12 @@ function mapStateToProps(state) {
|
|||
locked,
|
||||
password
|
||||
} = state['features/base/conference'];
|
||||
const { roomPasswordNumberOfDigits } = state['features/base/config'];
|
||||
const { roomPasswordNumberOfDigits, buttonsWithNotifyClick } = state['features/base/config'];
|
||||
|
||||
const showE2ee = Boolean(e2eeSupported) && isLocalParticipantModerator(state);
|
||||
|
||||
return {
|
||||
_buttonsWithNotifyClick: buttonsWithNotifyClick,
|
||||
_canEditPassword: isLocalParticipantModerator(state),
|
||||
_conference: conference,
|
||||
_dialIn: state['features/invite'],
|
||||
|
|
|
@ -40,17 +40,7 @@ class SettingsButton extends AbstractButton<Props, *> {
|
|||
* @returns {void}
|
||||
*/
|
||||
_handleClick() {
|
||||
const {
|
||||
defaultTab = SETTINGS_TABS.DEVICES,
|
||||
dispatch,
|
||||
handleClick
|
||||
} = this.props;
|
||||
|
||||
if (handleClick) {
|
||||
handleClick();
|
||||
|
||||
return;
|
||||
}
|
||||
const { defaultTab = SETTINGS_TABS.DEVICES, dispatch } = this.props;
|
||||
|
||||
sendAnalytics(createToolbarEvent('settings'));
|
||||
dispatch(openSettingsDialog(defaultTab));
|
||||
|
|
|
@ -66,14 +66,6 @@ class SharedVideoButton extends AbstractButton<Props, *> {
|
|||
* @returns {void}
|
||||
*/
|
||||
_handleClick() {
|
||||
const { handleClick } = this.props;
|
||||
|
||||
if (handleClick) {
|
||||
handleClick();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
this._doToggleSharedVideo();
|
||||
}
|
||||
|
||||
|
|
|
@ -20,13 +20,7 @@ class SpeakerStatsButton extends AbstractSpeakerStatsButton {
|
|||
* @returns {void}
|
||||
*/
|
||||
_handleClick() {
|
||||
const { dispatch, handleClick } = this.props;
|
||||
|
||||
if (handleClick) {
|
||||
handleClick();
|
||||
|
||||
return;
|
||||
}
|
||||
const { dispatch } = this.props;
|
||||
|
||||
sendAnalytics(createToolbarEvent('speaker.stats'));
|
||||
dispatch(openDialog(SpeakerStats));
|
||||
|
|
|
@ -38,13 +38,7 @@ export class AbstractClosedCaptionButton
|
|||
* @returns {void}
|
||||
*/
|
||||
async _handleClick() {
|
||||
const { _requestingSubtitles, dispatch, handleClick } = this.props;
|
||||
|
||||
if (handleClick) {
|
||||
handleClick();
|
||||
|
||||
return;
|
||||
}
|
||||
const { _requestingSubtitles, dispatch } = this.props;
|
||||
|
||||
sendAnalytics(createToolbarEvent('transcribing.ccButton',
|
||||
{
|
||||
|
|
|
@ -10,7 +10,7 @@ import { getFeatureFlag, AUDIO_MUTE_BUTTON_ENABLED } from '../../base/flags';
|
|||
import { translate } from '../../base/i18n';
|
||||
import { MEDIA_TYPE } from '../../base/media';
|
||||
import { connect } from '../../base/redux';
|
||||
import { AbstractAudioMuteButton } from '../../base/toolbox/components';
|
||||
import { AbstractAudioMuteButton, AbstractButton } from '../../base/toolbox/components';
|
||||
import type { AbstractButtonProps } from '../../base/toolbox/components';
|
||||
import { isLocalTrackMuted } from '../../base/tracks';
|
||||
import { muteLocal } from '../../video-menu/actions';
|
||||
|
@ -120,7 +120,7 @@ class AudioMuteButton extends AbstractAudioMuteButton<Props, *> {
|
|||
ACTION_SHORTCUT_TRIGGERED,
|
||||
{ enable: !this._isAudioMuted() }));
|
||||
|
||||
super._handleClick();
|
||||
AbstractButton.prototype._onClick.call(this);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -32,13 +32,7 @@ class DownloadButton extends AbstractButton<Props, *> {
|
|||
* @returns {void}
|
||||
*/
|
||||
_handleClick() {
|
||||
const { _downloadAppsUrl, handleClick } = this.props;
|
||||
|
||||
if (handleClick) {
|
||||
handleClick();
|
||||
|
||||
return;
|
||||
}
|
||||
const { _downloadAppsUrl } = this.props;
|
||||
|
||||
sendAnalytics(createToolbarEvent('download.pressed'));
|
||||
openURLInBrowser(_downloadAppsUrl);
|
||||
|
|
|
@ -33,13 +33,7 @@ class HelpButton extends AbstractButton<Props, *> {
|
|||
* @returns {void}
|
||||
*/
|
||||
_handleClick() {
|
||||
const { _userDocumentationURL, handleClick } = this.props;
|
||||
|
||||
if (handleClick) {
|
||||
handleClick();
|
||||
|
||||
return;
|
||||
}
|
||||
const { _userDocumentationURL } = this.props;
|
||||
|
||||
sendAnalytics(createToolbarEvent('help.pressed'));
|
||||
openURLInBrowser(_userDocumentationURL);
|
||||
|
|
|
@ -39,13 +39,7 @@ class MuteEveryoneButton extends AbstractButton<Props, *> {
|
|||
* @returns {void}
|
||||
*/
|
||||
_handleClick() {
|
||||
const { dispatch, localParticipantId, handleClick } = this.props;
|
||||
|
||||
if (handleClick) {
|
||||
handleClick();
|
||||
|
||||
return;
|
||||
}
|
||||
const { dispatch, localParticipantId } = this.props;
|
||||
|
||||
sendAnalytics(createToolbarEvent('mute.everyone.pressed'));
|
||||
dispatch(openDialog(MuteEveryoneDialog, {
|
||||
|
|
|
@ -39,13 +39,7 @@ class MuteEveryonesVideoButton extends AbstractButton<Props, *> {
|
|||
* @returns {void}
|
||||
*/
|
||||
_handleClick() {
|
||||
const { dispatch, localParticipantId, handleClick } = this.props;
|
||||
|
||||
if (handleClick) {
|
||||
handleClick();
|
||||
|
||||
return;
|
||||
}
|
||||
const { dispatch, localParticipantId } = this.props;
|
||||
|
||||
sendAnalytics(createToolbarEvent('mute.everyone.pressed'));
|
||||
dispatch(openDialog(MuteEveryonesVideoDialog, {
|
||||
|
|
|
@ -16,7 +16,7 @@ import {
|
|||
setVideoMuted
|
||||
} from '../../base/media';
|
||||
import { connect } from '../../base/redux';
|
||||
import { AbstractVideoMuteButton } from '../../base/toolbox/components';
|
||||
import { AbstractButton, AbstractVideoMuteButton } from '../../base/toolbox/components';
|
||||
import type { AbstractButtonProps } from '../../base/toolbox/components';
|
||||
import { getLocalVideoType, isLocalCameraTrackMuted } from '../../base/tracks';
|
||||
import { isVideoMuteButtonDisabled } from '../functions';
|
||||
|
@ -146,7 +146,7 @@ class VideoMuteButton extends AbstractVideoMuteButton<Props, *> {
|
|||
ACTION_SHORTCUT_TRIGGERED,
|
||||
{ enable: !this._isVideoMuted() }));
|
||||
|
||||
super._handleClick();
|
||||
AbstractButton.prototype._onClick.call(this);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -15,6 +15,11 @@ import AudioMuteButton from '../AudioMuteButton';
|
|||
|
||||
type Props = {
|
||||
|
||||
/**
|
||||
* The button's key.
|
||||
*/
|
||||
buttonKey?: string,
|
||||
|
||||
/**
|
||||
* External handler for click action.
|
||||
*/
|
||||
|
@ -35,6 +40,12 @@ type Props = {
|
|||
*/
|
||||
isDisabled: boolean,
|
||||
|
||||
/**
|
||||
* Notify mode for `toolbarButtonClicked` event -
|
||||
* whether to only notify or to also prevent button click routine.
|
||||
*/
|
||||
notifyMode?: string,
|
||||
|
||||
/**
|
||||
* Used for translation.
|
||||
*/
|
||||
|
@ -94,13 +105,7 @@ class AudioSettingsButton extends Component<Props> {
|
|||
* @returns {void}
|
||||
*/
|
||||
_onClick() {
|
||||
const { handleClick, onAudioOptionsClick } = this.props;
|
||||
|
||||
if (handleClick) {
|
||||
handleClick();
|
||||
|
||||
return;
|
||||
}
|
||||
const { onAudioOptionsClick } = this.props;
|
||||
|
||||
onAudioOptionsClick();
|
||||
}
|
||||
|
@ -111,7 +116,7 @@ class AudioSettingsButton extends Component<Props> {
|
|||
* @inheritdoc
|
||||
*/
|
||||
render() {
|
||||
const { handleClick, hasPermissions, isDisabled, visible, isOpen, t } = this.props;
|
||||
const { hasPermissions, isDisabled, visible, isOpen, buttonKey, notifyMode, t } = this.props;
|
||||
const settingsDisabled = !hasPermissions
|
||||
|| isDisabled
|
||||
|| !JitsiMeetJS.mediaDevices.isMultipleAudioInputSupported();
|
||||
|
@ -123,16 +128,22 @@ class AudioSettingsButton extends Component<Props> {
|
|||
ariaExpanded = { isOpen }
|
||||
ariaHasPopup = { true }
|
||||
ariaLabel = { t('toolbar.audioSettings') }
|
||||
buttonKey = { buttonKey }
|
||||
icon = { IconArrowUp }
|
||||
iconDisabled = { settingsDisabled }
|
||||
iconId = 'audio-settings-button'
|
||||
iconTooltip = { t('toolbar.audioSettings') }
|
||||
notifyMode = { notifyMode }
|
||||
onIconClick = { this._onClick }
|
||||
onIconKeyDown = { this._onEscClick }>
|
||||
<AudioMuteButton handleClick = { handleClick } />
|
||||
<AudioMuteButton
|
||||
buttonKey = { buttonKey }
|
||||
notifyMode = { notifyMode } />
|
||||
</ToolboxButtonWithIcon>
|
||||
</AudioSettingsPopup>
|
||||
) : <AudioMuteButton handleClick = { handleClick } />;
|
||||
) : <AudioMuteButton
|
||||
buttonKey = { buttonKey }
|
||||
notifyMode = { notifyMode } />;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -61,22 +61,6 @@ class FullscreenButton extends AbstractButton<Props, *> {
|
|||
// Unused.
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles clicking / pressing the button, and opens the appropriate dialog.
|
||||
*
|
||||
* @protected
|
||||
* @returns {void}
|
||||
*/
|
||||
_handleClick() {
|
||||
const { handleClick } = this.props;
|
||||
|
||||
if (handleClick) {
|
||||
handleClick();
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates whether this button is in toggled state or not.
|
||||
*
|
||||
|
|
|
@ -5,7 +5,6 @@ import React, { Component } from 'react';
|
|||
|
||||
import { createToolbarEvent, sendAnalytics } from '../../../analytics';
|
||||
import { translate } from '../../../base/i18n';
|
||||
import { IconHorizontalPoints } from '../../../base/icons';
|
||||
import { connect } from '../../../base/redux';
|
||||
import { ReactionEmoji, ReactionsMenu } from '../../../reactions/components';
|
||||
import { type ReactionEmojiProps } from '../../../reactions/constants';
|
||||
|
@ -13,7 +12,7 @@ import { getReactionsQueue } from '../../../reactions/functions.any';
|
|||
|
||||
import Drawer from './Drawer';
|
||||
import JitsiPortal from './JitsiPortal';
|
||||
import ToolbarButton from './ToolbarButton';
|
||||
import OverflowToggleButton from './OverflowToggleButton';
|
||||
|
||||
/**
|
||||
* The type of the React {@code Component} props of {@link OverflowMenuButton}.
|
||||
|
@ -78,8 +77,8 @@ class OverflowMenuButton extends Component<Props> {
|
|||
|
||||
// Bind event handlers so they are only bound once per instance.
|
||||
this._onCloseDialog = this._onCloseDialog.bind(this);
|
||||
this._onToggleDialogVisibility
|
||||
= this._onToggleDialogVisibility.bind(this);
|
||||
this._toggleDialogVisibility
|
||||
= this._toggleDialogVisibility.bind(this);
|
||||
this._onEscClick = this._onEscClick.bind(this);
|
||||
}
|
||||
|
||||
|
@ -113,7 +112,10 @@ class OverflowMenuButton extends Component<Props> {
|
|||
{
|
||||
overflowDrawer ? (
|
||||
<>
|
||||
{this._renderToolbarButton()}
|
||||
<OverflowToggleButton
|
||||
handleClick = { this._toggleDialogVisibility }
|
||||
isOpen = { isOpen }
|
||||
onKeyDown = { this._onEscClick } />
|
||||
<JitsiPortal>
|
||||
<Drawer
|
||||
isOpen = { isOpen }
|
||||
|
@ -136,7 +138,10 @@ class OverflowMenuButton extends Component<Props> {
|
|||
isOpen = { isOpen }
|
||||
onClose = { this._onCloseDialog }
|
||||
placement = 'top-end'>
|
||||
{this._renderToolbarButton()}
|
||||
<OverflowToggleButton
|
||||
handleClick = { this._toggleDialogVisibility }
|
||||
isOpen = { isOpen }
|
||||
onKeyDown = { this._onEscClick } />
|
||||
</InlineDialog>
|
||||
)
|
||||
}
|
||||
|
@ -144,30 +149,6 @@ class OverflowMenuButton extends Component<Props> {
|
|||
);
|
||||
}
|
||||
|
||||
_renderToolbarButton: () => React$Node;
|
||||
|
||||
/**
|
||||
* Renders the actual toolbar overflow menu button.
|
||||
*
|
||||
* @returns {ReactElement}
|
||||
*/
|
||||
_renderToolbarButton() {
|
||||
const { ariaControls, isOpen, t } = this.props;
|
||||
|
||||
return (
|
||||
<ToolbarButton
|
||||
accessibilityLabel =
|
||||
{ t('toolbar.accessibilityLabel.moreActions') }
|
||||
aria-controls = { ariaControls }
|
||||
aria-haspopup = 'true'
|
||||
icon = { IconHorizontalPoints }
|
||||
onClick = { this._onToggleDialogVisibility }
|
||||
onKeyDown = { this._onEscClick }
|
||||
toggled = { isOpen }
|
||||
tooltip = { t('toolbar.moreActions') } />
|
||||
);
|
||||
}
|
||||
|
||||
_onCloseDialog: () => void;
|
||||
|
||||
/**
|
||||
|
@ -181,7 +162,7 @@ class OverflowMenuButton extends Component<Props> {
|
|||
this.props.onVisibilityChange(false);
|
||||
}
|
||||
|
||||
_onToggleDialogVisibility: () => void;
|
||||
_toggleDialogVisibility: () => void;
|
||||
|
||||
/**
|
||||
* Callback invoked to signal that an event has occurred that should change
|
||||
|
@ -190,7 +171,7 @@ class OverflowMenuButton extends Component<Props> {
|
|||
* @private
|
||||
* @returns {void}
|
||||
*/
|
||||
_onToggleDialogVisibility() {
|
||||
_toggleDialogVisibility() {
|
||||
sendAnalytics(createToolbarEvent('overflow'));
|
||||
|
||||
this.props.onVisibilityChange(!this.props.isOpen);
|
||||
|
|
|
@ -0,0 +1,73 @@
|
|||
// @flow
|
||||
|
||||
import { translate } from '../../../base/i18n';
|
||||
import { IconHorizontalPoints } from '../../../base/icons';
|
||||
import { AbstractButton, type AbstractButtonProps } from '../../../base/toolbox/components';
|
||||
|
||||
|
||||
/**
|
||||
* The type of the React {@code Component} props of {@link OverflowToggleButton}.
|
||||
*/
|
||||
type Props = AbstractButtonProps & {
|
||||
|
||||
/**
|
||||
* Whether the more options menu is open.
|
||||
*/
|
||||
isOpen: boolean,
|
||||
|
||||
/**
|
||||
* External handler for key down action.
|
||||
*/
|
||||
onKeyDown: Function,
|
||||
};
|
||||
|
||||
/**
|
||||
* Implementation of a button for toggleing the overflow menu.
|
||||
*/
|
||||
class OverflowToggleButton extends AbstractButton<Props, *> {
|
||||
accessibilityLabel = 'toolbar.accessibilityLabel.moreActions';
|
||||
icon = IconHorizontalPoints;
|
||||
label = 'toolbar.moreActions';
|
||||
toggledLabel = 'toolbar.moreActions';
|
||||
|
||||
/**
|
||||
* Retrieves tooltip dynamically.
|
||||
*/
|
||||
get tooltip() {
|
||||
return 'toolbar.moreActions';
|
||||
}
|
||||
|
||||
/**
|
||||
* Required by linter due to AbstractButton overwritten prop being writable.
|
||||
*
|
||||
* @param {string} _value - The value.
|
||||
*/
|
||||
set tooltip(_value) {
|
||||
// Unused.
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates whether this button is in toggled state or not.
|
||||
*
|
||||
* @override
|
||||
* @protected
|
||||
* @returns {boolean}
|
||||
*/
|
||||
_isToggled() {
|
||||
return this.props.isOpen;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates whether a key was pressed.
|
||||
*
|
||||
* @override
|
||||
* @protected
|
||||
* @returns {boolean}
|
||||
*/
|
||||
_onKeyDown() {
|
||||
this.props.onKeyDown();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export default translate(OverflowToggleButton);
|
|
@ -95,13 +95,7 @@ class ProfileButton extends AbstractButton<Props, *> {
|
|||
* @returns {void}
|
||||
*/
|
||||
_handleClick() {
|
||||
const { dispatch, _unclickable, handleClick } = this.props;
|
||||
|
||||
if (handleClick) {
|
||||
handleClick();
|
||||
|
||||
return;
|
||||
}
|
||||
const { dispatch, _unclickable } = this.props;
|
||||
|
||||
if (!_unclickable) {
|
||||
sendAnalytics(createToolbarEvent('profile'));
|
||||
|
|
|
@ -67,22 +67,6 @@ class ShareDesktopButton extends AbstractButton<Props, *> {
|
|||
// Unused.
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles clicking / pressing the button, and opens the appropriate dialog.
|
||||
*
|
||||
* @protected
|
||||
* @returns {void}
|
||||
*/
|
||||
_handleClick() {
|
||||
const { handleClick } = this.props;
|
||||
|
||||
if (handleClick) {
|
||||
handleClick();
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates whether this button is in toggled state or not.
|
||||
*
|
||||
|
|
|
@ -41,13 +41,7 @@ class ToggleCameraButton extends AbstractButton<Props, any> {
|
|||
* @returns {void}
|
||||
*/
|
||||
_handleClick() {
|
||||
const { dispatch, handleClick } = this.props;
|
||||
|
||||
if (handleClick) {
|
||||
handleClick();
|
||||
|
||||
return;
|
||||
}
|
||||
const { dispatch } = this.props;
|
||||
|
||||
dispatch(toggleCamera());
|
||||
}
|
||||
|
|
|
@ -1,135 +0,0 @@
|
|||
/* @flow */
|
||||
|
||||
import React from 'react';
|
||||
|
||||
import { Icon } from '../../../base/icons';
|
||||
import { Tooltip } from '../../../base/tooltip';
|
||||
import AbstractToolbarButton from '../AbstractToolbarButton';
|
||||
import type { Props as AbstractToolbarButtonProps }
|
||||
from '../AbstractToolbarButton';
|
||||
|
||||
/**
|
||||
* The type of the React {@code Component} props of {@link ToolbarButton}.
|
||||
*/
|
||||
export type Props = AbstractToolbarButtonProps & {
|
||||
|
||||
/**
|
||||
* The text to display in the tooltip.
|
||||
*/
|
||||
tooltip: string,
|
||||
|
||||
/**
|
||||
* From which direction the tooltip should appear, relative to the
|
||||
* button.
|
||||
*/
|
||||
tooltipPosition: string,
|
||||
|
||||
/**
|
||||
* KeyDown handler.
|
||||
*/
|
||||
onKeyDown?: Function
|
||||
};
|
||||
|
||||
/**
|
||||
* Represents a button in the toolbar.
|
||||
*
|
||||
* @augments AbstractToolbarButton
|
||||
*/
|
||||
class ToolbarButton extends AbstractToolbarButton<Props> {
|
||||
/**
|
||||
* Default values for {@code ToolbarButton} component's properties.
|
||||
*
|
||||
* @static
|
||||
*/
|
||||
static defaultProps = {
|
||||
tooltipPosition: 'top'
|
||||
};
|
||||
|
||||
/**
|
||||
* Initializes a new {@code ToolbarButton} instance.
|
||||
*
|
||||
* @inheritdoc
|
||||
*/
|
||||
constructor(props: Props) {
|
||||
super(props);
|
||||
|
||||
this._onKeyPress = this._onKeyPress.bind(this);
|
||||
this._onClick = this._onClick.bind(this);
|
||||
}
|
||||
|
||||
_onKeyPress: (Object) => void;
|
||||
|
||||
/**
|
||||
* Handles 'Enter' and Space key on the button to trigger onClick for accessibility.
|
||||
*
|
||||
* @param {Object} event - The key event.
|
||||
* @private
|
||||
* @returns {void}
|
||||
*/
|
||||
_onKeyPress(event) {
|
||||
if (event.key === 'Enter' || event.key === ' ') {
|
||||
event.preventDefault();
|
||||
this.props.onClick();
|
||||
}
|
||||
}
|
||||
_onClick: (Object) => void;
|
||||
|
||||
/**
|
||||
* Handles button click.
|
||||
*
|
||||
* @param {Object} e - The key event.
|
||||
* @private
|
||||
* @returns {void}
|
||||
*/
|
||||
_onClick(e) {
|
||||
this.props.onClick(e);
|
||||
|
||||
// blur after click to release focus from button to allow PTT.
|
||||
e && e.currentTarget && e.currentTarget.blur();
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders the button of this {@code ToolbarButton}.
|
||||
*
|
||||
* @param {Object} children - The children, if any, to be rendered inside
|
||||
* the button. Presumably, contains the icon of this {@code ToolbarButton}.
|
||||
* @protected
|
||||
* @returns {ReactElement} The button of this {@code ToolbarButton}.
|
||||
*/
|
||||
_renderButton(children) {
|
||||
return (
|
||||
<div
|
||||
aria-label = { this.props.accessibilityLabel }
|
||||
aria-pressed = { this.props.toggled }
|
||||
className = 'toolbox-button'
|
||||
onClick = { this._onClick }
|
||||
onKeyDown = { this.props.onKeyDown }
|
||||
onKeyPress = { this._onKeyPress }
|
||||
role = 'button'
|
||||
tabIndex = { 0 }>
|
||||
{ this.props.tooltip
|
||||
? <Tooltip
|
||||
content = { this.props.tooltip }
|
||||
position = { this.props.tooltipPosition }>
|
||||
{ children }
|
||||
</Tooltip>
|
||||
: children }
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders the icon of this {@code ToolbarButton}.
|
||||
*
|
||||
* @inheritdoc
|
||||
*/
|
||||
_renderIcon() {
|
||||
return (
|
||||
<div className = { `toolbox-icon ${this.props.toggled ? 'toggled' : ''}` }>
|
||||
<Icon src = { this.props.icon } />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default ToolbarButton;
|
|
@ -76,7 +76,7 @@ import {
|
|||
setToolbarHovered,
|
||||
showToolbox
|
||||
} from '../../actions';
|
||||
import { THRESHOLDS, NOT_APPLICABLE, DRAWER_MAX_HEIGHT } from '../../constants';
|
||||
import { THRESHOLDS, NOT_APPLICABLE, DRAWER_MAX_HEIGHT, NOTIFY_CLICK_MODE } from '../../constants';
|
||||
import { isToolboxVisible } from '../../functions';
|
||||
import DownloadButton from '../DownloadButton';
|
||||
import HangupButton from '../HangupButton';
|
||||
|
@ -106,7 +106,7 @@ type Props = {
|
|||
/**
|
||||
* Toolbar buttons which have their click exposed through the API.
|
||||
*/
|
||||
_buttonsWithNotifyClick: Array<string>,
|
||||
_buttonsWithNotifyClick: Array<string | Object>,
|
||||
|
||||
/**
|
||||
* Whether or not the chat feature is currently displayed.
|
||||
|
@ -819,22 +819,31 @@ class Toolbox extends Component<Props> {
|
|||
}
|
||||
|
||||
/**
|
||||
* Overwrites click handlers for buttons in case click is exposed through the iframe API.
|
||||
* Sets the notify click mode for the buttons.
|
||||
*
|
||||
* @param {Object} buttons - The list of toolbar buttons.
|
||||
* @returns {void}
|
||||
*/
|
||||
_overwriteButtonsClickHandlers(buttons) {
|
||||
_setButtonsNotifyClickMode(buttons) {
|
||||
if (typeof APP === 'undefined' || !this.props._buttonsWithNotifyClick?.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
Object.values(buttons).forEach((button: any) => {
|
||||
if (
|
||||
typeof button === 'object'
|
||||
&& this.props._buttonsWithNotifyClick.includes(button.key)
|
||||
) {
|
||||
button.handleClick = () => APP.API.notifyToolbarButtonClicked(button.key);
|
||||
if (typeof button === 'object') {
|
||||
const notify = this.props._buttonsWithNotifyClick.find(
|
||||
(btn: string | Object) =>
|
||||
(typeof btn === 'string' && btn === button.key)
|
||||
|| (typeof btn === 'object' && btn.key === button.key)
|
||||
);
|
||||
|
||||
if (notify) {
|
||||
const notifyMode = typeof notify === 'string' || notify.preventExecution
|
||||
? NOTIFY_CLICK_MODE.PREVENT_AND_NOTIFY
|
||||
: NOTIFY_CLICK_MODE.ONLY_NOTIFY;
|
||||
|
||||
button.notifyMode = notifyMode;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -854,7 +863,7 @@ class Toolbox extends Component<Props> {
|
|||
|
||||
const buttons = this._getAllButtons();
|
||||
|
||||
this._overwriteButtonsClickHandlers(buttons);
|
||||
this._setButtonsNotifyClickMode(buttons);
|
||||
const isHangupVisible = isToolbarButtonEnabled('hangup', _toolbarButtons);
|
||||
const { order } = THRESHOLDS.find(({ width }) => _clientWidth > width)
|
||||
|| THRESHOLDS[THRESHOLDS.length - 1];
|
||||
|
@ -1265,6 +1274,7 @@ class Toolbox extends Component<Props> {
|
|||
{mainMenuButtons.map(({ Content, key, ...rest }) => Content !== Separator && (
|
||||
<Content
|
||||
{ ...rest }
|
||||
buttonKey = { key }
|
||||
key = { key } />))}
|
||||
|
||||
{Boolean(overflowMenuButtons.length) && (
|
||||
|
@ -1292,6 +1302,7 @@ class Toolbox extends Component<Props> {
|
|||
{showSeparator && <Separator key = { `hr${group}` } />}
|
||||
<Content
|
||||
{ ...rest }
|
||||
buttonKey = { key }
|
||||
key = { key }
|
||||
showLabel = { true } />
|
||||
</Fragment>
|
||||
|
|
|
@ -16,6 +16,11 @@ import VideoMuteButton from '../VideoMuteButton';
|
|||
|
||||
type Props = {
|
||||
|
||||
/**
|
||||
* The button's key.
|
||||
*/
|
||||
buttonKey?: string,
|
||||
|
||||
/**
|
||||
* External handler for click action.
|
||||
*/
|
||||
|
@ -41,6 +46,12 @@ type Props = {
|
|||
*/
|
||||
isDisabled: boolean,
|
||||
|
||||
/**
|
||||
* Notify mode for `toolbarButtonClicked` event -
|
||||
* whether to only notify or to also prevent button click routine.
|
||||
*/
|
||||
notifyMode?: string,
|
||||
|
||||
/**
|
||||
* Flag controlling the visibility of the button.
|
||||
* VideoSettings popup is currently disabled on mobile browsers
|
||||
|
@ -112,13 +123,7 @@ class VideoSettingsButton extends Component<Props> {
|
|||
* @returns {void}
|
||||
*/
|
||||
_onClick() {
|
||||
const { handleClick, onVideoOptionsClick } = this.props;
|
||||
|
||||
if (handleClick) {
|
||||
handleClick();
|
||||
|
||||
return;
|
||||
}
|
||||
const { onVideoOptionsClick } = this.props;
|
||||
|
||||
onVideoOptionsClick();
|
||||
}
|
||||
|
@ -129,7 +134,7 @@ class VideoSettingsButton extends Component<Props> {
|
|||
* @inheritdoc
|
||||
*/
|
||||
render() {
|
||||
const { handleClick, t, visible, isOpen } = this.props;
|
||||
const { t, visible, isOpen, buttonKey, notifyMode } = this.props;
|
||||
|
||||
return visible ? (
|
||||
<VideoSettingsPopup>
|
||||
|
@ -138,16 +143,22 @@ class VideoSettingsButton extends Component<Props> {
|
|||
ariaExpanded = { isOpen }
|
||||
ariaHasPopup = { true }
|
||||
ariaLabel = { this.props.t('toolbar.videoSettings') }
|
||||
buttonKey = { buttonKey }
|
||||
icon = { IconArrowUp }
|
||||
iconDisabled = { this._isIconDisabled() }
|
||||
iconId = 'video-settings-button'
|
||||
iconTooltip = { t('toolbar.videoSettings') }
|
||||
notifyMode = { notifyMode }
|
||||
onIconClick = { this._onClick }
|
||||
onIconKeyDown = { this._onEscClick }>
|
||||
<VideoMuteButton handleClick = { handleClick } />
|
||||
<VideoMuteButton
|
||||
buttonKey = { buttonKey }
|
||||
notifyMode = { notifyMode } />
|
||||
</ToolboxButtonWithIcon>
|
||||
</VideoSettingsPopup>
|
||||
) : <VideoMuteButton handleClick = { handleClick } />;
|
||||
) : <VideoMuteButton
|
||||
buttonKey = { buttonKey }
|
||||
notifyMode = { notifyMode } />;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
export { default as AudioSettingsButton } from './AudioSettingsButton';
|
||||
export { default as VideoSettingsButton } from './VideoSettingsButton';
|
||||
export { default as ToolbarButton } from './ToolbarButton';
|
||||
export { default as Toolbox } from './Toolbox';
|
||||
export { default as Drawer } from './Drawer';
|
||||
export { default as JitsiPortal } from './JitsiPortal';
|
||||
|
|
|
@ -33,3 +33,8 @@ export const NOT_APPLICABLE = 'N/A';
|
|||
export const TOOLBAR_TIMEOUT = 4000;
|
||||
|
||||
export const DRAWER_MAX_HEIGHT = '80vh - 64px';
|
||||
|
||||
export const NOTIFY_CLICK_MODE = {
|
||||
ONLY_NOTIFY: 'ONLY_NOTIFY',
|
||||
PREVENT_AND_NOTIFY: 'PREVENT_AND_NOTIFY'
|
||||
};
|
||||
|
|
|
@ -52,13 +52,7 @@ class TileViewButton<P: Props> extends AbstractButton<P, *> {
|
|||
* @returns {void}
|
||||
*/
|
||||
_handleClick() {
|
||||
const { _tileViewEnabled, dispatch, handleClick } = this.props;
|
||||
|
||||
if (handleClick) {
|
||||
handleClick();
|
||||
|
||||
return;
|
||||
}
|
||||
const { _tileViewEnabled, dispatch } = this.props;
|
||||
|
||||
const value = !_tileViewEnabled;
|
||||
|
||||
|
|
|
@ -39,24 +39,6 @@ class VideoQualityButton extends AbstractButton<Props, *> {
|
|||
label = 'videoStatus.performanceSettings';
|
||||
tooltip = 'videoStatus.performanceSettings';
|
||||
icon = IconGauge;
|
||||
|
||||
|
||||
/**
|
||||
* Handles clicking / pressing the button.
|
||||
*
|
||||
* @override
|
||||
* @protected
|
||||
* @returns {void}
|
||||
*/
|
||||
_handleClick() {
|
||||
const { handleClick } = this.props;
|
||||
|
||||
if (handleClick) {
|
||||
handleClick();
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default translate(VideoQualityButton);
|
||||
|
|
|
@ -43,13 +43,7 @@ class VideoBackgroundButton extends AbstractButton<Props, *> {
|
|||
* @returns {void}
|
||||
*/
|
||||
_handleClick() {
|
||||
const { dispatch, handleClick } = this.props;
|
||||
|
||||
if (handleClick) {
|
||||
handleClick();
|
||||
|
||||
return;
|
||||
}
|
||||
const { dispatch } = this.props;
|
||||
|
||||
dispatch(openDialog(VirtualBackgroundDialog));
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue