fix(branding): Fix dynamic logo display

* Display of the logo has been reworked (simplified).
* The logo will not be displayed if the call to `branding` endpoint fails.
* Add more docs.
This commit is contained in:
Vlad Piersec 2020-09-16 13:44:49 +03:00 committed by vp8x8
parent ed6e75b241
commit d2ec0ea6f3
4 changed files with 174 additions and 136 deletions

View File

@ -24,35 +24,19 @@ const _RIGHT_WATERMARK_STYLE = {
type Props = { 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, _showJitsiWatermark: 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,
/** /**
* The default value for the Jitsi logo URL. * The default value for the Jitsi logo URL.
@ -75,27 +59,11 @@ type State = {
*/ */
brandWatermarkLink: string, brandWatermarkLink: string,
/**
* The url to open when clicking the Jitsi watermark.
*/
jitsiWatermarkLink: string,
/** /**
* Whether or not the brand watermark should be displayed. * Whether or not the brand watermark should be displayed.
*/ */
showBrandWatermark: boolean, 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. * Whether or not the show the "powered by Jitsi.org" link.
*/ */
@ -117,29 +85,17 @@ class Watermarks extends Component<Props, State> {
super(props); super(props);
let showBrandWatermark; let showBrandWatermark;
let showJitsiWatermark;
let showJitsiWatermarkForGuests;
if (interfaceConfig.filmStripOnly) { if (interfaceConfig.filmStripOnly) {
showBrandWatermark = false; showBrandWatermark = false;
showJitsiWatermark = false;
showJitsiWatermarkForGuests = false;
} else { } else {
showBrandWatermark = interfaceConfig.SHOW_BRAND_WATERMARK; showBrandWatermark = interfaceConfig.SHOW_BRAND_WATERMARK;
showJitsiWatermark = interfaceConfig.SHOW_JITSI_WATERMARK;
showJitsiWatermarkForGuests
= interfaceConfig.SHOW_WATERMARK_FOR_GUESTS;
} }
this.state = { this.state = {
brandWatermarkLink: brandWatermarkLink:
showBrandWatermark ? interfaceConfig.BRAND_WATERMARK_LINK : '', showBrandWatermark ? interfaceConfig.BRAND_WATERMARK_LINK : '',
jitsiWatermarkLink:
showJitsiWatermark || showJitsiWatermarkForGuests
? interfaceConfig.JITSI_WATERMARK_LINK : '',
showBrandWatermark, showBrandWatermark,
showJitsiWatermark,
showJitsiWatermarkForGuests,
showPoweredBy: interfaceConfig.SHOW_POWERED_BY showPoweredBy: interfaceConfig.SHOW_POWERED_BY
}; };
} }
@ -166,55 +122,6 @@ 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,
_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. * Renders a brand watermark if it is enabled.
* *
@ -254,33 +161,28 @@ class Watermarks extends Component<Props, State> {
* @returns {ReactElement|null} * @returns {ReactElement|null}
*/ */
_renderJitsiWatermark() { _renderJitsiWatermark() {
const {
_logoLink,
_logoUrl,
_showJitsiWatermark
} = this.props;
let reactElement = null; let reactElement = null;
if (this._canDisplayJitsiWatermark()) { if (_showJitsiWatermark) {
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, backgroundImage: `url(${_logoUrl})`,
maxWidth: 140, maxWidth: 140,
maxHeight: 70, maxHeight: 70
...additionalStyles
}; };
reactElement = (<div reactElement = (<div
className = 'watermark leftwatermark' className = 'watermark leftwatermark'
style = { style } />); style = { style } />);
if (link) { if (_logoLink) {
reactElement = ( reactElement = (
<a <a
href = { link } href = { _logoLink }
target = '_new'> target = '_new'>
{ reactElement } { reactElement }
</a> </a>
@ -319,27 +221,52 @@ 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.
* @param {Object} ownProps - Component's own props.
* @returns {Props} * @returns {Props}
*/ */
function _mapStateToProps(state) { function _mapStateToProps(state, ownProps) {
const { isGuest } = state['features/base/jwt']; const { isGuest } = state['features/base/jwt'];
const { customizationReady, logoClickUrl, logoImageUrl } = state['features/dynamic-branding']; const {
const { room } = state['features/base/conference']; 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 { return {
/** _logoLink,
* The indicator which determines whether the local participant is a _logoUrl,
* guest in the conference. _showJitsiWatermark
*
* @private
* @type {boolean}
*/
_customLogoLink: logoClickUrl,
_customLogoUrl: logoImageUrl,
_isGuest: isGuest,
_isVpaas: isVpaasMeeting(state),
_readyToDisplayJitsiWatermark: customizationReady,
_welcomePageIsVisible: !room
}; };
} }

View File

@ -3,6 +3,11 @@
*/ */
export const SET_DYNAMIC_BRANDING_DATA = 'SET_DYNAMIC_BRANDING_DATA'; 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 * Action used to signal the branding elements are ready to be displayed
*/ */

View File

@ -4,7 +4,11 @@ import { getLogger } from 'jitsi-meet-logger';
import { doGetJSON } from '../base/util'; 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'; import { extractFqnFromPath } from './functions';
const logger = getLogger(__filename); const logger = getLogger(__filename);
@ -32,6 +36,8 @@ export function fetchCustomBrandingData() {
return dispatch(setDynamicBrandingData(res)); return dispatch(setDynamicBrandingData(res));
} catch (err) { } catch (err) {
logger.error('Error fetching branding data', err); logger.error('Error fetching branding data', err);
return dispatch(setDynamicBrandingFailed());
} }
} }
@ -63,3 +69,14 @@ function setDynamicBrandingReady() {
type: SET_DYNAMIC_BRANDING_READY type: SET_DYNAMIC_BRANDING_READY
}; };
} }
/**
* Action used to signal the branding request failed.
*
* @returns {Object}
*/
function setDynamicBrandingFailed() {
return {
type: SET_DYNAMIC_BRANDING_FAILED
};
}

View File

@ -2,7 +2,11 @@
import { ReducerRegistry } from '../base/redux'; 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 * 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 STORE_NAME = 'features/dynamic-branding';
const DEFAULT_STATE = { const DEFAULT_STATE = {
/**
* The custom background color for the LargeVideo.
*
* @public
* @type {string}
*/
backgroundColor: '', backgroundColor: '',
/**
* The custom background image used on the LargeVideo.
*
* @public
* @type {string}
*/
backgroundImageUrl: '', backgroundImageUrl: '',
/**
* Flag indicating that the logo (JitsiWatermark) can be displayed.
* This is used in order to avoid image flickering.
*
* @public
* @type {boolean}
*/
customizationReady: false, 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: '', inviteDomain: '',
/**
* The custom url used when the user clicks the logo.
*
* @public
* @type {string}
*/
logoClickUrl: '', 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) => { ReducerRegistry.register(STORE_NAME, (state = DEFAULT_STATE, action) => {
switch (action.type) { switch (action.type) {
case SET_DYNAMIC_BRANDING_DATA: { case SET_DYNAMIC_BRANDING_DATA: {
const { backgroundColor, backgroundImageUrl, inviteDomain, logoClickUrl, logoImageUrl } = action.value; const {
backgroundColor,
backgroundImageUrl,
defaultBranding,
inviteDomain,
logoClickUrl,
logoImageUrl
} = action.value;
return { return {
backgroundColor, backgroundColor,
backgroundImageUrl, backgroundImageUrl,
defaultBranding,
inviteDomain, inviteDomain,
logoClickUrl, logoClickUrl,
logoImageUrl, 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: case SET_DYNAMIC_BRANDING_READY:
@ -41,7 +131,6 @@ ReducerRegistry.register(STORE_NAME, (state = DEFAULT_STATE, action) => {
...state, ...state,
customizationReady: true customizationReady: true
}; };
} }
return state; return state;