feat(branding): Add custom avatar backgrounds

This commit is contained in:
hmuresan 2021-07-20 14:37:22 +03:00 committed by Horatiu Muresan
parent c10805f81b
commit 2bac757ca6
7 changed files with 62 additions and 8 deletions

View File

@ -574,6 +574,12 @@ function initCommands() {
}); });
break; break;
} }
case 'get-custom-avatar-backgrounds' : {
callback({
avatarBackgrounds: APP.store.getState()['features/dynamic-branding'].avatarBackgrounds
});
break;
}
default: default:
return false; return false;
} }

View File

@ -769,6 +769,17 @@ export default class JitsiMeetExternalAPI extends EventEmitter {
return getCurrentDevices(this._transport); return getCurrentDevices(this._transport);
} }
/**
* Returns any custom avatars backgrounds.
*
* @returns {Promise} - Resolves with the list of custom avatar backgrounds.
*/
getCustomAvatarBackgrounds() {
return this._transport.sendRequest({
name: 'get-custom-avatar-backgrounds'
});
}
/** /**
* Returns the current livestream url. * Returns the current livestream url.
* *

View File

@ -21,6 +21,7 @@ const TOOLBAR_TIMEOUT = 4000;
*/ */
type State = { type State = {
avatarURL: string, avatarURL: string,
customAvatarBackgrounds: Array<string>,
displayName: string, displayName: string,
formattedDisplayName: string, formattedDisplayName: string,
isVideoDisplayed: boolean, isVideoDisplayed: boolean,
@ -48,6 +49,7 @@ export default class AlwaysOnTop extends Component<*, State> {
this.state = { this.state = {
avatarURL: '', avatarURL: '',
customAvatarBackgrounds: [],
displayName: '', displayName: '',
formattedDisplayName: '', formattedDisplayName: '',
isVideoDisplayed: true, isVideoDisplayed: true,
@ -178,7 +180,14 @@ export default class AlwaysOnTop extends Component<*, State> {
* @returns {ReactElement} * @returns {ReactElement}
*/ */
_renderVideoNotAvailableScreen() { _renderVideoNotAvailableScreen() {
const { avatarURL, displayName, formattedDisplayName, isVideoDisplayed, userID } = this.state; const {
avatarURL,
customAvatarBackgrounds,
displayName,
formattedDisplayName,
isVideoDisplayed,
userID
} = this.state;
if (isVideoDisplayed) { if (isVideoDisplayed) {
return null; return null;
@ -188,7 +197,7 @@ export default class AlwaysOnTop extends Component<*, State> {
<div id = 'videoNotAvailableScreen'> <div id = 'videoNotAvailableScreen'>
<div id = 'avatarContainer'> <div id = 'avatarContainer'>
<StatelessAvatar <StatelessAvatar
color = { getAvatarColor(userID) } color = { getAvatarColor(userID, customAvatarBackgrounds) }
id = 'avatar' id = 'avatar'
initials = { getInitials(displayName) } initials = { getInitials(displayName) }
url = { avatarURL } />) url = { avatarURL } />)
@ -218,6 +227,12 @@ export default class AlwaysOnTop extends Component<*, State> {
window.addEventListener('mousemove', this._mouseMove); window.addEventListener('mousemove', this._mouseMove);
this._hideToolbarAfterTimeout(); this._hideToolbarAfterTimeout();
api.getCustomAvatarBackgrounds()
.then(res =>
this.setState({
customAvatarBackgrounds: res.avatarBackgrounds || []
}))
.catch(console.error);
} }
/** /**

View File

@ -10,6 +10,11 @@ import { StatelessAvatar } from '.';
export type Props = { export type Props = {
/**
* Custom avatar backgrounds from branding.
*/
_customAvatarBackgrounds: Array<string>,
/** /**
* The string we base the initials on (this is generated from a list of precedences). * The string we base the initials on (this is generated from a list of precedences).
*/ */
@ -133,6 +138,7 @@ class Avatar<P: Props> extends PureComponent<P, State> {
*/ */
render() { render() {
const { const {
_customAvatarBackgrounds,
_initialsBase, _initialsBase,
_loadableAvatarUrl, _loadableAvatarUrl,
className, className,
@ -172,7 +178,7 @@ class Avatar<P: Props> extends PureComponent<P, State> {
if (initials) { if (initials) {
if (dynamicColor) { if (dynamicColor) {
avatarProps.color = getAvatarColor(colorBase || _initialsBase); avatarProps.color = getAvatarColor(colorBase || _initialsBase, _customAvatarBackgrounds);
} }
avatarProps.initials = initials; avatarProps.initials = initials;
@ -211,6 +217,7 @@ export function _mapStateToProps(state: Object, ownProps: Props) {
const _initialsBase = _participant?.name ?? displayName; const _initialsBase = _participant?.name ?? displayName;
return { return {
_customAvatarBackgrounds: state['features/dynamic-branding'].avatarBackgrounds,
_initialsBase, _initialsBase,
_loadableAvatarUrl: _participant?.loadableAvatarUrl, _loadableAvatarUrl: _participant?.loadableAvatarUrl,
colorBase: !colorBase && _participant ? _participant.id : colorBase colorBase: !colorBase && _participant ? _participant.id : colorBase

View File

@ -125,7 +125,7 @@ export default class StatelessAvatar extends AbstractStatelessAvatar<Props> {
const { size } = this.props; const { size } = this.props;
return { return {
backgroundColor: color || undefined, background: color || undefined,
fontSize: size ? size * 0.5 : '180%', fontSize: size ? size * 0.5 : '180%',
height: size || '100%', height: size || '100%',
width: size || '100%' width: size || '100%'

View File

@ -16,9 +16,13 @@ const AVATAR_OPACITY = 0.4;
* Generates the background color of an initials based avatar. * Generates the background color of an initials based avatar.
* *
* @param {string?} initials - The initials of the avatar. * @param {string?} initials - The initials of the avatar.
* @param {Array<strig>} customAvatarBackgrounds - Custom avatar background values.
* @returns {string} * @returns {string}
*/ */
export function getAvatarColor(initials: ?string) { export function getAvatarColor(initials: ?string, customAvatarBackgrounds: Array<string>) {
const hasCustomAvatarBackgronds = customAvatarBackgrounds && customAvatarBackgrounds.length;
const colorsBase = hasCustomAvatarBackgronds ? customAvatarBackgrounds : AVATAR_COLORS;
let colorIndex = 0; let colorIndex = 0;
if (initials) { if (initials) {
@ -28,10 +32,10 @@ export function getAvatarColor(initials: ?string) {
nameHash += s.codePointAt(0); nameHash += s.codePointAt(0);
} }
colorIndex = nameHash % AVATAR_COLORS.length; colorIndex = nameHash % colorsBase.length;
} }
return `rgba(${AVATAR_COLORS[colorIndex]}, ${AVATAR_OPACITY})`; return hasCustomAvatarBackgronds ? colorsBase[colorIndex] : `rgba(${colorsBase[colorIndex]}, ${AVATAR_OPACITY})`;
} }
/** /**

View File

@ -15,6 +15,15 @@ import {
const STORE_NAME = 'features/dynamic-branding'; const STORE_NAME = 'features/dynamic-branding';
const DEFAULT_STATE = { const DEFAULT_STATE = {
/**
* The pool of avatar backgrounds.
*
* @public
* @type {Array<string>}
*/
avatarBackgrounds: [],
/** /**
* The custom background color for the LargeVideo. * The custom background color for the LargeVideo.
* *
@ -112,10 +121,12 @@ ReducerRegistry.register(STORE_NAME, (state = DEFAULT_STATE, action) => {
didPageUrl, didPageUrl,
inviteDomain, inviteDomain,
logoClickUrl, logoClickUrl,
logoImageUrl logoImageUrl,
avatarBackgrounds
} = action.value; } = action.value;
return { return {
avatarBackgrounds,
backgroundColor, backgroundColor,
backgroundImageUrl, backgroundImageUrl,
defaultBranding, defaultBranding,