diff --git a/react/features/base/react/components/web/Watermarks.js b/react/features/base/react/components/web/Watermarks.js index c23c3eb29..6d60248ac 100644 --- a/react/features/base/react/components/web/Watermarks.js +++ b/react/features/base/react/components/web/Watermarks.js @@ -24,35 +24,19 @@ const _RIGHT_WATERMARK_STYLE = { type Props = { /** - * The user selected url used to navigate to on logo click. + * The link used to navigate to on logo click. */ - _customLogoLink: string, + _logoLink: string, /** - * The url of the user selected logo. + * The url for the logo. */ - _customLogoUrl: string, + _logoUrl: string, /** - * Whether or not the current user is logged in through a JWT. + * If the Jitsi watermark should be displayed or not. */ - _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. - */ - _readyToDisplayJitsiWatermark: boolean, - - /** - * Returns true if welcome page is visible at the moment. - */ - _welcomePageIsVisible: boolean, + _showJitsiWatermark: boolean, /** * The default value for the Jitsi logo URL. @@ -75,27 +59,11 @@ type State = { */ brandWatermarkLink: string, - /** - * The url to open when clicking the Jitsi watermark. - */ - jitsiWatermarkLink: string, - /** * Whether or not the brand watermark should be displayed. */ showBrandWatermark: boolean, - /** - * Whether or not the Jitsi watermark should be displayed. - */ - showJitsiWatermark: boolean, - - /** - * Whether or not the Jitsi watermark should be displayed for users not - * logged in through a JWT. - */ - showJitsiWatermarkForGuests: boolean, - /** * Whether or not the show the "powered by Jitsi.org" link. */ @@ -117,29 +85,17 @@ class Watermarks extends Component { super(props); let showBrandWatermark; - let showJitsiWatermark; - let showJitsiWatermarkForGuests; if (interfaceConfig.filmStripOnly) { showBrandWatermark = false; - showJitsiWatermark = false; - showJitsiWatermarkForGuests = false; } else { showBrandWatermark = interfaceConfig.SHOW_BRAND_WATERMARK; - showJitsiWatermark = interfaceConfig.SHOW_JITSI_WATERMARK; - showJitsiWatermarkForGuests - = interfaceConfig.SHOW_WATERMARK_FOR_GUESTS; } this.state = { brandWatermarkLink: showBrandWatermark ? interfaceConfig.BRAND_WATERMARK_LINK : '', - jitsiWatermarkLink: - showJitsiWatermark || showJitsiWatermarkForGuests - ? interfaceConfig.JITSI_WATERMARK_LINK : '', showBrandWatermark, - showJitsiWatermark, - showJitsiWatermarkForGuests, showPoweredBy: interfaceConfig.SHOW_POWERED_BY }; } @@ -166,55 +122,6 @@ class Watermarks extends Component { ); } - /** - * Returns true if the watermark is ready to be displayed. - * - * @private - * @returns {boolean} - */ - _canDisplayJitsiWatermark() { - const { - showJitsiWatermark, - showJitsiWatermarkForGuests - } = this.state; - const { - _isGuest, - _readyToDisplayJitsiWatermark, - _welcomePageIsVisible - } = this.props; - - return (_readyToDisplayJitsiWatermark - && (showJitsiWatermark || (_isGuest && showJitsiWatermarkForGuests))) - || _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. * @@ -254,33 +161,28 @@ class Watermarks extends Component { * @returns {ReactElement|null} */ _renderJitsiWatermark() { + const { + _logoLink, + _logoUrl, + _showJitsiWatermark + } = this.props; let reactElement = null; - if (this._canDisplayJitsiWatermark()) { - const backgroundImage = this._getBackgroundImageStyle(); - const link = this.props._customLogoLink || this.state.jitsiWatermarkLink; - const additionalStyles = {}; - - if (backgroundImage === 'none') { - additionalStyles.height = 0; - additionalStyles.width = 0; - } - + if (_showJitsiWatermark) { const style = { - backgroundImage, + backgroundImage: `url(${_logoUrl})`, maxWidth: 140, - maxHeight: 70, - ...additionalStyles + maxHeight: 70 }; reactElement = (
); - if (link) { + if (_logoLink) { reactElement = ( { reactElement } @@ -319,27 +221,52 @@ class Watermarks extends Component { * Maps parts of Redux store to component prop types. * * @param {Object} state - Snapshot of Redux store. + * @param {Object} ownProps - Component's own props. * @returns {Props} */ -function _mapStateToProps(state) { +function _mapStateToProps(state, ownProps) { const { isGuest } = state['features/base/jwt']; - const { customizationReady, logoClickUrl, logoImageUrl } = state['features/dynamic-branding']; - const { room } = state['features/base/conference']; + const { + customizationReady, + customizationFailed, + defaultBranding, + useDynamicBrandingData, + logoClickUrl, + logoImageUrl + } = state['features/dynamic-branding']; + const isValidRoom = state['features/base/conference'].room; + const { + DEFAULT_LOGO_URL, + JITSI_WATERMARK_LINK, + SHOW_JITSI_WATERMARK, + SHOW_JITSI_WATERMARK_FOR_GUESTS, + filmStripOnly + } = interfaceConfig; + let _showJitsiWatermark = (!filmStripOnly + && (customizationReady && !customizationFailed) + && (SHOW_JITSI_WATERMARK || (isGuest && SHOW_JITSI_WATERMARK_FOR_GUESTS))) + || !isValidRoom; + let _logoUrl = logoImageUrl; + let _logoLink = logoClickUrl; + + if (useDynamicBrandingData) { + if (isVpaasMeeting(state)) { + // don't show logo if request fails or no logo set for vpaas meetings + _showJitsiWatermark = !customizationFailed && Boolean(logoImageUrl); + } else if (defaultBranding) { + _logoUrl = DEFAULT_LOGO_URL; + _logoLink = JITSI_WATERMARK_LINK; + } + } else { + // When there is no custom branding data use defaults + _logoUrl = ownProps.defaultJitsiLogoURL || DEFAULT_LOGO_URL; + _logoLink = JITSI_WATERMARK_LINK; + } return { - /** - * The indicator which determines whether the local participant is a - * guest in the conference. - * - * @private - * @type {boolean} - */ - _customLogoLink: logoClickUrl, - _customLogoUrl: logoImageUrl, - _isGuest: isGuest, - _isVpaas: isVpaasMeeting(state), - _readyToDisplayJitsiWatermark: customizationReady, - _welcomePageIsVisible: !room + _logoLink, + _logoUrl, + _showJitsiWatermark }; } diff --git a/react/features/dynamic-branding/actionTypes.js b/react/features/dynamic-branding/actionTypes.js index bab4b71bb..246dbe3b4 100644 --- a/react/features/dynamic-branding/actionTypes.js +++ b/react/features/dynamic-branding/actionTypes.js @@ -3,6 +3,11 @@ */ export const SET_DYNAMIC_BRANDING_DATA = 'SET_DYNAMIC_BRANDING_DATA'; +/** + * Action used to signal the customization failed. + */ +export const SET_DYNAMIC_BRANDING_FAILED = 'SET_DYNAMIC_BRANDING_FAILED'; + /** * Action used to signal the branding elements are ready to be displayed */ diff --git a/react/features/dynamic-branding/actions.js b/react/features/dynamic-branding/actions.js index 29f07752f..8db5aedc7 100644 --- a/react/features/dynamic-branding/actions.js +++ b/react/features/dynamic-branding/actions.js @@ -4,7 +4,11 @@ import { getLogger } from 'jitsi-meet-logger'; import { doGetJSON } from '../base/util'; -import { SET_DYNAMIC_BRANDING_DATA, SET_DYNAMIC_BRANDING_READY } from './actionTypes'; +import { + SET_DYNAMIC_BRANDING_DATA, + SET_DYNAMIC_BRANDING_FAILED, + SET_DYNAMIC_BRANDING_READY +} from './actionTypes'; import { extractFqnFromPath } from './functions'; const logger = getLogger(__filename); @@ -32,6 +36,8 @@ export function fetchCustomBrandingData() { return dispatch(setDynamicBrandingData(res)); } catch (err) { logger.error('Error fetching branding data', err); + + return dispatch(setDynamicBrandingFailed()); } } @@ -63,3 +69,14 @@ function setDynamicBrandingReady() { type: SET_DYNAMIC_BRANDING_READY }; } + +/** + * Action used to signal the branding request failed. + * + * @returns {Object} + */ +function setDynamicBrandingFailed() { + return { + type: SET_DYNAMIC_BRANDING_FAILED + }; +} diff --git a/react/features/dynamic-branding/reducer.js b/react/features/dynamic-branding/reducer.js index 2ba4bc12b..a76e4aa06 100644 --- a/react/features/dynamic-branding/reducer.js +++ b/react/features/dynamic-branding/reducer.js @@ -2,7 +2,11 @@ import { ReducerRegistry } from '../base/redux'; -import { SET_DYNAMIC_BRANDING_DATA, SET_DYNAMIC_BRANDING_READY } from './actionTypes'; +import { + SET_DYNAMIC_BRANDING_DATA, + SET_DYNAMIC_BRANDING_FAILED, + SET_DYNAMIC_BRANDING_READY +} from './actionTypes'; /** * The name of the redux store/state property which is the root of the redux @@ -11,12 +15,80 @@ import { SET_DYNAMIC_BRANDING_DATA, SET_DYNAMIC_BRANDING_READY } from './actionT const STORE_NAME = 'features/dynamic-branding'; const DEFAULT_STATE = { + /** + * The custom background color for the LargeVideo. + * + * @public + * @type {string} + */ backgroundColor: '', + + /** + * The custom background image used on the LargeVideo. + * + * @public + * @type {string} + */ backgroundImageUrl: '', + + /** + * Flag indicating that the logo (JitsiWatermark) can be displayed. + * This is used in order to avoid image flickering. + * + * @public + * @type {boolean} + */ customizationReady: false, + + /** + * Flag indicating that the dynamic branding data request has failed. + * When the request fails there is no logo (JitsiWatermark) displayed. + * + * @public + * @type {boolean} + */ + customizationFailed: false, + + /** + * Flag indicating that the dynamic branding has not been modified and should use + * the default options. + * + * @public + * @type {boolean} + */ + defaultBranding: true, + + /** + * The custom invite domain. + * + * @public + * @type {string} + */ inviteDomain: '', + + /** + * The custom url used when the user clicks the logo. + * + * @public + * @type {string} + */ logoClickUrl: '', - logoImageUrl: '' + + /** + * The custom logo (JitisWatermark). + * + * @public + * @type {string} + */ + logoImageUrl: '', + + /** + * Flag used to signal if the app should use a custom logo or not + * + * @public + * @type {boolean} + */ + useDynamicBrandingData: false }; /** @@ -25,15 +97,33 @@ const DEFAULT_STATE = { ReducerRegistry.register(STORE_NAME, (state = DEFAULT_STATE, action) => { switch (action.type) { case SET_DYNAMIC_BRANDING_DATA: { - const { backgroundColor, backgroundImageUrl, inviteDomain, logoClickUrl, logoImageUrl } = action.value; + const { + backgroundColor, + backgroundImageUrl, + defaultBranding, + inviteDomain, + logoClickUrl, + logoImageUrl + } = action.value; return { backgroundColor, backgroundImageUrl, + defaultBranding, inviteDomain, logoClickUrl, logoImageUrl, - customizationReady: true + customizationFailed: false, + customizationReady: true, + useDynamicBrandingData: true + }; + } + case SET_DYNAMIC_BRANDING_FAILED: { + return { + ...state, + customizationReady: true, + customizationFailed: true, + useDynamicBrandingData: true }; } case SET_DYNAMIC_BRANDING_READY: @@ -41,7 +131,6 @@ ReducerRegistry.register(STORE_NAME, (state = DEFAULT_STATE, action) => { ...state, customizationReady: true }; - } return state;