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,
|
parseURIString,
|
||||||
toURLString
|
toURLString
|
||||||
} from '../base/util';
|
} from '../base/util';
|
||||||
|
import { isVpaasMeeting } from '../billing-counter/functions';
|
||||||
import { clearNotifications, showNotification } from '../notifications';
|
import { clearNotifications, showNotification } from '../notifications';
|
||||||
import { setFatalError } from '../overlay';
|
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 close page is enabled redirect to it, without further action
|
||||||
if (enableClosePage) {
|
if (enableClosePage) {
|
||||||
|
if (isVpaasMeeting(getState())) {
|
||||||
|
redirectToStaticPage('/');
|
||||||
|
}
|
||||||
|
|
||||||
const { isGuest, jwt } = getState()['features/base/jwt'];
|
const { isGuest, jwt } = getState()['features/base/jwt'];
|
||||||
|
|
||||||
let hashParam;
|
let hashParam;
|
||||||
|
|
||||||
// save whether current user is guest or not, and pass auth token,
|
// 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/testing/middleware';
|
||||||
import '../base/tracks/middleware';
|
import '../base/tracks/middleware';
|
||||||
import '../base/user-interaction/middleware';
|
import '../base/user-interaction/middleware';
|
||||||
|
import '../billing-counter/middleware';
|
||||||
import '../calendar-sync/middleware';
|
import '../calendar-sync/middleware';
|
||||||
import '../chat/middleware';
|
import '../chat/middleware';
|
||||||
import '../conference/middleware';
|
import '../conference/middleware';
|
||||||
|
|
|
@ -2,9 +2,11 @@
|
||||||
|
|
||||||
import React, { Component } from 'react';
|
import React, { Component } from 'react';
|
||||||
|
|
||||||
|
import { isVpaasMeeting } from '../../../../billing-counter/functions';
|
||||||
import { translate } from '../../../i18n';
|
import { translate } from '../../../i18n';
|
||||||
import { connect } from '../../../redux';
|
import { connect } from '../../../redux';
|
||||||
|
|
||||||
|
|
||||||
declare var interfaceConfig: Object;
|
declare var interfaceConfig: Object;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -36,6 +38,11 @@ type Props = {
|
||||||
*/
|
*/
|
||||||
_isGuest: boolean,
|
_isGuest: boolean,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether or not the current meeting is a vpaas one.
|
||||||
|
*/
|
||||||
|
_isVpaas: boolean,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Flag used to signal that the logo can be displayed.
|
* Flag used to signal that the logo can be displayed.
|
||||||
* It becomes true after the user customization options are fetched.
|
* It becomes true after the user customization options are fetched.
|
||||||
|
@ -181,6 +188,33 @@ class Watermarks extends Component<Props, State> {
|
||||||
|| _welcomePageIsVisible;
|
|| _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.
|
* Renders a brand watermark if it is enabled.
|
||||||
*
|
*
|
||||||
|
@ -221,18 +255,22 @@ class Watermarks extends Component<Props, State> {
|
||||||
*/
|
*/
|
||||||
_renderJitsiWatermark() {
|
_renderJitsiWatermark() {
|
||||||
let reactElement = null;
|
let reactElement = null;
|
||||||
const {
|
|
||||||
_customLogoUrl,
|
|
||||||
_customLogoLink,
|
|
||||||
defaultJitsiLogoURL
|
|
||||||
} = this.props;
|
|
||||||
|
|
||||||
if (this._canDisplayJitsiWatermark()) {
|
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 = {
|
const style = {
|
||||||
backgroundImage: `url(${_customLogoUrl || defaultJitsiLogoURL || interfaceConfig.DEFAULT_LOGO_URL})`,
|
backgroundImage,
|
||||||
maxWidth: 140,
|
maxWidth: 140,
|
||||||
maxHeight: 70
|
maxHeight: 70,
|
||||||
|
...additionalStyles
|
||||||
};
|
};
|
||||||
|
|
||||||
reactElement = (<div
|
reactElement = (<div
|
||||||
|
@ -299,6 +337,7 @@ function _mapStateToProps(state) {
|
||||||
_customLogoLink: logoClickUrl,
|
_customLogoLink: logoClickUrl,
|
||||||
_customLogoUrl: logoImageUrl,
|
_customLogoUrl: logoImageUrl,
|
||||||
_isGuest: isGuest,
|
_isGuest: isGuest,
|
||||||
|
_isVpaas: isVpaasMeeting(state),
|
||||||
_readyToDisplayJitsiWatermark: customizationReady,
|
_readyToDisplayJitsiWatermark: customizationReady,
|
||||||
_welcomePageIsVisible: !room
|
_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 { Icon, IconClose } from '../../base/icons';
|
||||||
import { browser } from '../../base/lib-jitsi-meet';
|
import { browser } from '../../base/lib-jitsi-meet';
|
||||||
import { connect } from '../../base/redux';
|
import { connect } from '../../base/redux';
|
||||||
|
import { isVpaasMeeting } from '../../billing-counter/functions';
|
||||||
import logger from '../logger';
|
import logger from '../logger';
|
||||||
|
|
||||||
|
|
||||||
|
@ -50,6 +51,11 @@ type Props = {
|
||||||
*/
|
*/
|
||||||
iAmRecorder: boolean,
|
iAmRecorder: boolean,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether it's a vpaas meeting or not.
|
||||||
|
*/
|
||||||
|
isVpaas: boolean,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Invoked to obtain translated strings.
|
* Invoked to obtain translated strings.
|
||||||
*/
|
*/
|
||||||
|
@ -146,7 +152,8 @@ class ChromeExtensionBanner extends PureComponent<Props, State> {
|
||||||
_isSupportedEnvironment() {
|
_isSupportedEnvironment() {
|
||||||
return interfaceConfig.SHOW_CHROME_EXTENSION_BANNER
|
return interfaceConfig.SHOW_CHROME_EXTENSION_BANNER
|
||||||
&& browser.isChrome()
|
&& browser.isChrome()
|
||||||
&& !isMobileBrowser();
|
&& !isMobileBrowser()
|
||||||
|
&& !this.props.isVpaas;
|
||||||
}
|
}
|
||||||
|
|
||||||
_onClosePressed: () => void;
|
_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.
|
// Using emptyObject so that we don't change the reference every time when _mapStateToProps is called.
|
||||||
bannerCfg: state['features/base/config'].chromeExtensionBanner || emptyObject,
|
bannerCfg: state['features/base/config'].chromeExtensionBanner || emptyObject,
|
||||||
conference: getCurrentConference(state),
|
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';
|
} from '../../../base/react';
|
||||||
import { connect } from '../../../base/redux';
|
import { connect } from '../../../base/redux';
|
||||||
import { ColorPalette, StyleType } from '../../../base/styles';
|
import { ColorPalette, StyleType } from '../../../base/styles';
|
||||||
|
import { isVpaasMeeting } from '../../../billing-counter/functions';
|
||||||
import { authorizeDropbox, updateDropboxToken } from '../../../dropbox';
|
import { authorizeDropbox, updateDropboxToken } from '../../../dropbox';
|
||||||
import { RECORDING_TYPES } from '../../constants';
|
import { RECORDING_TYPES } from '../../constants';
|
||||||
import { getRecordingDurationEstimation } from '../../functions';
|
import { getRecordingDurationEstimation } from '../../functions';
|
||||||
|
@ -72,6 +73,11 @@ type Props = {
|
||||||
*/
|
*/
|
||||||
isValidating: boolean,
|
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
|
* The function will be called when there are changes related to the
|
||||||
* switches.
|
* switches.
|
||||||
|
@ -226,7 +232,7 @@ class StartRecordingDialogContent extends Component<Props> {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const { _dialogStyles, _styles: styles, isValidating, t } = this.props;
|
const { _dialogStyles, _styles: styles, isValidating, isVpaas, t } = this.props;
|
||||||
|
|
||||||
const switchContent
|
const switchContent
|
||||||
= this.props.integrationsEnabled
|
= this.props.integrationsEnabled
|
||||||
|
@ -240,6 +246,8 @@ class StartRecordingDialogContent extends Component<Props> {
|
||||||
value = { this.props.selectedRecordingService === RECORDING_TYPES.JITSI_REC_SERVICE } />
|
value = { this.props.selectedRecordingService === RECORDING_TYPES.JITSI_REC_SERVICE } />
|
||||||
) : null;
|
) : null;
|
||||||
|
|
||||||
|
const icon = isVpaas ? ICON_SHARE : JITSI_LOGO;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Container
|
<Container
|
||||||
className = 'recording-header'
|
className = 'recording-header'
|
||||||
|
@ -248,7 +256,7 @@ class StartRecordingDialogContent extends Component<Props> {
|
||||||
<Container className = 'recording-icon-container'>
|
<Container className = 'recording-icon-container'>
|
||||||
<Image
|
<Image
|
||||||
className = 'recording-icon'
|
className = 'recording-icon'
|
||||||
src = { JITSI_LOGO }
|
src = { icon }
|
||||||
style = { styles.recordingIcon } />
|
style = { styles.recordingIcon } />
|
||||||
</Container>
|
</Container>
|
||||||
<Text
|
<Text
|
||||||
|
@ -484,6 +492,7 @@ class StartRecordingDialogContent extends Component<Props> {
|
||||||
function _mapStateToProps(state) {
|
function _mapStateToProps(state) {
|
||||||
return {
|
return {
|
||||||
..._abstractMapStateToProps(state),
|
..._abstractMapStateToProps(state),
|
||||||
|
isVpaas: isVpaasMeeting(state),
|
||||||
_styles: ColorSchemeRegistry.get(state, 'StartRecordingDialogContent')
|
_styles: ColorSchemeRegistry.get(state, 'StartRecordingDialogContent')
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue