ref(avatars): remove Avatar.js (#2289)
* ref(avatars): remove Avatar.js - Rely on redux getting updated with new participant state and any calls to getAvatarURL passing in the redux participant state. This way the state within Avatar.js can be removed. - Clean up methods on UI.js. Because all state is in the store, separate methods for updating the avatar aren't as necessary. Instead centralize accessing of the avatar for components outside of redux and centralize the call to update avatars for non-react components. - Controversial: cache a participant's avatarURL on the participant state. Currently the participant's avatarURL that is generated without jwt (which sets the avatarURL directly) is not cached. Without cache, there can be many redundant calls to APP.API.notifyAvatarChanged. * Leverage middleware timing to diff avatars One alternative implementation is to leverage middleware's ability to intercept updates before and after redux has upated and then compare avatarURLs. * kill UI.getAvatarUrl * profile button sets its own avatar url (solves update timing) * remove calls to updating avatar outside of middleware * update UI.js doc * remove left over logic from initial implementation * try to move local user fallback into selector func * default to id 'local' in selector
This commit is contained in:
@@ -3,7 +3,10 @@ import md5 from 'js-md5';
|
||||
|
||||
import { toState } from '../redux';
|
||||
|
||||
import { DEFAULT_AVATAR_RELATIVE_PATH } from './constants';
|
||||
import {
|
||||
DEFAULT_AVATAR_RELATIVE_PATH,
|
||||
LOCAL_PARTICIPANT_DEFAULT_ID
|
||||
} from './constants';
|
||||
|
||||
declare var config: Object;
|
||||
declare var interfaceConfig: Object;
|
||||
@@ -74,6 +77,29 @@ export function getAvatarURL({ avatarID, avatarURL, email, id }: {
|
||||
return urlPrefix + md5.hex(key.trim().toLowerCase()) + urlSuffix;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the avatarURL for the participant associated with the passed in
|
||||
* participant ID.
|
||||
*
|
||||
* @param {(Function|Object|Participant[])} stateful - The redux state
|
||||
* features/base/participants, the (whole) redux state, or redux's
|
||||
* {@code getState} function to be used to retrieve the state
|
||||
* features/base/participants.
|
||||
* @param {string} id - The ID of the participant to retrieve.
|
||||
* @param {boolean} isLocal - An optional parameter indicating whether or not
|
||||
* the partcipant id is for the local user. If true, a different logic flow is
|
||||
* used find the local user, ignoring the id value as it can change through the
|
||||
* beginning and end of a call.
|
||||
* @returns {(string|undefined)}
|
||||
*/
|
||||
export function getAvatarURLByParticipantId(
|
||||
stateful: Object | Function,
|
||||
id: string = LOCAL_PARTICIPANT_DEFAULT_ID) {
|
||||
const participant = getParticipantById(stateful, id);
|
||||
|
||||
return participant && getAvatarURL(participant);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns local participant from Redux state.
|
||||
*
|
||||
|
||||
@@ -12,10 +12,15 @@ import { localParticipantIdChanged } from './actions';
|
||||
import {
|
||||
KICK_PARTICIPANT,
|
||||
MUTE_REMOTE_PARTICIPANT,
|
||||
PARTICIPANT_DISPLAY_NAME_CHANGED
|
||||
PARTICIPANT_DISPLAY_NAME_CHANGED,
|
||||
PARTICIPANT_JOINED,
|
||||
PARTICIPANT_UPDATED
|
||||
} from './actionTypes';
|
||||
import { LOCAL_PARTICIPANT_DEFAULT_ID } from './constants';
|
||||
import { getLocalParticipant } from './functions';
|
||||
import {
|
||||
getAvatarURLByParticipantId,
|
||||
getLocalParticipant
|
||||
} from './functions';
|
||||
|
||||
declare var APP: Object;
|
||||
|
||||
@@ -59,6 +64,38 @@ MiddlewareRegistry.register(store => next => action => {
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case PARTICIPANT_JOINED:
|
||||
case PARTICIPANT_UPDATED: {
|
||||
if (typeof APP !== 'undefined') {
|
||||
const participant = action.participant;
|
||||
const { id, local } = participant;
|
||||
|
||||
const preUpdateAvatarURL
|
||||
= getAvatarURLByParticipantId(store.getState(), id);
|
||||
|
||||
// Allow the redux update to go through and compare the old avatar
|
||||
// to the new avatar and emit out change events if necessary.
|
||||
const result = next(action);
|
||||
|
||||
const postUpdateAvatarURL
|
||||
= getAvatarURLByParticipantId(store.getState(), id);
|
||||
|
||||
if (preUpdateAvatarURL !== postUpdateAvatarURL) {
|
||||
const currentKnownId = local
|
||||
? APP.conference.getMyUserId() : id;
|
||||
|
||||
APP.UI.refreshAvatarDisplay(
|
||||
currentKnownId, postUpdateAvatarURL);
|
||||
APP.API.notifyAvatarChanged(
|
||||
currentKnownId, postUpdateAvatarURL);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return next(action);
|
||||
|
||||
@@ -5,7 +5,10 @@ import React, { Component } from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
|
||||
import { TOOLBAR_PROFILE_TOGGLED, sendAnalyticsEvent } from '../../analytics';
|
||||
import { DEFAULT_AVATAR_RELATIVE_PATH } from '../../base/participants';
|
||||
import {
|
||||
getAvatarURL,
|
||||
getLocalParticipant
|
||||
} from '../../base/participants';
|
||||
import UIEvents from '../../../../service/UI/UIEvents';
|
||||
|
||||
import ToolbarButton from './ToolbarButton';
|
||||
@@ -39,6 +42,11 @@ class ProfileButton extends Component<*> {
|
||||
* @static
|
||||
*/
|
||||
static propTypes = {
|
||||
/**
|
||||
* The redux representation of the local participant.
|
||||
*/
|
||||
_localParticipant: PropTypes.object,
|
||||
|
||||
/**
|
||||
* Whether the button support clicking or not.
|
||||
*/
|
||||
@@ -76,7 +84,12 @@ class ProfileButton extends Component<*> {
|
||||
* @returns {ReactElement}
|
||||
*/
|
||||
render() {
|
||||
const { _unclickable, tooltipPosition, toggled } = this.props;
|
||||
const {
|
||||
_localParticipant,
|
||||
_unclickable,
|
||||
tooltipPosition,
|
||||
toggled
|
||||
} = this.props;
|
||||
const buttonConfiguration = {
|
||||
...DEFAULT_BUTTON_CONFIGURATION,
|
||||
unclickable: _unclickable,
|
||||
@@ -90,7 +103,7 @@ class ProfileButton extends Component<*> {
|
||||
tooltipPosition = { tooltipPosition }>
|
||||
<img
|
||||
id = 'avatar'
|
||||
src = { DEFAULT_AVATAR_RELATIVE_PATH } />
|
||||
src = { getAvatarURL(_localParticipant) } />
|
||||
</ToolbarButton>
|
||||
);
|
||||
}
|
||||
@@ -115,11 +128,13 @@ class ProfileButton extends Component<*> {
|
||||
* @param {Object} state - The Redux state.
|
||||
* @private
|
||||
* @returns {{
|
||||
* _localParticipant: Object,
|
||||
* _unclickable: boolean
|
||||
* }}
|
||||
*/
|
||||
function _mapStateToProps(state) {
|
||||
return {
|
||||
_localParticipant: getLocalParticipant(state),
|
||||
_unclickable: !state['features/base/jwt'].isGuest
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user