From f7c0d4f1fec74d5af69e0d9ea2982c57680c26f3 Mon Sep 17 00:00:00 2001 From: hmuresan Date: Tue, 23 Mar 2021 14:11:11 +0200 Subject: [PATCH] feat(background alpha) Set background transparency --- config.js | 3 + modules/UI/UI.js | 8 +++ react/features/base/config/configWhitelist.js | 1 + react/features/base/util/helpers.js | 59 +++++++++++++++++++ .../conference/components/web/Conference.js | 41 ++++++++++++- .../large-video/components/LargeVideo.web.js | 13 ++++ 6 files changed, 124 insertions(+), 1 deletion(-) diff --git a/config.js b/config.js index f92b88d13c..253dd84aae 100644 --- a/config.js +++ b/config.js @@ -677,6 +677,9 @@ var config = { */ // dynamicBrandingUrl: '', + // Sets the background transparency level. '0' is fully transparent, '1' is opaque. + // backgroundAlpha: 1, + // The URL of the moderated rooms microservice, if available. If it // is present, a link to the service will be rendered on the welcome page, // otherwise the app doesn't render it. diff --git a/modules/UI/UI.js b/modules/UI/UI.js index 1c1dd98dc5..4125028ad4 100644 --- a/modules/UI/UI.js +++ b/modules/UI/UI.js @@ -7,6 +7,7 @@ import EventEmitter from 'events'; import Logger from 'jitsi-meet-logger'; import { isMobileBrowser } from '../../react/features/base/environment/utils'; +import { setColorAlpha } from '../../react/features/base/util'; import { toggleChat } from '../../react/features/chat'; import { setDocumentUrl } from '../../react/features/etherpad'; import { setFilmstripVisible } from '../../react/features/filmstrip'; @@ -129,6 +130,13 @@ UI.start = function() { $('body').addClass('mobile-browser'); } else { $('body').addClass('desktop-browser'); + + if (config.backgroundAlpha !== undefined) { + const backgroundColor = $('body').css('background-color'); + const alphaColor = setColorAlpha(backgroundColor, config.backgroundAlpha); + + $('body').css('background-color', alphaColor); + } } if (config.iAmRecorder) { diff --git a/react/features/base/config/configWhitelist.js b/react/features/base/config/configWhitelist.js index 3f6d2618a6..98a3a1baec 100644 --- a/react/features/base/config/configWhitelist.js +++ b/react/features/base/config/configWhitelist.js @@ -17,6 +17,7 @@ export default [ 'audioLevelsInterval', 'apiLogLevels', 'avgRtpStatsN', + 'backgroundAlpha', /** * The display name of the CallKit call representing the conference/meeting diff --git a/react/features/base/util/helpers.js b/react/features/base/util/helpers.js index aedb88ef3c..bac6a35295 100644 --- a/react/features/base/util/helpers.js +++ b/react/features/base/util/helpers.js @@ -126,3 +126,62 @@ export function reportError(e: Object, msg: string = '') { console.error(msg, e); window.onerror && window.onerror(msg, null, null, null, e); } + +/** + * Adds alpha to a color css string. + * + * @param {string} color - The color string either in rgb... Or #... Format. + * @param {number} opacity -The opacity(alpha) to apply to the color. Can take a value between 0 and 1, including. + * @returns {string} - The color with applied alpha. + */ +export function setColorAlpha(color: string, opacity: number) { + if (!color) { + return `rgba(0, 0, 0, ${opacity})`; + } + + let b, g, r; + + try { + if (color.startsWith('rgb')) { + [ r, g, b ] = color.split('(')[1].split(')')[0].split(',').map(c => c.trim()); + } else if (color.startsWith('#')) { + if (color.length === 4) { + [ r, g, b ] = parseShorthandColor(color); + } else { + r = parseInt(color.substring(1, 3), 16); + g = parseInt(color.substring(3, 5), 16); + b = parseInt(color.substring(5, 7), 16); + } + } else { + return color; + } + + return `rgba(${r}, ${g}, ${b}, ${opacity})`; + } catch { + return color; + } +} + +/** + * Gets the hexa rgb values for a shorthand css color. + * + * @param {string} color - + * @returns {Array} - Array containing parsed r, g, b values of the color. + */ +function parseShorthandColor(color) { + let b, g, r; + + r = color.substring(1, 2); + r += r; + r = parseInt(r, 16); + + g = color.substring(2, 3); + g += g; + g = parseInt(g, 16); + + b = color.substring(3, 4); + b += b; + b = parseInt(b, 16); + + return [ r, g, b ]; +} diff --git a/react/features/conference/components/web/Conference.js b/react/features/conference/components/web/Conference.js index d7b6f0d595..c01561fbbe 100644 --- a/react/features/conference/components/web/Conference.js +++ b/react/features/conference/components/web/Conference.js @@ -8,6 +8,7 @@ import { getConferenceNameForTitle } from '../../../base/conference'; import { connect, disconnect } from '../../../base/connection'; import { translate } from '../../../base/i18n'; import { connect as reactReduxConnect } from '../../../base/redux'; +import { setColorAlpha } from '../../../base/util'; import { Chat } from '../../../chat'; import { Filmstrip } from '../../../filmstrip'; import { CalleeInfoContainer } from '../../../invite'; @@ -61,6 +62,11 @@ const LAYOUT_CLASSNAMES = { */ type Props = AbstractProps & { + /** + * The alpha(opacity) of the background + */ + _backgroundAlpha: number, + /** * Whether the local participant is recording the conference. */ @@ -98,6 +104,7 @@ class Conference extends AbstractConference { _onFullScreenChange: Function; _onShowToolbar: Function; _originalOnShowToolbar: Function; + _setBackground: Function; /** * Initializes a new Conference instance. @@ -121,6 +128,7 @@ class Conference extends AbstractConference { // Bind event handler so it is only bound once for every instance. this._onFullScreenChange = this._onFullScreenChange.bind(this); + this._setBackground = this._setBackground.bind(this); } /** @@ -186,7 +194,8 @@ class Conference extends AbstractConference {
+ onMouseMove = { this._onShowToolbar } + ref = { this._setBackground }>
@@ -208,6 +217,35 @@ class Conference extends AbstractConference { ); } + /** + * Sets custom background opacity based on config. It also applies the + * opacity on parent element, as the parent element is not accessible directly, + * only though it's child. + * + * @param {Object} element - The DOM element for which to apply opacity. + * + * @private + * @returns {void} + */ + _setBackground(element) { + if (!element) { + return; + } + + if (this.props._backgroundAlpha !== undefined) { + const elemColor = element.style.background; + const alphaElemColor = setColorAlpha(elemColor, this.props._backgroundAlpha); + + element.style.background = alphaElemColor; + if (element.parentElement) { + const parentColor = element.parentElement.style.background; + const alphaParentColor = setColorAlpha(parentColor, this.props._backgroundAlpha); + + element.parentElement.style.background = alphaParentColor; + } + } + } + /** * Updates the Redux state when full screen mode has been enabled or * disabled. @@ -265,6 +303,7 @@ function _mapStateToProps(state) { return { ...abstractMapStateToProps(state), _iAmRecorder: state['features/base/config'].iAmRecorder, + _backgroundAlpha: state['features/base/config'].backgroundAlpha, _isLobbyScreenVisible: state['features/base/dialog']?.component === LobbyScreen, _layoutClassName: LAYOUT_CLASSNAMES[getCurrentLayout(state)], _roomName: getConferenceNameForTitle(state), diff --git a/react/features/large-video/components/LargeVideo.web.js b/react/features/large-video/components/LargeVideo.web.js index b54edec0a3..85d1d1d0e4 100644 --- a/react/features/large-video/components/LargeVideo.web.js +++ b/react/features/large-video/components/LargeVideo.web.js @@ -4,6 +4,7 @@ import React, { Component } from 'react'; import { Watermarks } from '../../base/react'; import { connect } from '../../base/redux'; +import { setColorAlpha } from '../../base/util'; import { Subject } from '../../conference'; import { fetchCustomBrandingData } from '../../dynamic-branding'; import { Captions } from '../../subtitles/'; @@ -12,6 +13,11 @@ declare var interfaceConfig: Object; type Props = { + /** + * The alpha(opacity) of the background + */ + _backgroundAlpha: number, + /** * The user selected background color. */ @@ -121,6 +127,12 @@ class LargeVideo extends Component { styles.backgroundColor = _customBackgroundColor || interfaceConfig.DEFAULT_BACKGROUND; + if (this.props._backgroundAlpha !== undefined) { + const alphaColor = setColorAlpha(styles.backgroundColor, this.props._backgroundAlpha); + + styles.backgroundColor = alphaColor; + } + if (_customBackgroundImageUrl) { styles.backgroundImage = `url(${_customBackgroundImageUrl})`; styles.backgroundSize = 'cover'; @@ -144,6 +156,7 @@ function _mapStateToProps(state) { const { isOpen: isChatOpen } = state['features/chat']; return { + _backgroundAlpha: state['features/base/config'].backgroundAlpha, _customBackgroundColor: backgroundColor, _customBackgroundImageUrl: backgroundImageUrl, _isChatOpen: isChatOpen,