From 1839a03953eeae1d5c29a28b390af611ea729cff Mon Sep 17 00:00:00 2001 From: Mateusz Titz Date: Tue, 7 May 2024 17:38:12 +0200 Subject: [PATCH 01/16] simplify avatar logic and use FallbackAvatar when user is unknown --- src/components/Avatar.tsx | 13 +++--- src/components/AvatarWithIndicator.tsx | 10 +++-- src/components/ReportActionItem/TaskView.tsx | 2 +- .../BaseUserDetailsTooltip/index.tsx | 4 +- src/libs/OptionsListUtils.ts | 27 ++++++------- src/libs/PersonalDetailsUtils.ts | 1 - src/libs/ReportUtils.ts | 40 +++++++++---------- src/libs/SidebarUtils.ts | 3 +- src/libs/UserUtils.ts | 23 ++++------- src/libs/actions/IOU.ts | 6 --- src/libs/actions/Report.ts | 2 - src/libs/actions/Task.ts | 3 +- src/pages/DetailsPage.tsx | 4 +- src/pages/ProfilePage.tsx | 8 ++-- src/pages/ReportParticipantDetailsPage.tsx | 5 +-- src/pages/ReportParticipantsPage.tsx | 3 +- src/pages/RoomMembersPage.tsx | 4 +- .../report/ReactionList/BaseReactionList.tsx | 4 +- .../ReportActionCompose/SuggestionMention.tsx | 4 +- .../home/report/ReportActionItemFragment.tsx | 2 +- .../home/report/ReportActionItemSingle.tsx | 16 +++++--- .../sidebar/ProfileAvatarWithIndicator.tsx | 4 +- src/pages/workspace/WorkspaceMembersPage.tsx | 3 +- src/pages/workspace/WorkspacesListRow.tsx | 1 + .../members/WorkspaceMemberDetailsPage.tsx | 5 +-- .../WorkspaceWorkflowsApproverPage.tsx | 4 +- .../workflows/WorkspaceWorkflowsPayerPage.tsx | 4 +- src/types/onyx/OnyxCommon.ts | 4 +- 28 files changed, 95 insertions(+), 114 deletions(-) diff --git a/src/components/Avatar.tsx b/src/components/Avatar.tsx index 4acf197ba178..3e514484ccdd 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(accountID)); + // if it's user avatar then accountID will be a number + const source = isWorkspace ? originalSource : UserUtils.getAvatar(originalSource, accountID 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(accountID?.toString() ?? ''); diff --git a/src/components/AvatarWithIndicator.tsx b/src/components/AvatarWithIndicator.tsx index 42b91b3d2d71..01a989d961cf 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/ReportActionItem/TaskView.tsx b/src/components/ReportActionItem/TaskView.tsx index ee3097e051b2..761832fee04a 100644 --- a/src/components/ReportActionItem/TaskView.tsx +++ b/src/components/ReportActionItem/TaskView.tsx @@ -152,7 +152,7 @@ function TaskView({report, ...props}: TaskViewProps) { { 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}; + // Todo check if the difference in accountID breaks something return { id: accountID, - source: UserUtils.getAvatar(userPersonalDetail.avatar, userPersonalDetail.accountID), + // source: UserUtils.getAvatar(userPersonalDetail.avatar, userPersonalDetail.accountID), + source: userPersonalDetail.avatar ?? FallbackAvatar, type: CONST.ICON_TYPE_AVATAR, name: userPersonalDetail.login ?? '', }; @@ -355,9 +359,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) { @@ -386,6 +388,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, @@ -396,7 +399,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, @@ -760,13 +763,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; @@ -1624,7 +1621,7 @@ function getUserToInviteOption({ // If user doesn't exist, use a fallback avatar userToInvite.icons = [ { - source: UserUtils.getAvatar('', optimisticAccountID), + source: FallbackAvatar, name: searchValue, type: CONST.ICON_TYPE_AVATAR, }, @@ -2031,7 +2028,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 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 64b77cae6313..600391053f9f 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'; @@ -1718,7 +1718,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 ?? '']); } @@ -1779,12 +1779,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 c0d0c9020a64..83b010cb3d81 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({ @@ -418,7 +417,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..2df02b494f64 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,13 +79,9 @@ 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; - } +function getDefaultAvatar(accountID = -1, avatarURL?: string): IconAsset | undefined { if (Number(accountID) === CONST.ACCOUNT_ID.CONCIERGE) { return ConciergeAvatar; } @@ -125,7 +121,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 +136,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 +146,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 +154,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 +165,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 +177,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 7171a7d6732a..b111fc2bce13 100644 --- a/src/libs/actions/IOU.ts +++ b/src/libs/actions/IOU.ts @@ -44,7 +44,6 @@ import * as ReportActionsUtils from '@libs/ReportActionsUtils'; import type {OptimisticChatReport, OptimisticCreatedReportAction, OptimisticInviteReportAction, 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'; @@ -1662,7 +1661,6 @@ function getSendInvoiceInformation( if (shouldCreateOptimisticPersonalDetails) { receiver = { accountID: receiverAccountID, - avatar: UserUtils.getDefaultAvatarURL(receiverAccountID), displayName: LocalePhoneNumber.formatPhoneNumber(receiverParticipant?.login ?? ''), login: receiverParticipant?.login, isOptimisticPersonalDetail: true, @@ -1890,7 +1888,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), @@ -3937,7 +3934,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), @@ -4400,7 +4396,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), @@ -5584,7 +5579,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 3154ae218d72..bc6a9263635e 100644 --- a/src/libs/actions/Report.ts +++ b/src/libs/actions/Report.ts @@ -73,7 +73,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'; @@ -874,7 +873,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 e9a7c4f1c57f..931912177d88 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'; @@ -662,7 +661,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..1cd4172ca951 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 f8f88e9cc3cf..c1fbbb9b423d 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'; @@ -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; @@ -164,7 +161,8 @@ function ProfilePage({route}: ProfilePageProps) { diff --git a/src/pages/ReportParticipantDetailsPage.tsx b/src/pages/ReportParticipantDetailsPage.tsx index b6ac1e29e5f2..4110910f04ce 100644 --- a/src/pages/ReportParticipantDetailsPage.tsx +++ b/src/pages/ReportParticipantDetailsPage.tsx @@ -19,7 +19,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'; @@ -52,7 +51,6 @@ function ReportParticipantDetails({personalDetails, report, route}: ReportPartic const member = report?.participants?.[accountID]; const details = personalDetails?.[accountID] ?? ({} as PersonalDetails); - const avatar = details.avatar ?? UserUtils.getDefaultAvatar(); const fallbackIcon = details.fallbackIcon ?? ''; const displayName = details.displayName ?? ''; const isCurrentUserAdmin = ReportUtils.isGroupChatAdmin(report, currentUserPersonalDetails?.accountID); @@ -82,7 +80,8 @@ function ReportParticipantDetails({personalDetails, report, route}: ReportPartic diff --git a/src/pages/ReportParticipantsPage.tsx b/src/pages/ReportParticipantsPage.tsx index 303cd6cccd6b..fb7536e9a6f7 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 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 05e1163da200..37709ad04b9a 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, 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 234147a30bd5..6bce236c4bb3 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 avatarAccountId: number | string | undefined = actorAccountID; if (isWorkspaceActor) { displayName = ReportUtils.getPolicyName(report); actorHint = displayName; avatarSource = ReportUtils.getWorkspaceAvatar(report); + avatarAccountId = 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,7 +102,9 @@ function ReportActionItemSingle({ const delegateDisplayName = delegateDetails?.displayName; actorHint = `${delegateDisplayName} (${translate('reportAction.asCopilot')} ${displayName})`; displayName = actorHint; - avatarSource = UserUtils.getAvatar(delegateDetails?.avatar ?? '', Number(action.delegateAccountID)); + // 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 @@ -117,7 +121,7 @@ function ReportActionItemSingle({ } secondaryAvatar = { - source: UserUtils.getAvatar(secondaryUserAvatar, secondaryAccountId), + source: secondaryUserAvatar, type: CONST.ICON_TYPE_AVATAR, name: secondaryDisplayName ?? '', id: secondaryAccountId, @@ -132,10 +136,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 ? '' : actorAccountID, + id: avatarAccountId, }; // 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 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 3fdeaba4da7c..1306b2381ee4 100644 --- a/src/pages/workspace/WorkspaceMembersPage.tsx +++ b/src/pages/workspace/WorkspaceMembersPage.tsx @@ -36,7 +36,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'; @@ -352,7 +351,7 @@ function WorkspaceMembersPage({personalDetails, invitedEmailsToAccountIDsDraft, rightElement: roleBadge, 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/workspace/WorkspacesListRow.tsx b/src/pages/workspace/WorkspacesListRow.tsx index a1affd935c50..4f46aed1752d 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 7b2aa78545f2..ecd17709d777 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 AccessOrNotFoundWrapper from '@pages/workspace/AccessOrNotFoundWrapper'; @@ -57,7 +56,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; @@ -143,7 +141,8 @@ function WorkspaceMemberDetailsPage({personalDetails, policy, route}: WorkspaceM diff --git a/src/pages/workspace/workflows/WorkspaceWorkflowsApproverPage.tsx b/src/pages/workspace/workflows/WorkspaceWorkflowsApproverPage.tsx index 0c12bace7a4d..a8c687a2d4cb 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 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 889d8571357b..866b7c1935a6 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; From a8879a1f35f055a1b53a85e5e36fdf9479b864ce Mon Sep 17 00:00:00 2001 From: Mateusz Titz Date: Wed, 8 May 2024 10:32:09 +0200 Subject: [PATCH 02/16] fix not found Page when opening ProfilePage from workspace members list in offline --- src/pages/ProfilePage.tsx | 200 +++++++++++++++++++------------------- 1 file changed, 98 insertions(+), 102 deletions(-) diff --git a/src/pages/ProfilePage.tsx b/src/pages/ProfilePage.tsx index c1fbbb9b423d..b51d41edb4f7 100755 --- a/src/pages/ProfilePage.tsx +++ b/src/pages/ProfilePage.tsx @@ -57,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 => @@ -114,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; @@ -142,114 +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 && } From 00ca93dc15c155418a9e1f96012099b4e86cbea6 Mon Sep 17 00:00:00 2001 From: Mateusz Titz Date: Wed, 8 May 2024 11:23:52 +0200 Subject: [PATCH 03/16] Remove more unnecessary usages of .getAvatar() function --- src/libs/OptionsListUtils.ts | 2 - src/libs/ReportUtils.ts | 40 +++++++++---------- .../home/report/ReportActionItemSingle.tsx | 1 - src/pages/settings/InitialSettingsPage.tsx | 2 +- 4 files changed, 21 insertions(+), 24 deletions(-) diff --git a/src/libs/OptionsListUtils.ts b/src/libs/OptionsListUtils.ts index ebf3783c94e0..f229c5fbe63f 100644 --- a/src/libs/OptionsListUtils.ts +++ b/src/libs/OptionsListUtils.ts @@ -332,10 +332,8 @@ function getAvatarsForAccountIDs(accountIDs: number[], personalDetails: OnyxEntr const login = reversedDefaultValues[accountID] ?? ''; const userPersonalDetail = personalDetails?.[accountID] ?? {login, accountID}; - // Todo check if the difference in accountID breaks something return { id: accountID, - // source: UserUtils.getAvatar(userPersonalDetail.avatar, userPersonalDetail.accountID), source: userPersonalDetail.avatar ?? FallbackAvatar, type: CONST.ICON_TYPE_AVATAR, name: userPersonalDetail.login ?? '', diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index 600391053f9f..95f7cfa3ca1d 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -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; @@ -587,8 +587,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 { @@ -2075,7 +2075,7 @@ function getDisplayNamesWithTooltips( const accountID = Number(user?.accountID); // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing const displayName = getDisplayNameForParticipant(accountID, isMultipleParticipantReport, shouldFallbackToHidden, shouldAddCurrentUserPostfix) || user?.login || ''; - const avatar = UserUtils.getDefaultAvatar(accountID); + const avatar = user && 'avatar' in user ? user.avatar : FallbackAvatar; let pronouns = user?.pronouns ?? undefined; if (pronouns?.startsWith(CONST.PRONOUNS.PREFIX)) { @@ -3399,7 +3399,7 @@ function buildOptimisticInviteReportAction(invitedUserDisplayName: string, invit }, ], automatic: false, - avatar: currentUser?.avatar ?? UserUtils.getDefaultAvatarURL(currentUserAccountID), + avatar: currentUser?.avatar, created: DateUtils.getDBTime(), message: [ { @@ -3936,7 +3936,7 @@ function buildOptimisticIOUReportAction( actionName: CONST.REPORT.ACTIONS.TYPE.IOU, actorAccountID: currentUserAccountID, automatic: false, - avatar: getCurrentUserAvatarOrDefault(), + avatar: getCurrentUserAvatar(), isAttachment: false, originalMessage, message: getIOUReportActionMessage(iouReportID, type, amount, comment, currency, paymentType, isSettlingUp), @@ -3969,7 +3969,7 @@ function buildOptimisticApprovedReportAction(amount: number, currency: string, e actionName: CONST.REPORT.ACTIONS.TYPE.APPROVED, actorAccountID: currentUserAccountID, automatic: false, - avatar: getCurrentUserAvatarOrDefault(), + avatar: getCurrentUserAvatar(), isAttachment: false, originalMessage, message: getIOUReportActionMessage(expenseReportID, CONST.REPORT.ACTIONS.TYPE.APPROVED, Math.abs(amount), '', currency), @@ -4011,7 +4011,7 @@ function buildOptimisticMovedReportAction(fromPolicyID: string, toPolicyID: stri actionName: CONST.REPORT.ACTIONS.TYPE.MOVED, actorAccountID: currentUserAccountID, automatic: false, - avatar: getCurrentUserAvatarOrDefault(), + avatar: getCurrentUserAvatar(), isAttachment: false, originalMessage, message: movedActionMessage, @@ -4045,7 +4045,7 @@ function buildOptimisticSubmittedReportAction(amount: number, currency: string, actorAccountID: currentUserAccountID, adminAccountID, automatic: false, - avatar: getCurrentUserAvatarOrDefault(), + avatar: getCurrentUserAvatar(), isAttachment: false, originalMessage, message: getIOUReportActionMessage(expenseReportID, CONST.REPORT.ACTIONS.TYPE.SUBMITTED, Math.abs(amount), '', currency), @@ -4119,7 +4119,7 @@ function buildOptimisticModifiedExpenseReportAction( actionName: CONST.REPORT.ACTIONS.TYPE.MODIFIED_EXPENSE, actorAccountID: currentUserAccountID, automatic: false, - avatar: getCurrentUserAvatarOrDefault(), + avatar: getCurrentUserAvatar(), created: DateUtils.getDBTime(), isAttachment: false, message: [ @@ -4155,7 +4155,7 @@ function buildOptimisticMovedTrackedExpenseModifiedReportAction(transactionThrea actionName: CONST.REPORT.ACTIONS.TYPE.MODIFIED_EXPENSE, actorAccountID: currentUserAccountID, automatic: false, - avatar: getCurrentUserAvatarOrDefault(), + avatar: getCurrentUserAvatar(), created: DateUtils.getDBTime(), isAttachment: false, message: [ @@ -4246,7 +4246,7 @@ function buildOptimisticTaskReportAction( actionName, actorAccountID, automatic: false, - avatar: getCurrentUserAvatarOrDefault(), + avatar: getCurrentUserAvatar(), isAttachment: false, originalMessage, message: [ @@ -4396,7 +4396,7 @@ function buildOptimisticCreatedReportAction(emailCreatingAction: string, created }, ], automatic: false, - avatar: getCurrentUserAvatarOrDefault(), + avatar: getCurrentUserAvatar(), created, shouldShow: true, }; @@ -4438,7 +4438,7 @@ function buildOptimisticRenamedRoomReportAction(newName: string, oldName: string lastModified: now, }, automatic: false, - avatar: getCurrentUserAvatarOrDefault(), + avatar: getCurrentUserAvatar(), created: now, shouldShow: true, }; @@ -4469,7 +4469,7 @@ function buildOptimisticHoldReportAction(created = DateUtils.getDBTime()): Optim }, ], automatic: false, - avatar: getCurrentUserAvatarOrDefault(), + avatar: getCurrentUserAvatar(), created, shouldShow: true, }; @@ -4500,7 +4500,7 @@ function buildOptimisticHoldReportActionComment(comment: string, created = DateU }, ], automatic: false, - avatar: getCurrentUserAvatarOrDefault(), + avatar: getCurrentUserAvatar(), created, shouldShow: true, }; @@ -4531,7 +4531,7 @@ function buildOptimisticUnHoldReportAction(created = DateUtils.getDBTime()): Opt }, ], automatic: false, - avatar: getCurrentUserAvatarOrDefault(), + avatar: getCurrentUserAvatar(), created, shouldShow: true, }; @@ -4576,7 +4576,7 @@ function buildOptimisticEditedTaskFieldReportAction({title, description}: Task): }, ], automatic: false, - avatar: getCurrentUserAvatarOrDefault(), + avatar: getCurrentUserAvatar(), created: DateUtils.getDBTime(), shouldShow: false, }; @@ -4603,7 +4603,7 @@ function buildOptimisticChangedTaskAssigneeReportAction(assigneeAccountID: numbe }, ], automatic: false, - avatar: getCurrentUserAvatarOrDefault(), + avatar: getCurrentUserAvatar(), created: DateUtils.getDBTime(), shouldShow: false, }; @@ -4619,7 +4619,7 @@ function buildOptimisticClosedReportAction(emailClosingReport: string, policyNam actionName: CONST.REPORT.ACTIONS.TYPE.CLOSED, actorAccountID: currentUserAccountID, automatic: false, - avatar: getCurrentUserAvatarOrDefault(), + avatar: getCurrentUserAvatar(), created: DateUtils.getDBTime(), message: [ { diff --git a/src/pages/home/report/ReportActionItemSingle.tsx b/src/pages/home/report/ReportActionItemSingle.tsx index 6bce236c4bb3..34880bb33ea5 100644 --- a/src/pages/home/report/ReportActionItemSingle.tsx +++ b/src/pages/home/report/ReportActionItemSingle.tsx @@ -102,7 +102,6 @@ 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; } diff --git a/src/pages/settings/InitialSettingsPage.tsx b/src/pages/settings/InitialSettingsPage.tsx index 2dfeb1841a74..3cad4cae036a 100755 --- a/src/pages/settings/InitialSettingsPage.tsx +++ b/src/pages/settings/InitialSettingsPage.tsx @@ -413,7 +413,7 @@ function InitialSettingsPage({session, userWallet, bankAccountList, fundList, wa > Date: Fri, 10 May 2024 10:40:33 +0200 Subject: [PATCH 04/16] Apply some fixes after review --- src/components/AvatarWithImagePicker.tsx | 5 +++++ src/components/MenuItem.tsx | 10 ++++++++-- src/libs/UserUtils.ts | 13 +++++++++---- src/pages/ReportDetailsPage.tsx | 1 + .../ReportActionCompose/SuggestionMention.tsx | 2 +- src/pages/settings/InitialSettingsPage.tsx | 1 + 6 files changed, 25 insertions(+), 7 deletions(-) diff --git a/src/components/AvatarWithImagePicker.tsx b/src/components/AvatarWithImagePicker.tsx index 5fa17e726c6f..64752e33da6c 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 */ + accountID: number | string | undefined; + /** Additional style props */ style?: StyleProp; @@ -136,6 +139,7 @@ function AvatarWithImagePicker({ errorRowStyles, onErrorClose = () => {}, source = '', + accountID, 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} + accountID={accountID} fallbackIcon={fallbackIcon} size={size} type={type} diff --git a/src/components/MenuItem.tsx b/src/components/MenuItem.tsx index 42de7e2fb7f4..b84e03ab3f5b 100644 --- a/src/components/MenuItem.tsx +++ b/src/components/MenuItem.tsx @@ -265,6 +265,9 @@ type MenuItemBaseProps = { /** Handles what to do when the item is focused */ onFocus?: () => void; + + /** Optional account id if it's user avatar or policy id if it's workspace avatar */ + accountID?: number | string; }; type MenuItemProps = (IconProps | AvatarProps | NoIcon) & MenuItemBaseProps; @@ -342,6 +345,7 @@ function MenuItem( isPaneMenu = false, shouldPutLeftPaddingWhenNoIcon = false, onFocus, + accountID, }: MenuItemProps, ref: PressableRef, ) { @@ -519,7 +523,8 @@ function MenuItem( diff --git a/src/libs/UserUtils.ts b/src/libs/UserUtils.ts index 2df02b494f64..ca50beb19a8c 100644 --- a/src/libs/UserUtils.ts +++ b/src/libs/UserUtils.ts @@ -82,10 +82,10 @@ function generateAccountID(searchValue: string): number { * @returns */ function getDefaultAvatar(accountID = -1, avatarURL?: string): IconAsset | undefined { - if (Number(accountID) === CONST.ACCOUNT_ID.CONCIERGE) { + if (accountID === CONST.ACCOUNT_ID.CONCIERGE) { return ConciergeAvatar; } - if (Number(accountID) === CONST.ACCOUNT_ID.NOTIFICATIONS) { + if (accountID === CONST.ACCOUNT_ID.NOTIFICATIONS) { return NotificationsAvatar; } @@ -94,14 +94,19 @@ function getDefaultAvatar(accountID = -1, avatarURL?: string): IconAsset | undef // When creating a chat, we generate an avatar using an ID and the backend response will modify the ID to 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}`]; } diff --git a/src/pages/ReportDetailsPage.tsx b/src/pages/ReportDetailsPage.tsx index ca66a0e97bb5..8b8044bc856c 100644 --- a/src/pages/ReportDetailsPage.tsx +++ b/src/pages/ReportDetailsPage.tsx @@ -235,6 +235,7 @@ function ReportDetailsPage({policies, report, session, personalDetails}: ReportD isGroupChat && !isThread ? ( Date: Fri, 10 May 2024 10:50:23 +0200 Subject: [PATCH 05/16] Make accountID optional in AvatarWithImagePicker --- src/components/AvatarWithImagePicker.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/AvatarWithImagePicker.tsx b/src/components/AvatarWithImagePicker.tsx index 64752e33da6c..13a018b1d410 100644 --- a/src/components/AvatarWithImagePicker.tsx +++ b/src/components/AvatarWithImagePicker.tsx @@ -50,7 +50,7 @@ type AvatarWithImagePickerProps = { source?: AvatarSource; /** Account id of user for which avatar is displayed */ - accountID: number | string | undefined; + accountID?: number | string; /** Additional style props */ style?: StyleProp; From f886a6b6323ff8e22cd799fa2041e1aef890cdc5 Mon Sep 17 00:00:00 2001 From: Mateusz Titz Date: Mon, 13 May 2024 16:37:04 +0200 Subject: [PATCH 06/16] Add updates after review --- src/libs/OptionsListUtils.ts | 3 ++- src/libs/SidebarUtils.ts | 2 +- .../home/report/ReportActionItemSingle.tsx | 4 ++-- src/pages/tasks/TaskAssigneeSelectorModal.tsx | 17 ++++++++++++++++- 4 files changed, 21 insertions(+), 5 deletions(-) diff --git a/src/libs/OptionsListUtils.ts b/src/libs/OptionsListUtils.ts index f229c5fbe63f..9e43e1e9341e 100644 --- a/src/libs/OptionsListUtils.ts +++ b/src/libs/OptionsListUtils.ts @@ -761,7 +761,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, personalDetail?.avatar, personalDetail?.login, personalDetail?.accountID); + result.icons = ReportUtils.getIcons(report, personalDetails, personalDetail?.avatar, personalDetail?.login, personalDetail?.accountID, null); result.subtitle = subtitle; return result; @@ -1620,6 +1620,7 @@ function getUserToInviteOption({ userToInvite.icons = [ { source: FallbackAvatar, + id: optimisticAccountID, name: searchValue, type: CONST.ICON_TYPE_AVATAR, }, diff --git a/src/libs/SidebarUtils.ts b/src/libs/SidebarUtils.ts index 83b010cb3d81..bbf486e688af 100644 --- a/src/libs/SidebarUtils.ts +++ b/src/libs/SidebarUtils.ts @@ -417,7 +417,7 @@ function getOptionData({ result.subtitle = subtitle; result.participantsList = participantPersonalDetailList; - result.icons = ReportUtils.getIcons(report, personalDetails, personalDetail?.avatar, '', -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/pages/home/report/ReportActionItemSingle.tsx b/src/pages/home/report/ReportActionItemSingle.tsx index 34880bb33ea5..a5610f1f0feb 100644 --- a/src/pages/home/report/ReportActionItemSingle.tsx +++ b/src/pages/home/report/ReportActionItemSingle.tsx @@ -110,9 +110,9 @@ function ReportActionItemSingle({ 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) { diff --git a/src/pages/tasks/TaskAssigneeSelectorModal.tsx b/src/pages/tasks/TaskAssigneeSelectorModal.tsx index 9a3993a6b649..036236a209c6 100644 --- a/src/pages/tasks/TaskAssigneeSelectorModal.tsx +++ b/src/pages/tasks/TaskAssigneeSelectorModal.tsx @@ -7,6 +7,7 @@ import {useOnyx, withOnyx} from 'react-native-onyx'; import type {OnyxCollection, OnyxEntry} from 'react-native-onyx'; import FullPageNotFoundView from '@components/BlockingViews/FullPageNotFoundView'; import HeaderWithBackButton from '@components/HeaderWithBackButton'; +import {FallbackAvatar} from '@components/Icon/Expensicons'; import {useBetas, useSession} from '@components/OnyxProvider'; import {useOptionsList} from '@components/OptionListContextProvider'; import ScreenWrapper from '@components/ScreenWrapper'; @@ -32,6 +33,7 @@ import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; import type SCREENS from '@src/SCREENS'; import type {Report, Task} from '@src/types/onyx'; +import type * as OnyxCommon from '@src/types/onyx/OnyxCommon'; type TaskAssigneeSelectorModalOnyxProps = { /** All reports shared with the user */ @@ -115,9 +117,22 @@ function TaskAssigneeSelectorModal({reports, task}: TaskAssigneeSelectorModalPro const sectionsList = []; if (currentUserOption) { + // We need to manually recreate icon for current user since OptionsListUtils.getFilteredOptions returns empty icon + const currentUserAvatarIcon: OnyxCommon.Icon = { + source: currentUserPersonalDetails.avatar ?? FallbackAvatar, + id: currentUserPersonalDetails.accountID, + type: CONST.ICON_TYPE_AVATAR, + name: currentUserPersonalDetails.displayName, + }; + sectionsList.push({ title: translate('newTaskPage.assignMe'), - data: [currentUserOption], + data: [ + { + ...currentUserOption, + icons: [currentUserAvatarIcon], + }, + ], shouldShow: true, }); } From 9091250821bcaccd7fcdea3e79793f9986e2daee Mon Sep 17 00:00:00 2001 From: Mateusz Titz Date: Mon, 13 May 2024 16:54:09 +0200 Subject: [PATCH 07/16] Fix typo after merge conflict --- src/libs/ReportUtils.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index 0c7d1ebb4edf..4d284e401ce1 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -4638,7 +4638,7 @@ function buildOptimisticDismissedViolationReportAction(originalMessage: Original return { actionName: CONST.REPORT.ACTIONS.TYPE.DISMISSED_VIOLATION, actorAccountID: currentUserAccountID, - avatar: getCurrentUserAvatarOrDefault(), + avatar: getCurrentUserAvatar(), created: DateUtils.getDBTime(), message: [ { From 4120f36632227a990ef04e9e3714e366e7c46a57 Mon Sep 17 00:00:00 2001 From: Mateusz Titz Date: Tue, 14 May 2024 10:00:26 +0200 Subject: [PATCH 08/16] Fix lint --- src/pages/tasks/TaskAssigneeSelectorModal.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/tasks/TaskAssigneeSelectorModal.tsx b/src/pages/tasks/TaskAssigneeSelectorModal.tsx index 036236a209c6..1d3e8ab63938 100644 --- a/src/pages/tasks/TaskAssigneeSelectorModal.tsx +++ b/src/pages/tasks/TaskAssigneeSelectorModal.tsx @@ -169,7 +169,7 @@ function TaskAssigneeSelectorModal({reports, task}: TaskAssigneeSelectorModalPro shouldShowSubscript: option.shouldShowSubscript ?? undefined, })), })); - }, [currentUserOption, personalDetails, recentReports, translate, userToInvite]); + }, [currentUserOption, currentUserPersonalDetails, personalDetails, recentReports, translate, userToInvite]); const selectReport = useCallback( (option: ListItem) => { From ee05310eb41411b4fc004066c9537bf5dd8a7438 Mon Sep 17 00:00:00 2001 From: Mateusz Titz Date: Tue, 14 May 2024 12:31:19 +0200 Subject: [PATCH 09/16] Remove task assignee avatar workaround --- src/pages/tasks/TaskAssigneeSelectorModal.tsx | 19 ++----------------- 1 file changed, 2 insertions(+), 17 deletions(-) diff --git a/src/pages/tasks/TaskAssigneeSelectorModal.tsx b/src/pages/tasks/TaskAssigneeSelectorModal.tsx index 1d3e8ab63938..9a3993a6b649 100644 --- a/src/pages/tasks/TaskAssigneeSelectorModal.tsx +++ b/src/pages/tasks/TaskAssigneeSelectorModal.tsx @@ -7,7 +7,6 @@ import {useOnyx, withOnyx} from 'react-native-onyx'; import type {OnyxCollection, OnyxEntry} from 'react-native-onyx'; import FullPageNotFoundView from '@components/BlockingViews/FullPageNotFoundView'; import HeaderWithBackButton from '@components/HeaderWithBackButton'; -import {FallbackAvatar} from '@components/Icon/Expensicons'; import {useBetas, useSession} from '@components/OnyxProvider'; import {useOptionsList} from '@components/OptionListContextProvider'; import ScreenWrapper from '@components/ScreenWrapper'; @@ -33,7 +32,6 @@ import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; import type SCREENS from '@src/SCREENS'; import type {Report, Task} from '@src/types/onyx'; -import type * as OnyxCommon from '@src/types/onyx/OnyxCommon'; type TaskAssigneeSelectorModalOnyxProps = { /** All reports shared with the user */ @@ -117,22 +115,9 @@ function TaskAssigneeSelectorModal({reports, task}: TaskAssigneeSelectorModalPro const sectionsList = []; if (currentUserOption) { - // We need to manually recreate icon for current user since OptionsListUtils.getFilteredOptions returns empty icon - const currentUserAvatarIcon: OnyxCommon.Icon = { - source: currentUserPersonalDetails.avatar ?? FallbackAvatar, - id: currentUserPersonalDetails.accountID, - type: CONST.ICON_TYPE_AVATAR, - name: currentUserPersonalDetails.displayName, - }; - sectionsList.push({ title: translate('newTaskPage.assignMe'), - data: [ - { - ...currentUserOption, - icons: [currentUserAvatarIcon], - }, - ], + data: [currentUserOption], shouldShow: true, }); } @@ -169,7 +154,7 @@ function TaskAssigneeSelectorModal({reports, task}: TaskAssigneeSelectorModalPro shouldShowSubscript: option.shouldShowSubscript ?? undefined, })), })); - }, [currentUserOption, currentUserPersonalDetails, personalDetails, recentReports, translate, userToInvite]); + }, [currentUserOption, personalDetails, recentReports, translate, userToInvite]); const selectReport = useCallback( (option: ListItem) => { From a71d08b9c43bfd0ac82a489cedc88746b29fa47e Mon Sep 17 00:00:00 2001 From: Mateusz Titz Date: Tue, 14 May 2024 12:51:57 +0200 Subject: [PATCH 10/16] Fix bug after merge conflicts --- src/libs/actions/IOU.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/libs/actions/IOU.ts b/src/libs/actions/IOU.ts index e5c07aeb200b..33b060b718e8 100644 --- a/src/libs/actions/IOU.ts +++ b/src/libs/actions/IOU.ts @@ -5578,7 +5578,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, From b549d46afed578edb09b5cbf2c1dab28336a1195 Mon Sep 17 00:00:00 2001 From: Mateusz Titz Date: Tue, 14 May 2024 13:15:03 +0200 Subject: [PATCH 11/16] Remove instance of unneeded FallbackAvatar --- src/libs/ReportUtils.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index 2d187245ae3a..0fd883700afd 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -2091,7 +2091,7 @@ function getDisplayNamesWithTooltips( const accountID = Number(user?.accountID); // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing const displayName = getDisplayNameForParticipant(accountID, shouldUseShortForm, shouldFallbackToHidden, shouldAddCurrentUserPostfix) || user?.login || ''; - const avatar = user && 'avatar' in user ? user.avatar : FallbackAvatar; + const avatar = user && 'avatar' in user ? user.avatar : undefined; let pronouns = user?.pronouns ?? undefined; if (pronouns?.startsWith(CONST.PRONOUNS.PREFIX)) { From 5f355ae5e8bfa36a8643e4980fc024d33d7901eb Mon Sep 17 00:00:00 2001 From: Mateusz Titz Date: Tue, 14 May 2024 13:26:27 +0200 Subject: [PATCH 12/16] Update tests for `getDisplayNamesWithTooltips` --- tests/unit/ReportUtilsTest.ts | 2 -- 1 file changed, 2 deletions(-) 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); From 0b8a92dee163cdf9c9fea0f4ed35c998105c521e Mon Sep 17 00:00:00 2001 From: Mateusz Titz Date: Thu, 16 May 2024 12:46:08 +0200 Subject: [PATCH 13/16] Update Avatar code after workspace avatar styling changes --- src/components/Avatar.tsx | 3 --- src/components/AvatarWithImagePicker.tsx | 6 +++--- src/components/AvatarWithIndicator.tsx | 2 +- src/components/MenuItem.tsx | 2 +- .../UserDetailsTooltip/BaseUserDetailsTooltip/index.tsx | 3 +-- src/pages/DetailsPage.tsx | 2 +- src/pages/ProfilePage.tsx | 2 +- src/pages/ReportDetailsPage.tsx | 2 +- src/pages/ReportParticipantDetailsPage.tsx | 2 +- src/pages/RoomMembersPage.tsx | 2 +- src/pages/settings/InitialSettingsPage.tsx | 2 +- src/pages/workspace/WorkspaceProfilePage.tsx | 2 +- src/pages/workspace/WorkspacesListRow.tsx | 2 +- src/pages/workspace/members/WorkspaceMemberDetailsPage.tsx | 2 +- 14 files changed, 15 insertions(+), 19 deletions(-) diff --git a/src/components/Avatar.tsx b/src/components/Avatar.tsx index 252d5194eeb7..fa977f705248 100644 --- a/src/components/Avatar.tsx +++ b/src/components/Avatar.tsx @@ -93,9 +93,6 @@ function Avatar({ 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 13a018b1d410..bf8b475aa016 100644 --- a/src/components/AvatarWithImagePicker.tsx +++ b/src/components/AvatarWithImagePicker.tsx @@ -50,7 +50,7 @@ type AvatarWithImagePickerProps = { source?: AvatarSource; /** Account id of user for which avatar is displayed */ - accountID?: number | string; + avatarID?: number | string; /** Additional style props */ style?: StyleProp; @@ -139,7 +139,7 @@ function AvatarWithImagePicker({ errorRowStyles, onErrorClose = () => {}, source = '', - accountID, + avatarID, fallbackIcon = Expensicons.FallbackAvatar, size = CONST.AVATAR_SIZE.DEFAULT, type = CONST.ICON_TYPE_AVATAR, @@ -333,7 +333,7 @@ function AvatarWithImagePicker({ containerStyles={avatarStyle} imageStyles={[avatarStyle, styles.alignSelfCenter]} source={source} - accountID={accountID} + avatarID={avatarID} fallbackIcon={fallbackIcon} size={size} type={type} diff --git a/src/components/AvatarWithIndicator.tsx b/src/components/AvatarWithIndicator.tsx index 01a989d961cf..eb153ee13623 100644 --- a/src/components/AvatarWithIndicator.tsx +++ b/src/components/AvatarWithIndicator.tsx @@ -40,7 +40,7 @@ function AvatarWithIndicator({source, accountID, tooltipText = '', fallbackIcon size={CONST.AVATAR_SIZE.SMALL} source={UserUtils.getSmallSizeAvatar(source, accountID)} fallbackIcon={fallbackIcon} - accountID={accountID} + avatarID={accountID} /> diff --git a/src/components/MenuItem.tsx b/src/components/MenuItem.tsx index 61b589380403..1d4c57c167ff 100644 --- a/src/components/MenuItem.tsx +++ b/src/components/MenuItem.tsx @@ -538,7 +538,7 @@ function MenuItem( diff --git a/src/components/UserDetailsTooltip/BaseUserDetailsTooltip/index.tsx b/src/components/UserDetailsTooltip/BaseUserDetailsTooltip/index.tsx index ca8075482fcb..35c0b5e23f1a 100644 --- a/src/components/UserDetailsTooltip/BaseUserDetailsTooltip/index.tsx +++ b/src/components/UserDetailsTooltip/BaseUserDetailsTooltip/index.tsx @@ -60,11 +60,10 @@ function BaseUserDetailsTooltip({accountID, fallbackUserDetails, icon, delegateA {title} diff --git a/src/pages/DetailsPage.tsx b/src/pages/DetailsPage.tsx index 1cd4172ca951..2198be675c11 100755 --- a/src/pages/DetailsPage.tsx +++ b/src/pages/DetailsPage.tsx @@ -115,7 +115,7 @@ function DetailsPage({personalDetails, route, session}: DetailsPageProps) { containerStyles={[styles.avatarLarge, styles.mb3]} imageStyles={[styles.avatarLarge]} source={details?.avatar} - accountID={details?.accountID} + avatarID={details?.accountID} size={CONST.AVATAR_SIZE.LARGE} fallbackIcon={details?.fallbackIcon} /> diff --git a/src/pages/ProfilePage.tsx b/src/pages/ProfilePage.tsx index 5586f525ceda..7ce9bcf47c9c 100755 --- a/src/pages/ProfilePage.tsx +++ b/src/pages/ProfilePage.tsx @@ -159,7 +159,7 @@ function ProfilePage({route}: ProfilePageProps) { containerStyles={[styles.avatarXLarge, styles.mb3]} imageStyles={[styles.avatarXLarge]} source={details.avatar} - accountID={accountID} + avatarID={accountID} size={CONST.AVATAR_SIZE.XLARGE} fallbackIcon={fallbackIcon} /> diff --git a/src/pages/ReportDetailsPage.tsx b/src/pages/ReportDetailsPage.tsx index 8b8044bc856c..2cd564b1f4c5 100644 --- a/src/pages/ReportDetailsPage.tsx +++ b/src/pages/ReportDetailsPage.tsx @@ -235,7 +235,7 @@ function ReportDetailsPage({policies, report, session, personalDetails}: ReportD isGroupChat && !isThread ? ( diff --git a/src/pages/RoomMembersPage.tsx b/src/pages/RoomMembersPage.tsx index abc8cc41cc4f..6920bb2a660b 100644 --- a/src/pages/RoomMembersPage.tsx +++ b/src/pages/RoomMembersPage.tsx @@ -216,7 +216,7 @@ function RoomMembersPage({report, session, policies}: RoomMembersPageProps) { 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/settings/InitialSettingsPage.tsx b/src/pages/settings/InitialSettingsPage.tsx index 1d2556c3bd04..e383077ee7f7 100755 --- a/src/pages/settings/InitialSettingsPage.tsx +++ b/src/pages/settings/InitialSettingsPage.tsx @@ -414,7 +414,7 @@ function InitialSettingsPage({session, userWallet, bankAccountList, fundList, wa ), diff --git a/src/pages/workspace/WorkspacesListRow.tsx b/src/pages/workspace/WorkspacesListRow.tsx index d652269b2d05..96963d1ffea0 100644 --- a/src/pages/workspace/WorkspacesListRow.tsx +++ b/src/pages/workspace/WorkspacesListRow.tsx @@ -214,7 +214,7 @@ function WorkspacesListRow({ <> diff --git a/src/pages/workspace/members/WorkspaceMemberDetailsPage.tsx b/src/pages/workspace/members/WorkspaceMemberDetailsPage.tsx index cd787230bb20..5885712efe36 100644 --- a/src/pages/workspace/members/WorkspaceMemberDetailsPage.tsx +++ b/src/pages/workspace/members/WorkspaceMemberDetailsPage.tsx @@ -159,7 +159,7 @@ function WorkspaceMemberDetailsPage({personalDetails, policy, route}: WorkspaceM containerStyles={[styles.avatarXLarge, styles.mv5, styles.noOutline]} imageStyles={[styles.avatarXLarge]} source={details.avatar} - accountID={accountID} + avatarID={accountID} size={CONST.AVATAR_SIZE.XLARGE} fallbackIcon={fallbackIcon} /> From d9844a69aed47f39e1031fefb66455064c180594 Mon Sep 17 00:00:00 2001 From: Mateusz Titz Date: Fri, 17 May 2024 15:26:54 +0200 Subject: [PATCH 14/16] Fix wrong WorkspaceAvatar colors on workspace profile page --- src/pages/workspace/WorkspaceAvatar.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/workspace/WorkspaceAvatar.tsx b/src/pages/workspace/WorkspaceAvatar.tsx index f1aa5235b42f..4139ecc679d9 100644 --- a/src/pages/workspace/WorkspaceAvatar.tsx +++ b/src/pages/workspace/WorkspaceAvatar.tsx @@ -28,7 +28,7 @@ function WorkspaceAvatar({policy, isLoadingApp = true}: WorkspaceAvatarProps) { source={UserUtils.getFullSizeAvatar(avatarURL, 0)} onModalClose={Navigation.goBack} isWorkspaceAvatar - originalFileName={policy?.originalFileName ?? policy?.name ?? ''} + originalFileName={policy?.originalFileName ?? policy?.id} shouldShowNotFoundPage={!Object.keys(policy ?? {}).length && !isLoadingApp} isLoading={!Object.keys(policy ?? {}).length && !!isLoadingApp} maybeIcon From 21c9e86a58fad7dba3f1b0312decfb79f565b1ac Mon Sep 17 00:00:00 2001 From: Mateusz Titz Date: Mon, 20 May 2024 09:22:04 +0200 Subject: [PATCH 15/16] Rename avatarId variable --- src/pages/home/report/ReportActionItemSingle.tsx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/pages/home/report/ReportActionItemSingle.tsx b/src/pages/home/report/ReportActionItemSingle.tsx index 43caefdf947b..f71db06c2d44 100644 --- a/src/pages/home/report/ReportActionItemSingle.tsx +++ b/src/pages/home/report/ReportActionItemSingle.tsx @@ -88,13 +88,13 @@ function ReportActionItemSingle({ const isInvoiceReport = ReportUtils.isInvoiceReport(iouReport ?? {}); const isWorkspaceActor = isInvoiceReport || (ReportUtils.isPolicyExpenseChat(report) && (!actorAccountID || displayAllActors)); let avatarSource = avatar; - let avatarAccountId: number | string | undefined = actorAccountID; + let avatarId: number | string | undefined = actorAccountID; if (isWorkspaceActor) { displayName = ReportUtils.getPolicyName(report); actorHint = displayName; avatarSource = ReportUtils.getWorkspaceAvatar(report); - avatarAccountId = report.policyID; + 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. @@ -103,7 +103,7 @@ function ReportActionItemSingle({ actorHint = `${delegateDisplayName} (${translate('reportAction.asCopilot')} ${displayName})`; displayName = actorHint; avatarSource = delegateDetails?.avatar; - avatarAccountId = action.delegateAccountID; + avatarId = action.delegateAccountID; } // If this is a report preview, display names and avatars of both people involved @@ -138,7 +138,7 @@ function ReportActionItemSingle({ source: avatarSource ?? FallbackAvatar, type: isWorkspaceActor ? CONST.ICON_TYPE_WORKSPACE : CONST.ICON_TYPE_AVATAR, name: primaryDisplayName ?? '', - id: isWorkspaceActor ? report.policyID : avatarAccountId, + id: avatarId, }; // Since the display name for a report action message is delivered with the report history as an array of fragments From b87b5863d14eea6c451c596807a940777df73561 Mon Sep 17 00:00:00 2001 From: Mateusz Titz Date: Thu, 23 May 2024 09:09:09 +0200 Subject: [PATCH 16/16] Add minor comment fixes in avatars --- src/components/Avatar.tsx | 2 +- src/components/AvatarWithIndicator.tsx | 2 +- src/libs/ReportUtils.ts | 8 ++++---- src/libs/UserUtils.ts | 3 +-- 4 files changed, 7 insertions(+), 8 deletions(-) diff --git a/src/components/Avatar.tsx b/src/components/Avatar.tsx index fa977f705248..c0d89f4acf1e 100644 --- a/src/components/Avatar.tsx +++ b/src/components/Avatar.tsx @@ -81,7 +81,7 @@ function Avatar({ const isWorkspace = type === CONST.ICON_TYPE_WORKSPACE; - // if it's user avatar then accountID will be a number + // 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; diff --git a/src/components/AvatarWithIndicator.tsx b/src/components/AvatarWithIndicator.tsx index eb153ee13623..f024a1239f4e 100644 --- a/src/components/AvatarWithIndicator.tsx +++ b/src/components/AvatarWithIndicator.tsx @@ -13,7 +13,7 @@ type AvatarWithIndicatorProps = { /** URL for the avatar */ source?: UserUtils.AvatarSource; - /** account id if it's user avatar */ + /** Account id if it's user avatar */ accountID?: number; /** To show a tooltip on hover */ diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index 9fdd5af23ceb..a6fdf0ad59a0 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -6496,10 +6496,10 @@ function hasMissingPaymentMethod(userWallet: OnyxEntry, 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/UserUtils.ts b/src/libs/UserUtils.ts index ca50beb19a8c..2acebd4636f5 100644 --- a/src/libs/UserUtils.ts +++ b/src/libs/UserUtils.ts @@ -79,7 +79,6 @@ function generateAccountID(searchValue: string): number { /** * Helper method to return the default avatar associated with the given accountID - * @returns */ function getDefaultAvatar(accountID = -1, avatarURL?: string): IconAsset | undefined { if (accountID === CONST.ACCOUNT_ID.CONCIERGE) { @@ -92,7 +91,7 @@ function getDefaultAvatar(accountID = -1, avatarURL?: string): IconAsset | undef // 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 | undefined; if (avatarURL) {