diff --git a/src/components/Avatar.tsx b/src/components/Avatar.tsx index f633869ccd30..c0d89f4acf1e 100644 --- a/src/components/Avatar.tsx +++ b/src/components/Avatar.tsx @@ -80,18 +80,19 @@ function Avatar({ }, [originalSource]); const isWorkspace = type === CONST.ICON_TYPE_WORKSPACE; - 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 source = isWorkspace ? originalSource : UserUtils.getAvatar(originalSource, Number(avatarID)); + // If it's user avatar then accountID will be a number + const source = isWorkspace ? originalSource : UserUtils.getAvatar(originalSource, avatarID as number); 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; + // We pass the color styles down to the SVG for the workspace and fallback avatar. + const iconSize = StyleUtils.getAvatarSize(size); + const imageStyle: StyleProp = [StyleUtils.getAvatarStyle(size), imageStyles, styles.noBorderRadius]; + const iconStyle = imageStyles ? [StyleUtils.getAvatarStyle(size), styles.bgTransparent, imageStyles] : undefined; + let iconColors; if (isWorkspace) { iconColors = StyleUtils.getDefaultWorkspaceAvatarColor(avatarID?.toString() ?? ''); diff --git a/src/components/AvatarWithImagePicker.tsx b/src/components/AvatarWithImagePicker.tsx index 5fa17e726c6f..bf8b475aa016 100644 --- a/src/components/AvatarWithImagePicker.tsx +++ b/src/components/AvatarWithImagePicker.tsx @@ -49,6 +49,9 @@ type AvatarWithImagePickerProps = { /** Avatar source to display */ source?: AvatarSource; + /** Account id of user for which avatar is displayed */ + avatarID?: number | string; + /** Additional style props */ style?: StyleProp; @@ -136,6 +139,7 @@ function AvatarWithImagePicker({ errorRowStyles, onErrorClose = () => {}, source = '', + avatarID, fallbackIcon = Expensicons.FallbackAvatar, size = CONST.AVATAR_SIZE.DEFAULT, type = CONST.ICON_TYPE_AVATAR, @@ -329,6 +333,7 @@ function AvatarWithImagePicker({ containerStyles={avatarStyle} imageStyles={[avatarStyle, styles.alignSelfCenter]} source={source} + avatarID={avatarID} fallbackIcon={fallbackIcon} size={size} type={type} diff --git a/src/components/AvatarWithIndicator.tsx b/src/components/AvatarWithIndicator.tsx index 42b91b3d2d71..f024a1239f4e 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,8 +38,9 @@ function AvatarWithIndicator({source, tooltipText = '', fallbackIcon = Expensico <> diff --git a/src/components/MenuItem.tsx b/src/components/MenuItem.tsx index ad06f9fcb78c..1d4c57c167ff 100644 --- a/src/components/MenuItem.tsx +++ b/src/components/MenuItem.tsx @@ -527,7 +527,7 @@ function MenuItem( diff --git a/src/components/ReportActionItem/TaskView.tsx b/src/components/ReportActionItem/TaskView.tsx index 9ddcecf8f098..43e896fe6578 100644 --- a/src/components/ReportActionItem/TaskView.tsx +++ b/src/components/ReportActionItem/TaskView.tsx @@ -152,7 +152,7 @@ function TaskView({report, ...props}: TaskViewProps) { {title} diff --git a/src/libs/OptionsListUtils.ts b/src/libs/OptionsListUtils.ts index 0d5854764946..f7414a2c5458 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'; @@ -337,13 +338,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 ?? '', }; @@ -366,9 +368,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) { @@ -397,6 +397,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, @@ -407,7 +408,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, @@ -781,13 +782,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, null); result.subtitle = subtitle; return result; @@ -1640,7 +1635,8 @@ function getUserToInviteOption({ // If user doesn't exist, use a fallback avatar userToInvite.icons = [ { - source: UserUtils.getAvatar('', optimisticAccountID), + source: FallbackAvatar, + id: optimisticAccountID, name: searchValue, type: CONST.ICON_TYPE_AVATAR, }, @@ -2043,7 +2039,7 @@ function getIOUConfirmationOptionsFromPayeePersonalDetail(personalDetail: Person alternateText: formattedLogin || PersonalDetailsUtils.getDisplayNameOrDefault(personalDetail, '', false), icons: [ { - source: UserUtils.getAvatar(personalDetail.avatar, personalDetail.accountID), + source: personalDetail.avatar ?? FallbackAvatar, name: personalDetail.login ?? '', type: CONST.ICON_TYPE_AVATAR, id: personalDetail.accountID, diff --git a/src/libs/PersonalDetailsUtils.ts b/src/libs/PersonalDetailsUtils.ts index a040c84b5c25..5b0469807f55 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 fe00c8f699e0..f1d63ec0f3a8 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 {MoneyRequestAmountInputProps} from '@components/MoneyRequestAmountInput'; @@ -80,7 +80,7 @@ import type {LastVisibleMessage} from './ReportActionsUtils'; import * as ReportActionsUtils from './ReportActionsUtils'; import * as TransactionUtils from './TransactionUtils'; import * as Url from './Url'; -import * as UserUtils from './UserUtils'; +import type * as UserUtils from './UserUtils'; type AvatarRange = 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18; @@ -586,8 +586,8 @@ function getLastUpdatedReport(): OnyxEntry { return lastUpdatedReport; } -function getCurrentUserAvatarOrDefault(): UserUtils.AvatarSource { - return currentUserPersonalDetails?.avatar ?? UserUtils.getDefaultAvatarURL(currentUserAccountID); +function getCurrentUserAvatar(): UserUtils.AvatarSource | undefined { + return currentUserPersonalDetails?.avatar; } function getCurrentUserDisplayNameOrEmail(): string | undefined { @@ -1732,7 +1732,7 @@ function getIconsForParticipants(participants: number[], personalDetails: OnyxEn 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 ?? '']); } @@ -1793,12 +1793,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 b4ae942547a1..0b6271844046 100644 --- a/src/libs/SidebarUtils.ts +++ b/src/libs/SidebarUtils.ts @@ -21,7 +21,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({ @@ -426,7 +425,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, personalDetail?.login, personalDetail?.accountID, 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..2acebd4636f5 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'; @@ -79,33 +79,33 @@ function generateAccountID(searchValue: string): number { /** * Helper method to return the default avatar associated with the given accountID - * @param [accountID] - * @returns */ -function getDefaultAvatar(accountID = -1, avatarURL?: string): IconAsset { - if (accountID <= 0) { - return FallbackAvatar; - } - if (Number(accountID) === CONST.ACCOUNT_ID.CONCIERGE) { +function getDefaultAvatar(accountID = -1, avatarURL?: string): IconAsset | undefined { + if (accountID === CONST.ACCOUNT_ID.CONCIERGE) { return ConciergeAvatar; } - if (Number(accountID) === CONST.ACCOUNT_ID.NOTIFICATIONS) { + if (accountID === CONST.ACCOUNT_ID.NOTIFICATIONS) { return NotificationsAvatar; } // There are 24 possible default avatars, so we choose which one this user has based // on a simple modulo operation of their login number. Note that Avatar count starts at 1. - // When creating a chat, we generate an avatar using an ID and the backend response will modify the ID to the actual user ID. + // When creating a chat the backend response will return the actual user ID. // But the avatar link still corresponds to the original ID-generated link. So we extract the SVG image number from the backend's link instead of using the user ID directly - let accountIDHashBucket: AvatarRange; + let accountIDHashBucket: AvatarRange | undefined; if (avatarURL) { const match = avatarURL.match(/(default-avatar_|avatar_)(\d+)(?=\.)/); const lastDigit = match && parseInt(match[2], 10); accountIDHashBucket = lastDigit as AvatarRange; - } else { + } else if (accountID > 0) { accountIDHashBucket = ((accountID % CONST.DEFAULT_AVATAR_COUNT) + 1) as AvatarRange; } + + if (!accountIDHashBucket) { + return; + } + return defaultAvatars[`Avatar${accountIDHashBucket}`]; } @@ -125,7 +125,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 +140,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 +150,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 +158,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 +169,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 +181,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 0bc72a387227..4cfef34ab159 100644 --- a/src/libs/actions/IOU.ts +++ b/src/libs/actions/IOU.ts @@ -43,7 +43,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'; @@ -1707,7 +1706,6 @@ function getSendInvoiceInformation( const receiverLogin = receiverParticipant && 'login' in receiverParticipant && receiverParticipant.login ? receiverParticipant.login : ''; receiver = { accountID: receiverAccountID, - avatar: UserUtils.getDefaultAvatarURL(receiverAccountID), displayName: LocalePhoneNumber.formatPhoneNumber(receiverLogin), login: receiverLogin, isOptimisticPersonalDetail: true, @@ -1934,7 +1932,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), @@ -4008,7 +4005,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), @@ -4479,7 +4475,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), @@ -5587,7 +5582,6 @@ function getSendMoneyParams( ? { [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 26aaa7661de7..c36b381c55ed 100644 --- a/src/libs/actions/Report.ts +++ b/src/libs/actions/Report.ts @@ -72,7 +72,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'; @@ -863,7 +862,6 @@ function openReport( optimisticPersonalDetails[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 1d4415f72f4b..55d898d3d4f3 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'; @@ -666,7 +665,7 @@ function setNewOptimisticAssignee(assigneeLogin: string, assigneeAccountID: numb const optimisticPersonalDetailsListAction: OnyxTypes.PersonalDetails = { accountID: assigneeAccountID, - avatar: allPersonalDetails?.[assigneeAccountID]?.avatar ?? UserUtils.getDefaultAvatarURL(assigneeAccountID), + avatar: allPersonalDetails?.[assigneeAccountID]?.avatar, displayName: allPersonalDetails?.[assigneeAccountID]?.displayName ?? assigneeLogin, login: assigneeLogin, }; diff --git a/src/pages/DetailsPage.tsx b/src/pages/DetailsPage.tsx index 49b3e856c65d..2198be675c11 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,7 +114,8 @@ function DetailsPage({personalDetails, route, session}: DetailsPageProps) { diff --git a/src/pages/ProfilePage.tsx b/src/pages/ProfilePage.tsx index d5e556b25dd0..7ce9bcf47c9c 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'; @@ -58,7 +57,7 @@ const getPhoneNumber = ({login = '', displayName = ''}: PersonalDetails | EmptyO }; /** - * This function narrow down the data from Onyx to just the properties that we want to trigger a re-render of the component. This helps minimize re-rendering + * This function narrows down the data from Onyx to just the properties that we want to trigger a re-render of the component. This helps minimize re-rendering * and makes the entire component more performant because it's not re-rendering when a bunch of properties change which aren't ever used in the UI. */ const chatReportSelector = (report: OnyxEntry): OnyxEntry => @@ -93,11 +92,9 @@ function ProfilePage({route}: ProfilePageProps) { const {translate, formatPhoneNumber} = useLocalize(); const accountID = Number(route.params?.accountID ?? 0); const isCurrentUser = session?.accountID === accountID; - const details: PersonalDetails | EmptyObject = personalDetails?.[accountID] ?? (ValidationUtils.isValidAccountRoute(accountID) ? {} : {accountID: 0, avatar: ''}); + const details: PersonalDetails | EmptyObject = personalDetails?.[accountID] ?? (ValidationUtils.isValidAccountRoute(accountID) ? {} : {accountID: 0}); 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; @@ -117,12 +114,9 @@ function ProfilePage({route}: ProfilePageProps) { const phoneNumber = getPhoneNumber(details); const phoneOrEmail = isSMSLogin ? getPhoneNumber(details) : login; - const hasMinimumDetails = !isEmptyObject(details.avatar); + const hasAvatar = Boolean(details.avatar); const isLoading = Boolean(personalDetailsMetadata?.[accountID]?.isLoading) || isEmptyObject(details); - // If the API returns an error for some reason there won't be any details and isLoading will get set to false, so we want to show a blocking screen - const shouldShowBlockingView = !hasMinimumDetails && !isLoading; - const statusEmojiCode = details?.status?.emojiCode ?? ''; const statusText = details?.status?.text ?? ''; const hasStatus = !!statusEmojiCode; @@ -145,113 +139,113 @@ function ProfilePage({route}: ProfilePageProps) { return ( - + Navigation.goBack(navigateBackTo)} /> - {hasMinimumDetails && ( - - - Navigation.navigate(ROUTES.PROFILE_AVATAR.getRoute(String(accountID)))} - accessibilityLabel={translate('common.profile')} - accessibilityRole={CONST.ACCESSIBILITY_ROLE.IMAGEBUTTON} + + + Navigation.navigate(ROUTES.PROFILE_AVATAR.getRoute(String(accountID)))} + accessibilityLabel={translate('common.profile')} + accessibilityRole={CONST.ACCESSIBILITY_ROLE.IMAGEBUTTON} + disabled={!hasAvatar} + > + + + + + {Boolean(displayName) && ( + - - - - - {Boolean(displayName) && ( + {displayName} + + )} + {hasStatus && ( + - {displayName} + {translate('statusPage.status')} - )} - {hasStatus && ( - - - {translate('statusPage.status')} - - {statusContent} - - )} - - {/* Don't display email if current user is anonymous */} - {!(isCurrentUser && SessionActions.isAnonymousUser()) && login ? ( - - - {translate(isSMSLogin ? 'common.phoneNumber' : 'common.email')} - - - - {isSMSLogin ? formatPhoneNumber(phoneNumber ?? '') : login} - - - - ) : null} - {pronouns ? ( - - - {translate('profilePage.preferredPronouns')} - - {pronouns} - - ) : null} - {shouldShowLocalTime && } - - {shouldShowNotificationPreference && ( - Navigation.navigate(ROUTES.REPORT_SETTINGS_NOTIFICATION_PREFERENCES.getRoute(report.reportID))} - wrapperStyle={[styles.mtn6, styles.mb5]} - /> - )} - {!isCurrentUser && !SessionActions.isAnonymousUser() && ( - ReportActions.navigateToAndOpenReportWithAccountIDs([accountID])} - wrapperStyle={styles.breakAll} - shouldShowRightIcon - /> + {statusContent} + )} - {!isEmptyObject(report) && report.reportID && !isCurrentUser && ( - ReportUtils.navigateToPrivateNotes(report, session)} - wrapperStyle={styles.breakAll} - shouldShowRightIcon - brickRoadIndicator={ReportActions.hasErrorInPrivateNotes(report) ? CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR : undefined} - /> - )} - - )} - {!hasMinimumDetails && isLoading && } + + {/* Don't display email if current user is anonymous */} + {!(isCurrentUser && SessionActions.isAnonymousUser()) && login ? ( + + + {translate(isSMSLogin ? 'common.phoneNumber' : 'common.email')} + + + + {isSMSLogin ? formatPhoneNumber(phoneNumber ?? '') : login} + + + + ) : null} + {pronouns ? ( + + + {translate('profilePage.preferredPronouns')} + + {pronouns} + + ) : null} + {shouldShowLocalTime && } + + {shouldShowNotificationPreference && ( + Navigation.navigate(ROUTES.REPORT_SETTINGS_NOTIFICATION_PREFERENCES.getRoute(report.reportID))} + wrapperStyle={[styles.mtn6, styles.mb5]} + /> + )} + {!isCurrentUser && !SessionActions.isAnonymousUser() && ( + ReportActions.navigateToAndOpenReportWithAccountIDs([accountID])} + wrapperStyle={styles.breakAll} + shouldShowRightIcon + /> + )} + {!isEmptyObject(report) && report.reportID && !isCurrentUser && ( + ReportUtils.navigateToPrivateNotes(report, session)} + wrapperStyle={styles.breakAll} + shouldShowRightIcon + brickRoadIndicator={ReportActions.hasErrorInPrivateNotes(report) ? CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR : undefined} + /> + )} + + {!hasAvatar && isLoading && } diff --git a/src/pages/ReportDetailsPage.tsx b/src/pages/ReportDetailsPage.tsx index a4d7c6eb7a22..30a2ff87dc8e 100644 --- a/src/pages/ReportDetailsPage.tsx +++ b/src/pages/ReportDetailsPage.tsx @@ -235,6 +235,7 @@ function ReportDetailsPage({policies, report, session, personalDetails}: ReportD isGroupChat && !isThread ? ( diff --git a/src/pages/ReportParticipantsPage.tsx b/src/pages/ReportParticipantsPage.tsx index a3670d722b34..74770203bdcc 100755 --- a/src/pages/ReportParticipantsPage.tsx +++ b/src/pages/ReportParticipantsPage.tsx @@ -27,7 +27,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'; @@ -101,7 +100,7 @@ function ReportParticipantsPage({report, personalDetails, session}: ReportPartic pendingAction, icons: [ { - source: UserUtils.getAvatar(details?.avatar, accountID), + source: details?.avatar ?? Expensicons.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 2a7c871742f5..aefd2603e859 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'; @@ -216,10 +216,10 @@ 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), + id: accountID, }, ], pendingAction: pendingChatMember?.pendingAction, 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 05e1163da200..a63f7b581286 100644 --- a/src/pages/home/report/ReportActionCompose/SuggestionMention.tsx +++ b/src/pages/home/report/ReportActionCompose/SuggestionMention.tsx @@ -16,7 +16,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 +241,10 @@ function SuggestionMention( icons: [ { name: detail?.login, - source: UserUtils.getAvatar(detail?.avatar, detail?.accountID), + source: detail?.avatar ?? Expensicons.FallbackAvatar, type: CONST.ICON_TYPE_AVATAR, fallbackIcon: detail?.fallbackIcon, + id: detail?.accountID, }, ], }); diff --git a/src/pages/home/report/ReportActionItemFragment.tsx b/src/pages/home/report/ReportActionItemFragment.tsx index de9e0b6a6ece..8f19d452f150 100644 --- a/src/pages/home/report/ReportActionItemFragment.tsx +++ b/src/pages/home/report/ReportActionItemFragment.tsx @@ -82,7 +82,7 @@ function ReportActionItemFragment({ source = '', style = [], delegateAccountID = 0, - actorIcon = {}, + actorIcon, isThreadParentMessage = false, isApprovedOrSubmittedReportAction = false, isHoldReportAction = false, diff --git a/src/pages/home/report/ReportActionItemSingle.tsx b/src/pages/home/report/ReportActionItemSingle.tsx index c082a9122dbb..f71db06c2d44 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'; @@ -87,12 +87,14 @@ function ReportActionItemSingle({ const displayAllActors = useMemo(() => action?.actionName === CONST.REPORT.ACTIONS.TYPE.REPORT_PREVIEW && iouReport, [action?.actionName, iouReport]); const isInvoiceReport = ReportUtils.isInvoiceReport(iouReport ?? {}); const isWorkspaceActor = isInvoiceReport || (ReportUtils.isPolicyExpenseChat(report) && (!actorAccountID || displayAllActors)); - let avatarSource = UserUtils.getAvatar(avatar ?? '', actorAccountID); + let avatarSource = avatar; + let avatarId: number | string | undefined = actorAccountID; if (isWorkspaceActor) { displayName = ReportUtils.getPolicyName(report); actorHint = displayName; avatarSource = ReportUtils.getWorkspaceAvatar(report); + avatarId = report.policyID; } 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. @@ -100,16 +102,17 @@ 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; + avatarId = action.delegateAccountID; } // If this is a report preview, display names and avatars of both people involved let secondaryAvatar: Icon; const primaryDisplayName = displayName; if (displayAllActors) { - // The ownerAccountID and actorAccountID can be the same if the a user submits an expense back from the IOU's original creator, in that case we need to use managerID to avoid displaying the same user twice + // The ownerAccountID and actorAccountID can be the same if the user submits an expense back from the IOU's original creator, in that case we need to use managerID to avoid displaying the same user twice const secondaryAccountId = iouReport?.ownerAccountID === actorAccountID || isInvoiceReport ? iouReport?.managerID : iouReport?.ownerAccountID; - const secondaryUserAvatar = personalDetails?.[secondaryAccountId ?? -1]?.avatar ?? ''; + const secondaryUserAvatar = personalDetails?.[secondaryAccountId ?? -1]?.avatar ?? FallbackAvatar; const secondaryDisplayName = ReportUtils.getDisplayNameForParticipant(secondaryAccountId); if (!isInvoiceReport) { @@ -117,7 +120,7 @@ function ReportActionItemSingle({ } secondaryAvatar = { - source: UserUtils.getAvatar(secondaryUserAvatar, secondaryAccountId), + source: secondaryUserAvatar, type: CONST.ICON_TYPE_AVATAR, name: secondaryDisplayName ?? '', id: secondaryAccountId, @@ -132,10 +135,10 @@ function ReportActionItemSingle({ 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 ? report.policyID : actorAccountID, + id: avatarId, }; // Since the display name for a report action message is delivered with the report history as an array of fragments diff --git a/src/pages/home/sidebar/ProfileAvatarWithIndicator.tsx b/src/pages/home/sidebar/ProfileAvatarWithIndicator.tsx index dd12963a7cde..b29506ea8e8f 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 = { @@ -22,7 +21,8 @@ function ProfileAvatarWithIndicator({isSelected = false}: ProfileAvatarWithIndic diff --git a/src/pages/settings/InitialSettingsPage.tsx b/src/pages/settings/InitialSettingsPage.tsx index 2dfeb1841a74..e383077ee7f7 100755 --- a/src/pages/settings/InitialSettingsPage.tsx +++ b/src/pages/settings/InitialSettingsPage.tsx @@ -413,7 +413,8 @@ function InitialSettingsPage({session, userWallet, bankAccountList, fundList, wa > ), diff --git a/src/pages/workspace/WorkspacesListRow.tsx b/src/pages/workspace/WorkspacesListRow.tsx index 86cbbabb1784..96963d1ffea0 100644 --- a/src/pages/workspace/WorkspacesListRow.tsx +++ b/src/pages/workspace/WorkspacesListRow.tsx @@ -214,6 +214,7 @@ function WorkspacesListRow({ <> diff --git a/src/pages/workspace/members/WorkspaceMemberDetailsPage.tsx b/src/pages/workspace/members/WorkspaceMemberDetailsPage.tsx index 4f1cb550cf05..5885712efe36 100644 --- a/src/pages/workspace/members/WorkspaceMemberDetailsPage.tsx +++ b/src/pages/workspace/members/WorkspaceMemberDetailsPage.tsx @@ -19,7 +19,6 @@ import useNetwork from '@hooks/useNetwork'; import usePrevious from '@hooks/usePrevious'; 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 NotFoundPage from '@pages/ErrorPage/NotFoundPage'; @@ -60,7 +59,6 @@ function WorkspaceMemberDetailsPage({personalDetails, policy, route}: WorkspaceM const member = policy?.employeeList?.[memberLogin]; const prevMember = usePrevious(member); 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; @@ -160,7 +158,8 @@ function WorkspaceMemberDetailsPage({personalDetails, policy, route}: WorkspaceM diff --git a/src/pages/workspace/workflows/WorkspaceWorkflowsApproverPage.tsx b/src/pages/workspace/workflows/WorkspaceWorkflowsApproverPage.tsx index 6125fef93183..e19c7d34758d 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 {FullScreenNavigatorParamList} from '@libs/Navigation/types'; 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 AccessOrNotFoundWrapper from '@pages/workspace/AccessOrNotFoundWrapper'; 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 ef373e7d78a6..3c8d57fc56d8 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 AccessOrNotFoundWrapper from '@pages/workspace/AccessOrNotFoundWrapper'; import withPolicyAndFullscreenLoading from '@pages/workspace/withPolicyAndFullscreenLoading'; @@ -87,7 +87,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 c4a3afc3e0b9..8b96a89a2a1b 100644 --- a/src/types/onyx/OnyxCommon.ts +++ b/src/types/onyx/OnyxCommon.ts @@ -25,10 +25,10 @@ type AvatarType = typeof CONST.ICON_TYPE_AVATAR | typeof CONST.ICON_TYPE_WORKSPA type Icon = { /** Avatar source to display */ - source?: AvatarSource; + source: AvatarSource; /** Denotes whether it is an avatar or a workspace avatar */ - type?: AvatarType; + type: AvatarType; /** Owner of the avatar. If user, displayName. If workspace, policy name */ name?: string; diff --git a/tests/unit/ReportUtilsTest.ts b/tests/unit/ReportUtilsTest.ts index d0005c59d39a..8b449b4c8445 100644 --- a/tests/unit/ReportUtilsTest.ts +++ b/tests/unit/ReportUtilsTest.ts @@ -99,13 +99,11 @@ describe('ReportUtils', () => { expect(participants[0].displayName).toBe('(833) 240-3627'); expect(participants[0].login).toBe('+18332403627@expensify.sms'); - expect(participants[2].avatar).toBeInstanceOf(Function); expect(participants[2].displayName).toBe('Lagertha Lothbrok'); expect(participants[2].login).toBe('lagertha@vikings.net'); expect(participants[2].accountID).toBe(3); expect(participants[2].pronouns).toBe('She/her'); - expect(participants[4].avatar).toBeInstanceOf(Function); expect(participants[4].displayName).toBe('Ragnar Lothbrok'); expect(participants[4].login).toBe('ragnar@vikings.net'); expect(participants[4].accountID).toBe(1);