|
| 1 | +import type {OnyxEntry} from 'react-native-onyx'; |
| 2 | +import type {ValueOf} from 'type-fest'; |
| 3 | +import {FallbackAvatar} from '@components/Icon/Expensicons'; |
| 4 | +import useOnyx from '@hooks/useOnyx'; |
| 5 | +import useReportIsArchived from '@hooks/useReportIsArchived'; |
| 6 | +import {getReportAction} from '@libs/ReportActionsUtils'; |
| 7 | +import { |
| 8 | + getDisplayNameForParticipant, |
| 9 | + getIcons, |
| 10 | + getReportActionActorAccountID, |
| 11 | + isChatThread, |
| 12 | + isInvoiceReport, |
| 13 | + isPolicyExpenseChat, |
| 14 | + isTripRoom, |
| 15 | + shouldReportShowSubscript, |
| 16 | +} from '@libs/ReportUtils'; |
| 17 | +import CONST from '@src/CONST'; |
| 18 | +import ONYXKEYS from '@src/ONYXKEYS'; |
| 19 | +import type {OnyxInputOrEntry, Report, ReportAction} from '@src/types/onyx'; |
| 20 | +import type {Icon as IconType} from '@src/types/onyx/OnyxCommon'; |
| 21 | +import useReportPreviewSenderID from './useReportPreviewSenderID'; |
| 22 | + |
| 23 | +function useReportActionAvatars({ |
| 24 | + report, |
| 25 | + action: passedAction, |
| 26 | + shouldStackHorizontally = false, |
| 27 | + shouldUseCardFeed = false, |
| 28 | + accountIDs = [], |
| 29 | +}: { |
| 30 | + report: OnyxEntry<Report>; |
| 31 | + action: OnyxEntry<ReportAction>; |
| 32 | + shouldStackHorizontally?: boolean; |
| 33 | + shouldUseCardFeed?: boolean; |
| 34 | + accountIDs?: number[]; |
| 35 | +}) { |
| 36 | + /* Get avatar type */ |
| 37 | + |
| 38 | + const [personalDetails] = useOnyx(ONYXKEYS.PERSONAL_DETAILS_LIST, { |
| 39 | + canBeMissing: true, |
| 40 | + }); |
| 41 | + const [policies] = useOnyx(ONYXKEYS.COLLECTION.POLICY, {canBeMissing: true}); |
| 42 | + |
| 43 | + const isReportAChatReport = report?.type === CONST.REPORT.TYPE.CHAT && report?.chatType !== CONST.REPORT.CHAT_TYPE.TRIP_ROOM; |
| 44 | + |
| 45 | + const [reportChatReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${report?.chatReportID}`, {canBeMissing: true}); |
| 46 | + |
| 47 | + const chatReport = isReportAChatReport ? report : reportChatReport; |
| 48 | + const iouReport = isReportAChatReport ? undefined : report; |
| 49 | + |
| 50 | + const action = passedAction ?? (iouReport?.parentReportActionID ? getReportAction(chatReport?.reportID ?? iouReport?.chatReportID, iouReport?.parentReportActionID) : undefined); |
| 51 | + |
| 52 | + const isReportArchived = useReportIsArchived(iouReport?.reportID); |
| 53 | + |
| 54 | + const reportPreviewSenderID = useReportPreviewSenderID({ |
| 55 | + iouReport, |
| 56 | + action, |
| 57 | + chatReport, |
| 58 | + }); |
| 59 | + |
| 60 | + const policyID = chatReport?.policyID === CONST.POLICY.ID_FAKE || !chatReport?.policyID ? (iouReport?.policyID ?? chatReport?.policyID) : chatReport?.policyID; |
| 61 | + const policy = policies?.[`${ONYXKEYS.COLLECTION.POLICY}${policyID}`]; |
| 62 | + |
| 63 | + const isATripRoom = isTripRoom(chatReport); |
| 64 | + const isWorkspaceWithoutChatReportProp = !chatReport && policy?.type !== CONST.POLICY.TYPE.PERSONAL; |
| 65 | + const isAWorkspaceChat = isPolicyExpenseChat(chatReport) || isWorkspaceWithoutChatReportProp; |
| 66 | + const isATripPreview = action?.actionName === CONST.REPORT.ACTIONS.TYPE.TRIP_PREVIEW; |
| 67 | + const isAReportPreviewAction = action?.actionName === CONST.REPORT.ACTIONS.TYPE.REPORT_PREVIEW; |
| 68 | + const isReportPreviewOrNoAction = !action || isAReportPreviewAction; |
| 69 | + const isReportPreviewInTripRoom = isAReportPreviewAction && isATripRoom; |
| 70 | + |
| 71 | + // We want to display only the sender's avatar next to the report preview if it only contains one person's expenses. |
| 72 | + const displayAllActors = isAReportPreviewAction && !isATripRoom && !isAWorkspaceChat && !reportPreviewSenderID; |
| 73 | + |
| 74 | + const shouldUseAccountIDs = accountIDs.length > 0; |
| 75 | + const shouldShowAllActors = displayAllActors && !reportPreviewSenderID; |
| 76 | + const isChatThreadOutsideTripRoom = isChatThread(chatReport) && !isATripRoom; |
| 77 | + const shouldShowSubscriptAvatar = shouldReportShowSubscript(iouReport ?? chatReport, isReportArchived) && policy?.type !== CONST.POLICY.TYPE.PERSONAL; |
| 78 | + const shouldShowConvertedSubscriptAvatar = (shouldStackHorizontally || shouldUseAccountIDs) && shouldShowSubscriptAvatar && !reportPreviewSenderID; |
| 79 | + |
| 80 | + const shouldUseSubscriptAvatar = |
| 81 | + (((shouldShowSubscriptAvatar && isReportPreviewOrNoAction) || isReportPreviewInTripRoom || isATripPreview) && |
| 82 | + !shouldStackHorizontally && |
| 83 | + !isChatThreadOutsideTripRoom && |
| 84 | + !shouldShowConvertedSubscriptAvatar) || |
| 85 | + shouldUseCardFeed; |
| 86 | + |
| 87 | + // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing |
| 88 | + const shouldUseMultipleAvatars = shouldUseAccountIDs || shouldShowAllActors || shouldShowConvertedSubscriptAvatar; |
| 89 | + |
| 90 | + let avatarType: ValueOf<typeof CONST.REPORT_ACTION_AVATARS.TYPE> = CONST.REPORT_ACTION_AVATARS.TYPE.SINGLE; |
| 91 | + |
| 92 | + if (shouldUseSubscriptAvatar) { |
| 93 | + avatarType = CONST.REPORT_ACTION_AVATARS.TYPE.SUBSCRIPT; |
| 94 | + } else if (shouldUseMultipleAvatars) { |
| 95 | + avatarType = CONST.REPORT_ACTION_AVATARS.TYPE.MULTIPLE; |
| 96 | + } |
| 97 | + |
| 98 | + /* Get correct primary & secondary icon */ |
| 99 | + |
| 100 | + const delegatePersonalDetails = action?.delegateAccountID ? personalDetails?.[action?.delegateAccountID] : undefined; |
| 101 | + const actorAccountID = getReportActionActorAccountID(action, iouReport, chatReport, delegatePersonalDetails); |
| 102 | + // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing |
| 103 | + const accountID = reportPreviewSenderID || (actorAccountID ?? CONST.DEFAULT_NUMBER_ID); |
| 104 | + const invoiceReceiverPolicy = |
| 105 | + chatReport?.invoiceReceiver && 'policyID' in chatReport.invoiceReceiver ? policies?.[`${ONYXKEYS.COLLECTION.POLICY}${chatReport.invoiceReceiver.policyID}`] : undefined; |
| 106 | + const {avatar, fallbackIcon, login} = personalDetails?.[accountID] ?? {}; |
| 107 | + |
| 108 | + const defaultDisplayName = getDisplayNameForParticipant({accountID, personalDetailsData: personalDetails}) ?? ''; |
| 109 | + const isAInvoiceReport = isInvoiceReport(iouReport ?? null); |
| 110 | + const isWorkspaceActor = isAInvoiceReport || (isAWorkspaceChat && (!actorAccountID || displayAllActors)); |
| 111 | + const isChatReportOnlyProp = !iouReport && chatReport; |
| 112 | + const isWorkspaceChatWithoutChatReport = !chatReport && isAWorkspaceChat; |
| 113 | + // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing |
| 114 | + const usePersonalDetailsAvatars = (isChatReportOnlyProp || isWorkspaceChatWithoutChatReport) && isReportPreviewOrNoAction && !isATripPreview; |
| 115 | + const useNearestReportAvatars = (!accountID || !action) && accountIDs.length === 0; |
| 116 | + |
| 117 | + const getIconsWithDefaults = (onyxReport: OnyxInputOrEntry<Report>) => |
| 118 | + getIcons(onyxReport, personalDetails, avatar ?? fallbackIcon ?? FallbackAvatar, defaultDisplayName, accountID, policy, invoiceReceiverPolicy); |
| 119 | + |
| 120 | + const reportIcons = getIconsWithDefaults(chatReport ?? iouReport); |
| 121 | + |
| 122 | + let primaryAvatar; |
| 123 | + |
| 124 | + if (useNearestReportAvatars) { |
| 125 | + primaryAvatar = getIconsWithDefaults(iouReport ?? chatReport).at(0); |
| 126 | + } else if (isWorkspaceActor || usePersonalDetailsAvatars) { |
| 127 | + primaryAvatar = reportIcons.at(0); |
| 128 | + } else if (delegatePersonalDetails) { |
| 129 | + primaryAvatar = getIconsWithDefaults(iouReport).at(0); |
| 130 | + } else if (isAReportPreviewAction && isATripRoom) { |
| 131 | + primaryAvatar = reportIcons.at(0); |
| 132 | + } |
| 133 | + |
| 134 | + primaryAvatar ??= { |
| 135 | + source: avatar ?? FallbackAvatar, |
| 136 | + id: accountID, |
| 137 | + name: defaultDisplayName, |
| 138 | + type: CONST.ICON_TYPE_AVATAR, |
| 139 | + fill: undefined, |
| 140 | + fallbackIcon, |
| 141 | + }; |
| 142 | + |
| 143 | + let secondaryAvatar; |
| 144 | + |
| 145 | + if (useNearestReportAvatars) { |
| 146 | + secondaryAvatar = getIconsWithDefaults(iouReport ?? chatReport).at(1); |
| 147 | + } else if (usePersonalDetailsAvatars) { |
| 148 | + secondaryAvatar = reportIcons.at(1); |
| 149 | + } else if (isATripPreview) { |
| 150 | + secondaryAvatar = reportIcons.at(0); |
| 151 | + } else if (isReportPreviewInTripRoom || displayAllActors) { |
| 152 | + const iouReportIcons = getIconsWithDefaults(iouReport); |
| 153 | + secondaryAvatar = iouReportIcons.at(iouReportIcons.at(1)?.id === primaryAvatar.id ? 0 : 1); |
| 154 | + } else if (!isWorkspaceActor) { |
| 155 | + // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing |
| 156 | + secondaryAvatar = reportIcons.at(chatReport?.isOwnPolicyExpenseChat || isAWorkspaceChat ? 0 : 1); |
| 157 | + } else if (isAInvoiceReport) { |
| 158 | + secondaryAvatar = reportIcons.at(1); |
| 159 | + } |
| 160 | + |
| 161 | + secondaryAvatar ??= { |
| 162 | + name: '', |
| 163 | + source: '', |
| 164 | + type: CONST.ICON_TYPE_AVATAR, |
| 165 | + id: 0, |
| 166 | + fill: undefined, |
| 167 | + fallbackIcon, |
| 168 | + }; |
| 169 | + |
| 170 | + const avatarsForAccountIDs: IconType[] = accountIDs.map((id) => ({ |
| 171 | + id, |
| 172 | + type: CONST.ICON_TYPE_AVATAR, |
| 173 | + source: personalDetails?.[id]?.avatar ?? FallbackAvatar, |
| 174 | + name: personalDetails?.[id]?.login ?? '', |
| 175 | + })); |
| 176 | + |
| 177 | + return { |
| 178 | + avatars: avatarsForAccountIDs.length > 0 && avatarType === CONST.REPORT_ACTION_AVATARS.TYPE.MULTIPLE ? avatarsForAccountIDs : [primaryAvatar, secondaryAvatar], |
| 179 | + avatarType, |
| 180 | + details: { |
| 181 | + ...(personalDetails?.[accountID] ?? {}), |
| 182 | + shouldDisplayAllActors: displayAllActors, |
| 183 | + isWorkspaceActor, |
| 184 | + // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing |
| 185 | + actorHint: String(isWorkspaceActor ? primaryAvatar.id : login || (defaultDisplayName ?? '')).replace(CONST.REGEX.MERGED_ACCOUNT_PREFIX, ''), |
| 186 | + accountID, |
| 187 | + delegateAccountID: !isWorkspaceActor && delegatePersonalDetails ? actorAccountID : undefined, |
| 188 | + }, |
| 189 | + source: { |
| 190 | + iouReport, |
| 191 | + chatReport, |
| 192 | + action, |
| 193 | + }, |
| 194 | + }; |
| 195 | +} |
| 196 | + |
| 197 | +export default useReportActionAvatars; |
0 commit comments