This commit is contained in:
4
react/features/jaas/actionTypes.ts
Normal file
4
react/features/jaas/actionTypes.ts
Normal file
@@ -0,0 +1,4 @@
|
||||
/**
|
||||
* Action used to store jaas customer details
|
||||
*/
|
||||
export const SET_DETAILS = 'SET_DETAILS';
|
||||
46
react/features/jaas/actions.any.ts
Normal file
46
react/features/jaas/actions.any.ts
Normal file
@@ -0,0 +1,46 @@
|
||||
import { IStore } from '../app/types';
|
||||
|
||||
import { SET_DETAILS } from './actionTypes';
|
||||
import { getVpaasTenant, sendGetDetailsRequest } from './functions';
|
||||
import logger from './logger';
|
||||
|
||||
/**
|
||||
* Action used to set the jaas customer details in store.
|
||||
*
|
||||
* @param {Object} details - The customer details object.
|
||||
* @returns {Object}
|
||||
*/
|
||||
function setCustomerDetails(details: Object) {
|
||||
return {
|
||||
type: SET_DETAILS,
|
||||
payload: details
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a request for retrieving jaas customer details.
|
||||
*
|
||||
* @returns {Function}
|
||||
*/
|
||||
export function getCustomerDetails() {
|
||||
return async function(dispatch: IStore['dispatch'], getState: IStore['getState']) {
|
||||
const state = getState();
|
||||
const baseUrl = state['features/base/config'].jaasActuatorUrl ?? '';
|
||||
const appId = getVpaasTenant(state);
|
||||
|
||||
const shouldSendRequest = Boolean(baseUrl && appId);
|
||||
|
||||
if (shouldSendRequest) {
|
||||
try {
|
||||
const details = await sendGetDetailsRequest({
|
||||
appId,
|
||||
baseUrl
|
||||
});
|
||||
|
||||
dispatch(setCustomerDetails(details));
|
||||
} catch (err) {
|
||||
logger.error('Could not send request', err);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
11
react/features/jaas/actions.native.ts
Normal file
11
react/features/jaas/actions.native.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
/**
|
||||
* Shows a dialog prompting users to upgrade, if requested feature is disabled.
|
||||
*
|
||||
* @param {string} _feature - Used on web.
|
||||
* @returns {Function}
|
||||
*/
|
||||
export function maybeShowPremiumFeatureDialog(_feature: string) {
|
||||
return function() {
|
||||
return false;
|
||||
};
|
||||
}
|
||||
25
react/features/jaas/actions.web.ts
Normal file
25
react/features/jaas/actions.web.ts
Normal file
@@ -0,0 +1,25 @@
|
||||
import { IStore } from '../app/types';
|
||||
import { openDialog } from '../base/dialog/actions';
|
||||
import { ParticipantFeaturesKey } from '../base/participants/types';
|
||||
|
||||
import PremiumFeatureDialog from './components/web/PremiumFeatureDialog';
|
||||
import { isFeatureDisabled } from './functions';
|
||||
|
||||
/**
|
||||
* Shows a dialog prompting users to upgrade, if requested feature is disabled.
|
||||
*
|
||||
* @param {ParticipantFeaturesKey} feature - The feature to check availability for.
|
||||
*
|
||||
* @returns {Function}
|
||||
*/
|
||||
export function maybeShowPremiumFeatureDialog(feature: ParticipantFeaturesKey) {
|
||||
return function(dispatch: IStore['dispatch'], getState: IStore['getState']) {
|
||||
if (isFeatureDisabled(getState(), feature)) {
|
||||
dispatch(openDialog(PremiumFeatureDialog));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
}
|
||||
56
react/features/jaas/components/web/PremiumFeatureDialog.tsx
Normal file
56
react/features/jaas/components/web/PremiumFeatureDialog.tsx
Normal file
@@ -0,0 +1,56 @@
|
||||
import React, { PureComponent } from 'react';
|
||||
|
||||
|
||||
import { translate } from '../../../base/i18n/functions';
|
||||
import Dialog from '../../../base/ui/components/web/Dialog';
|
||||
import { openURLInBrowser } from '../../../base/util/openURLInBrowser.web';
|
||||
import { JAAS_UPGRADE_URL } from '../../constants';
|
||||
|
||||
/**
|
||||
* Component that renders the premium feature dialog.
|
||||
*
|
||||
* @returns {React$Element<any>}
|
||||
*/
|
||||
class PremiumFeatureDialog extends PureComponent<any> {
|
||||
|
||||
/**
|
||||
* Instantiates a new component.
|
||||
*
|
||||
* @inheritdoc
|
||||
*/
|
||||
constructor(props: any) {
|
||||
super(props);
|
||||
|
||||
this._onSubmitValue = this._onSubmitValue.bind(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback to be invoked when the dialog ok is pressed.
|
||||
*
|
||||
* @returns {boolean}
|
||||
*/
|
||||
_onSubmitValue() {
|
||||
openURLInBrowser(JAAS_UPGRADE_URL, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements React's {@link Component#render()}.
|
||||
*
|
||||
* @inheritdoc
|
||||
*/
|
||||
override render() {
|
||||
const { t } = this.props;
|
||||
|
||||
return (
|
||||
<Dialog
|
||||
cancel = {{ hidden: true }}
|
||||
ok = {{ translationKey: 'dialog.viewUpgradeOptions' }}
|
||||
onSubmit = { this._onSubmitValue }
|
||||
titleKey = { t('dialog.viewUpgradeOptionsTitle') }>
|
||||
<span>{t('dialog.viewUpgradeOptionsContent')}</span>
|
||||
</Dialog>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default translate(PremiumFeatureDialog);
|
||||
17
react/features/jaas/constants.ts
Normal file
17
react/features/jaas/constants.ts
Normal file
@@ -0,0 +1,17 @@
|
||||
/**
|
||||
* JaaS customer statuses which represent their account state.
|
||||
*/
|
||||
export const STATUSES = {
|
||||
ACTIVE: 'ACTIVE',
|
||||
BLOCKED: 'BLOCKED'
|
||||
};
|
||||
|
||||
/**
|
||||
* URL for displaying JaaS upgrade options.
|
||||
*/
|
||||
export const JAAS_UPGRADE_URL = 'https://jaas.8x8.vc/#/plan/upgrade';
|
||||
|
||||
/**
|
||||
* The prefix for the vpaas tenant.
|
||||
*/
|
||||
export const VPAAS_TENANT_PREFIX = 'vpaas-magic-cookie-';
|
||||
170
react/features/jaas/functions.ts
Normal file
170
react/features/jaas/functions.ts
Normal file
@@ -0,0 +1,170 @@
|
||||
import { IReduxState } from '../app/types';
|
||||
import { IJitsiConference } from '../base/conference/reducer';
|
||||
import { ParticipantFeaturesKey } from '../base/participants/types';
|
||||
|
||||
import { VPAAS_TENANT_PREFIX } from './constants';
|
||||
import logger from './logger';
|
||||
|
||||
/**
|
||||
* Returns the full vpaas tenant if available, given a path.
|
||||
*
|
||||
* @param {string} path - The meeting url path.
|
||||
* @returns {string}
|
||||
*/
|
||||
function extractVpaasTenantFromPath(path: string) {
|
||||
const [ , tenant ] = path.split('/');
|
||||
|
||||
if (tenant.startsWith(VPAAS_TENANT_PREFIX)) {
|
||||
return tenant;
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the vpaas tenant.
|
||||
*
|
||||
* @param {IReduxState} state - The global state.
|
||||
* @returns {string}
|
||||
*/
|
||||
export function getVpaasTenant(state: IReduxState) {
|
||||
return extractVpaasTenantFromPath(state['features/base/connection'].locationURL?.pathname ?? '');
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the current meeting is a vpaas one.
|
||||
*
|
||||
* @param {IReduxState} state - The state of the app.
|
||||
* @returns {boolean}
|
||||
*/
|
||||
export function isVpaasMeeting(state: IReduxState) {
|
||||
const connection = state['features/base/connection'];
|
||||
|
||||
if (connection?.locationURL?.pathname) {
|
||||
return Boolean(
|
||||
extractVpaasTenantFromPath(connection?.locationURL?.pathname)
|
||||
);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a request for retrieving the conference creator's customer id.
|
||||
*
|
||||
* @param {IJitsiConference} conference - The conference state.
|
||||
* @param {IReduxState} state - The state of the app.
|
||||
* @returns {Object} - Object containing customerId field.
|
||||
*/
|
||||
export async function sendGetCustomerIdRequest(conference: IJitsiConference, state: IReduxState) {
|
||||
const { jaasConferenceCreatorUrl } = state['features/base/config'];
|
||||
|
||||
const roomJid = conference?.room?.roomjid;
|
||||
|
||||
if (jaasConferenceCreatorUrl && roomJid) {
|
||||
const fullUrl = `${jaasConferenceCreatorUrl}?conference=${encodeURIComponent(roomJid)}`;
|
||||
const response = await fetch(fullUrl);
|
||||
const responseBody = await response.json();
|
||||
|
||||
if (response.ok) {
|
||||
return responseBody;
|
||||
}
|
||||
|
||||
logger.error(`Failed to fetch ${fullUrl}. with: ${JSON.stringify(responseBody)}`);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a request for retrieving jaas customer details.
|
||||
*
|
||||
* @param {Object} reqData - The request info.
|
||||
* @param {string} reqData.appId - The client appId.
|
||||
* @param {string} reqData.baseUrl - The base url for the request.
|
||||
* @returns {void}
|
||||
*/
|
||||
export async function sendGetDetailsRequest({ appId, baseUrl }: {
|
||||
appId: string;
|
||||
baseUrl: string;
|
||||
}) {
|
||||
const fullUrl = `${baseUrl}/v1/public/tenants/${encodeURIComponent(appId)}`;
|
||||
|
||||
try {
|
||||
const res = await fetch(fullUrl);
|
||||
|
||||
if (res.ok) {
|
||||
return res.json();
|
||||
}
|
||||
|
||||
throw new Error('Request not successful');
|
||||
} catch (err: any) {
|
||||
throw new Error(err);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the billing id for vpaas meetings.
|
||||
*
|
||||
* @param {IReduxState} state - The state of the app.
|
||||
* @param {ParticipantFeaturesKey} feature - Feature to be looked up for disable state.
|
||||
* @returns {boolean}
|
||||
*/
|
||||
export function isFeatureDisabled(state: IReduxState, feature: ParticipantFeaturesKey) {
|
||||
return state['features/jaas'].disabledFeatures.includes(feature);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a request for retrieving jaas JWT.
|
||||
*
|
||||
* @param {Object} reqData - The request info.
|
||||
* @param {string} reqData.appId - The client appId.
|
||||
* @param {string} reqData.baseUrl - The base url for the request.
|
||||
* @returns {void}
|
||||
*/
|
||||
export async function sendGetJWTRequest({ appId, baseUrl }: {
|
||||
appId: string;
|
||||
baseUrl: string;
|
||||
}) {
|
||||
const fullUrl = `${baseUrl}/v1/public/token/${encodeURIComponent(appId)}`;
|
||||
|
||||
try {
|
||||
const res = await fetch(fullUrl, {
|
||||
method: 'GET'
|
||||
});
|
||||
|
||||
if (res.ok) {
|
||||
return res.json();
|
||||
}
|
||||
|
||||
throw new Error('Request not successful');
|
||||
} catch (err: any) {
|
||||
throw new Error(err);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a jaas JWT.
|
||||
*
|
||||
* @param {IReduxState} state - Redux state.
|
||||
* @returns {string} The JWT.
|
||||
*/
|
||||
export async function getJaasJWT(state: IReduxState) {
|
||||
const baseUrl = state['features/base/config'].jaasTokenUrl;
|
||||
const appId = getVpaasTenant(state);
|
||||
|
||||
const shouldSendRequest = Boolean(baseUrl && appId);
|
||||
|
||||
if (shouldSendRequest) {
|
||||
try {
|
||||
const jwt = await sendGetJWTRequest({
|
||||
appId,
|
||||
baseUrl: baseUrl ?? ''
|
||||
});
|
||||
|
||||
return jwt.token;
|
||||
} catch (err) {
|
||||
logger.error('Could not send request', err);
|
||||
}
|
||||
}
|
||||
}
|
||||
3
react/features/jaas/logger.ts
Normal file
3
react/features/jaas/logger.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
import { getLogger } from '../base/logging/functions';
|
||||
|
||||
export default getLogger('features/jaas');
|
||||
39
react/features/jaas/middleware.any.ts
Normal file
39
react/features/jaas/middleware.any.ts
Normal file
@@ -0,0 +1,39 @@
|
||||
import { createVpaasConferenceJoinedEvent } from '../analytics/AnalyticsEvents';
|
||||
import { sendAnalytics } from '../analytics/functions';
|
||||
import { IReduxState } from '../app/types';
|
||||
import { CONFERENCE_JOINED } from '../base/conference/actionTypes';
|
||||
import MiddlewareRegistry from '../base/redux/MiddlewareRegistry';
|
||||
|
||||
import { getVpaasTenant, isVpaasMeeting } from './functions';
|
||||
|
||||
/**
|
||||
* The redux middleware for billing counter.
|
||||
*
|
||||
* @param {Store} store - The redux store.
|
||||
* @returns {Function}
|
||||
*/
|
||||
|
||||
MiddlewareRegistry.register(store => next => action => {
|
||||
switch (action.type) {
|
||||
case CONFERENCE_JOINED: {
|
||||
_maybeTrackVpaasConferenceJoin(store.getState());
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return next(action);
|
||||
});
|
||||
|
||||
/**
|
||||
* Tracks the conference join event if the meeting is a vpaas one.
|
||||
*
|
||||
* @param {Store} state - The app state.
|
||||
* @returns {Function}
|
||||
*/
|
||||
function _maybeTrackVpaasConferenceJoin(state: IReduxState) {
|
||||
if (isVpaasMeeting(state)) {
|
||||
sendAnalytics(createVpaasConferenceJoinedEvent(
|
||||
getVpaasTenant(state)));
|
||||
}
|
||||
}
|
||||
0
react/features/jaas/middleware.native.ts
Normal file
0
react/features/jaas/middleware.native.ts
Normal file
47
react/features/jaas/middleware.web.ts
Normal file
47
react/features/jaas/middleware.web.ts
Normal file
@@ -0,0 +1,47 @@
|
||||
import { redirectToStaticPage } from '../app/actions.web';
|
||||
import { CONFERENCE_JOINED } from '../base/conference/actionTypes';
|
||||
import {
|
||||
JitsiConferenceErrors,
|
||||
JitsiConferenceEvents
|
||||
} from '../base/lib-jitsi-meet';
|
||||
import MiddlewareRegistry from '../base/redux/MiddlewareRegistry';
|
||||
|
||||
import { SET_DETAILS } from './actionTypes';
|
||||
import { STATUSES } from './constants';
|
||||
import logger from './logger';
|
||||
|
||||
/**
|
||||
* The redux middleware for jaas.
|
||||
*
|
||||
* @param {Store} store - The redux store.
|
||||
* @returns {Function}
|
||||
*/
|
||||
MiddlewareRegistry.register(store => next => action => {
|
||||
switch (action.type) {
|
||||
case CONFERENCE_JOINED: {
|
||||
const { conference } = action;
|
||||
|
||||
if (store.getState()['features/base/config'].iAmRecorder) {
|
||||
// We don't register anything on web if we are in iAmRecorder mode
|
||||
return next(action);
|
||||
}
|
||||
|
||||
conference.on(
|
||||
JitsiConferenceEvents.CONFERENCE_ERROR, (errorType: string, errorMsg: string) => {
|
||||
errorType === JitsiConferenceErrors.SETTINGS_ERROR && logger.error(errorMsg);
|
||||
});
|
||||
break;
|
||||
}
|
||||
|
||||
case SET_DETAILS: {
|
||||
const { status } = action.payload;
|
||||
|
||||
if (status === STATUSES.BLOCKED) {
|
||||
store.dispatch(redirectToStaticPage('/static/planLimit.html'));
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return next(action);
|
||||
});
|
||||
32
react/features/jaas/reducer.ts
Normal file
32
react/features/jaas/reducer.ts
Normal file
@@ -0,0 +1,32 @@
|
||||
import ReducerRegistry from '../base/redux/ReducerRegistry';
|
||||
|
||||
import {
|
||||
SET_DETAILS
|
||||
} from './actionTypes';
|
||||
import { STATUSES } from './constants';
|
||||
|
||||
const DEFAULT_STATE = {
|
||||
disabledFeatures: [],
|
||||
status: STATUSES.ACTIVE
|
||||
};
|
||||
|
||||
export interface IJaaSState {
|
||||
[key: string]: any;
|
||||
}
|
||||
|
||||
/**
|
||||
* Listen for actions that mutate the billing-counter state.
|
||||
*/
|
||||
ReducerRegistry.register<IJaaSState>(
|
||||
'features/jaas', (state = DEFAULT_STATE, action): IJaaSState => {
|
||||
switch (action.type) {
|
||||
|
||||
case SET_DETAILS: {
|
||||
return action.payload;
|
||||
}
|
||||
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
}
|
||||
);
|
||||
Reference in New Issue
Block a user