Skip to content

Commit 165ff6b

Browse files
authored
Merge pull request #89274 from BartekObudzinski/refactor/decompose-option-row-lhn
[No QA] refactor: decompose OptionRowLHN alternate text and action badge
2 parents 905e58e + 575553a commit 165ff6b

13 files changed

Lines changed: 384 additions & 232 deletions

src/components/LHNOptionsList/LHNOptionsList.tsx

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ import CONST from '@src/CONST';
1717
import ONYXKEYS from '@src/ONYXKEYS';
1818
import type {Report} from '@src/types/onyx';
1919
import LHNTooltipContextProvider from './LHNTooltipContextProvider';
20-
import OptionRowLHNData from './OptionRowLHNData';
20+
import OptionRowLHNData from './OptionRowLHN';
2121
import OptionRowRendererComponent from './OptionRowRendererComponent';
2222
import type {LHNOptionsListProps, RenderItemProps} from './types';
2323

@@ -34,7 +34,6 @@ function LHNOptionsList({style, contentContainerStyles, data, onSelectRow, optio
3434
const reportAttributes = useReportAttributes();
3535
const [policy] = useOnyx(ONYXKEYS.COLLECTION.POLICY);
3636
const [personalDetails] = useOnyx(ONYXKEYS.PERSONAL_DETAILS_LIST);
37-
const [conciergeReportID] = useOnyx(ONYXKEYS.CONCIERGE_REPORT_ID);
3837

3938
const styles = useThemeStyles();
4039
const estimatedItemSize = optionMode === CONST.OPTION_MODE.COMPACT ? variables.optionRowHeightCompact : variables.optionRowHeight;
@@ -84,7 +83,6 @@ function LHNOptionsList({style, contentContainerStyles, data, onSelectRow, optio
8483
policy={itemPolicy}
8584
invoiceReceiverPolicy={itemInvoiceReceiverPolicy}
8685
personalDetails={personalDetails ?? {}}
87-
conciergeReportID={conciergeReportID}
8886
viewMode={optionMode}
8987
isOptionFocused={!shouldDisableFocusOptions}
9088
onSelectRow={onSelectRow}
@@ -93,12 +91,12 @@ function LHNOptionsList({style, contentContainerStyles, data, onSelectRow, optio
9391
/>
9492
);
9593
},
96-
[reportAttributes, reports, policy, personalDetails, conciergeReportID, optionMode, shouldDisableFocusOptions, onSelectRow, onLayoutItem],
94+
[reportAttributes, reports, policy, personalDetails, optionMode, shouldDisableFocusOptions, onSelectRow, onLayoutItem],
9795
);
9896

9997
const extraData = useMemo(
100-
() => [reports, reportAttributes, policy, personalDetails, conciergeReportID, data.length, optionMode, isOffline],
101-
[reports, reportAttributes, policy, personalDetails, conciergeReportID, data.length, optionMode, isOffline],
98+
() => [reports, reportAttributes, policy, personalDetails, data.length, optionMode, isOffline],
99+
[reports, reportAttributes, policy, personalDetails, data.length, optionMode, isOffline],
102100
);
103101

104102
const previousOptionMode = usePrevious(optionMode);
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
import React from 'react';
2+
import type {StyleProp, TextStyle} from 'react-native';
3+
import type {ValueOf} from 'type-fest';
4+
import Text from '@components/Text';
5+
import useLocalize from '@hooks/useLocalize';
6+
import useThemeStyles from '@hooks/useThemeStyles';
7+
import {containsCustomEmoji as containsCustomEmojiUtils, containsOnlyCustomEmoji} from '@libs/EmojiUtils';
8+
import FS from '@libs/Fullstory';
9+
import TextWithEmojiFragment from '@pages/inbox/report/comment/TextWithEmojiFragment';
10+
import CONST from '@src/CONST';
11+
import type {Report} from '@src/types/onyx';
12+
13+
type OptionMode = ValueOf<typeof CONST.OPTION_MODE>;
14+
15+
type OptionRowAlternateTextProps = {
16+
alternateText: string | undefined;
17+
report?: Report;
18+
viewMode: OptionMode;
19+
isOptionFocused: boolean;
20+
style?: StyleProp<TextStyle>;
21+
};
22+
23+
function OptionRowAlternateText({alternateText, report, viewMode, isOptionFocused, style}: OptionRowAlternateTextProps) {
24+
const {translate} = useLocalize();
25+
const styles = useThemeStyles();
26+
27+
if (!alternateText) {
28+
return null;
29+
}
30+
31+
const isInFocusMode = viewMode === CONST.OPTION_MODE.COMPACT;
32+
const textStyle = isOptionFocused ? styles.sidebarLinkActiveText : styles.sidebarLinkText;
33+
const alternateTextStyle = isInFocusMode
34+
? [textStyle, styles.textLabelSupporting, styles.optionAlternateTextCompact, styles.ml2, style]
35+
: [textStyle, styles.optionAlternateText, styles.textLabelSupporting, style];
36+
const alternateTextFSClass = FS.getChatFSClass(report);
37+
38+
const containsCustomEmojiWithText = containsCustomEmojiUtils(alternateText) && !containsOnlyCustomEmoji(alternateText);
39+
40+
return (
41+
<Text
42+
style={alternateTextStyle}
43+
numberOfLines={1}
44+
accessibilityLabel={translate('accessibilityHints.lastChatMessagePreview')}
45+
fsClass={alternateTextFSClass}
46+
>
47+
{containsCustomEmojiWithText ? (
48+
<TextWithEmojiFragment
49+
message={alternateText}
50+
style={[alternateTextStyle, styles.mh0]}
51+
alignCustomEmoji
52+
/>
53+
) : (
54+
alternateText
55+
)}
56+
</Text>
57+
);
58+
}
59+
60+
OptionRowAlternateText.displayName = 'OptionRowAlternateText';
61+
62+
export default OptionRowAlternateText;

src/components/LHNOptionsList/OptionRowAvatar.tsx renamed to src/components/LHNOptionsList/OptionRowLHN/OptionRowAvatar.tsx

Lines changed: 21 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
import React from 'react';
22
import type {ColorValue, ViewStyle} from 'react-native';
33
import type {OnyxEntry} from 'react-native-onyx';
4+
import LHNAvatar from '@components/LHNOptionsList/LHNAvatar';
45
import {usePersonalDetails} from '@components/OnyxListItemProvider';
56
import {shouldOptionShowTooltip} from '@libs/OptionsListUtils';
67
import {getDelegateAccountIDFromReportAction} from '@libs/ReportActionsUtils';
78
import type {OptionData} from '@libs/ReportUtils';
89
import CONST from '@src/CONST';
910
import type {Report} from '@src/types/onyx';
10-
import LHNAvatar from './LHNAvatar';
1111

1212
type OptionRowAvatarProps = {
1313
optionItem: OptionData;
@@ -18,7 +18,7 @@ type OptionRowAvatarProps = {
1818
singleAvatarContainerStyle: ViewStyle[];
1919
};
2020

21-
function OptionRowAvatar({optionItem, report, isInFocusMode, subscriptAvatarBorderColor, secondaryAvatarBackgroundColor, singleAvatarContainerStyle}: OptionRowAvatarProps) {
21+
function OptionRowAvatarInner({optionItem, report, isInFocusMode, subscriptAvatarBorderColor, secondaryAvatarBackgroundColor, singleAvatarContainerStyle}: OptionRowAvatarProps) {
2222
const personalDetails = usePersonalDetails();
2323

2424
const delegateAccountID = getDelegateAccountIDFromReportAction(optionItem?.parentReportAction);
@@ -51,12 +51,6 @@ function OptionRowAvatar({optionItem, report, isInFocusMode, subscriptAvatarBord
5151
delegateTooltipAccountID = Number(optionItem.icons.at(0)?.id ?? CONST.DEFAULT_NUMBER_ID);
5252
}
5353

54-
const firstIcon = optionItem.icons?.at(0);
55-
56-
if (!optionItem.icons?.length || !firstIcon) {
57-
return null;
58-
}
59-
6054
return (
6155
<LHNAvatar
6256
icons={icons}
@@ -73,6 +67,25 @@ function OptionRowAvatar({optionItem, report, isInFocusMode, subscriptAvatarBord
7367
);
7468
}
7569

70+
OptionRowAvatarInner.displayName = 'OptionRowAvatarInner';
71+
72+
function OptionRowAvatar({optionItem, report, isInFocusMode, subscriptAvatarBorderColor, secondaryAvatarBackgroundColor, singleAvatarContainerStyle}: OptionRowAvatarProps) {
73+
// Bail out before subscribing to personal details when the row has no avatar to render.
74+
if (!optionItem.icons?.length || !optionItem.icons.at(0)) {
75+
return null;
76+
}
77+
return (
78+
<OptionRowAvatarInner
79+
optionItem={optionItem}
80+
report={report}
81+
isInFocusMode={isInFocusMode}
82+
subscriptAvatarBorderColor={subscriptAvatarBorderColor}
83+
secondaryAvatarBackgroundColor={secondaryAvatarBackgroundColor}
84+
singleAvatarContainerStyle={singleAvatarContainerStyle}
85+
/>
86+
);
87+
}
88+
7689
OptionRowAvatar.displayName = 'OptionRowAvatar';
7790

7891
export default OptionRowAvatar;
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
import React from 'react';
2+
import {View} from 'react-native';
3+
import Badge from '@components/Badge';
4+
import Icon from '@components/Icon';
5+
import {useMemoizedLazyExpensifyIcons} from '@hooks/useLazyAsset';
6+
import useTheme from '@hooks/useTheme';
7+
import useThemeStyles from '@hooks/useThemeStyles';
8+
import type {OptionData} from '@libs/ReportUtils';
9+
import CONST from '@src/CONST';
10+
11+
type OptionRowErrorBadgeProps = {
12+
brickRoadIndicator: OptionData['brickRoadIndicator'];
13+
actionBadgeText: string;
14+
};
15+
16+
function OptionRowErrorBadge({brickRoadIndicator, actionBadgeText}: OptionRowErrorBadgeProps) {
17+
const theme = useTheme();
18+
const styles = useThemeStyles();
19+
const {DotIndicator} = useMemoizedLazyExpensifyIcons(['DotIndicator']);
20+
21+
if (brickRoadIndicator !== CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR) {
22+
return null;
23+
}
24+
25+
return (
26+
<View style={[styles.alignItemsCenter, styles.justifyContentCenter]}>
27+
{actionBadgeText ? (
28+
<Badge
29+
text={actionBadgeText}
30+
error
31+
isStrong
32+
/>
33+
) : (
34+
<Icon
35+
testID="RBR Icon"
36+
src={DotIndicator}
37+
fill={theme.danger}
38+
/>
39+
)}
40+
</View>
41+
);
42+
}
43+
44+
OptionRowErrorBadge.displayName = 'OptionRowErrorBadge';
45+
46+
export default OptionRowErrorBadge;
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
import React from 'react';
2+
import {View} from 'react-native';
3+
import Badge from '@components/Badge';
4+
import Icon from '@components/Icon';
5+
import {useMemoizedLazyExpensifyIcons} from '@hooks/useLazyAsset';
6+
import useTheme from '@hooks/useTheme';
7+
import useThemeStyles from '@hooks/useThemeStyles';
8+
import type {OptionData} from '@libs/ReportUtils';
9+
import CONST from '@src/CONST';
10+
11+
type OptionRowInfoBadgeProps = {
12+
brickRoadIndicator: OptionData['brickRoadIndicator'];
13+
actionBadgeText: string;
14+
};
15+
16+
function OptionRowInfoBadge({brickRoadIndicator, actionBadgeText}: OptionRowInfoBadgeProps) {
17+
const theme = useTheme();
18+
const styles = useThemeStyles();
19+
const {DotIndicator} = useMemoizedLazyExpensifyIcons(['DotIndicator']);
20+
21+
if (brickRoadIndicator !== CONST.BRICK_ROAD_INDICATOR_STATUS.INFO) {
22+
return null;
23+
}
24+
25+
if (actionBadgeText) {
26+
return (
27+
<Badge
28+
text={actionBadgeText}
29+
success
30+
isStrong
31+
/>
32+
);
33+
}
34+
35+
return (
36+
<View style={styles.ml2}>
37+
<Icon
38+
testID="GBR Icon"
39+
src={DotIndicator}
40+
fill={theme.success}
41+
/>
42+
</View>
43+
);
44+
}
45+
46+
OptionRowInfoBadge.displayName = 'OptionRowInfoBadge';
47+
48+
export default OptionRowInfoBadge;

0 commit comments

Comments
 (0)