feat(vpaas): Add endpoint counter & remove branding on vpaas meetings
This commit is contained in:
parent
b7529863d5
commit
71d0577a49
|
@ -24,6 +24,7 @@ import {
|
|||
parseURIString,
|
||||
toURLString
|
||||
} from '../base/util';
|
||||
import { isVpaasMeeting } from '../billing-counter/functions';
|
||||
import { clearNotifications, showNotification } from '../notifications';
|
||||
import { setFatalError } from '../overlay';
|
||||
|
||||
|
@ -291,7 +292,12 @@ export function maybeRedirectToWelcomePage(options: Object = {}) {
|
|||
|
||||
// if close page is enabled redirect to it, without further action
|
||||
if (enableClosePage) {
|
||||
if (isVpaasMeeting(getState())) {
|
||||
redirectToStaticPage('/');
|
||||
}
|
||||
|
||||
const { isGuest, jwt } = getState()['features/base/jwt'];
|
||||
|
||||
let hashParam;
|
||||
|
||||
// save whether current user is guest or not, and pass auth token,
|
||||
|
|
|
@ -18,6 +18,7 @@ import '../base/sounds/middleware';
|
|||
import '../base/testing/middleware';
|
||||
import '../base/tracks/middleware';
|
||||
import '../base/user-interaction/middleware';
|
||||
import '../billing-counter/middleware';
|
||||
import '../calendar-sync/middleware';
|
||||
import '../chat/middleware';
|
||||
import '../conference/middleware';
|
||||
|
|
|
@ -2,9 +2,11 @@
|
|||
|
||||
import React, { Component } from 'react';
|
||||
|
||||
import { isVpaasMeeting } from '../../../../billing-counter/functions';
|
||||
import { translate } from '../../../i18n';
|
||||
import { connect } from '../../../redux';
|
||||
|
||||
|
||||
declare var interfaceConfig: Object;
|
||||
|
||||
/**
|
||||
|
@ -36,6 +38,11 @@ type Props = {
|
|||
*/
|
||||
_isGuest: boolean,
|
||||
|
||||
/**
|
||||
* Whether or not the current meeting is a vpaas one.
|
||||
*/
|
||||
_isVpaas: boolean,
|
||||
|
||||
/**
|
||||
* Flag used to signal that the logo can be displayed.
|
||||
* It becomes true after the user customization options are fetched.
|
||||
|
@ -181,6 +188,33 @@ class Watermarks extends Component<Props, State> {
|
|||
|| _welcomePageIsVisible;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the background image style.
|
||||
*
|
||||
* @private
|
||||
* @returns {string}
|
||||
*/
|
||||
_getBackgroundImageStyle() {
|
||||
const {
|
||||
_customLogoUrl,
|
||||
_isVpaas,
|
||||
defaultJitsiLogoURL
|
||||
} = this.props;
|
||||
let style = 'none';
|
||||
|
||||
if (_isVpaas) {
|
||||
if (_customLogoUrl) {
|
||||
style = `url(${_customLogoUrl})`;
|
||||
}
|
||||
} else {
|
||||
style = `url(${_customLogoUrl
|
||||
|| defaultJitsiLogoURL
|
||||
|| interfaceConfig.DEFAULT_LOGO_URL})`;
|
||||
}
|
||||
|
||||
return style;
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders a brand watermark if it is enabled.
|
||||
*
|
||||
|
@ -221,18 +255,22 @@ class Watermarks extends Component<Props, State> {
|
|||
*/
|
||||
_renderJitsiWatermark() {
|
||||
let reactElement = null;
|
||||
const {
|
||||
_customLogoUrl,
|
||||
_customLogoLink,
|
||||
defaultJitsiLogoURL
|
||||
} = this.props;
|
||||
|
||||
if (this._canDisplayJitsiWatermark()) {
|
||||
const link = _customLogoLink || this.state.jitsiWatermarkLink;
|
||||
const backgroundImage = this._getBackgroundImageStyle();
|
||||
const link = this.props._customLogoLink || this.state.jitsiWatermarkLink;
|
||||
const additionalStyles = {};
|
||||
|
||||
if (backgroundImage === 'none') {
|
||||
additionalStyles.height = 0;
|
||||
additionalStyles.width = 0;
|
||||
}
|
||||
|
||||
const style = {
|
||||
backgroundImage: `url(${_customLogoUrl || defaultJitsiLogoURL || interfaceConfig.DEFAULT_LOGO_URL})`,
|
||||
backgroundImage,
|
||||
maxWidth: 140,
|
||||
maxHeight: 70
|
||||
maxHeight: 70,
|
||||
...additionalStyles
|
||||
};
|
||||
|
||||
reactElement = (<div
|
||||
|
@ -299,6 +337,7 @@ function _mapStateToProps(state) {
|
|||
_customLogoLink: logoClickUrl,
|
||||
_customLogoUrl: logoImageUrl,
|
||||
_isGuest: isGuest,
|
||||
_isVpaas: isVpaasMeeting(state),
|
||||
_readyToDisplayJitsiWatermark: customizationReady,
|
||||
_welcomePageIsVisible: !room
|
||||
};
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
/**
|
||||
* Action used to store the billing id.
|
||||
*/
|
||||
export const SET_BILLING_ID = 'SET_BILLING_ID';
|
|
@ -0,0 +1,51 @@
|
|||
// @flow
|
||||
|
||||
import uuid from 'uuid';
|
||||
|
||||
import { SET_BILLING_ID } from './actionTypes';
|
||||
import { extractVpaasTenantFromPath, getBillingId, sendCountRequest } from './functions';
|
||||
|
||||
/**
|
||||
* Sends a billing count request when needed.
|
||||
* If there is no billingId, it presists one first and sends the request after.
|
||||
*
|
||||
* @returns {Function}
|
||||
*/
|
||||
export function countEndpoint() {
|
||||
return function(dispatch: Function, getState: Function) {
|
||||
const state = getState();
|
||||
const baseUrl = state['features/base/config'].billingCounterUrl;
|
||||
const jwt = state['features/base/jwt'].jwt;
|
||||
const tenant = extractVpaasTenantFromPath(state['features/base/connection'].locationURL.pathname);
|
||||
const shouldSendRequest = Boolean(baseUrl && jwt && tenant);
|
||||
|
||||
if (shouldSendRequest) {
|
||||
let billingId = getBillingId();
|
||||
|
||||
if (!billingId) {
|
||||
billingId = uuid.v4();
|
||||
dispatch(setBillingId(billingId));
|
||||
}
|
||||
|
||||
sendCountRequest({
|
||||
baseUrl,
|
||||
billingId,
|
||||
jwt,
|
||||
tenant
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Action used to set the user billing id.
|
||||
*
|
||||
* @param {string} value - The uid.
|
||||
* @returns {Object}
|
||||
*/
|
||||
function setBillingId(value) {
|
||||
return {
|
||||
type: SET_BILLING_ID,
|
||||
value
|
||||
};
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
/**
|
||||
* The key for the billing id stored in localStorage.
|
||||
*/
|
||||
export const BILLING_ID = 'billingId';
|
||||
|
||||
/**
|
||||
* The prefix for the vpaas tenant.
|
||||
*/
|
||||
export const VPAAS_TENANT_PREFIX = 'vpass-magic-cookie-';
|
|
@ -0,0 +1,91 @@
|
|||
// @flow
|
||||
|
||||
import { jitsiLocalStorage } from '@jitsi/js-utils';
|
||||
|
||||
import { BILLING_ID, 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}
|
||||
*/
|
||||
export function extractVpaasTenantFromPath(path: string) {
|
||||
const [ , tenant ] = path.split('/');
|
||||
|
||||
if (tenant.startsWith(VPAAS_TENANT_PREFIX)) {
|
||||
return tenant;
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the current meeting is a vpaas one.
|
||||
*
|
||||
* @param {Object} state - The state of the app.
|
||||
* @returns {boolean}
|
||||
*/
|
||||
export function isVpaasMeeting(state: Object) {
|
||||
return Boolean(
|
||||
state['features/base/config'].billingCounterUrl
|
||||
&& state['features/base/jwt'].jwt
|
||||
&& extractVpaasTenantFromPath(
|
||||
state['features/base/connection'].locationURL.pathname)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a billing counter request.
|
||||
*
|
||||
* @param {Object} reqData - The request info.
|
||||
* @param {string} reqData.baseUrl - The base url for the request.
|
||||
* @param {string} billingId - The unique id of the client.
|
||||
* @param {string} jwt - The JWT token.
|
||||
* @param {string} tenat - The client tenant.
|
||||
* @returns {void}
|
||||
*/
|
||||
export async function sendCountRequest({ baseUrl, billingId, jwt, tenant }: {
|
||||
baseUrl: string,
|
||||
billingId: string,
|
||||
jwt: string,
|
||||
tenant: string
|
||||
}) {
|
||||
const fullUrl = `${baseUrl}/${encodeURIComponent(tenant)}/${billingId}`;
|
||||
const headers = {
|
||||
'Authorization': `Bearer: ${jwt}`
|
||||
};
|
||||
|
||||
try {
|
||||
const res = await fetch(fullUrl, {
|
||||
method: 'GET',
|
||||
headers
|
||||
});
|
||||
|
||||
if (!res.ok) {
|
||||
logger.error('Status error:', res.status);
|
||||
}
|
||||
} catch (err) {
|
||||
logger.error('Could not send request', err);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the stored billing id.
|
||||
*
|
||||
* @returns {string}
|
||||
*/
|
||||
export function getBillingId() {
|
||||
return jitsiLocalStorage.getItem(BILLING_ID);
|
||||
}
|
||||
|
||||
/**
|
||||
* Stores the billing id.
|
||||
*
|
||||
* @param {string} value - The id to be stored.
|
||||
* @returns {void}
|
||||
*/
|
||||
export function setBillingId(value: string) {
|
||||
jitsiLocalStorage.setItem(BILLING_ID, value);
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
// @flow
|
||||
|
||||
import { getLogger } from '../base/logging/functions';
|
||||
|
||||
export default getLogger('features/billing-counter');
|
|
@ -0,0 +1,31 @@
|
|||
import { CONFERENCE_JOINED } from '../base/conference/actionTypes';
|
||||
import { MiddlewareRegistry } from '../base/redux';
|
||||
|
||||
import { SET_BILLING_ID } from './actionTypes';
|
||||
import { countEndpoint } from './actions';
|
||||
import { setBillingId } from './functions';
|
||||
|
||||
/**
|
||||
* The redux middleware for billing counter.
|
||||
*
|
||||
* @param {Store} store - The redux store.
|
||||
* @returns {Function}
|
||||
*/
|
||||
MiddlewareRegistry.register(store => next => async action => {
|
||||
switch (action.type) {
|
||||
case SET_BILLING_ID: {
|
||||
setBillingId(action.value);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case CONFERENCE_JOINED: {
|
||||
store.dispatch(countEndpoint());
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return next(action);
|
||||
});
|
|
@ -16,6 +16,7 @@ import { translate } from '../../base/i18n';
|
|||
import { Icon, IconClose } from '../../base/icons';
|
||||
import { browser } from '../../base/lib-jitsi-meet';
|
||||
import { connect } from '../../base/redux';
|
||||
import { isVpaasMeeting } from '../../billing-counter/functions';
|
||||
import logger from '../logger';
|
||||
|
||||
|
||||
|
@ -50,6 +51,11 @@ type Props = {
|
|||
*/
|
||||
iAmRecorder: boolean,
|
||||
|
||||
/**
|
||||
* Whether it's a vpaas meeting or not.
|
||||
*/
|
||||
isVpaas: boolean,
|
||||
|
||||
/**
|
||||
* Invoked to obtain translated strings.
|
||||
*/
|
||||
|
@ -146,7 +152,8 @@ class ChromeExtensionBanner extends PureComponent<Props, State> {
|
|||
_isSupportedEnvironment() {
|
||||
return interfaceConfig.SHOW_CHROME_EXTENSION_BANNER
|
||||
&& browser.isChrome()
|
||||
&& !isMobileBrowser();
|
||||
&& !isMobileBrowser()
|
||||
&& !this.props.isVpaas;
|
||||
}
|
||||
|
||||
_onClosePressed: () => void;
|
||||
|
@ -280,7 +287,8 @@ const _mapStateToProps = state => {
|
|||
// Using emptyObject so that we don't change the reference every time when _mapStateToProps is called.
|
||||
bannerCfg: state['features/base/config'].chromeExtensionBanner || emptyObject,
|
||||
conference: getCurrentConference(state),
|
||||
iAmRecorder: state['features/base/config'].iAmRecorder
|
||||
iAmRecorder: state['features/base/config'].iAmRecorder,
|
||||
isVpaas: isVpaasMeeting(state)
|
||||
};
|
||||
};
|
||||
|
||||
|
|
|
@ -21,6 +21,7 @@ import {
|
|||
} from '../../../base/react';
|
||||
import { connect } from '../../../base/redux';
|
||||
import { ColorPalette, StyleType } from '../../../base/styles';
|
||||
import { isVpaasMeeting } from '../../../billing-counter/functions';
|
||||
import { authorizeDropbox, updateDropboxToken } from '../../../dropbox';
|
||||
import { RECORDING_TYPES } from '../../constants';
|
||||
import { getRecordingDurationEstimation } from '../../functions';
|
||||
|
@ -72,6 +73,11 @@ type Props = {
|
|||
*/
|
||||
isValidating: boolean,
|
||||
|
||||
/**
|
||||
* Whether or not the current meeting is a vpaas one.
|
||||
*/
|
||||
isVpaas: boolean,
|
||||
|
||||
/**
|
||||
* The function will be called when there are changes related to the
|
||||
* switches.
|
||||
|
@ -226,7 +232,7 @@ class StartRecordingDialogContent extends Component<Props> {
|
|||
return null;
|
||||
}
|
||||
|
||||
const { _dialogStyles, _styles: styles, isValidating, t } = this.props;
|
||||
const { _dialogStyles, _styles: styles, isValidating, isVpaas, t } = this.props;
|
||||
|
||||
const switchContent
|
||||
= this.props.integrationsEnabled
|
||||
|
@ -240,6 +246,8 @@ class StartRecordingDialogContent extends Component<Props> {
|
|||
value = { this.props.selectedRecordingService === RECORDING_TYPES.JITSI_REC_SERVICE } />
|
||||
) : null;
|
||||
|
||||
const icon = isVpaas ? ICON_SHARE : JITSI_LOGO;
|
||||
|
||||
return (
|
||||
<Container
|
||||
className = 'recording-header'
|
||||
|
@ -248,7 +256,7 @@ class StartRecordingDialogContent extends Component<Props> {
|
|||
<Container className = 'recording-icon-container'>
|
||||
<Image
|
||||
className = 'recording-icon'
|
||||
src = { JITSI_LOGO }
|
||||
src = { icon }
|
||||
style = { styles.recordingIcon } />
|
||||
</Container>
|
||||
<Text
|
||||
|
@ -484,6 +492,7 @@ class StartRecordingDialogContent extends Component<Props> {
|
|||
function _mapStateToProps(state) {
|
||||
return {
|
||||
..._abstractMapStateToProps(state),
|
||||
isVpaas: isVpaasMeeting(state),
|
||||
_styles: ColorSchemeRegistry.get(state, 'StartRecordingDialogContent')
|
||||
};
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue