diff --git a/src/components/Avatar.tsx b/src/components/Avatar.tsx index bf48894beaab..99183a1e6ba7 100644 --- a/src/components/Avatar.tsx +++ b/src/components/Avatar.tsx @@ -7,6 +7,7 @@ import useTheme from '@hooks/useTheme'; import useThemeStyles from '@hooks/useThemeStyles'; import * as ReportUtils from '@libs/ReportUtils'; import type {AvatarSource} from '@libs/UserUtils'; +import * as UserUtils from '@libs/UserUtils'; import type {AvatarSizeName} from '@styles/utils'; import CONST from '@src/CONST'; import type {AvatarType} from '@src/types/onyx/OnyxCommon'; @@ -49,10 +50,13 @@ type AvatarProps = { /** Owner of the avatar. If user, displayName. If workspace, policy name */ name?: string; + + /** Optional account id if it's user avatar */ + accountID?: number; }; function Avatar({ - source, + source: originalSource, imageStyles, iconAdditionalStyles, containerStyles, @@ -62,6 +66,7 @@ function Avatar({ fallbackIconTestID = '', type = CONST.ICON_TYPE_AVATAR, name = '', + accountID, }: AvatarProps) { const theme = useTheme(); const styles = useThemeStyles(); @@ -72,16 +77,17 @@ function Avatar({ useEffect(() => { setImageError(false); - }, [source]); + }, [originalSource]); const isWorkspace = type === CONST.ICON_TYPE_WORKSPACE; - const iconSize = StyleUtils.getAvatarSize(size); + const iconSize = StyleUtils.getAvatarSize(size); const imageStyle: StyleProp = [StyleUtils.getAvatarStyle(size), imageStyles, styles.noBorderRadius]; const iconStyle = imageStyles ? [StyleUtils.getAvatarStyle(size), styles.bgTransparent, imageStyles] : undefined; // We pass the color styles down to the SVG for the workspace and fallback avatar. - const useFallBackAvatar = imageError || source === Expensicons.FallbackAvatar || !source; + const source = isWorkspace ? originalSource : UserUtils.getAvatar(originalSource, accountID); + const useFallBackAvatar = imageError || !source || source === Expensicons.FallbackAvatar; const fallbackAvatar = isWorkspace ? ReportUtils.getDefaultWorkspaceAvatar(name) : fallbackIcon || Expensicons.FallbackAvatar; const fallbackAvatarTestID = isWorkspace ? ReportUtils.getDefaultWorkspaceAvatarTestID(name) : fallbackIconTestID || 'SvgFallbackAvatar Icon'; const avatarSource = useFallBackAvatar ? fallbackAvatar : source; diff --git a/src/components/AvatarWithIndicator.tsx b/src/components/AvatarWithIndicator.tsx index 42b91b3d2d71..1bf18afb70ff 100644 --- a/src/components/AvatarWithIndicator.tsx +++ b/src/components/AvatarWithIndicator.tsx @@ -11,7 +11,10 @@ import Tooltip from './Tooltip'; type AvatarWithIndicatorProps = { /** URL for the avatar */ - source: UserUtils.AvatarSource; + source?: UserUtils.AvatarSource; + + /** account id if it's user avatar */ + accountID?: number; /** To show a tooltip on hover */ tooltipText?: string; @@ -23,7 +26,7 @@ type AvatarWithIndicatorProps = { isLoading?: boolean; }; -function AvatarWithIndicator({source, tooltipText = '', fallbackIcon = Expensicons.FallbackAvatar, isLoading = true}: AvatarWithIndicatorProps) { +function AvatarWithIndicator({source, accountID, tooltipText = '', fallbackIcon = Expensicons.FallbackAvatar, isLoading = true}: AvatarWithIndicatorProps) { const styles = useThemeStyles(); return ( @@ -35,7 +38,7 @@ function AvatarWithIndicator({source, tooltipText = '', fallbackIcon = Expensico <> diff --git a/src/components/MultipleAvatars.tsx b/src/components/MultipleAvatars.tsx index dedaba500a9c..31d3d35af58d 100644 --- a/src/components/MultipleAvatars.tsx +++ b/src/components/MultipleAvatars.tsx @@ -158,6 +158,7 @@ function MultipleAvatars({ name={icons[0].name} type={icons[0].type} fallbackIcon={icons[0].fallbackIcon} + accountID={icons[0].id} /> @@ -207,6 +208,7 @@ function MultipleAvatars({ name={icon.name} type={icon.type} fallbackIcon={icon.fallbackIcon} + accountID={icon.id} /> diff --git a/src/components/ReportActionItem/TaskView.tsx b/src/components/ReportActionItem/TaskView.tsx index 9711e126907f..e3e07ab0d7ad 100644 --- a/src/components/ReportActionItem/TaskView.tsx +++ b/src/components/ReportActionItem/TaskView.tsx @@ -156,7 +156,7 @@ function TaskView({report, shouldShowHorizontalRule, ...props}: TaskViewProps) { {title} diff --git a/src/libs/OptionsListUtils.ts b/src/libs/OptionsListUtils.ts index 79e767b48624..7011a59c40fe 100644 --- a/src/libs/OptionsListUtils.ts +++ b/src/libs/OptionsListUtils.ts @@ -7,6 +7,7 @@ import lodashSet from 'lodash/set'; import lodashSortBy from 'lodash/sortBy'; import Onyx from 'react-native-onyx'; import type {OnyxCollection, OnyxEntry} from 'react-native-onyx'; +import {FallbackAvatar} from '@components/Icon/Expensicons'; import type {SelectedTagOption} from '@components/TagPicker'; import CONST from '@src/CONST'; import type {TranslationPaths} from '@src/languages/types'; @@ -304,13 +305,14 @@ function getAvatarsForAccountIDs(accountIDs: number[], personalDetails: OnyxEntr Object.entries(defaultValues).forEach((item) => { reversedDefaultValues[item[1]] = item[0]; }); + return accountIDs.map((accountID) => { const login = reversedDefaultValues[accountID] ?? ''; - const userPersonalDetail = personalDetails?.[accountID] ?? {login, accountID, avatar: ''}; + const userPersonalDetail = personalDetails?.[accountID] ?? {login, accountID}; return { id: accountID, - source: UserUtils.getAvatar(userPersonalDetail.avatar, userPersonalDetail.accountID), + source: userPersonalDetail.avatar ?? FallbackAvatar, type: CONST.ICON_TYPE_AVATAR, name: userPersonalDetail.login ?? '', }; @@ -333,9 +335,7 @@ function getPersonalDetailsForAccountIDs(accountIDs: number[] | undefined, perso } let personalDetail: OnyxEntry = personalDetails[accountID]; if (!personalDetail) { - personalDetail = { - avatar: UserUtils.getDefaultAvatar(cleanAccountID), - } as PersonalDetails; + personalDetail = {} as PersonalDetails; } if (cleanAccountID === CONST.ACCOUNT_ID.CONCIERGE) { @@ -364,6 +364,7 @@ function getParticipantsOption(participant: ReportUtils.OptionData | Participant // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing const login = detail?.login || participant.login || ''; const displayName = PersonalDetailsUtils.getDisplayNameOrDefault(detail, LocalePhoneNumber.formatPhoneNumber(login)); + return { keyForList: String(detail?.accountID), login, @@ -374,7 +375,7 @@ function getParticipantsOption(participant: ReportUtils.OptionData | Participant alternateText: LocalePhoneNumber.formatPhoneNumber(login) || displayName, icons: [ { - source: UserUtils.getAvatar(detail?.avatar ?? '', detail?.accountID ?? -1), + source: detail?.avatar ?? FallbackAvatar, name: login, type: CONST.ICON_TYPE_AVATAR, id: detail?.accountID, @@ -758,13 +759,7 @@ function createOption( // Disabling this line for safeness as nullish coalescing works only if the value is undefined or null // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing result.searchText = getSearchText(report, reportName, personalDetailList, !!result.isChatRoom || !!result.isPolicyExpenseChat, !!result.isThread); - result.icons = ReportUtils.getIcons( - report, - personalDetails, - UserUtils.getAvatar(personalDetail?.avatar ?? '', personalDetail?.accountID), - personalDetail?.login, - personalDetail?.accountID, - ); + result.icons = ReportUtils.getIcons(report, personalDetails, personalDetail?.avatar, personalDetail?.login, personalDetail?.accountID); result.subtitle = subtitle; return result; @@ -1864,7 +1859,6 @@ function getOptions( [optimisticAccountID]: { accountID: optimisticAccountID, login: searchValue, - avatar: UserUtils.getDefaultAvatar(optimisticAccountID), }, }; userToInvite = createOption([optimisticAccountID], personalDetailsExtended, null, reportActions, { @@ -1877,10 +1871,10 @@ function getOptions( // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing userToInvite.alternateText = userToInvite.alternateText || searchValue; - // If user doesn't exist, use a default avatar + // If user doesn't exist, use a fallback avatar userToInvite.icons = [ { - source: UserUtils.getAvatar('', optimisticAccountID), + source: FallbackAvatar, name: searchValue, type: CONST.ICON_TYPE_AVATAR, }, @@ -1954,17 +1948,12 @@ function getShareLogOptions(options: OptionList, searchValue = '', betas: Beta[] */ function getIOUConfirmationOptionsFromPayeePersonalDetail(personalDetail: PersonalDetails | EmptyObject, amountText?: string): PayeePersonalDetails { const formattedLogin = LocalePhoneNumber.formatPhoneNumber(personalDetail.login ?? ''); + const icons = [{source: personalDetail.avatar ?? FallbackAvatar, name: personalDetail.login ?? '', type: CONST.ICON_TYPE_AVATAR, id: personalDetail.accountID}]; + return { text: PersonalDetailsUtils.getDisplayNameOrDefault(personalDetail, formattedLogin), alternateText: formattedLogin || PersonalDetailsUtils.getDisplayNameOrDefault(personalDetail, '', false), - icons: [ - { - source: UserUtils.getAvatar(personalDetail.avatar, personalDetail.accountID), - name: personalDetail.login ?? '', - type: CONST.ICON_TYPE_AVATAR, - id: personalDetail.accountID, - }, - ], + icons, descriptiveText: amountText ?? '', login: personalDetail.login ?? '', accountID: personalDetail.accountID, diff --git a/src/libs/PersonalDetailsUtils.ts b/src/libs/PersonalDetailsUtils.ts index ffa0605f1eba..961b3db13487 100644 --- a/src/libs/PersonalDetailsUtils.ts +++ b/src/libs/PersonalDetailsUtils.ts @@ -153,7 +153,6 @@ function getPersonalDetailsOnyxDataForOptimisticUsers(newLogins: string[], newAc personalDetailsNew[accountID] = { login, accountID, - avatar: UserUtils.getDefaultAvatarURL(accountID), displayName: LocalePhoneNumber.formatPhoneNumber(login), }; diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index c6d70a9da71c..72524de21c64 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -10,7 +10,7 @@ import type {OnyxCollection, OnyxEntry, OnyxUpdate} from 'react-native-onyx'; import Onyx from 'react-native-onyx'; import type {ValueOf} from 'type-fest'; import type {FileObject} from '@components/AttachmentModal'; -import * as Expensicons from '@components/Icon/Expensicons'; +import {FallbackAvatar} from '@components/Icon/Expensicons'; import * as defaultGroupAvatars from '@components/Icon/GroupDefaultAvatars'; import * as defaultWorkspaceAvatars from '@components/Icon/WorkspaceDefaultAvatars'; import type {IOUAction, IOUType} from '@src/CONST'; @@ -1629,7 +1629,7 @@ function getIconsForParticipants(participants: number[], personalDetails: OnyxCo const participantsList = participants || []; for (const accountID of participantsList) { - const avatarSource = UserUtils.getAvatar(personalDetails?.[accountID]?.avatar ?? '', accountID); + const avatarSource = personalDetails?.[accountID]?.avatar ?? FallbackAvatar; const displayNameLogin = personalDetails?.[accountID]?.displayName ? personalDetails?.[accountID]?.displayName : personalDetails?.[accountID]?.login; participantDetails.push([accountID, displayNameLogin ?? '', avatarSource, personalDetails?.[accountID]?.fallbackIcon ?? '']); } @@ -1690,12 +1690,12 @@ function getPersonalDetailsForAccountID(accountID: number): Partial, iouReportID: /** * Used from expense actions to decide if we need to build an optimistic expense report. - Create a new report if: - - we don't have an iouReport set in the chatReport - - we have one, but it's waiting on the payee adding a bank account - - we have one but we can't add more transactions to it due to: report is approved or settled, or report is processing and policy isn't on Instant submit reporting frequency + Create a new report if: + - we don't have an iouReport set in the chatReport + - we have one, but it's waiting on the payee adding a bank account + - we have one but we can't add more transactions to it due to: report is approved or settled, or report is processing and policy isn't on Instant submit reporting frequency */ function shouldCreateNewMoneyRequestReport(existingIOUReport: OnyxEntry | undefined | null, chatReport: OnyxEntry | null): boolean { return !existingIOUReport || hasIOUWaitingOnCurrentUserBankAccount(chatReport) || !canAddOrDeleteTransactions(existingIOUReport); diff --git a/src/libs/SidebarUtils.ts b/src/libs/SidebarUtils.ts index a218534e6b16..b1cdde61d6cd 100644 --- a/src/libs/SidebarUtils.ts +++ b/src/libs/SidebarUtils.ts @@ -20,7 +20,6 @@ import * as OptionsListUtils from './OptionsListUtils'; import * as ReportActionsUtils from './ReportActionsUtils'; import * as ReportUtils from './ReportUtils'; import * as TaskUtils from './TaskUtils'; -import * as UserUtils from './UserUtils'; const visibleReportActionItems: ReportActions = {}; Onyx.connect({ @@ -412,7 +411,7 @@ function getOptionData({ result.subtitle = subtitle; result.participantsList = participantPersonalDetailList; - result.icons = ReportUtils.getIcons(report, personalDetails, UserUtils.getAvatar(personalDetail?.avatar ?? {}, personalDetail?.accountID), '', -1, policy); + result.icons = ReportUtils.getIcons(report, personalDetails, personalDetail?.avatar, '', -1, policy); result.searchText = OptionsListUtils.getSearchText(report, reportName, participantPersonalDetailList, result.isChatRoom || result.isPolicyExpenseChat, result.isThread); result.displayNamesWithTooltips = displayNamesWithTooltips; diff --git a/src/libs/UserUtils.ts b/src/libs/UserUtils.ts index ce7e4963afc7..03066b21fa71 100644 --- a/src/libs/UserUtils.ts +++ b/src/libs/UserUtils.ts @@ -2,7 +2,7 @@ import Str from 'expensify-common/lib/str'; import type {OnyxEntry} from 'react-native-onyx'; import type {ValueOf} from 'type-fest'; import * as defaultAvatars from '@components/Icon/DefaultAvatars'; -import {ConciergeAvatar, FallbackAvatar, NotificationsAvatar} from '@components/Icon/Expensicons'; +import {ConciergeAvatar, NotificationsAvatar} from '@components/Icon/Expensicons'; import CONST from '@src/CONST'; import type {LoginList} from '@src/types/onyx'; import type Login from '@src/types/onyx/Login'; @@ -82,10 +82,7 @@ function generateAccountID(searchValue: string): number { * @param [accountID] * @returns */ -function getDefaultAvatar(accountID = -1, avatarURL?: string): IconAsset { - if (accountID <= 0) { - return FallbackAvatar; - } +function getDefaultAvatar(accountID = -1, avatarURL?: string): IconAsset | undefined { if (Number(accountID) === CONST.ACCOUNT_ID.CONCIERGE) { return ConciergeAvatar; } @@ -125,7 +122,7 @@ function getDefaultAvatarURL(accountID: string | number = ''): string { } /** - * Given a user's avatar path, returns true if user doesn't have an avatar or if URL points to a default avatar + * Given a user's avatar path, returns true if URL points to a default avatar, false otherwise * @param avatarSource - the avatar source from user's personalDetails */ function isDefaultAvatar(avatarSource?: AvatarSource): avatarSource is string | undefined { @@ -140,11 +137,6 @@ function isDefaultAvatar(avatarSource?: AvatarSource): avatarSource is string | } } - if (!avatarSource) { - // If source is undefined, we should also use a default avatar - return true; - } - return false; } @@ -155,7 +147,7 @@ function isDefaultAvatar(avatarSource?: AvatarSource): avatarSource is string | * @param avatarSource - the avatar source from user's personalDetails * @param accountID - the accountID of the user */ -function getAvatar(avatarSource?: AvatarSource, accountID?: number): AvatarSource { +function getAvatar(avatarSource?: AvatarSource, accountID?: number): AvatarSource | undefined { return isDefaultAvatar(avatarSource) ? getDefaultAvatar(accountID, avatarSource) : avatarSource; } @@ -163,7 +155,7 @@ function getAvatar(avatarSource?: AvatarSource, accountID?: number): AvatarSourc * 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 avatarURL - the avatar source from user's personalDetails + * @param avatarSource - the avatar source from user's personalDetails * @param accountID - the accountID of the user */ function getAvatarUrl(avatarSource: AvatarSource | undefined, accountID: number): AvatarSource { @@ -174,7 +166,7 @@ function getAvatarUrl(avatarSource: AvatarSource | undefined, accountID: number) * 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. */ -function getFullSizeAvatar(avatarSource: AvatarSource | undefined, accountID?: number): AvatarSource { +function getFullSizeAvatar(avatarSource: AvatarSource | undefined, accountID?: number): AvatarSource | undefined { const source = getAvatar(avatarSource, accountID); if (typeof source !== 'string') { return source; @@ -186,7 +178,7 @@ function getFullSizeAvatar(avatarSource: AvatarSource | undefined, accountID?: n * 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. */ -function getSmallSizeAvatar(avatarSource: AvatarSource, accountID?: number): AvatarSource { +function getSmallSizeAvatar(avatarSource?: AvatarSource, accountID?: number): AvatarSource | undefined { const source = getAvatar(avatarSource, accountID); if (typeof source !== 'string') { return source; diff --git a/src/libs/actions/IOU.ts b/src/libs/actions/IOU.ts index ed83267e11e7..7fb31cbed647 100644 --- a/src/libs/actions/IOU.ts +++ b/src/libs/actions/IOU.ts @@ -42,7 +42,6 @@ import * as ReportActionsUtils from '@libs/ReportActionsUtils'; import type {OptimisticChatReport, OptimisticCreatedReportAction, OptimisticIOUReportAction, TransactionDetails} from '@libs/ReportUtils'; import * as ReportUtils from '@libs/ReportUtils'; import * as TransactionUtils from '@libs/TransactionUtils'; -import * as UserUtils from '@libs/UserUtils'; import ViolationsUtils from '@libs/Violations/ViolationsUtils'; import type {IOUAction, IOUType} from '@src/CONST'; import CONST from '@src/CONST'; @@ -1468,7 +1467,6 @@ function getMoneyRequestInformation( ? { [payerAccountID]: { accountID: payerAccountID, - avatar: UserUtils.getDefaultAvatarURL(payerAccountID), // Disabling this line since participant.displayName can be an empty string // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing displayName: LocalePhoneNumber.formatPhoneNumber(participant.displayName || payerEmail), @@ -3416,7 +3414,6 @@ function createSplitsAndOnyxData( ? { [accountID]: { accountID, - avatar: UserUtils.getDefaultAvatarURL(accountID), // Disabling this line since participant.displayName can be an empty string // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing displayName: LocalePhoneNumber.formatPhoneNumber(participant.displayName || email), @@ -3860,7 +3857,6 @@ function startSplitBill({ value: { [accountID]: { accountID, - avatar: UserUtils.getDefaultAvatarURL(accountID), // Disabling this line since participant.displayName can be an empty string // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing displayName: LocalePhoneNumber.formatPhoneNumber(participant.displayName || email), @@ -5045,7 +5041,6 @@ function getSendMoneyParams( value: { [recipientAccountID]: { accountID: recipientAccountID, - avatar: UserUtils.getDefaultAvatarURL(recipient.accountID), // Disabling this line since participant.displayName can be an empty string // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing displayName: recipient.displayName || recipient.login, diff --git a/src/libs/actions/Report.ts b/src/libs/actions/Report.ts index 9de059acd1d7..cb1d5f42ad2b 100644 --- a/src/libs/actions/Report.ts +++ b/src/libs/actions/Report.ts @@ -71,7 +71,6 @@ import * as ReportUtils from '@libs/ReportUtils'; import {doesReportBelongToWorkspace} from '@libs/ReportUtils'; import type {OptimisticAddCommentReportAction} from '@libs/ReportUtils'; import shouldSkipDeepLinkNavigation from '@libs/shouldSkipDeepLinkNavigation'; -import * as UserUtils from '@libs/UserUtils'; import Visibility from '@libs/Visibility'; import CONFIG from '@src/CONFIG'; import type {OnboardingPurposeType} from '@src/CONST'; @@ -821,7 +820,6 @@ function openReport( optimisticPersonalDetails[accountID] = allPersonalDetails?.[accountID] ?? { login, accountID, - avatar: UserUtils.getDefaultAvatarURL(accountID), displayName: login, isOptimisticPersonalDetail: true, }; diff --git a/src/libs/actions/Task.ts b/src/libs/actions/Task.ts index 2a37b900ddd4..752cd9df0325 100644 --- a/src/libs/actions/Task.ts +++ b/src/libs/actions/Task.ts @@ -14,7 +14,6 @@ import * as PersonalDetailsUtils from '@libs/PersonalDetailsUtils'; import * as ReportActionsUtils from '@libs/ReportActionsUtils'; import * as ReportUtils from '@libs/ReportUtils'; import playSound, {SOUNDS} from '@libs/Sound'; -import * as UserUtils from '@libs/UserUtils'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; @@ -685,7 +684,7 @@ function setAssigneeValue( // If this is an optimistic report, we likely don't have their personal details yet so we set it here optimistically as well const optimisticPersonalDetailsListAction = { accountID: assigneeAccountID, - avatar: allPersonalDetails?.[assigneeAccountID]?.avatar ?? UserUtils.getDefaultAvatarURL(assigneeAccountID), + avatar: allPersonalDetails?.[assigneeAccountID]?.avatar, displayName: allPersonalDetails?.[assigneeAccountID]?.displayName ?? assigneeEmail, login: assigneeEmail, }; diff --git a/src/pages/DetailsPage.tsx b/src/pages/DetailsPage.tsx index 49b3e856c65d..338e51cb408e 100755 --- a/src/pages/DetailsPage.tsx +++ b/src/pages/DetailsPage.tsx @@ -70,7 +70,6 @@ function DetailsPage({personalDetails, route, session}: DetailsPageProps) { accountID: optimisticAccountID, login, displayName: login, - avatar: UserUtils.getDefaultAvatar(optimisticAccountID), }; } @@ -115,9 +114,10 @@ function DetailsPage({personalDetails, route, session}: DetailsPageProps) { diff --git a/src/pages/ProfilePage.tsx b/src/pages/ProfilePage.tsx index a8e4223c0180..310d77cf8391 100755 --- a/src/pages/ProfilePage.tsx +++ b/src/pages/ProfilePage.tsx @@ -25,7 +25,6 @@ import Navigation from '@libs/Navigation/Navigation'; import * as PersonalDetailsUtils from '@libs/PersonalDetailsUtils'; import {parsePhoneNumber} from '@libs/PhoneNumber'; import * as ReportUtils from '@libs/ReportUtils'; -import * as UserUtils from '@libs/UserUtils'; import * as ValidationUtils from '@libs/ValidationUtils'; import type {ProfileNavigatorParamList} from '@navigation/types'; import * as PersonalDetailsActions from '@userActions/PersonalDetails'; @@ -96,8 +95,6 @@ function ProfilePage({route}: ProfilePageProps) { const details: PersonalDetails | EmptyObject = personalDetails?.[accountID] ?? (ValidationUtils.isValidAccountRoute(accountID) ? {} : {accountID: 0, avatar: ''}); const displayName = PersonalDetailsUtils.getDisplayNameOrDefault(details, undefined, undefined, isCurrentUser); - // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing - const avatar = details?.avatar || UserUtils.getDefaultAvatar(); // we can have an empty string and in this case, we need to show the default avatar const fallbackIcon = details?.fallbackIcon ?? ''; const login = details?.login ?? ''; const timezone = details?.timezone; @@ -164,9 +161,10 @@ function ProfilePage({route}: ProfilePageProps) { diff --git a/src/pages/ReportParticipantDetailsPage.tsx b/src/pages/ReportParticipantDetailsPage.tsx index 2b1411641faa..563f24635759 100644 --- a/src/pages/ReportParticipantDetailsPage.tsx +++ b/src/pages/ReportParticipantDetailsPage.tsx @@ -18,7 +18,6 @@ import useStyleUtils from '@hooks/useStyleUtils'; import useThemeStyles from '@hooks/useThemeStyles'; import * as Report from '@libs/actions/Report'; import * as ReportUtils from '@libs/ReportUtils'; -import * as UserUtils from '@libs/UserUtils'; import Navigation from '@navigation/Navigation'; import type {ParticipantsNavigatorParamList} from '@navigation/types'; import CONST from '@src/CONST'; @@ -51,7 +50,7 @@ function ReportParticipantDetails({personalDetails, report, route}: ReportPartic const member = report?.participants?.[accountID]; const details = personalDetails?.[accountID] ?? ({} as PersonalDetails); - const avatar = details.avatar ?? UserUtils.getDefaultAvatar(); + const avatar = details.avatar; const fallbackIcon = details.fallbackIcon ?? ''; const displayName = details.displayName ?? ''; const isCurrentUserAdmin = ReportUtils.isGroupChatAdmin(report, currentUserPersonalDetails?.accountID); @@ -81,7 +80,8 @@ function ReportParticipantDetails({personalDetails, report, route}: ReportPartic diff --git a/src/pages/ReportParticipantsPage.tsx b/src/pages/ReportParticipantsPage.tsx index 9c0e19e85ee4..995dc3045bda 100755 --- a/src/pages/ReportParticipantsPage.tsx +++ b/src/pages/ReportParticipantsPage.tsx @@ -13,6 +13,7 @@ import type {DropdownOption, WorkspaceMemberBulkActionType} from '@components/Bu import ConfirmModal from '@components/ConfirmModal'; import HeaderWithBackButton from '@components/HeaderWithBackButton'; import * as Expensicons from '@components/Icon/Expensicons'; +import {FallbackAvatar} from '@components/Icon/Expensicons'; import ScreenWrapper from '@components/ScreenWrapper'; import SelectionList from '@components/SelectionList'; import TableListItem from '@components/SelectionList/TableListItem'; @@ -27,7 +28,6 @@ import Log from '@libs/Log'; import Navigation from '@libs/Navigation/Navigation'; import * as PersonalDetailsUtils from '@libs/PersonalDetailsUtils'; import * as ReportUtils from '@libs/ReportUtils'; -import * as UserUtils from '@libs/UserUtils'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; @@ -99,7 +99,7 @@ function ReportParticipantsPage({report, personalDetails, session}: ReportPartic pendingAction: pendingChatMember?.pendingAction, icons: [ { - source: UserUtils.getAvatar(details?.avatar, accountID), + source: details.avatar ?? FallbackAvatar, name: formatPhoneNumber(details?.login ?? ''), type: CONST.ICON_TYPE_AVATAR, id: accountID, diff --git a/src/pages/RoomMembersPage.tsx b/src/pages/RoomMembersPage.tsx index 488bde658c3f..abd99774d9e0 100644 --- a/src/pages/RoomMembersPage.tsx +++ b/src/pages/RoomMembersPage.tsx @@ -8,6 +8,7 @@ import FullPageNotFoundView from '@components/BlockingViews/FullPageNotFoundView import Button from '@components/Button'; import ConfirmModal from '@components/ConfirmModal'; import HeaderWithBackButton from '@components/HeaderWithBackButton'; +import {FallbackAvatar} from '@components/Icon/Expensicons'; import {usePersonalDetails} from '@components/OnyxProvider'; import ScreenWrapper from '@components/ScreenWrapper'; import SelectionList from '@components/SelectionList'; @@ -26,7 +27,6 @@ import * as OptionsListUtils from '@libs/OptionsListUtils'; import * as PersonalDetailsUtils from '@libs/PersonalDetailsUtils'; import * as PolicyUtils from '@libs/PolicyUtils'; import * as ReportUtils from '@libs/ReportUtils'; -import * as UserUtils from '@libs/UserUtils'; import * as Report from '@userActions/Report'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; @@ -209,7 +209,7 @@ function RoomMembersPage({report, session, policies}: RoomMembersPageProps) { alternateText: details?.login ? formatPhoneNumber(details.login) : '', icons: [ { - source: UserUtils.getAvatar(details.avatar, accountID), + source: details.avatar ?? FallbackAvatar, name: details.login ?? '', type: CONST.ICON_TYPE_AVATAR, id: Number(accountID), diff --git a/src/pages/home/report/ReactionList/BaseReactionList.tsx b/src/pages/home/report/ReactionList/BaseReactionList.tsx index 6f56f8f09632..23417c1395df 100755 --- a/src/pages/home/report/ReactionList/BaseReactionList.tsx +++ b/src/pages/home/report/ReactionList/BaseReactionList.tsx @@ -2,11 +2,11 @@ import Str from 'expensify-common/lib/str'; import React from 'react'; import {FlatList} from 'react-native'; import type {FlatListProps} from 'react-native'; +import {FallbackAvatar} from '@components/Icon/Expensicons'; import OptionRow from '@components/OptionRow'; import useThemeStyles from '@hooks/useThemeStyles'; import useWindowDimensions from '@hooks/useWindowDimensions'; import Navigation from '@libs/Navigation/Navigation'; -import * as UserUtils from '@libs/UserUtils'; import variables from '@styles/variables'; import CONST from '@src/CONST'; import ROUTES from '@src/ROUTES'; @@ -71,7 +71,7 @@ function BaseReactionList({hasUserReacted = false, users, isVisible = false, emo icons: [ { id: item.accountID, - source: UserUtils.getAvatar(item.avatar, item.accountID), + source: item.avatar ?? FallbackAvatar, name: item.login ?? '', type: CONST.ICON_TYPE_AVATAR, }, diff --git a/src/pages/home/report/ReportActionCompose/SuggestionMention.tsx b/src/pages/home/report/ReportActionCompose/SuggestionMention.tsx index dbd550e1cd7c..b2be605c3c07 100644 --- a/src/pages/home/report/ReportActionCompose/SuggestionMention.tsx +++ b/src/pages/home/report/ReportActionCompose/SuggestionMention.tsx @@ -5,6 +5,7 @@ import React, {forwardRef, useCallback, useEffect, useImperativeHandle, useRef, import {useOnyx} from 'react-native-onyx'; import type {OnyxCollection} from 'react-native-onyx'; import * as Expensicons from '@components/Icon/Expensicons'; +import {FallbackAvatar} from '@components/Icon/Expensicons'; import type {Mention} from '@components/MentionSuggestions'; import MentionSuggestions from '@components/MentionSuggestions'; import {usePersonalDetails} from '@components/OnyxProvider'; @@ -16,7 +17,6 @@ import * as LoginUtils from '@libs/LoginUtils'; import * as PersonalDetailsUtils from '@libs/PersonalDetailsUtils'; import * as ReportUtils from '@libs/ReportUtils'; import * as SuggestionsUtils from '@libs/SuggestionUtils'; -import * as UserUtils from '@libs/UserUtils'; import {isValidRoomName} from '@libs/ValidationUtils'; import * as ReportUserActions from '@userActions/Report'; import CONST from '@src/CONST'; @@ -242,9 +242,10 @@ function SuggestionMention( icons: [ { name: detail?.login, - source: UserUtils.getAvatar(detail?.avatar, detail?.accountID), + source: detail?.avatar ?? FallbackAvatar, type: CONST.ICON_TYPE_AVATAR, fallbackIcon: detail?.fallbackIcon, + id: detail?.accountID, }, ], }); diff --git a/src/pages/home/report/ReportActionItemSingle.tsx b/src/pages/home/report/ReportActionItemSingle.tsx index dda17e1e83d3..54b6775cfe13 100644 --- a/src/pages/home/report/ReportActionItemSingle.tsx +++ b/src/pages/home/report/ReportActionItemSingle.tsx @@ -3,6 +3,7 @@ import type {StyleProp, ViewStyle} from 'react-native'; import {View} from 'react-native'; import type {OnyxEntry} from 'react-native-onyx'; import Avatar from '@components/Avatar'; +import {FallbackAvatar} from '@components/Icon/Expensicons'; import MultipleAvatars from '@components/MultipleAvatars'; import OfflineWithFeedback from '@components/OfflineWithFeedback'; import {usePersonalDetails} from '@components/OnyxProvider'; @@ -19,7 +20,6 @@ import ControlSelection from '@libs/ControlSelection'; import DateUtils from '@libs/DateUtils'; import Navigation from '@libs/Navigation/Navigation'; import * as ReportUtils from '@libs/ReportUtils'; -import * as UserUtils from '@libs/UserUtils'; import CONST from '@src/CONST'; import ROUTES from '@src/ROUTES'; import type {Report, ReportAction} from '@src/types/onyx'; @@ -86,12 +86,14 @@ function ReportActionItemSingle({ let actorHint = (login || (displayName ?? '')).replace(CONST.REGEX.MERGED_ACCOUNT_PREFIX, ''); const displayAllActors = useMemo(() => action?.actionName === CONST.REPORT.ACTIONS.TYPE.REPORT_PREVIEW && iouReport, [action?.actionName, iouReport]); const isWorkspaceActor = ReportUtils.isPolicyExpenseChat(report) && (!actorAccountID || displayAllActors); - let avatarSource = UserUtils.getAvatar(avatar ?? '', actorAccountID); + let avatarSource = avatar; + let avatarAccountId = actorAccountID; if (isWorkspaceActor) { displayName = ReportUtils.getPolicyName(report); actorHint = displayName; avatarSource = ReportUtils.getWorkspaceAvatar(report); + avatarAccountId = undefined; } else if (action?.delegateAccountID && personalDetails[action?.delegateAccountID]) { // We replace the actor's email, name, and avatar with the Copilot manually for now. And only if we have their // details. This will be improved upon when the Copilot feature is implemented. @@ -99,7 +101,8 @@ function ReportActionItemSingle({ const delegateDisplayName = delegateDetails?.displayName; actorHint = `${delegateDisplayName} (${translate('reportAction.asCopilot')} ${displayName})`; displayName = actorHint; - avatarSource = UserUtils.getAvatar(delegateDetails?.avatar ?? '', Number(action.delegateAccountID)); + avatarSource = delegateDetails?.avatar; + avatarAccountId = action.delegateAccountID; } // If this is a report preview, display names and avatars of both people involved @@ -112,7 +115,7 @@ function ReportActionItemSingle({ const secondaryDisplayName = ReportUtils.getDisplayNameForParticipant(secondaryAccountId); displayName = `${primaryDisplayName} & ${secondaryDisplayName}`; secondaryAvatar = { - source: UserUtils.getAvatar(secondaryUserAvatar, secondaryAccountId), + source: secondaryUserAvatar, type: CONST.ICON_TYPE_AVATAR, name: secondaryDisplayName ?? '', id: secondaryAccountId, @@ -126,11 +129,12 @@ function ReportActionItemSingle({ } else { secondaryAvatar = {name: '', source: '', type: 'avatar'}; } + const icon = { - source: avatarSource, + source: avatarSource ?? FallbackAvatar, type: isWorkspaceActor ? CONST.ICON_TYPE_WORKSPACE : CONST.ICON_TYPE_AVATAR, name: primaryDisplayName ?? '', - id: isWorkspaceActor ? '' : actorAccountID, + id: avatarAccountId, }; // Since the display name for a report action message is delivered with the report history as an array of fragments @@ -201,6 +205,7 @@ function ReportActionItemSingle({ type={icon.type} name={icon.name} fallbackIcon={fallbackIcon} + accountID={icon.id} /> diff --git a/src/pages/home/sidebar/ProfileAvatarWithIndicator.tsx b/src/pages/home/sidebar/ProfileAvatarWithIndicator.tsx index e7726fb89537..b0287efb8990 100644 --- a/src/pages/home/sidebar/ProfileAvatarWithIndicator.tsx +++ b/src/pages/home/sidebar/ProfileAvatarWithIndicator.tsx @@ -5,7 +5,6 @@ import AvatarWithIndicator from '@components/AvatarWithIndicator'; import OfflineWithFeedback from '@components/OfflineWithFeedback'; import useCurrentUserPersonalDetails from '@hooks/useCurrentUserPersonalDetails'; import useThemeStyles from '@hooks/useThemeStyles'; -import * as UserUtils from '@libs/UserUtils'; import ONYXKEYS from '@src/ONYXKEYS'; type ProfileAvatarWithIndicatorProps = { @@ -23,7 +22,8 @@ function ProfileAvatarWithIndicator({isSelected = false}: ProfileAvatarWithIndic diff --git a/src/pages/workspace/WorkspaceMembersPage.tsx b/src/pages/workspace/WorkspaceMembersPage.tsx index 30d92363afcd..b18522662dde 100644 --- a/src/pages/workspace/WorkspaceMembersPage.tsx +++ b/src/pages/workspace/WorkspaceMembersPage.tsx @@ -14,6 +14,7 @@ import type {DropdownOption, WorkspaceMemberBulkActionType} from '@components/Bu import ConfirmModal from '@components/ConfirmModal'; import HeaderWithBackButton from '@components/HeaderWithBackButton'; import * as Expensicons from '@components/Icon/Expensicons'; +import {FallbackAvatar} from '@components/Icon/Expensicons'; import * as Illustrations from '@components/Icon/Illustrations'; import MessagesRow from '@components/MessagesRow'; import ScreenWrapper from '@components/ScreenWrapper'; @@ -36,7 +37,6 @@ import type {WorkspacesCentralPaneNavigatorParamList} from '@libs/Navigation/typ import * as OptionsListUtils from '@libs/OptionsListUtils'; import * as PersonalDetailsUtils from '@libs/PersonalDetailsUtils'; import * as PolicyUtils from '@libs/PolicyUtils'; -import * as UserUtils from '@libs/UserUtils'; import * as Policy from '@userActions/Policy'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; @@ -353,7 +353,7 @@ function WorkspaceMembersPage({personalDetails, invitedEmailsToAccountIDsDraft, rightElement: roleBadge, icons: [ { - source: UserUtils.getAvatar(details.avatar, accountID), + source: details.avatar ?? FallbackAvatar, name: formatPhoneNumber(details?.login ?? ''), type: CONST.ICON_TYPE_AVATAR, id: accountID, diff --git a/src/pages/workspace/members/WorkspaceMemberDetailsPage.tsx b/src/pages/workspace/members/WorkspaceMemberDetailsPage.tsx index 32b43a230619..17d5f56d1dbf 100644 --- a/src/pages/workspace/members/WorkspaceMemberDetailsPage.tsx +++ b/src/pages/workspace/members/WorkspaceMemberDetailsPage.tsx @@ -18,7 +18,6 @@ import useLocalize from '@hooks/useLocalize'; import useNetwork from '@hooks/useNetwork'; import useStyleUtils from '@hooks/useStyleUtils'; import useThemeStyles from '@hooks/useThemeStyles'; -import * as UserUtils from '@libs/UserUtils'; import Navigation from '@navigation/Navigation'; import type {SettingsNavigatorParamList} from '@navigation/types'; import AdminPolicyAccessOrNotFoundWrapper from '@pages/workspace/AdminPolicyAccessOrNotFoundWrapper'; @@ -58,7 +57,6 @@ function WorkspaceMemberDetailsPage({personalDetails, policy, route}: WorkspaceM const memberLogin = personalDetails?.[accountID]?.login ?? ''; const member = policy?.employeeList?.[memberLogin]; const details = personalDetails?.[accountID] ?? ({} as PersonalDetails); - const avatar = details.avatar ?? UserUtils.getDefaultAvatar(); const fallbackIcon = details.fallbackIcon ?? ''; const displayName = details.displayName ?? ''; const isSelectedMemberOwner = policy?.owner === details.login; @@ -142,9 +140,10 @@ function WorkspaceMemberDetailsPage({personalDetails, policy, route}: WorkspaceM {Boolean(details.displayName ?? '') && ( diff --git a/src/pages/workspace/workflows/WorkspaceWorkflowsApproverPage.tsx b/src/pages/workspace/workflows/WorkspaceWorkflowsApproverPage.tsx index 74434fbdd1f8..8be9afd790d8 100644 --- a/src/pages/workspace/workflows/WorkspaceWorkflowsApproverPage.tsx +++ b/src/pages/workspace/workflows/WorkspaceWorkflowsApproverPage.tsx @@ -5,6 +5,7 @@ import type {OnyxEntry} from 'react-native-onyx'; import Badge from '@components/Badge'; import FullPageNotFoundView from '@components/BlockingViews/FullPageNotFoundView'; import HeaderWithBackButton from '@components/HeaderWithBackButton'; +import {FallbackAvatar} from '@components/Icon/Expensicons'; import ScreenWrapper from '@components/ScreenWrapper'; import SelectionList from '@components/SelectionList'; import type {ListItem, Section} from '@components/SelectionList/types'; @@ -18,7 +19,6 @@ import type {WorkspacesCentralPaneNavigatorParamList} from '@libs/Navigation/typ import * as OptionsListUtils from '@libs/OptionsListUtils'; import * as PersonalDetailsUtils from '@libs/PersonalDetailsUtils'; import * as PolicyUtils from '@libs/PolicyUtils'; -import * as UserUtils from '@libs/UserUtils'; import FeatureEnabledAccessOrNotFoundWrapper from '@pages/workspace/FeatureEnabledAccessOrNotFoundWrapper'; import withPolicyAndFullscreenLoading from '@pages/workspace/withPolicyAndFullscreenLoading'; import type {WithPolicyAndFullscreenLoadingProps} from '@pages/workspace/withPolicyAndFullscreenLoading'; @@ -87,7 +87,7 @@ function WorkspaceWorkflowsApproverPage({policy, personalDetails, isLoadingRepor rightElement: roleBadge, icons: [ { - source: UserUtils.getAvatar(details.avatar, accountID), + source: details.avatar ?? FallbackAvatar, name: formatPhoneNumber(details?.login ?? ''), type: CONST.ICON_TYPE_AVATAR, id: accountID, diff --git a/src/pages/workspace/workflows/WorkspaceWorkflowsPayerPage.tsx b/src/pages/workspace/workflows/WorkspaceWorkflowsPayerPage.tsx index 3bd4ab9003c5..6a6d64d51eb1 100644 --- a/src/pages/workspace/workflows/WorkspaceWorkflowsPayerPage.tsx +++ b/src/pages/workspace/workflows/WorkspaceWorkflowsPayerPage.tsx @@ -5,6 +5,7 @@ import type {OnyxEntry} from 'react-native-onyx'; import Badge from '@components/Badge'; import FullPageNotFoundView from '@components/BlockingViews/FullPageNotFoundView'; import HeaderWithBackButton from '@components/HeaderWithBackButton'; +import {FallbackAvatar} from '@components/Icon/Expensicons'; import ScreenWrapper from '@components/ScreenWrapper'; import SelectionList from '@components/SelectionList'; import type {ListItem, Section} from '@components/SelectionList/types'; @@ -17,7 +18,6 @@ import Navigation from '@libs/Navigation/Navigation'; import * as OptionsListUtils from '@libs/OptionsListUtils'; import * as PersonalDetailsUtils from '@libs/PersonalDetailsUtils'; import * as PolicyUtils from '@libs/PolicyUtils'; -import * as UserUtils from '@libs/UserUtils'; import type {SettingsNavigatorParamList} from '@navigation/types'; import AdminPolicyAccessOrNotFoundWrapper from '@pages/workspace/AdminPolicyAccessOrNotFoundWrapper'; import PaidPolicyAccessOrNotFoundWrapper from '@pages/workspace/PaidPolicyAccessOrNotFoundWrapper'; @@ -88,7 +88,7 @@ function WorkspaceWorkflowsPayerPage({route, policy, personalDetails, isLoadingR rightElement: roleBadge, icons: [ { - source: UserUtils.getAvatar(details?.avatar, accountID), + source: details?.avatar ?? FallbackAvatar, name: formatPhoneNumber(details?.login ?? ''), type: CONST.ICON_TYPE_AVATAR, id: accountID, diff --git a/src/types/onyx/OnyxCommon.ts b/src/types/onyx/OnyxCommon.ts index 8b96a89a2a1b..9ac596a85777 100644 --- a/src/types/onyx/OnyxCommon.ts +++ b/src/types/onyx/OnyxCommon.ts @@ -34,7 +34,7 @@ type Icon = { name?: string; /** Avatar id */ - id?: number | string; + id?: number; /** A fallback avatar icon to display when there is an error on loading avatar from remote URL. */ fallbackIcon?: AvatarSource;