feat(branding): Add ability to customize logo & background
This commit is contained in:
parent
29dc63fbcb
commit
8758c222c6
17
config.js
17
config.js
|
@ -512,6 +512,23 @@ var config = {
|
||||||
// If set to true all muting operations of remote participants will be disabled.
|
// If set to true all muting operations of remote participants will be disabled.
|
||||||
// disableRemoteMute: true,
|
// disableRemoteMute: true,
|
||||||
|
|
||||||
|
/**
|
||||||
|
External API url used to receive branding specific information.
|
||||||
|
If there is no url set or there are missing fields, the defaults are applied.
|
||||||
|
None of the fieds are mandatory and the response must have the shape:
|
||||||
|
{
|
||||||
|
// The hex value for the colour used as background
|
||||||
|
backgroundColor: '#fff',
|
||||||
|
// The url for the image used as background
|
||||||
|
backgroundImageUrl: 'https://example.com/background-img.png',
|
||||||
|
// The anchor url used when clicking the logo image
|
||||||
|
logoClickUrl: 'https://example-company.org',
|
||||||
|
// The url used for the image used as logo
|
||||||
|
logoImageUrl: 'https://example.com/logo-img.png'
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
// brandingDataUrl: '',
|
||||||
|
|
||||||
// List of undocumented settings used in jitsi-meet
|
// List of undocumented settings used in jitsi-meet
|
||||||
/**
|
/**
|
||||||
_immediateReloadThreshold
|
_immediateReloadThreshold
|
||||||
|
|
|
@ -115,8 +115,9 @@ form {
|
||||||
.leftwatermark {
|
.leftwatermark {
|
||||||
left: 32px;
|
left: 32px;
|
||||||
top: 32px;
|
top: 32px;
|
||||||
background-image: url($defaultWatermarkLink);
|
|
||||||
background-position: center left;
|
background-position: center left;
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
background-size: contain;
|
||||||
}
|
}
|
||||||
|
|
||||||
.rightwatermark {
|
.rightwatermark {
|
||||||
|
|
|
@ -101,7 +101,6 @@ $sidebarWidth: 375px;
|
||||||
* Misc.
|
* Misc.
|
||||||
*/
|
*/
|
||||||
$borderRadius: 4px;
|
$borderRadius: 4px;
|
||||||
$defaultWatermarkLink: '../images/watermark.png';
|
|
||||||
$popoverMenuPadding: 13px;
|
$popoverMenuPadding: 13px;
|
||||||
$happySoftwareBackground: transparent;
|
$happySoftwareBackground: transparent;
|
||||||
$desktopAppDragBarHeight: 25px;
|
$desktopAppDragBarHeight: 25px;
|
||||||
|
@ -270,4 +269,3 @@ $chromeExtensionBannerTop: 80px;
|
||||||
$chromeExtensionBannerRight: 16px;
|
$chromeExtensionBannerRight: 16px;
|
||||||
$chromeExtensionBannerTopInMeeting: 10px;
|
$chromeExtensionBannerTopInMeeting: 10px;
|
||||||
$chromeExtensionBannerRightInMeeeting: 10px;
|
$chromeExtensionBannerRightInMeeeting: 10px;
|
||||||
|
|
||||||
|
|
|
@ -40,9 +40,6 @@
|
||||||
#remotePresenceMessage {
|
#remotePresenceMessage {
|
||||||
display: none !important;
|
display: none !important;
|
||||||
}
|
}
|
||||||
#largeVideoContainer {
|
|
||||||
background-color: $defaultBackground !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Thumbnail popover menus can overlap other thumbnails. Setting an auto
|
* Thumbnail popover menus can overlap other thumbnails. Setting an auto
|
||||||
|
|
|
@ -1,9 +1,8 @@
|
||||||
/* eslint-disable no-unused-vars, no-var, max-len */
|
/* eslint-disable no-unused-vars, no-var, max-len */
|
||||||
|
|
||||||
var interfaceConfig = {
|
var interfaceConfig = {
|
||||||
// TO FIX: this needs to be handled from SASS variables. There are some
|
|
||||||
// methods allowing to use variables both in css and js.
|
|
||||||
DEFAULT_BACKGROUND: '#474747',
|
DEFAULT_BACKGROUND: '#474747',
|
||||||
|
DEFAULT_LOGO_URL: '../images/watermark.png',
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Whether or not the blurred video background for large video should be
|
* Whether or not the blurred video background for large video should be
|
||||||
|
|
|
@ -498,9 +498,6 @@ export class VideoContainer extends LargeContainer {
|
||||||
});
|
});
|
||||||
|
|
||||||
this._updateBackground();
|
this._updateBackground();
|
||||||
|
|
||||||
// Reset the large video background depending on the stream.
|
|
||||||
this.setLargeVideoBackground(this.avatarDisplayed);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -533,14 +530,6 @@ export class VideoContainer extends LargeContainer {
|
||||||
* @param {boolean} show
|
* @param {boolean} show
|
||||||
*/
|
*/
|
||||||
showAvatar(show) {
|
showAvatar(show) {
|
||||||
// TO FIX: Video background need to be black, so that we don't have a
|
|
||||||
// flickering effect when scrolling between videos and have the screen
|
|
||||||
// move to grey before going back to video. Avatars though can have the
|
|
||||||
// default background set.
|
|
||||||
// In order to fix this code we need to introduce video background or
|
|
||||||
// find a workaround for the video flickering.
|
|
||||||
this.setLargeVideoBackground(show);
|
|
||||||
|
|
||||||
this.$avatar.css('visibility', show ? 'visible' : 'hidden');
|
this.$avatar.css('visibility', show ? 'visible' : 'hidden');
|
||||||
this.avatarDisplayed = show;
|
this.avatarDisplayed = show;
|
||||||
|
|
||||||
|
@ -596,21 +585,6 @@ export class VideoContainer extends LargeContainer {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the large video container background depending on the container
|
|
||||||
* type and the parameter indicating if an avatar is currently shown on
|
|
||||||
* large.
|
|
||||||
*
|
|
||||||
* @param {boolean} isAvatar - Indicates if the avatar is currently shown
|
|
||||||
* on the large video.
|
|
||||||
* @returns {void}
|
|
||||||
*/
|
|
||||||
setLargeVideoBackground(isAvatar) {
|
|
||||||
$('#largeVideoContainer').css('background',
|
|
||||||
this.videoType === VIDEO_CONTAINER_TYPE && !isAvatar
|
|
||||||
? '#000' : interfaceConfig.DEFAULT_BACKGROUND);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Callback invoked when the video element changes dimensions.
|
* Callback invoked when the video element changes dimensions.
|
||||||
*
|
*
|
||||||
|
|
|
@ -18,6 +18,7 @@ export default [
|
||||||
'CONNECTION_INDICATOR_AUTO_HIDE_TIMEOUT',
|
'CONNECTION_INDICATOR_AUTO_HIDE_TIMEOUT',
|
||||||
'CONNECTION_INDICATOR_DISABLED',
|
'CONNECTION_INDICATOR_DISABLED',
|
||||||
'DEFAULT_BACKGROUND',
|
'DEFAULT_BACKGROUND',
|
||||||
|
'DEFAULT_LOGO_URL',
|
||||||
'DISABLE_PRESENCE_STATUS',
|
'DISABLE_PRESENCE_STATUS',
|
||||||
'DISABLE_JOIN_LEAVE_NOTIFICATIONS',
|
'DISABLE_JOIN_LEAVE_NOTIFICATIONS',
|
||||||
'DEFAULT_LOCAL_DISPLAY_NAME',
|
'DEFAULT_LOCAL_DISPLAY_NAME',
|
||||||
|
|
|
@ -21,11 +21,27 @@ const _RIGHT_WATERMARK_STYLE = {
|
||||||
*/
|
*/
|
||||||
type Props = {
|
type Props = {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The user selected url used to navigate to on logo click.
|
||||||
|
*/
|
||||||
|
_customLogoLink: string,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The url of the user selected logo.
|
||||||
|
*/
|
||||||
|
_customLogoUrl: string,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Whether or not the current user is logged in through a JWT.
|
* Whether or not the current user is logged in through a JWT.
|
||||||
*/
|
*/
|
||||||
_isGuest: boolean,
|
_isGuest: boolean,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Flag used to signal that the logo can be displayed.
|
||||||
|
* It becomes true after the user customization options are fetched.
|
||||||
|
*/
|
||||||
|
_readyToDisplayJitsiWatermark: boolean,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Invoked to obtain translated strings.
|
* Invoked to obtain translated strings.
|
||||||
*/
|
*/
|
||||||
|
@ -133,6 +149,26 @@ class Watermarks extends Component<Props, State> {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if the watermark is ready to be displayed.
|
||||||
|
*
|
||||||
|
* @private
|
||||||
|
* @returns {boolean}
|
||||||
|
*/
|
||||||
|
_canDisplayJitsiWatermark() {
|
||||||
|
const {
|
||||||
|
showJitsiWatermark,
|
||||||
|
showJitsiWatermarkForGuests
|
||||||
|
} = this.state;
|
||||||
|
const {
|
||||||
|
_isGuest,
|
||||||
|
_readyToDisplayJitsiWatermark
|
||||||
|
} = this.props;
|
||||||
|
|
||||||
|
return _readyToDisplayJitsiWatermark
|
||||||
|
&& (showJitsiWatermark || (_isGuest && showJitsiWatermarkForGuests));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Renders a brand watermark if it is enabled.
|
* Renders a brand watermark if it is enabled.
|
||||||
*
|
*
|
||||||
|
@ -173,18 +209,27 @@ class Watermarks extends Component<Props, State> {
|
||||||
*/
|
*/
|
||||||
_renderJitsiWatermark() {
|
_renderJitsiWatermark() {
|
||||||
let reactElement = null;
|
let reactElement = null;
|
||||||
|
const {
|
||||||
|
_customLogoUrl,
|
||||||
|
_customLogoLink
|
||||||
|
} = this.props;
|
||||||
|
|
||||||
if (this.state.showJitsiWatermark
|
if (this._canDisplayJitsiWatermark()) {
|
||||||
|| (this.props._isGuest
|
const link = _customLogoLink || this.state.jitsiWatermarkLink;
|
||||||
&& this.state.showJitsiWatermarkForGuests)) {
|
const style = {
|
||||||
reactElement = <div className = 'watermark leftwatermark' />;
|
backgroundImage: `url(${_customLogoUrl || interfaceConfig.DEFAULT_LOGO_URL})`,
|
||||||
|
maxWidth: 140,
|
||||||
|
maxHeight: 70
|
||||||
|
};
|
||||||
|
|
||||||
const { jitsiWatermarkLink } = this.state;
|
reactElement = (<div
|
||||||
|
className = 'watermark leftwatermark'
|
||||||
|
style = { style } />);
|
||||||
|
|
||||||
if (jitsiWatermarkLink) {
|
if (link) {
|
||||||
reactElement = (
|
reactElement = (
|
||||||
<a
|
<a
|
||||||
href = { jitsiWatermarkLink }
|
href = { link }
|
||||||
target = '_new'>
|
target = '_new'>
|
||||||
{ reactElement }
|
{ reactElement }
|
||||||
</a>
|
</a>
|
||||||
|
@ -223,12 +268,11 @@ class Watermarks extends Component<Props, State> {
|
||||||
* Maps parts of Redux store to component prop types.
|
* Maps parts of Redux store to component prop types.
|
||||||
*
|
*
|
||||||
* @param {Object} state - Snapshot of Redux store.
|
* @param {Object} state - Snapshot of Redux store.
|
||||||
* @returns {{
|
* @returns {Props}
|
||||||
* _isGuest: boolean
|
|
||||||
* }}
|
|
||||||
*/
|
*/
|
||||||
function _mapStateToProps(state) {
|
function _mapStateToProps(state) {
|
||||||
const { isGuest } = state['features/base/jwt'];
|
const { isGuest } = state['features/base/jwt'];
|
||||||
|
const { customizationReady, logoClickUrl, logoImageUrl } = state['features/dynamic-branding'];
|
||||||
|
|
||||||
return {
|
return {
|
||||||
/**
|
/**
|
||||||
|
@ -238,7 +282,10 @@ function _mapStateToProps(state) {
|
||||||
* @private
|
* @private
|
||||||
* @type {boolean}
|
* @type {boolean}
|
||||||
*/
|
*/
|
||||||
_isGuest: isGuest
|
_customLogoLink: logoClickUrl,
|
||||||
|
_customLogoUrl: logoImageUrl,
|
||||||
|
_isGuest: isGuest,
|
||||||
|
_readyToDisplayJitsiWatermark: customizationReady
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,9 @@
|
||||||
|
/**
|
||||||
|
* Action used to set custom user properties.
|
||||||
|
*/
|
||||||
|
export const SET_DYNAMIC_BRANDING_DATA = 'SET_DYNAMIC_BRANDING_DATA';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Action used to signal the branding elements are ready to be displayed
|
||||||
|
*/
|
||||||
|
export const SET_DYNAMIC_BRANDING_READY = 'SET_DYNAMIC_BRANDING_READY';
|
|
@ -0,0 +1,66 @@
|
||||||
|
// @flow
|
||||||
|
|
||||||
|
import { getLogger } from 'jitsi-meet-logger';
|
||||||
|
|
||||||
|
import { doGetJSON } from '../base/util';
|
||||||
|
|
||||||
|
import { SET_DYNAMIC_BRANDING_DATA, SET_DYNAMIC_BRANDING_READY } from './actionTypes';
|
||||||
|
import { extractFqnFromPath } from './functions';
|
||||||
|
|
||||||
|
const logger = getLogger(__filename);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetches custom branding data.
|
||||||
|
* If there is no data or the request fails, sets the `customizationReady` flag
|
||||||
|
* so the defaults can be displayed.
|
||||||
|
*
|
||||||
|
* @returns {Function}
|
||||||
|
*/
|
||||||
|
export function fetchCustomBrandingData() {
|
||||||
|
return async function(dispatch: Function, getState: Function) {
|
||||||
|
const state = getState();
|
||||||
|
const baseUrl = state['features/base/config'].brandingDataUrl;
|
||||||
|
const { customizationReady } = state['features/dynamic-branding'];
|
||||||
|
|
||||||
|
if (!customizationReady) {
|
||||||
|
const fqn = extractFqnFromPath(state['features/base/connection'].locationURL.pathname);
|
||||||
|
|
||||||
|
if (baseUrl && fqn) {
|
||||||
|
try {
|
||||||
|
const res = await doGetJSON(`${baseUrl}?conferenceFqn=${encodeURIComponent(fqn)}`);
|
||||||
|
|
||||||
|
return dispatch(setDynamicBrandingData(res));
|
||||||
|
} catch (err) {
|
||||||
|
logger.error('Error fetching branding data', err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dispatch(setDynamicBrandingReady());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Action used to set the user customizations.
|
||||||
|
*
|
||||||
|
* @param {Object} value - The custom data to be set.
|
||||||
|
* @returns {Object}
|
||||||
|
*/
|
||||||
|
function setDynamicBrandingData(value) {
|
||||||
|
return {
|
||||||
|
type: SET_DYNAMIC_BRANDING_DATA,
|
||||||
|
value
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Action used to signal the branding elements are ready to be displayed.
|
||||||
|
*
|
||||||
|
* @returns {Object}
|
||||||
|
*/
|
||||||
|
function setDynamicBrandingReady() {
|
||||||
|
return {
|
||||||
|
type: SET_DYNAMIC_BRANDING_READY
|
||||||
|
};
|
||||||
|
}
|
|
@ -0,0 +1,15 @@
|
||||||
|
// @flow
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extracts the fqn part from a path, where fqn represents
|
||||||
|
* tenant/roomName.
|
||||||
|
*
|
||||||
|
* @param {string} path - The URL path.
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
export function extractFqnFromPath(path: string) {
|
||||||
|
const parts = path.split('/');
|
||||||
|
const len = parts.length;
|
||||||
|
|
||||||
|
return parts.length > 2 ? `${parts[len - 2]}/${parts[len - 1]}` : '';
|
||||||
|
}
|
|
@ -0,0 +1,4 @@
|
||||||
|
export * from './actions';
|
||||||
|
export * from './functions';
|
||||||
|
|
||||||
|
import './reducer';
|
|
@ -0,0 +1,46 @@
|
||||||
|
// @flow
|
||||||
|
|
||||||
|
import { ReducerRegistry } from '../base/redux';
|
||||||
|
|
||||||
|
import { SET_DYNAMIC_BRANDING_DATA, SET_DYNAMIC_BRANDING_READY } from './actionTypes';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The name of the redux store/state property which is the root of the redux
|
||||||
|
* state of the feature {@code dynamic-branding}.
|
||||||
|
*/
|
||||||
|
const STORE_NAME = 'features/dynamic-branding';
|
||||||
|
|
||||||
|
const DEFAULT_STATE = {
|
||||||
|
backgroundColor: '',
|
||||||
|
backgroundImageUrl: '',
|
||||||
|
customizationReady: false,
|
||||||
|
logoClickUrl: '',
|
||||||
|
logoImageUrl: ''
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reduces redux actions for the purposes of the feature {@code dynamic-branding}.
|
||||||
|
*/
|
||||||
|
ReducerRegistry.register(STORE_NAME, (state = DEFAULT_STATE, action) => {
|
||||||
|
switch (action.type) {
|
||||||
|
case SET_DYNAMIC_BRANDING_DATA: {
|
||||||
|
const { backgroundColor, backgroundImageUrl, logoClickUrl, logoImageUrl } = action.value;
|
||||||
|
|
||||||
|
return {
|
||||||
|
backgroundColor,
|
||||||
|
backgroundImageUrl,
|
||||||
|
logoClickUrl,
|
||||||
|
logoImageUrl,
|
||||||
|
customizationReady: true
|
||||||
|
};
|
||||||
|
}
|
||||||
|
case SET_DYNAMIC_BRANDING_READY:
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
customizationReady: true
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return state;
|
||||||
|
});
|
|
@ -4,12 +4,28 @@ import React, { Component } from 'react';
|
||||||
|
|
||||||
import { Watermarks } from '../../base/react';
|
import { Watermarks } from '../../base/react';
|
||||||
import { connect } from '../../base/redux';
|
import { connect } from '../../base/redux';
|
||||||
|
import { fetchCustomBrandingData } from '../../dynamic-branding';
|
||||||
import { Captions } from '../../subtitles/';
|
import { Captions } from '../../subtitles/';
|
||||||
|
|
||||||
declare var interfaceConfig: Object;
|
declare var interfaceConfig: Object;
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The user selected background color.
|
||||||
|
*/
|
||||||
|
_customBackgroundColor: string,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The user selected background image url.
|
||||||
|
*/
|
||||||
|
_customBackgroundImageUrl: string,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetches the branding data.
|
||||||
|
*/
|
||||||
|
_fetchCustomBrandingData: Function,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Used to determine the value of the autoplay attribute of the underlying
|
* Used to determine the value of the autoplay attribute of the underlying
|
||||||
* video element.
|
* video element.
|
||||||
|
@ -24,6 +40,15 @@ type Props = {
|
||||||
* @extends Component
|
* @extends Component
|
||||||
*/
|
*/
|
||||||
class LargeVideo extends Component<Props> {
|
class LargeVideo extends Component<Props> {
|
||||||
|
/**
|
||||||
|
* Implements React's {@link Component#componentDidMount}.
|
||||||
|
*
|
||||||
|
* @inheritdoc
|
||||||
|
*/
|
||||||
|
componentDidMount() {
|
||||||
|
this.props._fetchCustomBrandingData();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Implements React's {@link Component#render()}.
|
* Implements React's {@link Component#render()}.
|
||||||
*
|
*
|
||||||
|
@ -31,10 +56,13 @@ class LargeVideo extends Component<Props> {
|
||||||
* @returns {React$Element}
|
* @returns {React$Element}
|
||||||
*/
|
*/
|
||||||
render() {
|
render() {
|
||||||
|
const style = this._getCustomSyles();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className = 'videocontainer'
|
className = 'videocontainer'
|
||||||
id = 'largeVideoContainer'>
|
id = 'largeVideoContainer'
|
||||||
|
style = { style }>
|
||||||
<div id = 'sharedVideo'>
|
<div id = 'sharedVideo'>
|
||||||
<div id = 'sharedVideoIFrame' />
|
<div id = 'sharedVideoIFrame' />
|
||||||
</div>
|
</div>
|
||||||
|
@ -72,6 +100,26 @@ class LargeVideo extends Component<Props> {
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates the custom styles object.
|
||||||
|
*
|
||||||
|
* @private
|
||||||
|
* @returns {Object}
|
||||||
|
*/
|
||||||
|
_getCustomSyles() {
|
||||||
|
const styles = {};
|
||||||
|
const { _customBackgroundColor, _customBackgroundImageUrl } = this.props;
|
||||||
|
|
||||||
|
styles.backgroundColor = _customBackgroundColor || interfaceConfig.DEFAULT_BACKGROUND;
|
||||||
|
|
||||||
|
if (_customBackgroundImageUrl) {
|
||||||
|
styles.backgroundImage = `url(${_customBackgroundImageUrl})`;
|
||||||
|
styles.backgroundSize = 'cover';
|
||||||
|
}
|
||||||
|
|
||||||
|
return styles;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -80,17 +128,21 @@ class LargeVideo extends Component<Props> {
|
||||||
*
|
*
|
||||||
* @param {Object} state - The Redux state.
|
* @param {Object} state - The Redux state.
|
||||||
* @private
|
* @private
|
||||||
* @returns {{
|
* @returns {Props}
|
||||||
* _noAutoPlayVideo: boolean
|
|
||||||
* }}
|
|
||||||
*/
|
*/
|
||||||
function _mapStateToProps(state) {
|
function _mapStateToProps(state) {
|
||||||
const testingConfig = state['features/base/config'].testing;
|
const testingConfig = state['features/base/config'].testing;
|
||||||
|
const { backgroundColor, backgroundImageUrl } = state['features/dynamic-branding'];
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
_customBackgroundColor: backgroundColor,
|
||||||
|
_customBackgroundImageUrl: backgroundImageUrl,
|
||||||
_noAutoPlayVideo: testingConfig?.noAutoPlayVideo
|
_noAutoPlayVideo: testingConfig?.noAutoPlayVideo
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const _mapDispatchToProps = {
|
||||||
|
_fetchCustomBrandingData: fetchCustomBrandingData
|
||||||
|
};
|
||||||
|
|
||||||
export default connect(_mapStateToProps)(LargeVideo);
|
export default connect(_mapStateToProps, _mapDispatchToProps)(LargeVideo);
|
||||||
|
|
Loading…
Reference in New Issue