diff --git a/src/components/AvatarWithIndicator.js b/src/components/AvatarWithIndicator.js
index e7246d721535..1835e739c25b 100644
--- a/src/components/AvatarWithIndicator.js
+++ b/src/components/AvatarWithIndicator.js
@@ -16,7 +16,6 @@ import walletTermsPropTypes from '../pages/EnablePayments/walletTermsPropTypes';
import * as PolicyUtils from '../libs/PolicyUtils';
import * as PaymentMethods from '../libs/actions/PaymentMethods';
import * as ReimbursementAccountProps from '../pages/ReimbursementAccount/reimbursementAccountPropTypes';
-import * as ReportUtils from '../libs/ReportUtils';
import * as UserUtils from '../libs/UserUtils';
import themeColors from '../styles/themes/default';
@@ -103,7 +102,7 @@ const AvatarWithIndicator = (props) => {
return (
-
+
{(shouldShowErrorIndicator || shouldShowInfoIndicator) && }
diff --git a/src/components/MoneyRequestHeader.js b/src/components/MoneyRequestHeader.js
index bdaeb2cd076e..8bf719e679d7 100644
--- a/src/components/MoneyRequestHeader.js
+++ b/src/components/MoneyRequestHeader.js
@@ -27,6 +27,7 @@ import * as CurrencyUtils from '../libs/CurrencyUtils';
import MenuItemWithTopDescription from './MenuItemWithTopDescription';
import DateUtils from '../libs/DateUtils';
import reportPropTypes from '../pages/reportPropTypes';
+import * as UserUtils from '../libs/UserUtils';
const propTypes = {
/** The report currently being looked at */
@@ -82,7 +83,7 @@ const MoneyRequestHeader = (props) => {
const payeeName = isExpenseReport ? ReportUtils.getPolicyName(moneyRequestReport, props.policies) : ReportUtils.getDisplayNameForParticipant(moneyRequestReport.managerEmail);
const payeeAvatar = isExpenseReport
? ReportUtils.getWorkspaceAvatar(moneyRequestReport)
- : ReportUtils.getAvatar(lodashGet(props.personalDetails, [moneyRequestReport.managerEmail, 'avatar']), moneyRequestReport.managerEmail);
+ : UserUtils.getAvatar(lodashGet(props.personalDetails, [moneyRequestReport.managerEmail, 'avatar']), moneyRequestReport.managerEmail);
const policy = props.policies[`${ONYXKEYS.COLLECTION.POLICY}${props.report.policyID}`];
const isPayer =
Policy.isAdminOfFreePolicy([policy]) || (ReportUtils.isMoneyRequestReport(moneyRequestReport) && lodashGet(props.session, 'email', null) === moneyRequestReport.managerEmail);
diff --git a/src/components/TaskHeader.js b/src/components/TaskHeader.js
index 4b21d5a2ebe5..73b7afca0957 100644
--- a/src/components/TaskHeader.js
+++ b/src/components/TaskHeader.js
@@ -20,6 +20,7 @@ import Icon from './Icon';
import MenuItemWithTopDescription from './MenuItemWithTopDescription';
import Button from './Button';
import * as TaskUtils from '../libs/actions/Task';
+import * as UserUtils from '../libs/UserUtils';
const propTypes = {
/** The report currently being looked at */
@@ -34,7 +35,7 @@ const propTypes = {
function TaskHeader(props) {
const title = ReportUtils.getReportName(props.report);
const assigneeName = ReportUtils.getDisplayNameForParticipant(props.report.managerEmail);
- const assigneeAvatar = ReportUtils.getAvatar(lodashGet(props.personalDetails, [props.report.managerEmail, 'avatar']), props.report.managerEmail);
+ const assigneeAvatar = UserUtils.getAvatar(lodashGet(props.personalDetails, [props.report.managerEmail, 'avatar']), props.report.managerEmail);
const isOpen = props.report.stateNum === CONST.REPORT.STATE_NUM.OPEN && props.report.statusNum === CONST.REPORT.STATUS.OPEN;
const isCompleted = props.report.stateNum === CONST.REPORT.STATE_NUM.SUBMITTED && props.report.statusNum === CONST.REPORT.STATUS.APPROVED;
const parentReportID = props.report.parentReportID;
diff --git a/src/libs/OptionsListUtils.js b/src/libs/OptionsListUtils.js
index d2a0aa0aace4..27a70e59ee5e 100644
--- a/src/libs/OptionsListUtils.js
+++ b/src/libs/OptionsListUtils.js
@@ -14,6 +14,7 @@ import * as CollectionUtils from './CollectionUtils';
import Navigation from './Navigation/Navigation';
import * as LoginUtils from './LoginUtils';
import * as LocalePhoneNumber from './LocalePhoneNumber';
+import * as UserUtils from './UserUtils';
/**
* OptionsListUtils is used to build a list options passed to the OptionsList component. Several different UI views can
@@ -152,7 +153,7 @@ function getAvatarsForLogins(logins, personalDetails) {
return _.map(logins, (login) => {
const userPersonalDetail = lodashGet(personalDetails, login, {login, avatar: ''});
return {
- source: ReportUtils.getAvatar(userPersonalDetail.avatar, userPersonalDetail.login),
+ source: UserUtils.getAvatar(userPersonalDetail.avatar, userPersonalDetail.login),
type: CONST.ICON_TYPE_AVATAR,
name: userPersonalDetail.login,
};
@@ -181,7 +182,7 @@ function getPersonalDetailsForLogins(logins, personalDetails) {
personalDetail = {
login,
displayName: LocalePhoneNumber.formatPhoneNumber(login),
- avatar: ReportUtils.getDefaultAvatar(login),
+ avatar: UserUtils.getDefaultAvatar(login),
};
}
@@ -220,7 +221,7 @@ function getParticipantsOptions(report, personalDetails) {
alternateText: Str.isSMSLogin(details.login || '') ? LocalePhoneNumber.formatPhoneNumber(details.login) : details.login,
icons: [
{
- source: ReportUtils.getAvatar(details.avatar, details.login),
+ source: UserUtils.getAvatar(details.avatar, details.login),
name: details.login,
type: CONST.ICON_TYPE_AVATAR,
},
@@ -482,7 +483,7 @@ function createOption(logins, personalDetails, report, reportActions = {}, {show
result.text = reportName;
result.searchText = getSearchText(report, reportName, personalDetailList, result.isChatRoom || result.isPolicyExpenseChat, result.isThread);
- result.icons = ReportUtils.getIcons(report, personalDetails, ReportUtils.getAvatar(personalDetail.avatar, personalDetail.login));
+ result.icons = ReportUtils.getIcons(report, personalDetails, UserUtils.getAvatar(personalDetail.avatar, personalDetail.login));
result.subtitle = subtitle;
return result;
@@ -727,7 +728,7 @@ function getOptions(
// If user doesn't exist, use a default avatar
userToInvite.icons = [
{
- source: ReportUtils.getAvatar('', searchValue),
+ source: UserUtils.getAvatar('', searchValue),
name: searchValue,
type: CONST.ICON_TYPE_AVATAR,
},
@@ -806,7 +807,7 @@ function getIOUConfirmationOptionsFromPayeePersonalDetail(personalDetail, amount
alternateText: personalDetail.login,
icons: [
{
- source: ReportUtils.getAvatar(personalDetail.avatar, personalDetail.login),
+ source: UserUtils.getAvatar(personalDetail.avatar, personalDetail.login),
name: personalDetail.login,
type: CONST.ICON_TYPE_AVATAR,
},
diff --git a/src/libs/ReportUtils.js b/src/libs/ReportUtils.js
index fc229887fb75..1b9d17029b47 100644
--- a/src/libs/ReportUtils.js
+++ b/src/libs/ReportUtils.js
@@ -8,7 +8,6 @@ import ONYXKEYS from '../ONYXKEYS';
import CONST from '../CONST';
import * as Localize from './Localize';
import * as Expensicons from '../components/Icon/Expensicons';
-import hashCode from './hashCode';
import Navigation from './Navigation/Navigation';
import ROUTES from '../ROUTES';
import * as NumberUtils from './NumberUtils';
@@ -17,11 +16,11 @@ import * as ReportActionsUtils from './ReportActionsUtils';
import Permissions from './Permissions';
import DateUtils from './DateUtils';
import linkingConfig from './Navigation/linkingConfig';
-import * as defaultAvatars from '../components/Icon/DefaultAvatars';
import isReportMessageAttachment from './isReportMessageAttachment';
import * as defaultWorkspaceAvatars from '../components/Icon/WorkspaceDefaultAvatars';
import * as LocalePhoneNumber from './LocalePhoneNumber';
import * as CurrencyUtils from './CurrencyUtils';
+import * as UserUtils from './UserUtils';
let sessionEmail;
Onyx.connect({
@@ -644,36 +643,6 @@ function formatReportLastMessageText(lastMessageText) {
return Str.htmlDecode(String(lastMessageText)).replace(CONST.REGEX.AFTER_FIRST_LINE_BREAK, '').substring(0, CONST.REPORT.LAST_MESSAGE_TEXT_MAX_LENGTH).trim();
}
-/**
- * Hashes provided string and returns a value between [0, range)
- * @param {String} login
- * @param {Number} range
- * @returns {Number}
- */
-function hashLogin(login, range) {
- return Math.abs(hashCode(login.toLowerCase())) % range;
-}
-
-/**
- * Helper method to return the default avatar associated with the given login
- * @param {String} [login]
- * @returns {String}
- */
-function getDefaultAvatar(login = '') {
- if (!login) {
- return Expensicons.FallbackAvatar;
- }
- if (login === CONST.EMAIL.CONCIERGE) {
- return Expensicons.ConciergeAvatar;
- }
-
- // There are 24 possible default avatars, so we choose which one this user has based
- // on a simple hash of their login. Note that Avatar count starts at 1.
- const loginHashBucket = hashLogin(login, CONST.DEFAULT_AVATAR_COUNT) + 1;
-
- return defaultAvatars[`Avatar${loginHashBucket}`];
-}
-
/**
* Helper method to return the default avatar associated with the given login
* @param {String} [workspaceName]
@@ -698,102 +667,6 @@ function getWorkspaceAvatar(report) {
return lodashGet(allPolicies, [`${ONYXKEYS.COLLECTION.POLICY}${report.policyID}`, 'avatar']) || getDefaultWorkspaceAvatar(workspaceName);
}
-/**
- * Helper method to return old dot default avatar associated with login
- *
- * @param {String} [login]
- * @returns {String}
- */
-function getOldDotDefaultAvatar(login = '') {
- if (login === CONST.EMAIL.CONCIERGE) {
- return CONST.CONCIERGE_ICON_URL;
- }
-
- // There are 8 possible old dot default avatars, so we choose which one this user has based
- // on a simple hash of their login. Note that Avatar count starts at 1.
- const loginHashBucket = hashLogin(login, CONST.OLD_DEFAULT_AVATAR_COUNT) + 1;
-
- return `${CONST.CLOUDFRONT_URL}/images/avatars/avatar_${loginHashBucket}.png`;
-}
-
-/**
- * Given a user's avatar path, returns true if user doesn't have an avatar or if URL points to a default avatar
- * @param {String} [avatarURL] - the avatar source from user's personalDetails
- * @returns {Boolean}
- */
-function isDefaultAvatar(avatarURL) {
- if (
- _.isString(avatarURL) &&
- (avatarURL.includes('images/avatars/avatar_') || avatarURL.includes('images/avatars/default-avatar_') || avatarURL.includes('images/avatars/user/default'))
- ) {
- return true;
- }
-
- // If null URL, we should also use a default avatar
- if (!avatarURL) {
- return true;
- }
- return false;
-}
-
-/**
- * Provided a source URL, if source is a default avatar, return the associated SVG.
- * Otherwise, return the URL pointing to a user-uploaded avatar.
- *
- * @param {String} [avatarURL] - the avatar source from user's personalDetails
- * @param {String} [login] - the email of the user
- * @returns {String|Function}
- */
-function getAvatar(avatarURL, login) {
- if (isDefaultAvatar(avatarURL)) {
- return getDefaultAvatar(login);
- }
- return avatarURL;
-}
-
-/**
- * Avatars uploaded by users will have a _128 appended so that the asset server returns a small version.
- * This removes that part of the URL so the full version of the image can load.
- *
- * @param {String} [avatarURL]
- * @param {String} [login]
- * @returns {String|Function}
- */
-function getFullSizeAvatar(avatarURL, login) {
- const source = getAvatar(avatarURL, login);
- if (!_.isString(source)) {
- return source;
- }
- return source.replace('_128', '');
-}
-
-/**
- * Small sized avatars end with _128.. This adds the _128 at the end of the
- * source URL (before the file type) if it doesn't exist there already.
- *
- * @param {String} avatarURL
- * @param {String} login
- * @returns {String|Function}
- */
-function getSmallSizeAvatar(avatarURL, login) {
- const source = getAvatar(avatarURL, login);
- if (!_.isString(source)) {
- return source;
- }
-
- // Because other urls than CloudFront do not support dynamic image sizing (_SIZE suffix), the current source is already what we want to use here.
- if (!CONST.CLOUDFRONT_DOMAIN_REGEX.test(source)) {
- return source;
- }
-
- // If image source already has _128 at the end, the given avatar URL is already what we want to use here.
- const lastPeriodIndex = source.lastIndexOf('.');
- if (source.substring(lastPeriodIndex - 4, lastPeriodIndex) === '_128') {
- return source;
- }
- return `${source.substring(0, lastPeriodIndex)}_128${source.substring(lastPeriodIndex)}`;
-}
-
/**
* Returns the appropriate icons for the given chat report using the stored personalDetails.
* The Avatar sources can be URLs or Icon components according to the chat type.
@@ -808,7 +681,7 @@ function getIconsForParticipants(participants, personalDetails) {
for (let i = 0; i < participantsList.length; i++) {
const login = participantsList[i];
- const avatarSource = getAvatar(lodashGet(personalDetails, [login, 'avatar'], ''), login);
+ const avatarSource = UserUtils.getAvatar(lodashGet(personalDetails, [login, 'avatar'], ''), login);
participantDetails.push([login, lodashGet(personalDetails, [login, 'firstName'], ''), avatarSource]);
}
@@ -863,7 +736,7 @@ function getIcons(report, personalDetails, defaultIcon = null, isPayer = false)
const actorEmail = lodashGet(parentReportAction, 'actorEmail', '');
const actorIcon = {
- source: getAvatar(lodashGet(personalDetails, [actorEmail, 'avatar']), actorEmail),
+ source: UserUtils.getAvatar(lodashGet(personalDetails, [actorEmail, 'avatar']), actorEmail),
name: actorEmail,
type: CONST.ICON_TYPE_AVATAR,
};
@@ -900,7 +773,7 @@ function getIcons(report, personalDetails, defaultIcon = null, isPayer = false)
}
const adminIcon = {
- source: getAvatar(lodashGet(personalDetails, [report.ownerEmail, 'avatar']), report.ownerEmail),
+ source: UserUtils.getAvatar(lodashGet(personalDetails, [report.ownerEmail, 'avatar']), report.ownerEmail),
name: report.ownerEmail,
type: CONST.ICON_TYPE_AVATAR,
};
@@ -919,7 +792,7 @@ function getIcons(report, personalDetails, defaultIcon = null, isPayer = false)
const email = isPayer ? report.managerEmail : report.ownerEmail;
return [
{
- source: getAvatar(lodashGet(personalDetails, [email, 'avatar']), email),
+ source: UserUtils.getAvatar(lodashGet(personalDetails, [email, 'avatar']), email),
name: email,
type: CONST.ICON_TYPE_AVATAR,
},
@@ -943,7 +816,7 @@ function getPersonalDetailsForLogin(login) {
(allPersonalDetails && allPersonalDetails[login]) || {
login,
displayName: LocalePhoneNumber.formatPhoneNumber(login),
- avatar: getDefaultAvatar(login),
+ avatar: UserUtils.getDefaultAvatar(login),
}
);
}
@@ -1227,7 +1100,7 @@ function buildOptimisticAddCommentReportAction(text, file) {
},
],
automatic: false,
- avatar: lodashGet(allPersonalDetails, [currentUserEmail, 'avatar'], getDefaultAvatar(currentUserEmail)),
+ avatar: lodashGet(allPersonalDetails, [currentUserEmail, 'avatar'], UserUtils.getDefaultAvatar(currentUserEmail)),
created: DateUtils.getDBTime(),
message: [
{
@@ -1449,7 +1322,7 @@ function buildOptimisticIOUReportAction(type, amount, currency, comment, partici
actorAccountID: currentUserAccountID,
actorEmail: currentUserEmail,
automatic: false,
- avatar: lodashGet(currentUserPersonalDetails, 'avatar', getDefaultAvatar(currentUserEmail)),
+ avatar: lodashGet(currentUserPersonalDetails, 'avatar', UserUtils.getDefaultAvatar(currentUserEmail)),
isAttachment: false,
originalMessage,
message: getIOUReportActionMessage(type, amount, textForNewCommentDecoded, currency, paymentType, isSettlingUp),
@@ -1502,7 +1375,7 @@ function buildOptimisticTaskReportAction(taskReportID, actionName, message = '')
actorAccountID: currentUserAccountID,
actorEmail: currentUserEmail,
automatic: false,
- avatar: lodashGet(currentUserPersonalDetails, 'avatar', getDefaultAvatar(currentUserEmail)),
+ avatar: lodashGet(currentUserPersonalDetails, 'avatar', UserUtils.getDefaultAvatar(currentUserEmail)),
isAttachment: false,
originalMessage,
message: [
@@ -1615,7 +1488,7 @@ function buildOptimisticCreatedReportAction(ownerEmail) {
},
],
automatic: false,
- avatar: lodashGet(allPersonalDetails, [currentUserEmail, 'avatar'], getDefaultAvatar(currentUserEmail)),
+ avatar: lodashGet(allPersonalDetails, [currentUserEmail, 'avatar'], UserUtils.getDefaultAvatar(currentUserEmail)),
created: DateUtils.getDBTime(),
shouldShow: true,
};
@@ -1655,7 +1528,7 @@ function buildOptimisticEditedTaskReportAction(ownerEmail) {
},
],
automatic: false,
- avatar: lodashGet(allPersonalDetails, [currentUserEmail, 'avatar'], getDefaultAvatar(currentUserEmail)),
+ avatar: lodashGet(allPersonalDetails, [currentUserEmail, 'avatar'], UserUtils.getDefaultAvatar(currentUserEmail)),
created: DateUtils.getDBTime(),
shouldShow: false,
};
@@ -1674,7 +1547,7 @@ function buildOptimisticClosedReportAction(ownerEmail, policyName, reason = CONS
actionName: CONST.REPORT.ACTIONS.TYPE.CLOSED,
actorAccountID: currentUserAccountID,
automatic: false,
- avatar: lodashGet(allPersonalDetails, [currentUserEmail, 'avatar'], getDefaultAvatar(currentUserEmail)),
+ avatar: lodashGet(allPersonalDetails, [currentUserEmail, 'avatar'], UserUtils.getDefaultAvatar(currentUserEmail)),
created: DateUtils.getDBTime(),
message: [
{
@@ -2272,7 +2145,6 @@ export {
formatReportLastMessageText,
chatIncludesConcierge,
isPolicyExpenseChat,
- getDefaultAvatar,
getIconsForParticipants,
getIcons,
getRoomWelcomeMessage,
@@ -2310,17 +2182,11 @@ export {
isTaskReport,
isMoneyRequestReport,
chatIncludesChronos,
- getAvatar,
- isDefaultAvatar,
- getOldDotDefaultAvatar,
getNewMarkerReportActionID,
canSeeDefaultRoom,
- hashLogin,
getDefaultWorkspaceAvatar,
getCommentLength,
getParsedComment,
- getFullSizeAvatar,
- getSmallSizeAvatar,
getMoneyRequestOptions,
canRequestMoney,
getWhisperDisplayNames,
diff --git a/src/libs/SidebarUtils.js b/src/libs/SidebarUtils.js
index 3f9caafcf828..7b3b15291e74 100644
--- a/src/libs/SidebarUtils.js
+++ b/src/libs/SidebarUtils.js
@@ -11,6 +11,7 @@ import CONST from '../CONST';
import * as OptionsListUtils from './OptionsListUtils';
import * as CollectionUtils from './CollectionUtils';
import * as LocalePhoneNumber from './LocalePhoneNumber';
+import * as UserUtils from './UserUtils';
// Note: It is very important that the keys subscribed to here are the same
// keys that are connected to SidebarLinks withOnyx(). If there was a key missing from SidebarLinks and it's data was updated
@@ -332,7 +333,7 @@ function getOptionData(reportID) {
result.subtitle = subtitle;
result.participantsList = participantPersonalDetailList;
- result.icons = ReportUtils.getIcons(result.isTaskReport ? parentReport : report, personalDetails, ReportUtils.getAvatar(personalDetail.avatar, personalDetail.login), true);
+ result.icons = ReportUtils.getIcons(result.isTaskReport ? parentReport : report, personalDetails, UserUtils.getAvatar(personalDetail.avatar, personalDetail.login), true);
result.searchText = OptionsListUtils.getSearchText(report, reportName, participantPersonalDetailList, result.isChatRoom || result.isPolicyExpenseChat, result.isThread);
result.displayNamesWithTooltips = displayNamesWithTooltips;
return result;
diff --git a/src/libs/UserUtils.js b/src/libs/UserUtils.js
index 1abeef19cd84..e7d8235f0004 100644
--- a/src/libs/UserUtils.js
+++ b/src/libs/UserUtils.js
@@ -1,6 +1,9 @@
import _ from 'underscore';
import lodashGet from 'lodash/get';
import CONST from '../CONST';
+import hashCode from './hashCode';
+import * as Expensicons from '../components/Icon/Expensicons';
+import * as defaultAvatars from '../components/Icon/DefaultAvatars';
/**
* Searches through given loginList for any contact method / login with an error.
@@ -61,4 +64,153 @@ function getLoginListBrickRoadIndicator(loginList) {
return '';
}
-export {hasLoginListError, hasLoginListInfo, getLoginListBrickRoadIndicator};
+/**
+ * Hashes provided string and returns a value between [0, range)
+ * @param {String} login
+ * @param {Number} range
+ * @returns {Number}
+ */
+function hashLogin(login, range) {
+ return Math.abs(hashCode(login.toLowerCase())) % range;
+}
+
+/**
+ * Helper method to return the default avatar associated with the given login
+ * @param {String} [login]
+ * @returns {String}
+ */
+function getDefaultAvatar(login = '') {
+ if (!login) {
+ return Expensicons.FallbackAvatar;
+ }
+ if (login === CONST.EMAIL.CONCIERGE) {
+ return Expensicons.ConciergeAvatar;
+ }
+
+ // There are 24 possible default avatars, so we choose which one this user has based
+ // on a simple hash of their login. Note that Avatar count starts at 1.
+ const loginHashBucket = hashLogin(login, CONST.DEFAULT_AVATAR_COUNT) + 1;
+
+ return defaultAvatars[`Avatar${loginHashBucket}`];
+}
+
+/**
+ * Helper method to return default avatar URL associated with login
+ *
+ * @param {String} [login]
+ * @param {Boolean} [isNewDot]
+ * @returns {String}
+ */
+function getDefaultAvatarURL(login = '', isNewDot = false) {
+ if (login === CONST.EMAIL.CONCIERGE) {
+ return CONST.CONCIERGE_ICON_URL;
+ }
+
+ // The default avatar for a user is based on a simple hash of their login.
+ // Note that Avatar count starts at 1 which is why 1 has to be added to the result (or else 0 would result in a broken avatar link)
+ const loginHashBucket = hashLogin(login, isNewDot ? CONST.DEFAULT_AVATAR_COUNT : CONST.OLD_DEFAULT_AVATAR_COUNT) + 1;
+ const avatarPrefix = isNewDot ? `default-avatar` : `avatar`;
+
+ return `${CONST.CLOUDFRONT_URL}/images/avatars/${avatarPrefix}_${loginHashBucket}.png`;
+}
+
+/**
+ * Given a user's avatar path, returns true if user doesn't have an avatar or if URL points to a default avatar
+ * @param {String} [avatarURL] - the avatar source from user's personalDetails
+ * @returns {Boolean}
+ */
+function isDefaultAvatar(avatarURL) {
+ if (
+ _.isString(avatarURL) &&
+ (avatarURL.includes('images/avatars/avatar_') || avatarURL.includes('images/avatars/default-avatar_') || avatarURL.includes('images/avatars/user/default'))
+ ) {
+ return true;
+ }
+
+ // If null URL, we should also use a default avatar
+ if (!avatarURL) {
+ return true;
+ }
+ return false;
+}
+
+/**
+ * Provided a source URL, if source is a default avatar, return the associated SVG.
+ * Otherwise, return the URL pointing to a user-uploaded avatar.
+ *
+ * @param {String} avatarURL - the avatar source from user's personalDetails
+ * @param {String} login - the email of the user
+ * @returns {String|Function}
+ */
+function getAvatar(avatarURL, login) {
+ return isDefaultAvatar(avatarURL) ? getDefaultAvatar(login) : avatarURL;
+}
+
+/**
+ * Provided an avatar URL, if avatar is a default avatar, return NewDot default avatar URL.
+ * Otherwise, return the URL pointing to a user-uploaded avatar.
+ *
+ * @param {String} avatarURL - the avatar source from user's personalDetails
+ * @param {String} login - the email of the user
+ * @returns {String}
+ */
+function getAvatarUrl(avatarURL, login) {
+ return isDefaultAvatar(avatarURL) ? getDefaultAvatarURL(login, true) : avatarURL;
+}
+
+/**
+ * Avatars uploaded by users will have a _128 appended so that the asset server returns a small version.
+ * This removes that part of the URL so the full version of the image can load.
+ *
+ * @param {String} [avatarURL]
+ * @param {String} [login]
+ * @returns {String|Function}
+ */
+function getFullSizeAvatar(avatarURL, login) {
+ const source = getAvatar(avatarURL, login);
+ if (!_.isString(source)) {
+ return source;
+ }
+ return source.replace('_128', '');
+}
+
+/**
+ * Small sized avatars end with _128.. This adds the _128 at the end of the
+ * source URL (before the file type) if it doesn't exist there already.
+ *
+ * @param {String} avatarURL
+ * @param {String} login
+ * @returns {String|Function}
+ */
+function getSmallSizeAvatar(avatarURL, login) {
+ const source = getAvatar(avatarURL, login);
+ if (!_.isString(source)) {
+ return source;
+ }
+
+ // Because other urls than CloudFront do not support dynamic image sizing (_SIZE suffix), the current source is already what we want to use here.
+ if (!CONST.CLOUDFRONT_DOMAIN_REGEX.test(source)) {
+ return source;
+ }
+
+ // If image source already has _128 at the end, the given avatar URL is already what we want to use here.
+ const lastPeriodIndex = source.lastIndexOf('.');
+ if (source.substring(lastPeriodIndex - 4, lastPeriodIndex) === '_128') {
+ return source;
+ }
+ return `${source.substring(0, lastPeriodIndex)}_128${source.substring(lastPeriodIndex)}`;
+}
+
+export {
+ hashLogin,
+ hasLoginListError,
+ hasLoginListInfo,
+ getLoginListBrickRoadIndicator,
+ getDefaultAvatar,
+ getDefaultAvatarURL,
+ isDefaultAvatar,
+ getAvatar,
+ getAvatarUrl,
+ getSmallSizeAvatar,
+ getFullSizeAvatar,
+};
diff --git a/src/libs/actions/PersonalDetails.js b/src/libs/actions/PersonalDetails.js
index c9c5889c914b..e1d983eecf95 100644
--- a/src/libs/actions/PersonalDetails.js
+++ b/src/libs/actions/PersonalDetails.js
@@ -5,7 +5,7 @@ import _ from 'underscore';
import ONYXKEYS from '../../ONYXKEYS';
import CONST from '../../CONST';
import * as API from '../API';
-import * as ReportUtils from '../ReportUtils';
+import * as UserUtils from '../UserUtils';
import * as LocalePhoneNumber from '../LocalePhoneNumber';
import ROUTES from '../../ROUTES';
import Navigation from '../Navigation/Navigation';
@@ -373,7 +373,7 @@ function updateAvatar(file) {
*/
function deleteAvatar() {
// We want to use the old dot avatar here as this affects both platforms.
- const defaultAvatar = ReportUtils.getOldDotDefaultAvatar(currentUserEmail);
+ const defaultAvatar = UserUtils.getDefaultAvatarURL(currentUserEmail);
API.write(
'DeleteUserAvatar',
diff --git a/src/libs/actions/Task.js b/src/libs/actions/Task.js
index d9b134555815..da751e4209a8 100644
--- a/src/libs/actions/Task.js
+++ b/src/libs/actions/Task.js
@@ -9,6 +9,7 @@ import Navigation from '../Navigation/Navigation';
import ROUTES from '../../ROUTES';
import CONST from '../../CONST';
import DateUtils from '../DateUtils';
+import * as UserUtils from '../UserUtils';
/**
* Clears out the task info from the store
@@ -495,7 +496,7 @@ function getAssignee(details) {
subtitle: '',
};
}
- const source = ReportUtils.getAvatar(lodashGet(details, 'avatar', ''), lodashGet(details, 'login', ''));
+ const source = UserUtils.getAvatar(lodashGet(details, 'avatar', ''), lodashGet(details, 'login', ''));
return {
icons: [{source, type: 'avatar', name: details.login}],
displayName: details.displayName,
diff --git a/src/pages/DetailsPage.js b/src/pages/DetailsPage.js
index 93bf382d7182..3c1f124aca1a 100755
--- a/src/pages/DetailsPage.js
+++ b/src/pages/DetailsPage.js
@@ -28,6 +28,7 @@ import * as Report from '../libs/actions/Report';
import OfflineWithFeedback from '../components/OfflineWithFeedback';
import AutoUpdateTime from '../components/AutoUpdateTime';
import FullPageNotFoundView from '../components/BlockingViews/FullPageNotFoundView';
+import * as UserUtils from '../libs/UserUtils';
const matchType = PropTypes.shape({
params: PropTypes.shape({
@@ -95,7 +96,7 @@ class DetailsPage extends React.PureComponent {
details = {
login,
displayName: ReportUtils.getDisplayNameForParticipant(login),
- avatar: ReportUtils.getAvatar(lodashGet(details, 'avatar', ''), login),
+ avatar: UserUtils.getAvatar(lodashGet(details, 'avatar', ''), login),
};
}
@@ -135,7 +136,7 @@ class DetailsPage extends React.PureComponent {
@@ -148,7 +149,7 @@ class DetailsPage extends React.PureComponent {
diff --git a/src/pages/ReportParticipantsPage.js b/src/pages/ReportParticipantsPage.js
index b8f988f78eaf..81b4bb0ec2ab 100755
--- a/src/pages/ReportParticipantsPage.js
+++ b/src/pages/ReportParticipantsPage.js
@@ -20,6 +20,7 @@ import reportPropTypes from './reportPropTypes';
import withReportOrNotFound from './home/report/withReportOrNotFound';
import FullPageNotFoundView from '../components/BlockingViews/FullPageNotFoundView';
import CONST from '../CONST';
+import * as UserUtils from '../libs/UserUtils';
const propTypes = {
/* Onyx Props */
@@ -65,7 +66,7 @@ const getAllParticipants = (report, personalDetails) => {
displayName: userPersonalDetail.displayName,
icons: [
{
- source: ReportUtils.getAvatar(userPersonalDetail.avatar, login),
+ source: UserUtils.getAvatar(userPersonalDetail.avatar, login),
name: login,
type: CONST.ICON_TYPE_AVATAR,
},
diff --git a/src/pages/ShareCodePage.js b/src/pages/ShareCodePage.js
index cec57c6e8aa9..d6c419b10882 100644
--- a/src/pages/ShareCodePage.js
+++ b/src/pages/ShareCodePage.js
@@ -17,6 +17,7 @@ import * as Expensicons from '../components/Icon/Expensicons';
import getPlatform from '../libs/getPlatform';
import CONST from '../CONST';
import ContextMenuItem from '../components/ContextMenuItem';
+import * as UserUtils from '../libs/UserUtils';
const propTypes = {
/** The report currently being looked at */
@@ -60,7 +61,7 @@ class ShareCodePage extends React.Component {
url={url}
title={isReport ? this.props.report.reportName : this.props.currentUserPersonalDetails.displayName}
subtitle={isReport ? subtitle : this.props.session.email}
- logo={isReport ? roomAvatar : this.props.currentUserPersonalDetails.avatar}
+ logo={isReport ? roomAvatar : UserUtils.getAvatarUrl(this.props.currentUserPersonalDetails.avatar, this.props.currentUserPersonalDetails.login)}
/>
diff --git a/src/pages/home/report/ReactionList/BaseReactionList.js b/src/pages/home/report/ReactionList/BaseReactionList.js
index d42a2284f22f..eda4f4db0f48 100755
--- a/src/pages/home/report/ReactionList/BaseReactionList.js
+++ b/src/pages/home/report/ReactionList/BaseReactionList.js
@@ -5,7 +5,7 @@ import PropTypes from 'prop-types';
import Str from 'expensify-common/lib/str';
import styles from '../../../../styles/styles';
import HeaderReactionList from './HeaderReactionList';
-import * as ReportUtils from '../../../../libs/ReportUtils';
+import * as UserUtils from '../../../../libs/UserUtils';
import CONST from '../../../../CONST';
import participantPropTypes from '../../../../components/participantPropTypes';
import reactionPropTypes from './reactionPropTypes';
@@ -87,7 +87,7 @@ const BaseReactionList = (props) => {
participantsList: [item],
icons: [
{
- source: ReportUtils.getAvatar(item.avatar, item.login),
+ source: UserUtils.getAvatar(item.avatar, item.login),
name: item.login,
type: CONST.ICON_TYPE_AVATAR,
},
diff --git a/src/pages/home/report/ReportActionItemSingle.js b/src/pages/home/report/ReportActionItemSingle.js
index 7f8b4237bbb3..3cbe52cd0e38 100644
--- a/src/pages/home/report/ReportActionItemSingle.js
+++ b/src/pages/home/report/ReportActionItemSingle.js
@@ -21,6 +21,7 @@ import OfflineWithFeedback from '../../../components/OfflineWithFeedback';
import CONST from '../../../CONST';
import SubscriptAvatar from '../../../components/SubscriptAvatar';
import reportPropTypes from '../../reportPropTypes';
+import * as UserUtils from '../../../libs/UserUtils';
const propTypes = {
/** All the data of the action */
@@ -63,7 +64,7 @@ const showUserDetails = (email) => {
const ReportActionItemSingle = (props) => {
const actorEmail = props.action.actorEmail.replace(CONST.REGEX.MERGED_ACCOUNT_PREFIX, '');
const {avatar, displayName, pendingFields} = props.personalDetails[actorEmail] || {};
- const avatarSource = ReportUtils.getAvatar(avatar, actorEmail);
+ const avatarSource = UserUtils.getAvatar(avatar, actorEmail);
// Since the display name for a report action message is delivered with the report history as an array of fragments
// we'll need to take the displayName from personal details and have it be in the same format for now. Eventually,
diff --git a/src/pages/home/sidebar/SidebarLinks.js b/src/pages/home/sidebar/SidebarLinks.js
index 4bf4279b51cf..912f63384034 100644
--- a/src/pages/home/sidebar/SidebarLinks.js
+++ b/src/pages/home/sidebar/SidebarLinks.js
@@ -21,7 +21,6 @@ import CONST from '../../../CONST';
import participantPropTypes from '../../../components/participantPropTypes';
import withLocalize, {withLocalizePropTypes} from '../../../components/withLocalize';
import * as App from '../../../libs/actions/App';
-import * as ReportUtils from '../../../libs/ReportUtils';
import withCurrentUserPersonalDetails from '../../../components/withCurrentUserPersonalDetails';
import withWindowDimensions from '../../../components/withWindowDimensions';
import reportActionPropTypes from '../report/reportActionPropTypes';
@@ -34,6 +33,7 @@ import defaultTheme from '../../../styles/themes/default';
import OptionsListSkeletonView from '../../../components/OptionsListSkeletonView';
import variables from '../../../styles/variables';
import LogoComponent from '../../../../assets/images/expensify-wordmark.svg';
+import * as UserUtils from '../../../libs/UserUtils';
const propTypes = {
/** Toggles the navigation menu open and closed */
@@ -178,7 +178,7 @@ class SidebarLinks extends React.Component {
>
@@ -256,7 +256,7 @@ const personalDetailsSelector = (personalDetails) =>
login: personalData.login,
displayName: personalData.displayName,
firstName: personalData.firstName,
- avatar: ReportUtils.getAvatar(personalData.avatar, personalData.login),
+ avatar: UserUtils.getAvatar(personalData.avatar, personalData.login),
};
return finalPersonalDetails;
},
diff --git a/src/pages/settings/InitialSettingsPage.js b/src/pages/settings/InitialSettingsPage.js
index ed3e4ba1f846..4e775bcd2cd2 100755
--- a/src/pages/settings/InitialSettingsPage.js
+++ b/src/pages/settings/InitialSettingsPage.js
@@ -332,7 +332,7 @@ class InitialSettingsPage extends React.Component {
diff --git a/src/pages/settings/Profile/ProfilePage.js b/src/pages/settings/Profile/ProfilePage.js
index 1a216fd0aacb..d58cb4ceec3d 100755
--- a/src/pages/settings/Profile/ProfilePage.js
+++ b/src/pages/settings/Profile/ProfilePage.js
@@ -17,12 +17,11 @@ import CONST from '../../../CONST';
import * as PersonalDetails from '../../../libs/actions/PersonalDetails';
import compose from '../../../libs/compose';
import Navigation from '../../../libs/Navigation/Navigation';
-import * as ReportUtils from '../../../libs/ReportUtils';
+import * as UserUtils from '../../../libs/UserUtils';
import ROUTES from '../../../ROUTES';
import styles from '../../../styles/styles';
import * as Expensicons from '../../../components/Icon/Expensicons';
import ONYXKEYS from '../../../ONYXKEYS';
-import * as UserUtils from '../../../libs/UserUtils';
import withWindowDimensions, {windowDimensionsPropTypes} from '../../../components/withWindowDimensions';
const propTypes = {
@@ -98,8 +97,8 @@ const ProfilePage = (props) => {
onClose={PersonalDetails.clearAvatarErrors}
>
{
<>
diff --git a/src/pages/workspace/WorkspaceMembersPage.js b/src/pages/workspace/WorkspaceMembersPage.js
index c9d6deab2b0e..b03b08ef76e5 100644
--- a/src/pages/workspace/WorkspaceMembersPage.js
+++ b/src/pages/workspace/WorkspaceMembersPage.js
@@ -28,7 +28,7 @@ import OfflineWithFeedback from '../../components/OfflineWithFeedback';
import {withNetwork} from '../../components/OnyxProvider';
import FullPageNotFoundView from '../../components/BlockingViews/FullPageNotFoundView';
import networkPropTypes from '../../components/networkPropTypes';
-import * as ReportUtils from '../../libs/ReportUtils';
+import * as UserUtils from '../../libs/UserUtils';
import FormHelpMessage from '../../components/FormHelpMessage';
import TextInput from '../../components/TextInput';
import KeyboardDismissingFlatList from '../../components/KeyboardDismissingFlatList';
@@ -344,7 +344,7 @@ class WorkspaceMembersPage extends React.Component {
participantsList: [item],
icons: [
{
- source: ReportUtils.getAvatar(item.avatar, item.login),
+ source: UserUtils.getAvatar(item.avatar, item.login),
name: item.login,
type: CONST.ICON_TYPE_AVATAR,
},
diff --git a/src/styles/StyleUtils.js b/src/styles/StyleUtils.js
index 586e5dcf0b31..cf7312b2580b 100644
--- a/src/styles/StyleUtils.js
+++ b/src/styles/StyleUtils.js
@@ -6,7 +6,7 @@ import variables from './variables';
import colors from './colors';
import positioning from './utilities/positioning';
import styles from './styles';
-import * as ReportUtils from '../libs/ReportUtils';
+import * as UserUtils from '../libs/UserUtils';
const workspaceColorOptions = [
{backgroundColor: colors.blue200, fill: colors.blue700},
@@ -162,7 +162,7 @@ function getAvatarBorderStyle(size, type) {
* @returns {Object}
*/
function getDefaultWorkspaceAvatarColor(workspaceName) {
- const colorHash = ReportUtils.hashLogin(workspaceName.trim(), workspaceColorOptions.length);
+ const colorHash = UserUtils.hashLogin(workspaceName.trim(), workspaceColorOptions.length);
return workspaceColorOptions[colorHash];
}