Skip to content

Commit dad65f7

Browse files
committed
Change getReportActionAvatars to useReportActionAvatars
1 parent c35bdc8 commit dad65f7

7 files changed

Lines changed: 237 additions & 259 deletions

File tree

src/components/ReportActionAvatars/ReportActionAvatar.tsx

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,14 @@ import lodashSortBy from 'lodash/sortBy';
22
import React, {useMemo} from 'react';
33
import type {ColorValue, ImageStyle, StyleProp, ViewStyle} from 'react-native';
44
import {View} from 'react-native';
5-
import type {OnyxEntry} from 'react-native-onyx';
65
import type {ValueOf} from 'type-fest';
76
import Avatar from '@components/Avatar';
87
import Icon from '@components/Icon';
98
import {WorkspaceBuilding} from '@components/Icon/WorkspaceDefaultAvatars';
109
import Text from '@components/Text';
1110
import Tooltip from '@components/Tooltip';
1211
import UserDetailsTooltip from '@components/UserDetailsTooltip';
12+
import useOnyx from '@hooks/useOnyx';
1313
import useStyleUtils from '@hooks/useStyleUtils';
1414
import useTheme from '@hooks/useTheme';
1515
import useThemeIllustrations from '@hooks/useThemeIllustrations';
@@ -20,6 +20,7 @@ import {getUserDetailTooltipText} from '@libs/ReportUtils';
2020
import type {AvatarSource} from '@libs/UserUtils';
2121
import variables from '@styles/variables';
2222
import CONST from '@src/CONST';
23+
import ONYXKEYS from '@src/ONYXKEYS';
2324
import type {CompanyCardFeed, OnyxInputOrEntry, PersonalDetailsList} from '@src/types/onyx';
2425
import type {Icon as IconType} from '@src/types/onyx/OnyxCommon';
2526

