feat(toolbar-buttons) Hide buttons disabled through JWT (#12261)
This commit is contained in:
parent
c35d1d8d4b
commit
a2d0492007
|
@ -1,17 +1,29 @@
|
||||||
/**
|
/**
|
||||||
* The list of supported meeting features to enable/disable through jwt.
|
* The list of supported meeting features to enable/disable through jwt.
|
||||||
*/
|
*/
|
||||||
export const MEET_FEATURES = [
|
export const MEET_FEATURES = {
|
||||||
'branding',
|
BRANDING: 'branding',
|
||||||
'calendar',
|
CALENDAR: 'calendar',
|
||||||
'callstats',
|
CALLSTATS: 'callstats',
|
||||||
'livestreaming',
|
FLIP: 'flip',
|
||||||
'lobby',
|
INBOUND_CALL: 'inbound-call',
|
||||||
'moderation',
|
LIVESTREAMING: 'livestreaming',
|
||||||
'outbound-call',
|
LOBBY: 'lobby',
|
||||||
'recording',
|
MODERATION: 'moderation',
|
||||||
'room',
|
OUTBOUND_CALL: 'outbound-call',
|
||||||
'screen-sharing',
|
RECORDING: 'recording',
|
||||||
'sip-outbound-call',
|
ROOM: 'room',
|
||||||
'transcription'
|
SCREEN_SHARING: 'screen-sharing',
|
||||||
];
|
SIP_INBOUND_CALL: 'sip-inbound-call',
|
||||||
|
SIP_OUTBOUND_CALL: 'sip-outbound-call',
|
||||||
|
TRANSCRIPTION: 'transcription'
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A mapping between jwt features and toolbar buttons keys.
|
||||||
|
*/
|
||||||
|
export const FEATURES_TO_BUTTONS_MAPPING = {
|
||||||
|
'livestreaming': 'livestreaming',
|
||||||
|
'recording': 'recording',
|
||||||
|
'transcription': 'closedcaptions'
|
||||||
|
};
|
||||||
|
|
|
@ -147,9 +147,10 @@ export function validateJwt(jwt: string) {
|
||||||
errors.push('- `context` object is missing from the payload');
|
errors.push('- `context` object is missing from the payload');
|
||||||
} else if (context.features) {
|
} else if (context.features) {
|
||||||
const { features } = context;
|
const { features } = context;
|
||||||
|
const meetFeatures = Object.values(MEET_FEATURES);
|
||||||
|
|
||||||
Object.keys(features).forEach(feature => {
|
Object.keys(features).forEach(feature => {
|
||||||
if (MEET_FEATURES.includes(feature)) {
|
if (meetFeatures.includes(feature)) {
|
||||||
const featureValue = features[feature];
|
const featureValue = features[feature];
|
||||||
|
|
||||||
// cannot use truthy or falsy because we need the exact value and type check.
|
// cannot use truthy or falsy because we need the exact value and type check.
|
||||||
|
|
|
@ -6,19 +6,6 @@ export const STATUSES = {
|
||||||
BLOCKED: 'BLOCKED'
|
BLOCKED: 'BLOCKED'
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
* Service features for JaaS users.
|
|
||||||
*/
|
|
||||||
export const FEATURES = {
|
|
||||||
INBOUND_CALL: 'inbound-call',
|
|
||||||
OUTBOUND_CALL: 'outbound-call',
|
|
||||||
RECORDING: 'recording',
|
|
||||||
SIP_INBOUND_CALL: 'sip-inbound-call',
|
|
||||||
SIP_OUTBOUND_CALL: 'sip-outbound-call',
|
|
||||||
STREAMING: 'streaming',
|
|
||||||
TRANSCRIPTION: 'transcription'
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* URL for displaying JaaS upgrade options.
|
* URL for displaying JaaS upgrade options.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -1,13 +1,13 @@
|
||||||
// @flow
|
// @flow
|
||||||
|
|
||||||
import { IconLiveStreaming } from '../../../base/icons';
|
import { IconLiveStreaming } from '../../../base/icons';
|
||||||
|
import { MEET_FEATURES } from '../../../base/jwt/constants';
|
||||||
import { isJwtFeatureEnabled } from '../../../base/jwt/functions';
|
import { isJwtFeatureEnabled } from '../../../base/jwt/functions';
|
||||||
import { JitsiRecordingConstants } from '../../../base/lib-jitsi-meet';
|
import { JitsiRecordingConstants } from '../../../base/lib-jitsi-meet';
|
||||||
import { isLocalParticipantModerator } from '../../../base/participants';
|
import { isLocalParticipantModerator } from '../../../base/participants';
|
||||||
import { AbstractButton, type AbstractButtonProps } from '../../../base/toolbox/components';
|
import { AbstractButton, type AbstractButtonProps } from '../../../base/toolbox/components';
|
||||||
import { isInBreakoutRoom } from '../../../breakout-rooms/functions';
|
import { isInBreakoutRoom } from '../../../breakout-rooms/functions';
|
||||||
import { maybeShowPremiumFeatureDialog } from '../../../jaas/actions';
|
import { maybeShowPremiumFeatureDialog } from '../../../jaas/actions';
|
||||||
import { FEATURES } from '../../../jaas/constants';
|
|
||||||
import { getActiveSession } from '../../functions';
|
import { getActiveSession } from '../../functions';
|
||||||
|
|
||||||
import { getLiveStreaming } from './functions';
|
import { getLiveStreaming } from './functions';
|
||||||
|
@ -85,7 +85,7 @@ export default class AbstractLiveStreamButton<P: Props> extends AbstractButton<P
|
||||||
async _handleClick() {
|
async _handleClick() {
|
||||||
const { dispatch } = this.props;
|
const { dispatch } = this.props;
|
||||||
|
|
||||||
const dialogShown = await dispatch(maybeShowPremiumFeatureDialog(FEATURES.RECORDING));
|
const dialogShown = await dispatch(maybeShowPremiumFeatureDialog(MEET_FEATURES.RECORDING));
|
||||||
|
|
||||||
if (!dialogShown) {
|
if (!dialogShown) {
|
||||||
this._onHandleClick();
|
this._onHandleClick();
|
||||||
|
|
|
@ -4,9 +4,9 @@ import { Component } from 'react';
|
||||||
|
|
||||||
import { getActiveSession, isHighlightMeetingMomentDisabled } from '../..';
|
import { getActiveSession, isHighlightMeetingMomentDisabled } from '../..';
|
||||||
import { openDialog } from '../../../base/dialog';
|
import { openDialog } from '../../../base/dialog';
|
||||||
|
import { MEET_FEATURES } from '../../../base/jwt/constants';
|
||||||
import { JitsiRecordingConstants } from '../../../base/lib-jitsi-meet';
|
import { JitsiRecordingConstants } from '../../../base/lib-jitsi-meet';
|
||||||
import { maybeShowPremiumFeatureDialog } from '../../../jaas/actions';
|
import { maybeShowPremiumFeatureDialog } from '../../../jaas/actions';
|
||||||
import { FEATURES } from '../../../jaas/constants';
|
|
||||||
import {
|
import {
|
||||||
NOTIFICATION_TIMEOUT_TYPE,
|
NOTIFICATION_TIMEOUT_TYPE,
|
||||||
NOTIFICATION_TYPE,
|
NOTIFICATION_TYPE,
|
||||||
|
@ -79,7 +79,7 @@ export default class AbstractHighlightButton<P: Props> extends Component<P> {
|
||||||
customActionNameKey: [ 'localRecording.start' ],
|
customActionNameKey: [ 'localRecording.start' ],
|
||||||
customActionHandler: [ async () => {
|
customActionHandler: [ async () => {
|
||||||
dispatch(hideNotification(PROMPT_RECORDING_NOTIFICATION_ID));
|
dispatch(hideNotification(PROMPT_RECORDING_NOTIFICATION_ID));
|
||||||
const dialogShown = await dispatch(maybeShowPremiumFeatureDialog(FEATURES.RECORDING));
|
const dialogShown = await dispatch(maybeShowPremiumFeatureDialog(MEET_FEATURES.RECORDING));
|
||||||
|
|
||||||
if (!dialogShown) {
|
if (!dialogShown) {
|
||||||
dispatch(openDialog(StartRecordingDialog));
|
dispatch(openDialog(StartRecordingDialog));
|
||||||
|
|
|
@ -5,10 +5,10 @@ import {
|
||||||
sendAnalytics
|
sendAnalytics
|
||||||
} from '../../../analytics';
|
} from '../../../analytics';
|
||||||
import { IconToggleRecording } from '../../../base/icons';
|
import { IconToggleRecording } from '../../../base/icons';
|
||||||
|
import { MEET_FEATURES } from '../../../base/jwt/constants';
|
||||||
import { JitsiRecordingConstants } from '../../../base/lib-jitsi-meet';
|
import { JitsiRecordingConstants } from '../../../base/lib-jitsi-meet';
|
||||||
import { AbstractButton, type AbstractButtonProps } from '../../../base/toolbox/components';
|
import { AbstractButton, type AbstractButtonProps } from '../../../base/toolbox/components';
|
||||||
import { maybeShowPremiumFeatureDialog } from '../../../jaas/actions';
|
import { maybeShowPremiumFeatureDialog } from '../../../jaas/actions';
|
||||||
import { FEATURES } from '../../../jaas/constants';
|
|
||||||
import { getActiveSession, getRecordButtonProps } from '../../functions';
|
import { getActiveSession, getRecordButtonProps } from '../../functions';
|
||||||
|
|
||||||
import LocalRecordingManager from './LocalRecordingManager';
|
import LocalRecordingManager from './LocalRecordingManager';
|
||||||
|
@ -91,7 +91,7 @@ export default class AbstractRecordButton<P: Props> extends AbstractButton<P, *>
|
||||||
'is_recording': _isRecordingRunning,
|
'is_recording': _isRecordingRunning,
|
||||||
type: JitsiRecordingConstants.mode.FILE
|
type: JitsiRecordingConstants.mode.FILE
|
||||||
}));
|
}));
|
||||||
const dialogShown = await dispatch(maybeShowPremiumFeatureDialog(FEATURES.RECORDING));
|
const dialogShown = await dispatch(maybeShowPremiumFeatureDialog(MEET_FEATURES.RECORDING));
|
||||||
|
|
||||||
if (!dialogShown) {
|
if (!dialogShown) {
|
||||||
this._onHandleClick();
|
this._onHandleClick();
|
||||||
|
|
|
@ -9,6 +9,7 @@ import { StartRecordingDialog } from '../..';
|
||||||
import { openDialog } from '../../../../base/dialog';
|
import { openDialog } from '../../../../base/dialog';
|
||||||
import { translate } from '../../../../base/i18n/functions';
|
import { translate } from '../../../../base/i18n/functions';
|
||||||
import { IconHighlight } from '../../../../base/icons/svg';
|
import { IconHighlight } from '../../../../base/icons/svg';
|
||||||
|
import { MEET_FEATURES } from '../../../../base/jwt/constants';
|
||||||
import Label from '../../../../base/label/components/web/Label';
|
import Label from '../../../../base/label/components/web/Label';
|
||||||
import { connect } from '../../../../base/redux/functions';
|
import { connect } from '../../../../base/redux/functions';
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
|
@ -16,7 +17,6 @@ import { Tooltip } from '../../../../base/tooltip';
|
||||||
import BaseTheme from '../../../../base/ui/components/BaseTheme.web';
|
import BaseTheme from '../../../../base/ui/components/BaseTheme.web';
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
import { maybeShowPremiumFeatureDialog } from '../../../../jaas/actions';
|
import { maybeShowPremiumFeatureDialog } from '../../../../jaas/actions';
|
||||||
import { FEATURES } from '../../../../jaas/constants';
|
|
||||||
import AbstractHighlightButton, {
|
import AbstractHighlightButton, {
|
||||||
type Props as AbstractProps,
|
type Props as AbstractProps,
|
||||||
_abstractMapStateToProps
|
_abstractMapStateToProps
|
||||||
|
@ -140,7 +140,7 @@ export class HighlightButton extends AbstractHighlightButton<Props, State> {
|
||||||
async _onOpenDialog() {
|
async _onOpenDialog() {
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
const { dispatch } = this.props;
|
const { dispatch } = this.props;
|
||||||
const dialogShown = await dispatch(maybeShowPremiumFeatureDialog(FEATURES.RECORDING));
|
const dialogShown = await dispatch(maybeShowPremiumFeatureDialog(MEET_FEATURES.RECORDING));
|
||||||
|
|
||||||
if (!dialogShown) {
|
if (!dialogShown) {
|
||||||
dispatch(openDialog(StartRecordingDialog));
|
dispatch(openDialog(StartRecordingDialog));
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
// @flow
|
// @flow
|
||||||
|
|
||||||
import { createToolbarEvent, sendAnalytics } from '../../analytics';
|
import { createToolbarEvent, sendAnalytics } from '../../analytics';
|
||||||
|
import { MEET_FEATURES } from '../../base/jwt/constants';
|
||||||
import { isLocalParticipantModerator } from '../../base/participants';
|
import { isLocalParticipantModerator } from '../../base/participants';
|
||||||
import { AbstractButton, type AbstractButtonProps } from '../../base/toolbox/components';
|
import { AbstractButton, type AbstractButtonProps } from '../../base/toolbox/components';
|
||||||
import { maybeShowPremiumFeatureDialog } from '../../jaas/actions';
|
import { maybeShowPremiumFeatureDialog } from '../../jaas/actions';
|
||||||
import { FEATURES } from '../../jaas/constants';
|
|
||||||
|
|
||||||
export type AbstractProps = AbstractButtonProps & {
|
export type AbstractProps = AbstractButtonProps & {
|
||||||
|
|
||||||
|
@ -61,7 +61,7 @@ export class AbstractClosedCaptionButton
|
||||||
'requesting_subtitles': Boolean(_requestingSubtitles)
|
'requesting_subtitles': Boolean(_requestingSubtitles)
|
||||||
}));
|
}));
|
||||||
|
|
||||||
const dialogShown = await dispatch(maybeShowPremiumFeatureDialog(FEATURES.RECORDING));
|
const dialogShown = await dispatch(maybeShowPremiumFeatureDialog(MEET_FEATURES.RECORDING));
|
||||||
|
|
||||||
if (!dialogShown) {
|
if (!dialogShown) {
|
||||||
this._handleClickOpenLanguageSelector();
|
this._handleClickOpenLanguageSelector();
|
||||||
|
|
|
@ -115,6 +115,7 @@ import {
|
||||||
import { NOTIFY_CLICK_MODE, NOT_APPLICABLE, THRESHOLDS } from '../../constants';
|
import { NOTIFY_CLICK_MODE, NOT_APPLICABLE, THRESHOLDS } from '../../constants';
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
import { isDesktopShareButtonDisabled, isToolboxVisible } from '../../functions';
|
import { isDesktopShareButtonDisabled, isToolboxVisible } from '../../functions';
|
||||||
|
import { getJwtDisabledButtons } from '../../functions.any';
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
import DownloadButton from '../DownloadButton';
|
import DownloadButton from '../DownloadButton';
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
|
@ -251,6 +252,11 @@ interface Props extends WithTranslation {
|
||||||
*/
|
*/
|
||||||
_isVpaasMeeting: boolean;
|
_isVpaasMeeting: boolean;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The array of toolbar buttons disabled through jwt features.
|
||||||
|
*/
|
||||||
|
_jwtDisabledButons: string[];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The ID of the local participant.
|
* The ID of the local participant.
|
||||||
*/
|
*/
|
||||||
|
@ -1009,10 +1015,10 @@ class Toolbox extends Component<Props> {
|
||||||
_getVisibleButtons() {
|
_getVisibleButtons() {
|
||||||
const {
|
const {
|
||||||
_clientWidth,
|
_clientWidth,
|
||||||
_toolbarButtons
|
_toolbarButtons,
|
||||||
|
_jwtDisabledButons
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
|
|
||||||
const buttons = this._getAllButtons();
|
const buttons = this._getAllButtons();
|
||||||
|
|
||||||
this._setButtonsNotifyClickMode(buttons);
|
this._setButtonsNotifyClickMode(buttons);
|
||||||
|
@ -1027,7 +1033,9 @@ class Toolbox extends Component<Props> {
|
||||||
...order.map(key => buttons[key as keyof typeof buttons]),
|
...order.map(key => buttons[key as keyof typeof buttons]),
|
||||||
...Object.values(buttons).filter((button, index) => !order.includes(keys[index]))
|
...Object.values(buttons).filter((button, index) => !order.includes(keys[index]))
|
||||||
].filter(Boolean).filter(({ key, alias = NOT_APPLICABLE }) =>
|
].filter(Boolean).filter(({ key, alias = NOT_APPLICABLE }) =>
|
||||||
isToolbarButtonEnabled(key, _toolbarButtons) || isToolbarButtonEnabled(alias, _toolbarButtons));
|
!_jwtDisabledButons.includes(key)
|
||||||
|
&& (isToolbarButtonEnabled(key, _toolbarButtons) || isToolbarButtonEnabled(alias, _toolbarButtons))
|
||||||
|
);
|
||||||
|
|
||||||
if (isHangupVisible) {
|
if (isHangupVisible) {
|
||||||
sliceIndex -= 1;
|
sliceIndex -= 1;
|
||||||
|
@ -1534,6 +1542,7 @@ function _mapStateToProps(state: IState, ownProps: Partial<Props>) {
|
||||||
_isIosMobile: isIosMobileBrowser(),
|
_isIosMobile: isIosMobileBrowser(),
|
||||||
_isMobile: isMobileBrowser(),
|
_isMobile: isMobileBrowser(),
|
||||||
_isVpaasMeeting: isVpaasMeeting(state),
|
_isVpaasMeeting: isVpaasMeeting(state),
|
||||||
|
_jwtDisabledButons: getJwtDisabledButtons(state),
|
||||||
_hasSalesforce: isSalesforceEnabled(state),
|
_hasSalesforce: isSalesforceEnabled(state),
|
||||||
_hangupMenuVisible: hangupMenuVisible,
|
_hangupMenuVisible: hangupMenuVisible,
|
||||||
_localParticipantID: localParticipant?.id,
|
_localParticipantID: localParticipant?.id,
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
import { IState } from '../app/types';
|
import { IState } from '../app/types';
|
||||||
|
import { FEATURES_TO_BUTTONS_MAPPING } from '../base/jwt/constants';
|
||||||
|
import { isJwtFeatureEnabled } from '../base/jwt/functions';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Indicates if the audio mute button is disabled or not.
|
* Indicates if the audio mute button is disabled or not.
|
||||||
|
@ -12,3 +14,19 @@ export function isAudioMuteButtonDisabled(state: IState) {
|
||||||
|
|
||||||
return Boolean(!available || startSilent || (muted && unmuteBlocked));
|
return Boolean(!available || startSilent || (muted && unmuteBlocked));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the buttons corresponding to features disabled through jwt.
|
||||||
|
*
|
||||||
|
* @param {IState} state - The state from the Redux store.
|
||||||
|
* @returns {string[]} - The disabled by jwt buttons array.
|
||||||
|
*/
|
||||||
|
export function getJwtDisabledButtons(state: IState) {
|
||||||
|
return Object.keys(FEATURES_TO_BUTTONS_MAPPING).reduce((acc: string[], current: string) => {
|
||||||
|
if (!isJwtFeatureEnabled(state, current, true)) {
|
||||||
|
acc.push(current);
|
||||||
|
}
|
||||||
|
|
||||||
|
return acc;
|
||||||
|
}, []);
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue