Skip to content

Commit 08dce90

Browse files
committed
Merge branch 'main' into @zfurtak/migrate-AssigneeStep
2 parents cf90041 + 0bc6f14 commit 08dce90

29 files changed

+362
-327
lines changed

src/ROUTES.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1678,7 +1678,12 @@ const ROUTES = {
16781678
},
16791679
WORKSPACE_WORKFLOWS_AUTOREPORTING_FREQUENCY: {
16801680
route: 'workspaces/:policyID/workflows/auto-reporting-frequency',
1681-
getRoute: (policyID: string) => `workspaces/${policyID}/workflows/auto-reporting-frequency` as const,
1681+
getRoute: (policyID: string | undefined) => {
1682+
if (!policyID) {
1683+
Log.warn('Invalid policyID is used to build the WORKSPACE_WORKFLOWS_AUTOREPORTING_FREQUENCY route');
1684+
}
1685+
return `workspaces/${policyID}/workflows/auto-reporting-frequency` as const;
1686+
},
16821687
},
16831688
WORKSPACE_WORKFLOWS_AUTOREPORTING_MONTHLY_OFFSET: {
16841689
route: 'workspaces/:policyID/workflows/auto-reporting-frequency/monthly-offset',

src/components/CountryPicker/CountrySelectorModal.tsx

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@ import React, {useMemo} from 'react';
22
import HeaderWithBackButton from '@components/HeaderWithBackButton';
33
import Modal from '@components/Modal';
44
import ScreenWrapper from '@components/ScreenWrapper';
5-
import SelectionList from '@components/SelectionListWithSections';
6-
import RadioListItem from '@components/SelectionListWithSections/RadioListItem';
5+
import SelectionList from '@components/SelectionList';
6+
import RadioListItem from '@components/SelectionList/ListItem/RadioListItem';
77
import useDebouncedState from '@hooks/useDebouncedState';
88
import useLocalize from '@hooks/useLocalize';
99
import useThemeStyles from '@hooks/useThemeStyles';
@@ -57,6 +57,16 @@ function CountrySelectorModal({isVisible, currentCountry, onCountrySelected, onC
5757

5858
const styles = useThemeStyles();
5959

60+
const textInputOptions = useMemo(
61+
() => ({
62+
value: searchValue,
63+
label: translate('common.search'),
64+
onChangeText: setSearchValue,
65+
headerMessage,
66+
}),
67+
[headerMessage, searchValue, translate, setSearchValue],
68+
);
69+
6070
return (
6171
<Modal
6272
type={CONST.MODAL.MODAL_TYPE.RIGHT_DOCKED}
@@ -77,17 +87,13 @@ function CountrySelectorModal({isVisible, currentCountry, onCountrySelected, onC
7787
onBackButtonPress={onClose}
7888
/>
7989
<SelectionList
80-
headerMessage={headerMessage}
81-
sections={[{data: searchResults}]}
82-
textInputValue={searchValue}
83-
textInputLabel={translate('common.search')}
84-
onChangeText={setSearchValue}
90+
data={searchResults}
91+
textInputOptions={textInputOptions}
8592
onSelectRow={onCountrySelected}
8693
ListItem={RadioListItem}
87-
initiallyFocusedOptionKey={currentCountry}
94+
initiallyFocusedItemKey={currentCountry}
8895
shouldSingleExecuteRowSelect
8996
shouldStopPropagation
90-
shouldUseDynamicMaxToRenderPerBatch
9197
/>
9298
</ScreenWrapper>
9399
</Modal>

src/components/MoneyReportHeader.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1196,7 +1196,7 @@ function MoneyReportHeader({
11961196
Navigation.goBack(route.params?.backTo);
11971197
// eslint-disable-next-line @typescript-eslint/no-deprecated
11981198
InteractionManager.runAfterInteractions(() => {
1199-
deleteAppReport(moneyRequestReport?.reportID);
1199+
deleteAppReport(moneyRequestReport?.reportID, email ?? '');
12001200
});
12011201
},
12021202
},

src/components/ReportActionItem/TransactionPreview/TransactionPreviewContent.tsx

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import ReportActionItemImages from '@components/ReportActionItem/ReportActionIte
1111
import UserInfoCellsWithArrow from '@components/SelectionListWithSections/Search/UserInfoCellsWithArrow';
1212
import Text from '@components/Text';
1313
import TransactionPreviewSkeletonView from '@components/TransactionPreviewSkeletonView';
14+
import useCurrentUserPersonalDetails from '@hooks/useCurrentUserPersonalDetails';
1415
import useEnvironment from '@hooks/useEnvironment';
1516
import {useMemoizedLazyExpensifyIcons} from '@hooks/useLazyAsset';
1617
import useLocalize from '@hooks/useLocalize';
@@ -82,7 +83,7 @@ function TransactionPreviewContent({
8283
const isReportAPolicyExpenseChat = isPolicyExpenseChat(chatReport);
8384
const [reportActions] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${getNonEmptyStringOnyxID(report?.reportID)}`, {canBeMissing: true});
8485
const isChatReportArchived = useReportIsArchived(chatReport?.reportID);
85-
86+
const currentUserDetails = useCurrentUserPersonalDetails();
8687
const transactionPreviewCommonArguments = useMemo(
8788
() => ({
8889
iouReport: report,
@@ -101,8 +102,9 @@ function TransactionPreviewContent({
101102
...transactionPreviewCommonArguments,
102103
areThereDuplicates,
103104
isReportAPolicyExpenseChat,
105+
currentUserEmail: currentUserDetails.email ?? '',
104106
}),
105-
[areThereDuplicates, transactionPreviewCommonArguments, isReportAPolicyExpenseChat],
107+
[areThereDuplicates, transactionPreviewCommonArguments, isReportAPolicyExpenseChat, currentUserDetails.email],
106108
);
107109

108110
const {shouldShowRBR, shouldShowMerchant, shouldShowSplitShare, shouldShowTag, shouldShowCategory, shouldShowSkeleton, shouldShowDescription} = conditionals;
@@ -120,9 +122,10 @@ function TransactionPreviewContent({
120122
shouldShowRBR,
121123
violationMessage,
122124
reportActions,
125+
currentUserEmail: currentUserDetails.email ?? '',
123126
originalTransaction,
124127
}),
125-
[transactionPreviewCommonArguments, shouldShowRBR, violationMessage, reportActions, originalTransaction],
128+
[transactionPreviewCommonArguments, shouldShowRBR, violationMessage, reportActions, currentUserDetails.email, originalTransaction],
126129
);
127130
const getTranslatedText = (item: TranslationPathOrText) => (item.translationPath ? translate(item.translationPath) : (item.text ?? ''));
128131

src/components/Search/FilterDropdowns/SingleSelectPopup.tsx

Lines changed: 28 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
import React, {useCallback, useMemo, useState} from 'react';
22
import {View} from 'react-native';
33
import Button from '@components/Button';
4-
import SelectionList from '@components/SelectionListWithSections';
5-
import SingleSelectListItem from '@components/SelectionListWithSections/SingleSelectListItem';
6-
import type {ListItem} from '@components/SelectionListWithSections/types';
4+
import SelectionList from '@components/SelectionList';
5+
import SingleSelectListItem from '@components/SelectionList/ListItem/SingleSelectListItem';
6+
import type {ListItem} from '@components/SelectionList/types';
77
import Text from '@components/Text';
88
import useDebouncedState from '@hooks/useDebouncedState';
99
import useLocalize from '@hooks/useLocalize';
@@ -48,55 +48,37 @@ function SingleSelectPopup<T extends string>({label, value, items, closeOverlay,
4848
const [selectedItem, setSelectedItem] = useState(value);
4949
const [searchTerm, debouncedSearchTerm, setSearchTerm] = useDebouncedState('');
5050

51-
const {sections, noResultsFound} = useMemo(() => {
51+
const {options, noResultsFound} = useMemo(() => {
5252
// If the selection is searchable, we push the initially selected item into its own section and display it at the top
5353
if (isSearchable) {
54-
const initiallySelectedItemSection = value?.text.toLowerCase().includes(debouncedSearchTerm?.toLowerCase())
54+
const initiallySelectedOption = value?.text.toLowerCase().includes(debouncedSearchTerm?.toLowerCase())
5555
? [{text: value.text, keyForList: value.value, isSelected: selectedItem?.value === value.value}]
5656
: [];
57-
const remainingItemsSection = items
57+
const remainingOptions = items
5858
.filter((item) => item?.value !== value?.value && item?.text?.toLowerCase().includes(debouncedSearchTerm?.toLowerCase()))
5959
.map((item) => ({
6060
text: item.text,
6161
keyForList: item.value,
6262
isSelected: selectedItem?.value === item.value,
6363
}));
64-
const isEmpty = !initiallySelectedItemSection.length && !remainingItemsSection.length;
64+
const allOptions = [...initiallySelectedOption, ...remainingOptions];
65+
const isEmpty = allOptions.length === 0;
6566
return {
66-
sections: isEmpty
67-
? []
68-
: [
69-
{
70-
data: initiallySelectedItemSection,
71-
shouldShow: initiallySelectedItemSection.length > 0,
72-
indexOffset: 0,
73-
},
74-
{
75-
data: remainingItemsSection,
76-
shouldShow: remainingItemsSection.length > 0,
77-
indexOffset: initiallySelectedItemSection.length,
78-
},
79-
],
67+
options: allOptions,
8068
noResultsFound: isEmpty,
8169
};
8270
}
8371

8472
return {
85-
sections: [
86-
{
87-
data: items.map((item) => ({
88-
text: item.text,
89-
keyForList: item.value,
90-
isSelected: item.value === selectedItem?.value,
91-
})),
92-
},
93-
],
73+
options: items.map((item) => ({
74+
text: item.text,
75+
keyForList: item.value,
76+
isSelected: item.value === selectedItem?.value,
77+
})),
9478
noResultsFound: false,
9579
};
9680
}, [isSearchable, items, value, selectedItem, debouncedSearchTerm]);
9781

98-
const dataLength = useMemo(() => sections.flatMap((section) => section.data).length, [sections]);
99-
10082
const updateSelectedItem = useCallback(
10183
(item: ListItem) => {
10284
const newItem = items.find((i) => i.value === item.keyForList) ?? null;
@@ -115,22 +97,29 @@ function SingleSelectPopup<T extends string>({label, value, items, closeOverlay,
11597
closeOverlay();
11698
}, [closeOverlay, onChange]);
11799

100+
const textInputOptions = useMemo(
101+
() => ({
102+
value: searchTerm,
103+
label: isSearchable ? (searchPlaceholder ?? translate('common.search')) : undefined,
104+
onChangeText: setSearchTerm,
105+
headerMessage: noResultsFound ? translate('common.noResultsFound') : undefined,
106+
}),
107+
[searchTerm, isSearchable, searchPlaceholder, translate, setSearchTerm, noResultsFound],
108+
);
109+
118110
return (
119111
<View style={[!isSmallScreenWidth && styles.pv4, styles.gap2]}>
120112
{isSmallScreenWidth && <Text style={[styles.textLabel, styles.textSupporting, styles.ph5, styles.pv1]}>{label}</Text>}
121113

122-
<View style={[styles.getSelectionListPopoverHeight(dataLength || 1, windowHeight, isSearchable ?? false)]}>
114+
<View style={[styles.getSelectionListPopoverHeight(options.length || 1, windowHeight, isSearchable ?? false)]}>
123115
<SelectionList
116+
data={options}
124117
shouldSingleExecuteRowSelect
125-
sections={sections}
126118
ListItem={SingleSelectListItem}
127119
onSelectRow={updateSelectedItem}
128-
textInputValue={searchTerm}
129-
onChangeText={setSearchTerm}
130-
textInputLabel={isSearchable ? (searchPlaceholder ?? translate('common.search')) : undefined}
120+
textInputOptions={textInputOptions}
131121
shouldUpdateFocusedIndex={isSearchable}
132-
initiallyFocusedOptionKey={isSearchable ? value?.value : undefined}
133-
headerMessage={noResultsFound ? translate('common.noResultsFound') : undefined}
122+
initiallyFocusedItemKey={isSearchable ? value?.value : undefined}
134123
showLoadingPlaceholder={!noResultsFound}
135124
/>
136125
</View>

src/components/StatePicker/StateSelectorModal.tsx

Lines changed: 17 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@ import React, {useMemo} from 'react';
33
import HeaderWithBackButton from '@components/HeaderWithBackButton';
44
import Modal from '@components/Modal';
55
import ScreenWrapper from '@components/ScreenWrapper';
6-
import SelectionList from '@components/SelectionListWithSections';
7-
import RadioListItem from '@components/SelectionListWithSections/RadioListItem';
6+
import SelectionList from '@components/SelectionList';
7+
import RadioListItem from '@components/SelectionList/ListItem/RadioListItem';
88
import useDebouncedState from '@hooks/useDebouncedState';
99
import useLocalize from '@hooks/useLocalize';
1010
import useThemeStyles from '@hooks/useThemeStyles';
@@ -38,6 +38,7 @@ type StateSelectorModalProps = {
3838
function StateSelectorModal({isVisible, currentState, onStateSelected, onClose, label, onBackdropPress}: StateSelectorModalProps) {
3939
const {translate} = useLocalize();
4040
const [searchValue, debouncedSearchValue, setSearchValue] = useDebouncedState('');
41+
const styles = useThemeStyles();
4142

4243
const countryStates = useMemo(
4344
() =>
@@ -57,9 +58,16 @@ function StateSelectorModal({isVisible, currentState, onStateSelected, onClose,
5758
);
5859

5960
const searchResults = searchOptions(debouncedSearchValue, countryStates);
60-
const headerMessage = debouncedSearchValue.trim() && !searchResults.length ? translate('common.noResultsFound') : '';
6161

62-
const styles = useThemeStyles();
62+
const textInputOptions = useMemo(
63+
() => ({
64+
headerMessage: debouncedSearchValue.trim() && !searchResults.length ? translate('common.noResultsFound') : '',
65+
value: searchValue,
66+
label: translate('common.search'),
67+
onChangeText: setSearchValue,
68+
}),
69+
[debouncedSearchValue, searchResults.length, searchValue, setSearchValue, translate],
70+
);
6371

6472
return (
6573
<Modal
@@ -81,17 +89,14 @@ function StateSelectorModal({isVisible, currentState, onStateSelected, onClose,
8189
onBackButtonPress={onClose}
8290
/>
8391
<SelectionList
84-
headerMessage={headerMessage}
85-
sections={[{data: searchResults}]}
86-
textInputValue={searchValue}
87-
textInputLabel={translate('common.search')}
88-
onChangeText={setSearchValue}
89-
onSelectRow={onStateSelected}
92+
data={searchResults}
9093
ListItem={RadioListItem}
91-
initiallyFocusedOptionKey={currentState}
94+
onSelectRow={onStateSelected}
95+
textInputOptions={textInputOptions}
96+
initiallyFocusedItemKey={currentState}
97+
disableMaintainingScrollPosition
9298
shouldSingleExecuteRowSelect
9399
shouldStopPropagation
94-
shouldUseDynamicMaxToRenderPerBatch
95100
/>
96101
</ScreenWrapper>
97102
</Modal>

src/libs/PersonalDetailsUtils.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import {isEmptyObject} from '@src/types/utils/EmptyObject';
1212
import {translateLocal} from './Localize';
1313
import {areEmailsFromSamePrivateDomain} from './LoginUtils';
1414
import {parsePhoneNumber} from './PhoneNumber';
15+
import {getDefaultAvatarURL} from './UserAvatarUtils';
1516
import {generateAccountID} from './UserUtils';
1617

1718
type FirstAndLastName = {
@@ -117,7 +118,7 @@ function getPersonalDetailsByIDs({
117118
personalDetailsParam = allPersonalDetails,
118119
}: {
119120
accountIDs: number[];
120-
currentUserAccountID: number;
121+
currentUserAccountID?: number;
121122
shouldChangeUserDisplayName?: boolean;
122123
personalDetailsParam?: Partial<PersonalDetailsList>;
123124
}): PersonalDetails[] {
@@ -223,6 +224,7 @@ function getPersonalDetailsOnyxDataForOptimisticUsers(
223224
personalDetailsNew[accountID] = {
224225
login,
225226
accountID,
227+
avatar: getDefaultAvatarURL({accountID, accountEmail: login}),
226228
displayName: formatPhoneNumber(login),
227229
isOptimisticPersonalDetail: true,
228230
};

src/libs/ReportUtils.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2762,7 +2762,7 @@ function isMoneyRequestReportEligibleForMerge(reportID: string, isAdmin: boolean
27622762
return isManager && isExpenseReport(report) && isProcessingReport(report);
27632763
}
27642764

2765-
function hasOutstandingChildRequest(chatReport: Report, iouReportOrID: OnyxEntry<Report> | string) {
2765+
function hasOutstandingChildRequest(chatReport: Report, iouReportOrID: OnyxEntry<Report> | string, currentUserEmailParam: string) {
27662766
const reportActions = getAllReportActions(chatReport.reportID);
27672767
// This will be fixed as part of https://github.com/Expensify/Expensify/issues/507850
27682768
// eslint-disable-next-line @typescript-eslint/no-deprecated
@@ -2781,7 +2781,9 @@ function hasOutstandingChildRequest(chatReport: Report, iouReportOrID: OnyxEntry
27812781
const iouReport = typeof iouReportOrID !== 'string' && iouReportOrID?.reportID === iouReportID ? iouReportOrID : getReportOrDraftReport(iouReportID);
27822782
const transactions = getReportTransactions(iouReportID);
27832783
return (
2784-
canIOUBePaid(iouReport, chatReport, policy, transactions) || canApproveIOU(iouReport, policy, transactions) || canSubmitReport(iouReport, policy, transactions, undefined, false)
2784+
canIOUBePaid(iouReport, chatReport, policy, transactions) ||
2785+
canApproveIOU(iouReport, policy, transactions) ||
2786+
canSubmitReport(iouReport, policy, transactions, undefined, false, currentUserEmailParam)
27852787
);
27862788
});
27872789
}

src/libs/SearchUIUtils.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1329,7 +1329,7 @@ function getActions(
13291329
}
13301330

13311331
// We check for isAllowedToApproveExpenseReport because if the policy has preventSelfApprovals enabled, we disable the Submit action and in that case we want to show the View action instead
1332-
if (canSubmitReport(report, policy, allReportTransactions, allViolations, isIOUReportArchived || isChatReportArchived) && isAllowedToApproveExpenseReport) {
1332+
if (canSubmitReport(report, policy, allReportTransactions, allViolations, isIOUReportArchived || isChatReportArchived, currentUserEmail) && isAllowedToApproveExpenseReport) {
13331333
allActions.push(CONST.SEARCH.ACTION_TYPES.SUBMIT);
13341334
}
13351335

0 commit comments

Comments
 (0)