Skip to content

Commit 2238175

Browse files
authored
Merge pull request Expensify#67881 from software-mansion-labs/korytko/avatar-follow-ups
[CP Staging][Avatars - Reports] Follow-ups to fix deploy blockers
2 parents 771486c + c1c609f commit 2238175

9 files changed

Lines changed: 121 additions & 10 deletions

File tree

src/components/ReportActionAvatars/ReportActionAvatar.tsx

Lines changed: 56 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,11 @@ import type {ValueOf} from 'type-fest';
66
import Avatar from '@components/Avatar';
77
import Icon from '@components/Icon';
88
import {WorkspaceBuilding} from '@components/Icon/WorkspaceDefaultAvatars';
9+
import PressableWithoutFocus from '@components/Pressable/PressableWithoutFocus';
910
import Text from '@components/Text';
1011
import Tooltip from '@components/Tooltip';
1112
import UserDetailsTooltip from '@components/UserDetailsTooltip';
13+
import useLocalize from '@hooks/useLocalize';
1214
import useOnyx from '@hooks/useOnyx';
1315
import useStyleUtils from '@hooks/useStyleUtils';
1416
import useTheme from '@hooks/useTheme';
@@ -18,9 +20,11 @@ import {getCardFeedIcon} from '@libs/CardUtils';
1820
import localeCompare from '@libs/LocaleCompare';
1921
import {getUserDetailTooltipText} from '@libs/ReportUtils';
2022
import type {AvatarSource} from '@libs/UserUtils';
23+
import Navigation from '@navigation/Navigation';
2124
import variables from '@styles/variables';
2225
import CONST from '@src/CONST';
2326
import ONYXKEYS from '@src/ONYXKEYS';
27+
import ROUTES from '@src/ROUTES';
2428
import type {CompanyCardFeed, OnyxInputOrEntry, PersonalDetailsList} from '@src/types/onyx';
2529
import type {Icon as IconType} from '@src/types/onyx/OnyxCommon';
2630

@@ -61,6 +65,38 @@ type AvatarSizeToStyles = typeof CONST.AVATAR_SIZE.SMALL | typeof CONST.AVATAR_S
6165

6266
type AvatarSizeToStylesMap = Record<AvatarSizeToStyles, AvatarStyles>;
6367

