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 = {
/**
* 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<Props, State> {
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<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.
*
@ -254,33 +161,28 @@ class Watermarks extends Component<Props, State> {
* @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 = (<div
className = 'watermark leftwatermark'
style = { style } />);
if (link) {
if (_logoLink) {
reactElement = (
<a
href = { link }
href = { _logoLink }
target = '_new'>
{ reactElement }
</a>
@ -319,27 +221,52 @@ class Watermarks extends Component<Props, State> {
* 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
};
}

View File

@ -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
*/

View File

@ -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
};
}

View File

@ -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;