* 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
226 lines
7.4 KiB
JavaScript
226 lines
7.4 KiB
JavaScript
// @flow
|
|
import md5 from 'js-md5';
|
|
|
|
import { toState } from '../redux';
|
|
|
|
import {
|
|
DEFAULT_AVATAR_RELATIVE_PATH,
|
|
LOCAL_PARTICIPANT_DEFAULT_ID
|
|
} from './constants';
|
|
|
|
declare var config: Object;
|
|
declare var interfaceConfig: Object;
|
|
|
|
/**
|
|
* Returns the URL of the image for the avatar of a specific participant.
|
|
*
|
|
* @param {Participant} [participant] - The participant to return the avatar URL
|
|
* of.
|
|
* @param {string} [participant.avatarID] - Participant's avatar ID.
|
|
* @param {string} [participant.avatarURL] - Participant's avatar URL.
|
|
* @param {string} [participant.email] - Participant's e-mail address.
|
|
* @param {string} [participant.id] - Participant's ID.
|
|
* @public
|
|
* @returns {string} The URL of the image for the avatar of the specified
|
|
* participant.
|
|
*/
|
|
export function getAvatarURL({ avatarID, avatarURL, email, id }: {
|
|
avatarID: string,
|
|
avatarURL: string,
|
|
email: string,
|
|
id: string
|
|
}) {
|
|
// If disableThirdPartyRequests disables third-party avatar services, we are
|
|
// restricted to a stock image of ours.
|
|
if (typeof config === 'object' && config.disableThirdPartyRequests) {
|
|
return DEFAULT_AVATAR_RELATIVE_PATH;
|
|
}
|
|
|
|
// If an avatarURL is specified, then obviously there's nothing to generate.
|
|
if (avatarURL) {
|
|
return avatarURL;
|
|
}
|
|
|
|
let key = email || avatarID;
|
|
let urlPrefix;
|
|
let urlSuffix;
|
|
|
|
// If the ID looks like an e-mail address, we'll use Gravatar because it
|
|
// supports e-mail addresses.
|
|
if (key && key.indexOf('@') > 0) {
|
|
urlPrefix = 'https://www.gravatar.com/avatar/';
|
|
urlSuffix = '?d=wavatar&size=200';
|
|
} else {
|
|
// Otherwise, we do not have much a choice but a random avatar (fetched
|
|
// from a configured avatar service).
|
|
if (!key) {
|
|
key = id;
|
|
if (!key) {
|
|
return undefined;
|
|
}
|
|
}
|
|
|
|
// The deployment is allowed to choose the avatar service which is to
|
|
// generate the random avatars.
|
|
urlPrefix
|
|
= typeof interfaceConfig === 'object'
|
|
&& interfaceConfig.RANDOM_AVATAR_URL_PREFIX;
|
|
if (urlPrefix) {
|
|
urlSuffix = interfaceConfig.RANDOM_AVATAR_URL_SUFFIX;
|
|
} else {
|
|
// Otherwise, use a default (meeples, of course).
|
|
urlPrefix = 'https://abotars.jitsi.net/meeple/';
|
|
urlSuffix = '';
|
|
}
|
|
}
|
|
|
|
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.
|
|
*
|
|
* @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.
|
|
* @returns {(Participant|undefined)}
|
|
*/
|
|
export function getLocalParticipant(stateful: Object | Function) {
|
|
const participants = _getAllParticipants(stateful);
|
|
|
|
return participants.find(p => p.local);
|
|
}
|
|
|
|
/**
|
|
* Returns participant by ID from Redux state.
|
|
*
|
|
* @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.
|
|
* @private
|
|
* @returns {(Participant|undefined)}
|
|
*/
|
|
export function getParticipantById(
|
|
stateful: Object | Function,
|
|
id: string) {
|
|
const participants = _getAllParticipants(stateful);
|
|
|
|
return participants.find(p => p.id === id);
|
|
}
|
|
|
|
/**
|
|
* Returns a count of the known participants in the passed in redux state,
|
|
* excluding any fake participants.
|
|
*
|
|
* @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.
|
|
* @returns {number}
|
|
*/
|
|
export function getParticipantCount(stateful: Object | Function) {
|
|
return getParticipants(stateful).length;
|
|
}
|
|
|
|
/**
|
|
* Returns participant's display name.
|
|
* FIXME: remove the hardcoded strings once interfaceConfig is stored in redux
|
|
* and merge with a similarly named method in conference.js.
|
|
*
|
|
* @param {(Function|Object)} stateful - The (whole) redux state, or redux's
|
|
* {@code getState} function to be used to retrieve the state.
|
|
* @param {string} id - The ID of the participant's display name to retrieve.
|
|
* @private
|
|
* @returns {string}
|
|
*/
|
|
export function getParticipantDisplayName(
|
|
stateful: Object | Function, id: string) {
|
|
const participant = getParticipantById(stateful, id);
|
|
|
|
if (participant) {
|
|
if (participant.name) {
|
|
return participant.name;
|
|
}
|
|
|
|
if (participant.local) {
|
|
return typeof interfaceConfig === 'object'
|
|
? interfaceConfig.DEFAULT_LOCAL_DISPLAY_NAME
|
|
: 'me';
|
|
}
|
|
}
|
|
|
|
return typeof interfaceConfig === 'object'
|
|
? interfaceConfig.DEFAULT_REMOTE_DISPLAY_NAME
|
|
: 'Fellow Jitster';
|
|
}
|
|
|
|
/**
|
|
* Selectors for getting all known participants with fake participants filtered
|
|
* out.
|
|
*
|
|
* @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.
|
|
* @returns {Participant[]}
|
|
*/
|
|
export function getParticipants(stateful: Object | Function) {
|
|
return _getAllParticipants(stateful).filter(p => !p.isBot);
|
|
}
|
|
|
|
/**
|
|
* Returns the participant which has its pinned state set to truthy.
|
|
*
|
|
* @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.
|
|
* @returns {(Participant|undefined)}
|
|
*/
|
|
export function getPinnedParticipant(stateful: Object | Function) {
|
|
return _getAllParticipants(stateful).find(p => p.pinned);
|
|
}
|
|
|
|
/**
|
|
* Returns array of participants from Redux state.
|
|
*
|
|
* @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.
|
|
* @private
|
|
* @returns {Participant[]}
|
|
*/
|
|
function _getAllParticipants(stateful) {
|
|
return (
|
|
Array.isArray(stateful)
|
|
? stateful
|
|
: toState(stateful)['features/base/participants'] || []);
|
|
}
|