feat(toolbar-buttons): Add event for notify overwritten toolbar buttons

This commit is contained in:
Horatiu Muresan 2021-09-14 10:07:20 +03:00 committed by GitHub
parent aadbe59d00
commit 1add438a1f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
38 changed files with 380 additions and 74 deletions

View File

@ -539,6 +539,43 @@ var config = {
// '__end' // '__end'
// ], // ],
// Toolbar buttons which have their click event exposed through the API on
// `toolbarButtonClicked` event instead of executing the normal click routine.
// 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'
// ],
// List of pre meeting screens buttons to hide. The values must be one or more of the 5 allowed buttons: // List of pre meeting screens buttons to hide. The values must be one or more of the 5 allowed buttons:
// 'microphone', 'camera', 'select-background', 'invite', 'settings' // 'microphone', 'camera', 'select-background', 'invite', 'settings'
// hiddenPremeetingButtons: [], // hiddenPremeetingButtons: [],

View File

@ -1342,6 +1342,19 @@ class API {
}); });
} }
/**
* Notify external application ( if API is enabled) that a toolbar button was clicked.
*
* @param {string} key - The key of the toolbar button.
* @returns {void}
*/
notifyToolbarButtonClicked(key: string) {
this._sendEvent({
name: 'toolbar-button-clicked',
key
});
}
/** /**
* Disposes the allocated resources. * Disposes the allocated resources.
* *

View File

@ -112,7 +112,8 @@ const events = {
'dominant-speaker-changed': 'dominantSpeakerChanged', 'dominant-speaker-changed': 'dominantSpeakerChanged',
'subject-change': 'subjectChange', 'subject-change': 'subjectChange',
'suspend-detected': 'suspendDetected', 'suspend-detected': 'suspendDetected',
'tile-view-changed': 'tileViewChanged' 'tile-view-changed': 'tileViewChanged',
'toolbar-button-clicked': 'toolbarButtonClicked'
}; };
/** /**

View File

@ -19,6 +19,7 @@ export default [
'apiLogLevels', 'apiLogLevels',
'avgRtpStatsN', 'avgRtpStatsN',
'backgroundAlpha', 'backgroundAlpha',
'buttonsWithNotifyClick',
/** /**
* The display name of the CallKit call representing the conference/meeting * The display name of the CallKit call representing the conference/meeting

View File

@ -23,6 +23,14 @@ export default class AbstractAudioMuteButton<P: Props, S: *>
* @returns {void} * @returns {void}
*/ */
_handleClick() { _handleClick() {
const { handleClick } = this.props;
if (handleClick) {
handleClick();
return;
}
this._setAudioMuted(!this._isAudioMuted()); this._setAudioMuted(!this._isAudioMuted());
} }

View File

