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.
|
||||
*/
|
||||
export const MEET_FEATURES = [
|
||||
'branding',
|
||||
'calendar',
|
||||
'callstats',
|
||||
'livestreaming',
|
||||
'lobby',
|
||||
'moderation',
|
||||
'outbound-call',
|
||||
'recording',
|
||||
'room',
|
||||
'screen-sharing',
|
||||
'sip-outbound-call',
|
||||
'transcription'
|
||||
];
|
||||
export const MEET_FEATURES = {
|
||||
BRANDING: 'branding',
|
||||
CALENDAR: 'calendar',
|
||||
CALLSTATS: 'callstats',
|
||||
FLIP: 'flip',
|
||||
INBOUND_CALL: 'inbound-call',
|
||||
LIVESTREAMING: 'livestreaming',
|
||||
LOBBY: 'lobby',
|
||||
MODERATION: 'moderation',
|
||||
OUTBOUND_CALL: 'outbound-call',
|
||||
RECORDING: 'recording',
|
||||
ROOM: 'room',
|
||||
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');
|
||||
} else if (context.features) {
|
||||
const { features } = context;
|
||||
const meetFeatures = Object.values(MEET_FEATURES);
|
||||
|
||||
Object.keys(features).forEach(feature => {
|
||||
if (MEET_FEATURES.includes(feature)) {
|
||||
if (meetFeatures.includes(feature)) {
|
||||
const featureValue = features[feature];
|
||||
|
||||
// cannot use truthy or falsy because we need the exact value and type check.
|
||||
|
|
|
@ -6,19 +6,6 @@ export const STATUSES = {
|
|||
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.
|
||||
*/
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
// @flow
|
||||
|
||||
import { IconLiveStreaming } from '../../../base/icons';
|
||||
import { MEET_FEATURES } from '../../../base/jwt/constants';
|
||||
import { isJwtFeatureEnabled } from '../../../base/jwt/functions';
|
||||
import { JitsiRecordingConstants } from '../../../base/lib-jitsi-meet';
|
||||
import { isLocalParticipantModerator } from '../../../base/participants';
|
||||
import { AbstractButton, type AbstractButtonProps } from '../../../base/toolbox/components';
|
||||
import { isInBreakoutRoom } from '../../../breakout-rooms/functions';
|
||||
import { maybeShowPremiumFeatureDialog } from '../../../jaas/actions';
|
||||
import { FEATURES } from '../../../jaas/constants';
|
||||
import { getActiveSession } from '../../functions';
|
||||
|
||||
import { getLiveStreaming } from './functions';
|
||||
|
@ -85,7 +85,7 @@ export default class AbstractLiveStreamButton<P: Props> extends AbstractButton<P
|
|||
async _handleClick() {
|
||||
const { dispatch } = this.props;
|
||||
|
||||
const dialogShown = await dispatch(maybeShowPremiumFeatureDialog(FEATURES.RECORDING));
|
||||
const dialogShown = await dispatch(maybeShowPremiumFeatureDialog(MEET_FEATURES.RECORDING));
|
||||
|
||||
if (!dialogShown) {
|
||||
this._onHandleClick();
|
||||
|
|
|
@ -4,9 +4,9 @@ import { Component } from 'react';
|
|||
|
||||
import { getActiveSession, isHighlightMeetingMomentDisabled } from '../..';
|
||||
import { openDialog } from '../../../base/dialog';
|
||||
import { MEET_FEATURES } from '../../../base/jwt/constants';
|
||||
import { JitsiRecordingConstants } from '../../../base/lib-jitsi-meet';
|
||||
import { maybeShowPremiumFeatureDialog } from '../../../jaas/actions';
|
||||
import { FEATURES } from '../../../jaas/constants';
|
||||
import {
|
||||
NOTIFICATION_TIMEOUT_TYPE,
|
||||
NOTIFICATION_TYPE,
|
||||
|
@ -79,7 +79,7 @@ export default class AbstractHighlightButton<P: Props> extends Component<P> {
|
|||
customActionNameKey: [ 'localRecording.start' ],
|
||||
customActionHandler: [ async () => {
|
||||
dispatch(hideNotification(PROMPT_RECORDING_NOTIFICATION_ID));
|
||||
const dialogShown = await dispatch(maybeShowPremiumFeatureDialog(FEATURES.RECORDING));
|
||||
const dialogShown = await dispatch(maybeShowPremiumFeatureDialog(MEET_FEATURES.RECORDING));
|
||||
|
||||
if (!dialogShown) {
|
||||
dispatch(openDialog(StartRecordingDialog));
|
||||
|
|
|
@ -5,10 +5,10 @@ import {
|
|||
sendAnalytics
|
||||
} from '../../../analytics';
|
||||
import { IconToggleRecording } from '../../../base/icons';
|
||||
import { MEET_FEATURES } from '../../../base/jwt/constants';
|
||||
import { JitsiRecordingConstants } from '../../../base/lib-jitsi-meet';
|
||||
import { AbstractButton, type AbstractButtonProps } from '../../../base/toolbox/components';
|
||||
import { maybeShowPremiumFeatureDialog } from '../../../jaas/actions';
|
||||
import { FEATURES } from '../../../jaas/constants';
|
||||
import { getActiveSession, getRecordButtonProps } from '../../functions';
|
||||
|
||||
import LocalRecordingManager from './LocalRecordingManager';
|
||||
|
@ -91,7 +91,7 @@ export default class AbstractRecordButton<P: Props> extends AbstractButton<P, *>
|
|||
'is_recording': _isRecordingRunning,
|
||||
type: JitsiRecordingConstants.mode.FILE
|
||||
}));
|
||||
const dialogShown = await dispatch(maybeShowPremiumFeatureDialog(FEATURES.RECORDING));
|
||||
const dialogShown = await dispatch(maybeShowPremiumFeatureDialog(MEET_FEATURES.RECORDING));
|
||||
|
||||
if (!dialogShown) {
|
||||
this._onHandleClick();
|
||||
|
|
|
@ -9,6 +9,7 @@ import { StartRecordingDialog } from '../..';
|
|||
import { openDialog } from '../../../../base/dialog';
|
||||
import { translate } from '../../../../base/i18n/functions';
|
||||
import { IconHighlight } from '../../../../base/icons/svg';
|
||||
import { MEET_FEATURES } from '../../../../base/jwt/constants';
|
||||
import Label from '../../../../base/label/components/web/Label';
|
||||
import { connect } from '../../../../base/redux/functions';
|
||||
// @ts-ignore
|
||||
|
@ -16,7 +17,6 @@ import { Tooltip } from '../../../../base/tooltip';
|
|||
import BaseTheme from '../../../../base/ui/components/BaseTheme.web';
|
||||
// @ts-ignore
|
||||
import { maybeShowPremiumFeatureDialog } from '../../../../jaas/actions';
|
||||
import { FEATURES } from '../../../../jaas/constants';
|
||||
import AbstractHighlightButton, {
|
||||
type Props as AbstractProps,
|
||||
_abstractMapStateToProps
|
||||
|
@ -140,7 +140,7 @@ export class HighlightButton extends AbstractHighlightButton<Props, State> {
|
|||
async _onOpenDialog() {
|
||||
// @ts-ignore
|
||||
const { dispatch } = this.props;
|
||||
const dialogShown = await dispatch(maybeShowPremiumFeatureDialog(FEATURES.RECORDING));
|
||||
const dialogShown = await dispatch(maybeShowPremiumFeatureDialog(MEET_FEATURES.RECORDING));
|
||||
|
||||
if (!dialogShown) {
|
||||
dispatch(openDialog(StartRecordingDialog));
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
// @flow
|
||||
|
||||
import { createToolbarEvent, sendAnalytics } from '../../analytics';
|
||||
import { MEET_FEATURES } from '../../base/jwt/constants';
|
||||
import { isLocalParticipantModerator } from '../../base/participants';
|
||||
import { AbstractButton, type AbstractButtonProps } from '../../base/toolbox/components';
|
||||
import { maybeShowPremiumFeatureDialog } from '../../jaas/actions';
|
||||
import { FEATURES } from '../../jaas/constants';
|
||||
|
||||
export type AbstractProps = AbstractButtonProps & {
|
||||
|
||||
|
@ -61,7 +61,7 @@ export class AbstractClosedCaptionButton
|
|||
'requesting_subtitles': Boolean(_requestingSubtitles)
|
||||
}));
|
||||
|
||||
const dialogShown = await dispatch(maybeShowPremiumFeatureDialog(FEATURES.RECORDING));
|
||||
const dialogShown = await dispatch(maybeShowPremiumFeatureDialog(MEET_FEATURES.RECORDING));
|
||||
|
||||
if (!dialogShown) {
|
||||
this._handleClickOpenLanguageSelector();
|
||||
|
|
|
@ -115,6 +115,7 @@ import {
|
|||
import { NOTIFY_CLICK_MODE, NOT_APPLICABLE, THRESHOLDS } from '../../constants';
|
||||
// @ts-ignore
|
||||
import { isDesktopShareButtonDisabled, isToolboxVisible } from '../../functions';
|
||||
import { getJwtDisabledButtons } from '../../functions.any';
|
||||
// @ts-ignore
|
||||
import DownloadButton from '../DownloadButton';
|
||||
// @ts-ignore
|
||||
|
@ -251,6 +252,11 @@ interface Props extends WithTranslation {
|
|||
*/
|
||||
_isVpaasMeeting: boolean;
|
||||
|
||||
/**
|
||||
* The array of toolbar buttons disabled through jwt features.
|
||||
*/
|
||||
_jwtDisabledButons: string[];
|
||||
|
||||
/**
|
||||
* The ID of the local participant.
|
||||
*/
|
||||
|
@ -1009,10 +1015,10 @@ class Toolbox extends Component<Props> {
|
|||
_getVisibleButtons() {
|
||||
const {
|
||||
_clientWidth,
|
||||
_toolbarButtons
|
||||
_toolbarButtons,
|
||||
_jwtDisabledButons
|
||||
} = this.props;
|
||||
|
||||
|
||||
const buttons = this._getAllButtons();
|
||||
|
||||
this._setButtonsNotifyClickMode(buttons);
|
||||
|
@ -1027,7 +1033,9 @@ class Toolbox extends Component<Props> {
|
|||
...order.map(key => buttons[key as keyof typeof buttons]),
|
||||
...Object.values(buttons).filter((button, index) => !order.includes(keys[index]))
|
||||
].filter(Boolean).filter(({ key, alias = NOT_APPLICABLE }) =>
|
||||
isToolbarButtonEnabled(key, _toolbarButtons) || isToolbarButtonEnabled(alias, _toolbarButtons));
|
||||
!_jwtDisabledButons.includes(key)
|
||||
&& (isToolbarButtonEnabled(key, _toolbarButtons) || isToolbarButtonEnabled(alias, _toolbarButtons))
|
||||
);
|
||||
|
||||
if (isHangupVisible) {
|
||||
sliceIndex -= 1;
|
||||
|
@ -1534,6 +1542,7 @@ function _mapStateToProps(state: IState, ownProps: Partial<Props>) {
|
|||
_isIosMobile: isIosMobileBrowser(),
|
||||
_isMobile: isMobileBrowser(),
|
||||
_isVpaasMeeting: isVpaasMeeting(state),
|
||||
_jwtDisabledButons: getJwtDisabledButtons(state),
|
||||
_hasSalesforce: isSalesforceEnabled(state),
|
||||
_hangupMenuVisible: hangupMenuVisible,
|
||||
_localParticipantID: localParticipant?.id,
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
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.
|
||||
|
@ -12,3 +14,19 @@ export function isAudioMuteButtonDisabled(state: IState) {
|
|||
|
||||
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