@@ -265,18 +266,20 @@ function ReportActionAvatarMultipleHorizontal({
265266
icons: unsortedIcons,
266267
isInReportAction,
267268
sort: sortAvatars,
268-
personalDetails,
269269
}: HorizontalStacking & {
270270
size: ValueOf<typeof CONST.AVATAR_SIZE>;
271271
shouldShowTooltip: boolean;
272272
icons: IconType[];
273273
isInReportAction: boolean;
274-
personalDetails: OnyxEntry<PersonalDetailsList>;
275274
}) {
276275
const theme = useTheme();
277276
const styles = useThemeStyles();
278277
const StyleUtils = useStyleUtils();
279278

279+
const [personalDetails] = useOnyx(ONYXKEYS.PERSONAL_DETAILS_LIST, {
280+
canBeMissing: true,
281+
});
282+
280283
const oneAvatarSize = StyleUtils.getAvatarStyle(size);
281284
const overlapSize = oneAvatarSize.width / overlapDivider;
282285
const oneAvatarBorderWidth = StyleUtils.getAvatarBorderWidth(size).borderWidth ?? 0;

src/components/ReportActionAvatars/index.tsx

Lines changed: 9 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,12 @@ import type {ColorValue, StyleProp, ViewStyle} from 'react-native';
33
import type {OnyxEntry} from 'react-native-onyx';
44
import type {ValueOf} from 'type-fest';
55
import useOnyx from '@hooks/useOnyx';
6-
import useReportIsArchived from '@hooks/useReportIsArchived';
7-
import useReportPreviewSenderID from '@hooks/useReportPreviewSenderID';
8-
import {getReportAction} from '@libs/ReportActionsUtils';
9-
import {getReportActionAvatars, isChatReport} from '@libs/ReportUtils';
106
import CONST from '@src/CONST';
117
import ONYXKEYS from '@src/ONYXKEYS';
128
import type {CompanyCardFeed, ReportAction} from '@src/types/onyx';
139
import type {HorizontalStacking} from './ReportActionAvatar';
1410
import ReportActionAvatar from './ReportActionAvatar';
11+
import useReportActionAvatars from './useReportActionAvatars';
1512

1613
type ReportActionAvatarsProps = {
1714
horizontalStacking?: HorizontalStacking | boolean;
@@ -55,7 +52,7 @@ type ReportActionAvatarsProps = {
5552

5653
function ReportActionAvatars({
5754
reportID: potentialReportID,
58-
action: passedAction,
55+
action,
5956
accountIDs: passedAccountIDs = [],
6057
size = CONST.AVATAR_SIZE.DEFAULT,
6158
shouldShowTooltip = true,
@@ -72,44 +69,23 @@ function ReportActionAvatars({
7269

7370
const reportID =
7471
potentialReportID ??
75-
([CONST.REPORT.ACTIONS.TYPE.REPORT_PREVIEW, CONST.REPORT.ACTIONS.TYPE.TRIP_PREVIEW].find((act) => act === passedAction?.actionName) ? passedAction?.childReportID : undefined);
72+
([CONST.REPORT.ACTIONS.TYPE.REPORT_PREVIEW, CONST.REPORT.ACTIONS.TYPE.TRIP_PREVIEW].find((act) => act === action?.actionName) ? action?.childReportID : undefined);
7673

7774
const [report] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${reportID}`, {canBeMissing: true});
7875

79-
const [potentialChatReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${report?.chatReportID}`, {canBeMissing: true});
80-
81-
const [personalDetails] = useOnyx(ONYXKEYS.PERSONAL_DETAILS_LIST, {
82-
canBeMissing: true,
83-
});
84-
const [policies] = useOnyx(ONYXKEYS.COLLECTION.POLICY, {canBeMissing: true});
85-
86-
const iouReport = isChatReport(report) && report?.chatType !== CONST.REPORT.CHAT_TYPE.TRIP_ROOM ? undefined : report;
87-
const chatReport = isChatReport(report) && report?.chatType !== CONST.REPORT.CHAT_TYPE.TRIP_ROOM ? report : potentialChatReport;
88-
89-
const action = passedAction ?? (iouReport?.parentReportActionID ? getReportAction(chatReport?.reportID ?? iouReport?.chatReportID, iouReport?.parentReportActionID) : undefined);
90-
91-
const reportPreviewSenderID = useReportPreviewSenderID({action, iouReport, chatReport});
92-
9376
const shouldStackHorizontally = !!horizontalStacking;
9477
const isHorizontalStackingAnObject = shouldStackHorizontally && typeof horizontalStacking !== 'boolean';
9578
const {isHovered = false} = isHorizontalStackingAnObject ? horizontalStacking : {};
9679

97-
// We want to display only the sender's avatar next to the report preview if it only contains one person's expenses.
98-
const isReportArchived = useReportIsArchived(reportID);
99-
10080
const {
10181
avatarType: notPreciseAvatarType,
102-
icons,
103-
delegateAccountID,
104-
} = getReportActionAvatars({
105-
chatReport,
106-
iouReport,
82+
avatars: icons,
83+
details: {delegateAccountID},
84+
source,
85+
} = useReportActionAvatars({
86+
report,
10787
action,
108-
personalDetails,
109-
reportPreviewSenderID,
110-
policies,
11188
shouldStackHorizontally,
112-
isReportArchived,
11389
shouldUseCardFeed: !!subscriptCardFeed,
11490
accountIDs,
11591
});
@@ -149,7 +125,6 @@ function ReportActionAvatars({
149125
icons={icons}
150126
isInReportAction={isInReportAction}
151127
shouldShowTooltip={shouldShowTooltip}
152-
personalDetails={personalDetails}
153128
/>
154129
);
155130
}
@@ -175,7 +150,7 @@ function ReportActionAvatars({
175150
containerStyles={shouldStackHorizontally ? [] : singleAvatarContainerStyle}
176151
shouldShowTooltip={shouldShowTooltip}
177152
accountID={Number(delegateAccountID ?? primaryAvatar.id ?? CONST.DEFAULT_NUMBER_ID)}
178-
delegateAccountID={action?.delegateAccountID}
153+
delegateAccountID={source.action?.delegateAccountID}
179154
fallbackIcon={primaryAvatar.fallbackIcon}
180155
/>
181156
);
Lines changed: 197 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,197 @@
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;

src/hooks/useReportPreviewSenderID.ts renamed to src/components/ReportActionAvatars/useReportPreviewSenderID.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
11
import {useMemo} from 'react';
22
import type {OnyxEntry} from 'react-native-onyx';
3+
import useOnyx from '@hooks/useOnyx';
4+
import useTransactionsAndViolationsForReport from '@hooks/useTransactionsAndViolationsForReport';
35
import {getAllNonDeletedTransactions} from '@libs/MoneyRequestReportUtils';
46
import {getPersonalDetailByEmail} from '@libs/PersonalDetailsUtils';
57
import {getOriginalMessage, isMoneyRequestAction} from '@libs/ReportActionsUtils';
68
import {isDM} from '@libs/ReportUtils';
79
import CONST from '@src/CONST';
810
import ONYXKEYS from '@src/ONYXKEYS';
911
import type {Report, ReportAction, Transaction} from '@src/types/onyx';
10-
import useOnyx from './useOnyx';
11-
import useTransactionsAndViolationsForReport from './useTransactionsAndViolationsForReport';
1212

1313
function getSplitAuthor(transaction: Transaction, splits?: Array<ReportAction<typeof CONST.REPORT.ACTIONS.TYPE.IOU>>) {
1414
const {originalTransactionID, source} = transaction.comment ?? {};

0 commit comments

Comments
 (0)