@ -20,6 +20,11 @@ export type Props = {
*/ */
disabledStyles: ?Styles, disabledStyles: ?Styles,
/**
* External handler for click action.
*/
handleClick?: Function,
/** /**
* Whether to show the label or not. * Whether to show the label or not.
*/ */

View File

@ -22,6 +22,14 @@ export default class AbstractVideoMuteButton<P : Props, S : *>
* @returns {void} * @returns {void}
*/ */
_handleClick() { _handleClick() {
const { handleClick } = this.props;
if (handleClick) {
handleClick();
return;
}
this._setVideoMuted(!this._isVideoMuted()); this._setVideoMuted(!this._isVideoMuted());
} }

View File

@ -18,11 +18,6 @@ type Props = AbstractButtonProps & {
* Whether or not the chat feature is currently displayed. * Whether or not the chat feature is currently displayed.
*/ */
_chatOpen: boolean, _chatOpen: boolean,
/**
* External handler for click action.
*/
handleClick: Function
}; };
/** /**
@ -61,7 +56,13 @@ class ChatButton extends AbstractButton<Props, *> {
* @returns {void} * @returns {void}
*/ */
_handleClick() { _handleClick() {
this.props.handleClick(); const { handleClick } = this.props;
if (handleClick) {
handleClick();
return;
}
} }
/** /**

View File

@ -36,7 +36,13 @@ class EmbedMeetingButton extends AbstractButton<Props, *> {
* @returns {void} * @returns {void}
*/ */
_handleClick() { _handleClick() {
const { dispatch } = this.props; const { dispatch, handleClick } = this.props;
if (handleClick) {
handleClick();
return;
}
sendAnalytics(createToolbarEvent('embed.meeting')); sendAnalytics(createToolbarEvent('embed.meeting'));
dispatch(openDialog(EmbedMeetingDialog)); dispatch(openDialog(EmbedMeetingDialog));

View File

@ -59,12 +59,20 @@ class SharedDocumentButton extends AbstractButton<Props, *> {
* @returns {void} * @returns {void}
*/ */
_handleClick() { _handleClick() {
const { _editing, dispatch, handleClick } = this.props;
if (handleClick) {
handleClick();
return;
}
sendAnalytics(createToolbarEvent( sendAnalytics(createToolbarEvent(
'toggle.etherpad', 'toggle.etherpad',
{ {
enable: !this.props._editing enable: !_editing
})); }));
this.props.dispatch(toggleDocument()); dispatch(toggleDocument());
} }
/** /**

View File

@ -40,7 +40,13 @@ class FeedbackButton extends AbstractButton<Props, *> {
* @returns {void} * @returns {void}
*/ */
_handleClick() { _handleClick() {
const { _conference, dispatch } = this.props; const { _conference, dispatch, handleClick } = this.props;
if (handleClick) {
handleClick();
return;
}
sendAnalytics(createToolbarEvent('feedback')); sendAnalytics(createToolbarEvent('feedback'));
dispatch(openFeedbackDialog(_conference)); dispatch(openFeedbackDialog(_conference));

View File

@ -34,7 +34,13 @@ class InviteButton extends AbstractButton<Props, *> {
* @returns {void} * @returns {void}
*/ */
_handleClick() { _handleClick() {
const { dispatch } = this.props; const { dispatch, handleClick } = this.props;
if (handleClick) {
handleClick();
return;
}
sendAnalytics(createToolbarEvent('invite')); sendAnalytics(createToolbarEvent('invite'));
dispatch(beginAddPeople()); dispatch(beginAddPeople());

View File

@ -34,7 +34,13 @@ class KeyboardShortcutsButton extends AbstractButton<Props, *> {
* @returns {void} * @returns {void}
*/ */
_handleClick() { _handleClick() {
const { dispatch } = this.props; const { dispatch, handleClick } = this.props;
if (handleClick) {
handleClick();
return;
}
sendAnalytics(createToolbarEvent('shortcuts')); sendAnalytics(createToolbarEvent('shortcuts'));
dispatch(openKeyboardShortcutsDialog()); dispatch(openKeyboardShortcutsDialog());

View File

@ -36,7 +36,13 @@ class LocalRecording extends AbstractButton<Props, *> {
* @returns {void} * @returns {void}
*/ */
_handleClick() { _handleClick() {
const { dispatch } = this.props; const { dispatch, handleClick } = this.props;
if (handleClick) {
handleClick();
return;
}
sendAnalytics(createToolbarEvent('local.recording')); sendAnalytics(createToolbarEvent('local.recording'));
dispatch(openDialog(LocalRecordingInfoDialog)); dispatch(openDialog(LocalRecordingInfoDialog));

View File

@ -14,11 +14,6 @@ type Props = AbstractButtonProps & {
* Whether or not the participants pane is open. * Whether or not the participants pane is open.
*/ */
_isOpen: boolean, _isOpen: boolean,
/**
* External handler for click action.
*/
handleClick: Function
}; };
/** /**
@ -37,7 +32,13 @@ class ParticipantsPaneButton extends AbstractButton<Props, *> {
* @returns {void} * @returns {void}
*/ */
_handleClick() { _handleClick() {
this.props.handleClick(); const { handleClick } = this.props;
if (handleClick) {
handleClick();
return;
}
} }
/** /**

View File

@ -76,7 +76,13 @@ export default class AbstractLiveStreamButton<P: Props> extends AbstractButton<P
* @returns {void} * @returns {void}
*/ */
async _handleClick() { async _handleClick() {
const { _isLiveStreamRunning, dispatch } = this.props; const { _isLiveStreamRunning, dispatch, handleClick } = this.props;
if (handleClick) {
handleClick();
return;
}
const dialogShown = await dispatch(maybeShowPremiumFeatureDialog(FEATURES.RECORDING)); const dialogShown = await dispatch(maybeShowPremiumFeatureDialog(FEATURES.RECORDING));

View File

@ -77,7 +77,13 @@ export default class AbstractRecordButton<P: Props> extends AbstractButton<P, *>
* @returns {void} * @returns {void}
*/ */
async _handleClick() { async _handleClick() {
const { _isRecordingRunning, dispatch } = this.props; const { _isRecordingRunning, dispatch, handleClick } = this.props;
if (handleClick) {
handleClick();
return;
}
sendAnalytics(createToolbarEvent( sendAnalytics(createToolbarEvent(
'recording.button', 'recording.button',

View File

@ -47,8 +47,16 @@ class ShareAudioButton extends AbstractButton<Props, *> {
* @returns {void} * @returns {void}
*/ */
_handleClick() { _handleClick() {
this.props.dispatch(startAudioScreenShareFlow()); const { dispatch, handleClick } = this.props;
this.props.dispatch(setOverflowMenuVisible(false));
if (handleClick) {
handleClick();
return;
}
dispatch(startAudioScreenShareFlow());
dispatch(setOverflowMenuVisible(false));
} }
/** /**

View File

@ -48,8 +48,16 @@ class SecurityDialogButton extends AbstractButton<Props, *> {
* @returns {void} * @returns {void}
*/ */
_handleClick() { _handleClick() {
sendAnalytics(createToolbarEvent('toggle.security', { enable: !this.props._locked })); const { _locked, dispatch, handleClick } = this.props;
this.props.dispatch(toggleSecurityDialog());
if (handleClick) {
handleClick();
return;
}
sendAnalytics(createToolbarEvent('toggle.security', { enable: !_locked }));
dispatch(toggleSecurityDialog());
} }
/** /**

View File

@ -42,7 +42,15 @@ class SettingsButton extends AbstractButton<Props, *> {
_handleClick() { _handleClick() {
const { const {
defaultTab = SETTINGS_TABS.DEVICES, defaultTab = SETTINGS_TABS.DEVICES,
dispatch } = this.props; dispatch,
handleClick
} = this.props;
if (handleClick) {
handleClick();
return;
}
sendAnalytics(createToolbarEvent('settings')); sendAnalytics(createToolbarEvent('settings'));
dispatch(openSettingsDialog(defaultTab)); dispatch(openSettingsDialog(defaultTab));

View File

@ -66,6 +66,14 @@ class SharedVideoButton extends AbstractButton<Props, *> {
* @returns {void} * @returns {void}
*/ */
_handleClick() { _handleClick() {
const { handleClick } = this.props;
if (handleClick) {
handleClick();
return;
}
this._doToggleSharedVideo(); this._doToggleSharedVideo();
} }

View File

@ -41,7 +41,13 @@ class SpeakerStatsButton extends AbstractButton<Props, *> {
* @returns {void} * @returns {void}
*/ */
_handleClick() { _handleClick() {
const { _conference, dispatch } = this.props; const { _conference, dispatch, handleClick } = this.props;
if (handleClick) {
handleClick();
return;
}
sendAnalytics(createToolbarEvent('speaker.stats')); sendAnalytics(createToolbarEvent('speaker.stats'));
dispatch(openDialog(SpeakerStats, { dispatch(openDialog(SpeakerStats, {

View File

@ -38,7 +38,13 @@ export class AbstractClosedCaptionButton
* @returns {void} * @returns {void}
*/ */
async _handleClick() { async _handleClick() {
const { _requestingSubtitles, dispatch } = this.props; const { _requestingSubtitles, dispatch, handleClick } = this.props;
if (handleClick) {
handleClick();
return;
}
sendAnalytics(createToolbarEvent('transcribing.ccButton', sendAnalytics(createToolbarEvent('transcribing.ccButton',
{ {

View File

@ -32,8 +32,16 @@ class DownloadButton extends AbstractButton<Props, *> {
* @returns {void} * @returns {void}
*/ */
_handleClick() { _handleClick() {
const { _downloadAppsUrl, handleClick } = this.props;
if (handleClick) {
handleClick();
return;
}
sendAnalytics(createToolbarEvent('download.pressed')); sendAnalytics(createToolbarEvent('download.pressed'));
openURLInBrowser(this.props._downloadAppsUrl); openURLInBrowser(_downloadAppsUrl);
} }
} }

View File

@ -33,8 +33,16 @@ class HelpButton extends AbstractButton<Props, *> {
* @returns {void} * @returns {void}
*/ */
_handleClick() { _handleClick() {
const { _userDocumentationURL, handleClick } = this.props;
if (handleClick) {
handleClick();
return;
}
sendAnalytics(createToolbarEvent('help.pressed')); sendAnalytics(createToolbarEvent('help.pressed'));
openURLInBrowser(this.props._userDocumentationURL); openURLInBrowser(_userDocumentationURL);
} }
} }

View File

@ -39,7 +39,13 @@ class MuteEveryoneButton extends AbstractButton<Props, *> {
* @returns {void} * @returns {void}
*/ */
_handleClick() { _handleClick() {
const { dispatch, localParticipantId } = this.props; const { dispatch, localParticipantId, handleClick } = this.props;
if (handleClick) {
handleClick();
return;
}
sendAnalytics(createToolbarEvent('mute.everyone.pressed')); sendAnalytics(createToolbarEvent('mute.everyone.pressed'));
dispatch(openDialog(MuteEveryoneDialog, { dispatch(openDialog(MuteEveryoneDialog, {

View File

@ -39,7 +39,13 @@ class MuteEveryonesVideoButton extends AbstractButton<Props, *> {
* @returns {void} * @returns {void}
*/ */
_handleClick() { _handleClick() {
const { dispatch, localParticipantId } = this.props; const { dispatch, localParticipantId, handleClick } = this.props;
if (handleClick) {
handleClick();
return;
}
sendAnalytics(createToolbarEvent('mute.everyone.pressed')); sendAnalytics(createToolbarEvent('mute.everyone.pressed'));
dispatch(openDialog(MuteEveryonesVideoDialog, { dispatch(openDialog(MuteEveryonesVideoDialog, {

View File

@ -15,6 +15,11 @@ import AudioMuteButton from '../AudioMuteButton';
type Props = { type Props = {
/**
* External handler for click action.
*/
handleClick: Function,
/** /**
* Indicates whether audio permissions have been granted or denied. * Indicates whether audio permissions have been granted or denied.
*/ */
@ -62,6 +67,7 @@ class AudioSettingsButton extends Component<Props> {
super(props); super(props);
this._onEscClick = this._onEscClick.bind(this); this._onEscClick = this._onEscClick.bind(this);
this._onClick = this._onClick.bind(this);
} }
_onEscClick: (KeyboardEvent) => void; _onEscClick: (KeyboardEvent) => void;
@ -76,17 +82,36 @@ class AudioSettingsButton extends Component<Props> {
if (event.key === 'Escape' && this.props.isOpen) { if (event.key === 'Escape' && this.props.isOpen) {
event.preventDefault(); event.preventDefault();
event.stopPropagation(); event.stopPropagation();
this.props.onAudioOptionsClick(); this._onClick();
} }
} }
_onClick: () => void;
/**
* Click handler for the more actions entries.
*
* @returns {void}
*/
_onClick() {
const { handleClick, onAudioOptionsClick } = this.props;
if (handleClick) {
handleClick();
return;
}
onAudioOptionsClick();
}
/** /**
* Implements React's {@link Component#render}. * Implements React's {@link Component#render}.
* *
* @inheritdoc * @inheritdoc
*/ */
render() { render() {
const { hasPermissions, isDisabled, onAudioOptionsClick, visible, isOpen, t } = this.props; const { handleClick, hasPermissions, isDisabled, visible, isOpen, t } = this.props;
const settingsDisabled = !hasPermissions const settingsDisabled = !hasPermissions
|| isDisabled || isDisabled
|| !JitsiMeetJS.mediaDevices.isMultipleAudioInputSupported(); || !JitsiMeetJS.mediaDevices.isMultipleAudioInputSupported();
@ -102,12 +127,12 @@ class AudioSettingsButton extends Component<Props> {
iconDisabled = { settingsDisabled } iconDisabled = { settingsDisabled }
iconId = 'audio-settings-button' iconId = 'audio-settings-button'
iconTooltip = { t('toolbar.audioSettings') } iconTooltip = { t('toolbar.audioSettings') }
onIconClick = { onAudioOptionsClick } onIconClick = { this._onClick }
onIconKeyDown = { this._onEscClick }> onIconKeyDown = { this._onEscClick }>
<AudioMuteButton /> <AudioMuteButton handleClick = { handleClick } />
</ToolboxButtonWithIcon> </ToolboxButtonWithIcon>
</AudioSettingsPopup> </AudioSettingsPopup>
) : <AudioMuteButton />; ) : <AudioMuteButton handleClick = { handleClick } />;
} }
} }

View File

@ -11,11 +11,6 @@ type Props = AbstractButtonProps & {
* Whether or not the app is currently in full screen. * Whether or not the app is currently in full screen.
*/ */
_fullScreen: boolean, _fullScreen: boolean,
/**
* External handler for click action.
*/
handleClick: Function
}; };
/** /**
@ -73,7 +68,13 @@ class FullscreenButton extends AbstractButton<Props, *> {
* @returns {void} * @returns {void}
*/ */
_handleClick() { _handleClick() {
this.props.handleClick(); const { handleClick } = this.props;
if (handleClick) {
handleClick();
return;
}
} }
/** /**

View File

@ -87,7 +87,13 @@ class ProfileButton extends AbstractButton<Props, *> {
* @returns {void} * @returns {void}
*/ */
_handleClick() { _handleClick() {
const { dispatch, _unclickable } = this.props; const { dispatch, _unclickable, handleClick } = this.props;
if (handleClick) {
handleClick();
return;
}
if (!_unclickable) { if (!_unclickable) {
sendAnalytics(createToolbarEvent('profile')); sendAnalytics(createToolbarEvent('profile'));

View File

@ -12,11 +12,6 @@ type Props = AbstractButtonProps & {
* Whether or not the local participant's hand is raised. * Whether or not the local participant's hand is raised.
*/ */
_raisedHand: boolean, _raisedHand: boolean,
/**
* External handler for click action.
*/
handleClick: Function
}; };
/** /**
@ -51,7 +46,13 @@ class RaiseHandButton extends AbstractButton<Props, *> {
* @returns {void} * @returns {void}
*/ */
_handleClick() { _handleClick() {
this.props.handleClick(); const { handleClick } = this.props;
if (handleClick) {
handleClick();
return;
}
} }
/** /**

View File

@ -29,11 +29,6 @@ type Props = AbstractButtonProps & {
* The redux {@code dispatch} function. * The redux {@code dispatch} function.
*/ */
dispatch: Function, dispatch: Function,
/**
* External handler for click action.
*/
handleClick: Function
}; };
/** /**
@ -79,7 +74,13 @@ class ShareDesktopButton extends AbstractButton<Props, *> {
* @returns {void} * @returns {void}
*/ */
_handleClick() { _handleClick() {
this.props.handleClick(); const { handleClick } = this.props;
if (handleClick) {
handleClick();
return;
}
} }
/** /**

View File

@ -41,7 +41,15 @@ class ToggleCameraButton extends AbstractButton<Props, any> {
* @returns {void} * @returns {void}
*/ */
_handleClick() { _handleClick() {
this.props.dispatch(toggleCamera()); const { dispatch, handleClick } = this.props;
if (handleClick) {
handleClick();
return;
}
dispatch(toggleCamera());
} }
/** /**

View File

@ -103,6 +103,11 @@ type Props = {
*/ */
_backgroundType: String, _backgroundType: String,
/**
* Toolbar buttons which have their click exposed through the API.
*/
_buttonsWithNotifyClick: Array<string>,
/** /**
* Whether or not the chat feature is currently displayed. * Whether or not the chat feature is currently displayed.
*/ */
@ -835,6 +840,24 @@ class Toolbox extends Component<Props, State> {
}; };
} }
/**
* Overwrites click handlers for buttons in case click is exposed through the iframe API.
*
* @param {Object} buttons - The list of toolbar buttons.
* @returns {void}
*/
_overwriteButtonsClickHandlers(buttons) {
if (typeof APP === 'undefined' || !this.props._buttonsWithNotifyClick?.length) {
return;
}
Object.values(buttons).forEach((button: any) => {
if (this.props._buttonsWithNotifyClick.includes(button.key)) {
button.handleClick = () => APP.API.notifyToolbarButtonClicked(button.key);
}
});
}
/** /**
* Returns all buttons that need to be rendered. * Returns all buttons that need to be rendered.
* *
@ -849,6 +872,8 @@ class Toolbox extends Component<Props, State> {
const buttons = this._getAllButtons(); const buttons = this._getAllButtons();
this._overwriteButtonsClickHandlers(buttons);
const isHangupVisible = isToolbarButtonEnabled('hangup', _toolbarButtons); const isHangupVisible = isToolbarButtonEnabled('hangup', _toolbarButtons);
const { order } = THRESHOLDS.find(({ width }) => _clientWidth > width) const { order } = THRESHOLDS.find(({ width }) => _clientWidth > width)
|| THRESHOLDS[THRESHOLDS.length - 1]; || THRESHOLDS[THRESHOLDS.length - 1];
@ -1313,7 +1338,9 @@ function _mapStateToProps(state, ownProps) {
let desktopSharingEnabled = JitsiMeetJS.isDesktopSharingEnabled(); let desktopSharingEnabled = JitsiMeetJS.isDesktopSharingEnabled();
const { const {
callStatsID, callStatsID,
enableFeaturesBasedOnToken disableProfile,
enableFeaturesBasedOnToken,
buttonsWithNotifyClick
} = state['features/base/config']; } = state['features/base/config'];
const { const {
fullScreen, fullScreen,
@ -1345,6 +1372,7 @@ function _mapStateToProps(state, ownProps) {
return { return {
_backgroundType: state['features/virtual-background'].backgroundType, _backgroundType: state['features/virtual-background'].backgroundType,
_buttonsWithNotifyClick: buttonsWithNotifyClick,
_chatOpen: state['features/chat'].isOpen, _chatOpen: state['features/chat'].isOpen,
_clientWidth: clientWidth, _clientWidth: clientWidth,
_conference: conference, _conference: conference,
@ -1353,7 +1381,7 @@ function _mapStateToProps(state, ownProps) {
_dialog: Boolean(state['features/base/dialog'].component), _dialog: Boolean(state['features/base/dialog'].component),
_feedbackConfigured: Boolean(callStatsID), _feedbackConfigured: Boolean(callStatsID),
_fullScreen: fullScreen, _fullScreen: fullScreen,
_isProfileDisabled: Boolean(state['features/base/config'].disableProfile), _isProfileDisabled: Boolean(disableProfile),
_isMobile: isMobileBrowser(), _isMobile: isMobileBrowser(),
_isVpaasMeeting: isVpaasMeeting(state), _isVpaasMeeting: isVpaasMeeting(state),
_localParticipantID: localParticipant?.id, _localParticipantID: localParticipant?.id,

View File

@ -16,6 +16,11 @@ import VideoMuteButton from '../VideoMuteButton';
type Props = { type Props = {
/**
* External handler for click action.
*/
handleClick: Function,
/** /**
* Click handler for the small icon. Opens video options. * Click handler for the small icon. Opens video options.
*/ */
@ -62,7 +67,7 @@ type Props = {
*/ */
class VideoSettingsButton extends Component<Props> { class VideoSettingsButton extends Component<Props> {
/** /**
* Initializes a new {@code AudioSettingsButton} instance. * Initializes a new {@code VideoSettingsButton} instance.
* *
* @inheritdoc * @inheritdoc
*/ */
@ -70,6 +75,7 @@ class VideoSettingsButton extends Component<Props> {
super(props); super(props);
this._onEscClick = this._onEscClick.bind(this); this._onEscClick = this._onEscClick.bind(this);
this._onClick = this._onClick.bind(this);
} }
/** /**
@ -94,17 +100,36 @@ class VideoSettingsButton extends Component<Props> {
if (event.key === 'Escape' && this.props.isOpen) { if (event.key === 'Escape' && this.props.isOpen) {
event.preventDefault(); event.preventDefault();
event.stopPropagation(); event.stopPropagation();
this.props.onVideoOptionsClick(); this._onClick();
} }
} }
_onClick: () => void;
/**
* Click handler for the more actions entries.
*
* @returns {void}
*/
_onClick() {
const { handleClick, onVideoOptionsClick } = this.props;
if (handleClick) {
handleClick();
return;
}
onVideoOptionsClick();
}
/** /**
* Implements React's {@link Component#render}. * Implements React's {@link Component#render}.
* *
* @inheritdoc * @inheritdoc
*/ */
render() { render() {
const { onVideoOptionsClick, t, visible, isOpen } = this.props; const { handleClick, t, visible, isOpen } = this.props;
return visible ? ( return visible ? (
<VideoSettingsPopup> <VideoSettingsPopup>
@ -117,12 +142,12 @@ class VideoSettingsButton extends Component<Props> {
iconDisabled = { this._isIconDisabled() } iconDisabled = { this._isIconDisabled() }
iconId = 'video-settings-button' iconId = 'video-settings-button'
iconTooltip = { t('toolbar.videoSettings') } iconTooltip = { t('toolbar.videoSettings') }
onIconClick = { onVideoOptionsClick } onIconClick = { this._onClick }
onIconKeyDown = { this._onEscClick }> onIconKeyDown = { this._onEscClick }>
<VideoMuteButton /> <VideoMuteButton handleClick = { handleClick } />
</ToolboxButtonWithIcon> </ToolboxButtonWithIcon>
</VideoSettingsPopup> </VideoSettingsPopup>
) : <VideoMuteButton />; ) : <VideoMuteButton handleClick = { handleClick } />;
} }
} }

View File

@ -51,7 +51,14 @@ class TileViewButton<P: Props> extends AbstractButton<P, *> {
* @returns {void} * @returns {void}
*/ */
_handleClick() { _handleClick() {
const { _tileViewEnabled, dispatch } = this.props; const { _tileViewEnabled, dispatch, handleClick } = this.props;
if (handleClick) {
handleClick();
return;
}
const value = !_tileViewEnabled; const value = !_tileViewEnabled;
sendAnalytics(createToolbarEvent( sendAnalytics(createToolbarEvent(

View File

@ -42,11 +42,6 @@ type Props = AbstractButtonProps & {
*/ */
_videoQuality: number, _videoQuality: number,
/**
* Callback to invoke when {@link VideoQualityButton} is clicked.
*/
handleClick: Function,
/** /**
* Invoked to obtain translated strings. * Invoked to obtain translated strings.
*/ */
@ -97,7 +92,13 @@ class VideoQualityButton extends AbstractButton<Props, *> {
* @returns {void} * @returns {void}
*/ */
_handleClick() { _handleClick() {
this.props.handleClick(); const { handleClick } = this.props;
if (handleClick) {
handleClick();
return;
}
} }
} }

View File

@ -43,7 +43,13 @@ class VideoBackgroundButton extends AbstractButton<Props, *> {
* @returns {void} * @returns {void}
*/ */
_handleClick() { _handleClick() {
const { dispatch } = this.props; const { dispatch, handleClick } = this.props;
if (handleClick) {
handleClick();
return;
}
dispatch(openDialog(VirtualBackgroundDialog)); dispatch(openDialog(VirtualBackgroundDialog));
} }