68+
function ProfileAvatar(props: Parameters<typeof Avatar>[0] & {useProfileNavigationWrapper?: boolean}) {
69+
const {translate} = useLocalize();
70+
const {avatarID, useProfileNavigationWrapper, type} = props;
71+
72+
if (!useProfileNavigationWrapper) {
73+
return (
74+
/* eslint-disable-next-line react/jsx-props-no-spreading */
75+
<Avatar {...{...props, useProfileNavigationWrapper: undefined}} />
76+
);
77+
}
78+
79+
const isWorkspace = type === CONST.ICON_TYPE_WORKSPACE;
80+
81+
const onPress = () => {
82+
if (isWorkspace) {
83+
return Navigation.navigate(ROUTES.WORKSPACE_AVATAR.getRoute(String(avatarID)));
84+
}
85+
return Navigation.navigate(ROUTES.PROFILE_AVATAR.getRoute(Number(avatarID), Navigation.getActiveRoute()));
86+
};
87+
88+
return (
89+
<PressableWithoutFocus
90+
onPress={onPress}
91+
accessibilityLabel={translate(isWorkspace ? 'common.workspaces' : 'common.profile')}
92+
accessibilityRole={CONST.ROLE.BUTTON}
93+
>
94+
{/* eslint-disable-next-line react/jsx-props-no-spreading */}
95+
<Avatar {...{...props, useProfileNavigationWrapper: undefined}} />
96+
</PressableWithoutFocus>
97+
);
98+
}
99+
64100
function ReportActionAvatarSingle({
65101
avatar,
66102
size,
@@ -70,6 +106,7 @@ function ReportActionAvatarSingle({
70106
accountID,
71107
fallbackIcon,
72108
isInReportAction,
109+
useProfileNavigationWrapper,
73110
}: {
74111
avatar: IconType | undefined;
75112
size: ValueOf<typeof CONST.AVATAR_SIZE>;
@@ -79,6 +116,7 @@ function ReportActionAvatarSingle({
79116
delegateAccountID?: number;
80117
fallbackIcon?: AvatarSource;
81118
isInReportAction?: boolean;
119+
useProfileNavigationWrapper?: boolean;
82120
}) {
83121
const StyleUtils = useStyleUtils();
84122
const avatarContainerStyles = StyleUtils.getContainerStyles(size, isInReportAction);
@@ -94,7 +132,8 @@ function ReportActionAvatarSingle({
94132
shouldRender={shouldShowTooltip}
95133
>
96134
<View>
97-
<Avatar
135+
<ProfileAvatar
136+
useProfileNavigationWrapper={useProfileNavigationWrapper}
98137
containerStyles={containerStyles ?? avatarContainerStyles}
99138
source={avatar?.source}
100139
type={avatar?.type ?? CONST.ICON_TYPE_AVATAR}
@@ -118,6 +157,7 @@ function ReportActionAvatarSubscript({
118157
noRightMarginOnContainer,
119158
subscriptAvatarBorderColor,
120159
subscriptCardFeed,
160+
useProfileNavigationWrapper,
121161
}: {
122162
primaryAvatar: IconType;
123163
secondaryAvatar: IconType;
@@ -126,6 +166,7 @@ function ReportActionAvatarSubscript({
126166
noRightMarginOnContainer?: boolean;
127167
subscriptAvatarBorderColor?: ColorValue;
128168
subscriptCardFeed?: CompanyCardFeed | typeof CONST.EXPENSIFY_CARD.BANK;
169+
useProfileNavigationWrapper?: boolean;
129170
}) {
130171
const theme = useTheme();
131172
const styles = useThemeStyles();
@@ -167,7 +208,8 @@ function ReportActionAvatarSubscript({
167208
}}
168209
>
169210
<View>
170-
<Avatar
211+
<ProfileAvatar
212+
useProfileNavigationWrapper={useProfileNavigationWrapper}
171213
containerStyles={StyleUtils.getWidthAndHeightStyle(StyleUtils.getAvatarSize(size || CONST.AVATAR_SIZE.DEFAULT))}
172214
source={primaryAvatar.source}
173215
size={size}
@@ -191,7 +233,8 @@ function ReportActionAvatarSubscript({
191233
// https://stackoverflow.com/questions/56338939/hover-in-css-is-not-working-with-electron
192234
dataSet={{dragArea: false}}
193235
>
194-
<Avatar
236+
<ProfileAvatar
237+
useProfileNavigationWrapper={useProfileNavigationWrapper}
195238
iconAdditionalStyles={[
196239
StyleUtils.getAvatarBorderWidth(isSmall ? CONST.AVATAR_SIZE.SMALL_SUBSCRIPT : subscriptAvatarSize),
197240
StyleUtils.getBorderColorStyle(subscriptAvatarBorderColor ?? theme.componentBG),
@@ -266,11 +309,13 @@ function ReportActionAvatarMultipleHorizontal({
266309
icons: unsortedIcons,
267310
isInReportAction,
268311
sort: sortAvatars,
312+
useProfileNavigationWrapper,
269313
}: HorizontalStacking & {
270314
size: ValueOf<typeof CONST.AVATAR_SIZE>;
271315
shouldShowTooltip: boolean;
272316
icons: IconType[];
273317
isInReportAction: boolean;
318+
useProfileNavigationWrapper?: boolean;
274319
}) {
275320
const theme = useTheme();
276321
const styles = useThemeStyles();
@@ -335,7 +380,8 @@ function ReportActionAvatarMultipleHorizontal({
335380
shouldRender={shouldShowTooltip}
336381
>
337382
<View style={[StyleUtils.getHorizontalStackedAvatarStyle(index, overlapSize), StyleUtils.getAvatarBorderRadius(size, icon.type)]}>
338-
<Avatar
383+
<ProfileAvatar
384+
useProfileNavigationWrapper={useProfileNavigationWrapper}
339385
iconAdditionalStyles={[
340386
StyleUtils.getHorizontalStackedAvatarBorderStyle({
341387
theme,
@@ -404,6 +450,7 @@ function ReportActionAvatarMultipleDiagonal({
404450
useMidSubscriptSize,
405451
secondaryAvatarContainerStyle,
406452
isHovered = false,
453+
useProfileNavigationWrapper,
407454
}: {
408455
size: ValueOf<typeof CONST.AVATAR_SIZE>;
409456
shouldShowTooltip: boolean;
@@ -412,6 +459,7 @@ function ReportActionAvatarMultipleDiagonal({
412459
useMidSubscriptSize: boolean;
413460
secondaryAvatarContainerStyle?: StyleProp<ViewStyle>;
414461
isHovered?: boolean;
462+
useProfileNavigationWrapper?: boolean;
415463
}) {
416464
const theme = useTheme();
417465
const styles = useThemeStyles();
@@ -478,7 +526,8 @@ function ReportActionAvatarMultipleDiagonal({
478526
>
479527
{/* View is necessary for tooltip to show for multiple avatars in LHN */}
480528
<View>
481-
<Avatar
529+
<ProfileAvatar
530+
useProfileNavigationWrapper={useProfileNavigationWrapper}
482531
source={icons.at(0)?.source ?? WorkspaceBuilding}
483532
size={avatarSize}
484533
imageStyles={[singleAvatarStyle]}
@@ -507,7 +556,8 @@ function ReportActionAvatarMultipleDiagonal({
507556
shouldRender={shouldShowTooltip}
508557
>
509558
<View>
510-
<Avatar
559+
<ProfileAvatar
560+
useProfileNavigationWrapper={useProfileNavigationWrapper}
511561
source={icons.at(1)?.source ?? WorkspaceBuilding}
512562
size={avatarSize}
513563
imageStyles={[singleAvatarStyle]}

src/components/ReportActionAvatars/index.tsx

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,15 @@ import useReportActionAvatars from './useReportActionAvatars';
1313
type ReportActionAvatarsProps = {
1414
horizontalStacking?: HorizontalStacking | boolean;
1515

16-
/** IOU Report ID for single avatar */
16+
/** Report ID for the report action avatars */
1717
reportID?: string;
1818

19-
/** IOU Report ID for single avatar */
19+
/** Action for the report action avatars */
2020
action?: OnyxEntry<ReportAction>;
2121

22+
/** Policy ID for the workspace avatar */
23+
policyID?: string;
24+
2225
/** Single avatar container styles */
2326
singleAvatarContainerStyle?: ViewStyle[];
2427

@@ -48,11 +51,15 @@ type ReportActionAvatarsProps = {
4851

4952
/** Subscript card feed to display instead of the second avatar */
5053
subscriptCardFeed?: CompanyCardFeed | typeof CONST.EXPENSIFY_CARD.BANK;
54+
55+
/** Whether we want to be redirected to profile on avatars click */
56+
useProfileNavigationWrapper?: boolean;
5157
};
5258

5359
/**
5460
* The component that renders proper user avatars based on either:
5561
*
62+
* - policyID - this can be passed if we have no other option, and we want to display workspace avatar, it makes component ignore the props below
5663
* - accountIDs - if this is passed, it is prioritized and render even if report or action has different avatars attached, useful for option items, menu items etc.
5764
* - action - this is useful when we want to display avatars of chat threads, messages, report/trip previews etc.
5865
* - reportID - this can be passed without above props, when we want to display chat report avatars, DM chat avatars etc.
@@ -62,6 +69,7 @@ function ReportActionAvatars({
6269
reportID: potentialReportID,
6370
action,
6471
accountIDs: passedAccountIDs = [],
72+
policyID,
6573
size = CONST.AVATAR_SIZE.DEFAULT,
6674
shouldShowTooltip = true,
6775
horizontalStacking,
@@ -72,6 +80,7 @@ function ReportActionAvatars({
7280
secondaryAvatarContainerStyle,
7381
useMidSubscriptSizeForMultipleAvatars = false,
7482
isInReportAction = false,
83+
useProfileNavigationWrapper,
7584
}: ReportActionAvatarsProps) {
7685
const accountIDs = passedAccountIDs.filter((accountID) => accountID !== CONST.DEFAULT_NUMBER_ID);
7786

@@ -96,6 +105,7 @@ function ReportActionAvatars({
96105
shouldStackHorizontally,
97106
shouldUseCardFeed: !!subscriptCardFeed,
98107
accountIDs,
108+
policyID,
99109
});
100110

101111
let avatarType: ValueOf<typeof CONST.REPORT_ACTION_AVATARS.TYPE> = notPreciseAvatarType;
@@ -120,6 +130,7 @@ function ReportActionAvatars({
120130
noRightMarginOnContainer={noRightMarginOnSubscriptContainer}
121131
subscriptAvatarBorderColor={subscriptAvatarBorderColor}
122132
subscriptCardFeed={subscriptCardFeed}
133+
useProfileNavigationWrapper={useProfileNavigationWrapper}
123134
/>
124135
);
125136
}
@@ -133,6 +144,7 @@ function ReportActionAvatars({
133144
icons={icons}
134145
isInReportAction={isInReportAction}
135146
shouldShowTooltip={shouldShowTooltip}
147+
useProfileNavigationWrapper={useProfileNavigationWrapper}
136148
/>
137149
);
138150
}
@@ -147,6 +159,7 @@ function ReportActionAvatars({
147159
useMidSubscriptSize={useMidSubscriptSizeForMultipleAvatars}
148160
secondaryAvatarContainerStyle={secondaryAvatarContainerStyle}
149161
isHovered={isHovered}
162+
useProfileNavigationWrapper={useProfileNavigationWrapper}
150163
/>
151164
);
152165
}
@@ -160,6 +173,7 @@ function ReportActionAvatars({
160173
accountID={Number(delegateAccountID ?? primaryAvatar.id ?? CONST.DEFAULT_NUMBER_ID)}
161174
delegateAccountID={source.action?.delegateAccountID}
162175
fallbackIcon={primaryAvatar.fallbackIcon}
176+
useProfileNavigationWrapper={useProfileNavigationWrapper}
163177
/>
164178
);
165179
}

src/components/ReportActionAvatars/useReportActionAvatars.ts

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import {
88
getDisplayNameForParticipant,
99
getIcons,
1010
getReportActionActorAccountID,
11+
getWorkspaceIcon,
1112
isChatThread,
1213
isInvoiceReport,
1314
isPolicyExpenseChat,
@@ -26,12 +27,14 @@ function useReportActionAvatars({
2627
shouldStackHorizontally = false,
2728
shouldUseCardFeed = false,
2829
accountIDs = [],
30+
policyID: passedPolicyID,
2931
}: {
3032
report: OnyxEntry<Report>;
3133
action: OnyxEntry<ReportAction>;
3234
shouldStackHorizontally?: boolean;
3335
shouldUseCardFeed?: boolean;
3436
accountIDs?: number[];
37+
policyID?: string;
3538
}) {
3639
/* Get avatar type */
3740

@@ -57,9 +60,33 @@ function useReportActionAvatars({
5760
chatReport,
5861
});
5962

60-
const policyID = chatReport?.policyID === CONST.POLICY.ID_FAKE || !chatReport?.policyID ? (iouReport?.policyID ?? chatReport?.policyID) : chatReport?.policyID;
63+
const policyID = passedPolicyID ?? (chatReport?.policyID === CONST.POLICY.ID_FAKE || !chatReport?.policyID ? (iouReport?.policyID ?? chatReport?.policyID) : chatReport?.policyID);
6164
const policy = policies?.[`${ONYXKEYS.COLLECTION.POLICY}${policyID}`];
6265

66+
const {chatReportIDAdmins, chatReportIDAnnounce, workspaceAccountID} = policy ?? {};
67+
const [policyChatReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${chatReportIDAnnounce ?? chatReportIDAdmins}`, {canBeMissing: true});
68+
69+
if (passedPolicyID) {
70+
const policyChatReportAvatar = {...getWorkspaceIcon(policyChatReport, policy), id: policyID, name: policy?.name};
71+
72+
return {
73+
avatars: [policyChatReportAvatar],
74+
avatarType: CONST.REPORT_ACTION_AVATARS.TYPE.SINGLE,
75+
details: {
76+
...(personalDetails?.[workspaceAccountID ?? CONST.DEFAULT_NUMBER_ID] ?? {}),
77+
shouldDisplayAllActors: false,
78+
isWorkspaceActor: false,
79+
// eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
80+
actorHint: String(policyID).replace(CONST.REGEX.MERGED_ACCOUNT_PREFIX, ''),
81+
accountID: workspaceAccountID,
82+
delegateAccountID: undefined,
83+
},
84+
source: {
85+
policyChatReport,
86+
},
87+
};
88+
}
89+
6390
const isATripRoom = isTripRoom(chatReport);
6491
const isWorkspaceWithoutChatReportProp = !chatReport && policy?.type !== CONST.POLICY.TYPE.PERSONAL;
6592
const isAWorkspaceChat = isPolicyExpenseChat(chatReport) || isWorkspaceWithoutChatReportProp;

src/components/ReportActionAvatars/useReportPreviewSenderID.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import {getAllNonDeletedTransactions} from '@libs/MoneyRequestReportUtils';
66
import {getPersonalDetailByEmail} from '@libs/PersonalDetailsUtils';
77
import {getOriginalMessage, isMoneyRequestAction} from '@libs/ReportActionsUtils';
88
import {isDM} from '@libs/ReportUtils';
9+
import {getCurrentUserAccountID} from '@userActions/Report';
910
import CONST from '@src/CONST';
1011
import ONYXKEYS from '@src/ONYXKEYS';
1112
import type {Report, ReportAction, Transaction} from '@src/types/onyx';
@@ -47,6 +48,10 @@ function useReportPreviewSenderID({iouReport, action, chatReport}: {action: Onyx
4748
canBeMissing: true,
4849
});
4950

51+
if (action?.isOptimisticAction && action?.actionName === CONST.REPORT.ACTIONS.TYPE.REPORT_PREVIEW) {
52+
return getCurrentUserAccountID();
53+
}
54+
5055
// 1. If all amounts have the same sign - either all amounts are positive or all amounts are negative.
5156
// We have to do it this way because there can be a case when actions are not available
5257
// See: https://github.com/Expensify/App/pull/64802#issuecomment-3008944401

src/components/SelectionList/UserListItem.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@ function UserListItem<TItem extends ListItem>({
9999
</View>
100100
</PressableWithFeedback>
101101
)}
102-
{!!item.icons && (
102+
{(!!item.reportID || !!item.accountID || !!item.policyID) && (
103103
<ReportActionAvatars
104104
subscriptAvatarBorderColor={hovered && !isFocused ? hoveredBackgroundColor : subscriptAvatarBorderColor}
105105
shouldShowTooltip={showTooltip}
@@ -110,6 +110,7 @@ function UserListItem<TItem extends ListItem>({
110110
]}
111111
reportID={item.reportID}
112112
accountIDs={[Number(item.accountID)]}
113+
policyID={!item.reportID && !item.accountID ? item.policyID : undefined}
113114
singleAvatarContainerStyle={[styles.actionAvatar, styles.mr3]}
114115
/>
115116
)}

src/libs/ReportUtils.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6709,6 +6709,7 @@ function buildOptimisticReportPreview(
67096709
actorAccountID: hasReceipt ? currentUserAccountID : reportActorAccountID,
67106710
childReportID: childReportID ?? iouReport?.reportID,
67116711
childMoneyRequestCount: 1,
6712+
isOptimisticAction: true,
67126713
childLastActorAccountID: currentUserAccountID,
67136714
childLastMoneyRequestComment: comment,
67146715
childRecentReceiptTransactionIDs: hasReceipt && !isEmptyObject(transaction) && transaction?.transactionID ? {[transaction.transactionID]: created} : undefined,

0 commit comments

Comments
 (0)