Skip to content

Commit 516eeb7

Browse files
authored
Merge pull request Expensify#62472 from software-mansion-labs/korytko/generalise-search-context
[Better Expense Report View] Generalise SearchContext + Expensify#61777 follow-up
2 parents 30e3de8 + 7e83182 commit 516eeb7

17 files changed

Lines changed: 205 additions & 303 deletions

File tree

.storybook/preview.tsx

Lines changed: 2 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import React from 'react';
33
import Onyx from 'react-native-onyx';
44
import {SafeAreaProvider} from 'react-native-safe-area-context';
55
import type {Parameters} from 'storybook/internal/types';
6-
import {MoneyRequestReportContextProvider} from '@components/MoneyRequestReportView/MoneyRequestReportContext';
6+
import {SearchContextProvider} from '@components/Search/SearchContext';
77
import ComposeProviders from '@src/components/ComposeProviders';
88
import HTMLEngineProvider from '@src/components/HTMLEngineProvider';
99
import {LocaleContextProvider} from '@src/components/LocaleContextProvider';
@@ -23,16 +23,7 @@ Onyx.init({
2323
const decorators = [
2424
(Story: React.ElementType) => (
2525
<ComposeProviders
26-
components={[
27-
OnyxProvider,
28-
LocaleContextProvider,
29-
HTMLEngineProvider,
30-
SafeAreaProvider,
31-
PortalProvider,
32-
EnvironmentProvider,
33-
KeyboardStateProvider,
34-
MoneyRequestReportContextProvider,
35-
]}
26+
components={[OnyxProvider, LocaleContextProvider, HTMLEngineProvider, SafeAreaProvider, PortalProvider, EnvironmentProvider, KeyboardStateProvider, SearchContextProvider]}
3627
>
3728
<Story />
3829
</ComposeProviders>

src/components/MoneyReportHeader.tsx

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -111,10 +111,10 @@ import MoneyReportHeaderStatusBar from './MoneyReportHeaderStatusBar';
111111
import MoneyReportHeaderStatusBarSkeleton from './MoneyReportHeaderStatusBarSkeleton';
112112
import type {MoneyRequestHeaderStatusBarProps} from './MoneyRequestHeaderStatusBar';
113113
import MoneyRequestHeaderStatusBar from './MoneyRequestHeaderStatusBar';
114-
import {useMoneyRequestReportContext} from './MoneyRequestReportView/MoneyRequestReportContext';
115114
import type {PopoverMenuItem} from './PopoverMenu';
116115
import type {ActionHandledType} from './ProcessMoneyReportHoldMenu';
117116
import ProcessMoneyReportHoldMenu from './ProcessMoneyReportHoldMenu';
117+
import {useSearchContext} from './Search/SearchContext';
118118
import AnimatedSettlementButton from './SettlementButton/AnimatedSettlementButton';
119119
import Text from './Text';
120120

@@ -264,7 +264,7 @@ function MoneyReportHeader({
264264

265265
const [isDownloadErrorModalVisible, setIsDownloadErrorModalVisible] = useState(false);
266266

267-
const {selectedTransactionsID, setSelectedTransactionsID} = useMoneyRequestReportContext();
267+
const {selectedTransactionIDs, clearSelectedTransactions} = useSearchContext();
268268

269269
const {
270270
options: selectedTransactionsOptions,
@@ -881,8 +881,8 @@ function MoneyReportHeader({
881881
if (!transactionThreadReportID) {
882882
return;
883883
}
884-
setSelectedTransactionsID([]);
885-
// We don't need to run the effect on change of setSelectedTransactionsID since it can cause the infinite loop.
884+
clearSelectedTransactions(true);
885+
// We don't need to run the effect on change of clearSelectedTransactions since it can cause the infinite loop.
886886
// eslint-disable-next-line react-compiler/react-compiler
887887
// eslint-disable-next-line react-hooks/exhaustive-deps
888888
}, [transactionThreadReportID]);
@@ -909,7 +909,7 @@ function MoneyReportHeader({
909909
<HeaderWithBackButton
910910
title={translate('common.selectMultiple')}
911911
onBackButtonPress={() => {
912-
setSelectedTransactionsID([]);
912+
clearSelectedTransactions(true);
913913
turnOffMobileSelectionMode();
914914
}}
915915
/>
@@ -982,7 +982,7 @@ function MoneyReportHeader({
982982
<ButtonWithDropdownMenu
983983
onPress={() => null}
984984
options={selectedTransactionsOptions}
985-
customText={translate('workspace.common.selected', {count: selectedTransactionsID.length})}
985+
customText={translate('workspace.common.selected', {count: selectedTransactionIDs.length})}
986986
isSplitButton={false}
987987
shouldAlwaysShowDropdownMenu
988988
/>
@@ -1004,7 +1004,7 @@ function MoneyReportHeader({
10041004
<ButtonWithDropdownMenu
10051005
onPress={() => null}
10061006
options={selectedTransactionsOptions}
1007-
customText={translate('workspace.common.selected', {count: selectedTransactionsID.length})}
1007+
customText={translate('workspace.common.selected', {count: selectedTransactionIDs.length})}
10081008
isSplitButton={false}
10091009
shouldAlwaysShowDropdownMenu
10101010
wrapperStyle={styles.w100}
@@ -1100,11 +1100,11 @@ function MoneyReportHeader({
11001100
shouldEnableNewFocusManagement
11011101
/>
11021102
<ConfirmModal
1103-
title={translate('iou.deleteExpense', {count: selectedTransactionsID.length})}
1103+
title={translate('iou.deleteExpense', {count: selectedTransactionIDs.length})}
11041104
isVisible={hookDeleteModalVisible}
11051105
onConfirm={handleDeleteTransactions}
11061106
onCancel={hideDeleteModal}
1107-
prompt={translate('iou.deleteConfirmation', {count: selectedTransactionsID.length})}
1107+
prompt={translate('iou.deleteConfirmation', {count: selectedTransactionIDs.length})}
11081108
confirmText={translate('common.delete')}
11091109
cancelText={translate('common.cancel')}
11101110
danger

src/components/MoneyRequestReportView/MoneyRequestReportActionsList.tsx

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import DecisionModal from '@components/DecisionModal';
1414
import FlatList from '@components/FlatList';
1515
import {AUTOSCROLL_TO_TOP_THRESHOLD} from '@components/InvertedFlatList/BaseInvertedFlatList';
1616
import {PressableWithFeedback} from '@components/Pressable';
17+
import {useSearchContext} from '@components/Search/SearchContext';
1718
import Text from '@components/Text';
1819
import useLoadReportActions from '@hooks/useLoadReportActions';
1920
import useLocalize from '@hooks/useLocalize';
@@ -57,7 +58,6 @@ import CONST from '@src/CONST';
5758
import ONYXKEYS from '@src/ONYXKEYS';
5859
import type SCREENS from '@src/SCREENS';
5960
import type * as OnyxTypes from '@src/types/onyx';
60-
import {useMoneyRequestReportContext} from './MoneyRequestReportContext';
6161
import MoneyRequestReportTransactionList from './MoneyRequestReportTransactionList';
6262
import MoneyRequestViewReportFields from './MoneyRequestViewReportFields';
6363
import ReportActionsListLoadingSkeleton from './ReportActionsListLoadingSkeleton';
@@ -148,7 +148,7 @@ function MoneyRequestReportActionsList({
148148
const [session] = useOnyx(ONYXKEYS.SESSION, {canBeMissing: false});
149149
const [isDownloadErrorModalVisible, setIsDownloadErrorModalVisible] = useState(false);
150150

151-
const {selectedTransactionsID, setSelectedTransactionsID} = useMoneyRequestReportContext();
151+
const {selectedTransactionIDs, setSelectedTransactions, clearSelectedTransactions} = useSearchContext();
152152

153153
const {selectionMode} = useMobileSelectionMode();
154154
const {
@@ -537,47 +537,47 @@ function MoneyRequestReportActionsList({
537537
<ButtonWithDropdownMenu
538538
onPress={() => null}
539539
options={selectedTransactionsOptions}
540-
customText={translate('workspace.common.selected', {count: selectedTransactionsID.length})}
540+
customText={translate('workspace.common.selected', {count: selectedTransactionIDs.length})}
541541
isSplitButton={false}
542542
shouldAlwaysShowDropdownMenu
543543
wrapperStyle={[styles.w100, styles.ph5]}
544544
/>
545545
<View style={[styles.alignItemsCenter, styles.userSelectNone, styles.flexRow, styles.pt6, styles.ph8]}>
546546
<Checkbox
547547
accessibilityLabel={translate('workspace.people.selectAll')}
548-
isChecked={selectedTransactionsID.length === transactions.length}
549-
isIndeterminate={selectedTransactionsID.length > 0 && selectedTransactionsID.length !== transactions.length}
548+
isChecked={selectedTransactionIDs.length === transactions.length}
549+
isIndeterminate={selectedTransactionIDs.length > 0 && selectedTransactionIDs.length !== transactions.length}
550550
onPress={() => {
551-
if (selectedTransactionsID.length !== 0) {
552-
setSelectedTransactionsID([]);
551+
if (selectedTransactionIDs.length !== 0) {
552+
clearSelectedTransactions(true);
553553
} else {
554-
setSelectedTransactionsID(transactions.filter((t) => !isTransactionPendingDelete(t)).map((t) => t.transactionID));
554+
setSelectedTransactions(transactions.filter((t) => !isTransactionPendingDelete(t)).map((t) => t.transactionID));
555555
}
556556
}}
557557
/>
558558
<PressableWithFeedback
559559
style={[styles.userSelectNone, styles.alignItemsCenter]}
560560
onPress={() => {
561-
if (selectedTransactionsID.length === transactions.length) {
562-
setSelectedTransactionsID([]);
561+
if (selectedTransactionIDs.length === transactions.length) {
562+
clearSelectedTransactions(true);
563563
} else {
564-
setSelectedTransactionsID(transactions.filter((t) => !isTransactionPendingDelete(t)).map((t) => t.transactionID));
564+
setSelectedTransactions(transactions.filter((t) => !isTransactionPendingDelete(t)).map((t) => t.transactionID));
565565
}
566566
}}
567567
accessibilityLabel={translate('workspace.people.selectAll')}
568568
role="button"
569-
accessibilityState={{checked: selectedTransactionsID.length === transactions.length}}
569+
accessibilityState={{checked: selectedTransactionIDs.length === transactions.length}}
570570
dataSet={{[CONST.SELECTION_SCRAPER_HIDDEN_ELEMENT]: true}}
571571
>
572572
<Text style={[styles.textStrong, styles.ph3]}>{translate('workspace.people.selectAll')}</Text>
573573
</PressableWithFeedback>
574574
</View>
575575
<ConfirmModal
576-
title={translate('iou.deleteExpense', {count: selectedTransactionsID.length})}
576+
title={translate('iou.deleteExpense', {count: selectedTransactionIDs.length})}
577577
isVisible={isDeleteModalVisible}
578578
onConfirm={handleDeleteTransactions}
579579
onCancel={hideDeleteModal}
580-
prompt={translate('iou.deleteConfirmation', {count: selectedTransactionsID.length})}
580+
prompt={translate('iou.deleteConfirmation', {count: selectedTransactionIDs.length})}
581581
confirmText={translate('common.delete')}
582582
cancelText={translate('common.cancel')}
583583
danger

src/components/MoneyRequestReportView/MoneyRequestReportContext.tsx

Lines changed: 0 additions & 69 deletions
This file was deleted.

src/components/MoneyRequestReportView/MoneyRequestReportTransactionList.tsx

Lines changed: 27 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import * as Expensicons from '@components/Icon/Expensicons';
1010
import MenuItem from '@components/MenuItem';
1111
import Modal from '@components/Modal';
1212
import PressableWithFeedback from '@components/Pressable/PressableWithFeedback';
13+
import {useSearchContext} from '@components/Search/SearchContext';
1314
import type {SortOrder} from '@components/Search/types';
1415
import Text from '@components/Text';
1516
import TransactionItemRow from '@components/TransactionItemRow';
@@ -40,7 +41,6 @@ import type {Route} from '@src/ROUTES';
4041
import ROUTES from '@src/ROUTES';
4142
import type SCREENS from '@src/SCREENS';
4243
import type * as OnyxTypes from '@src/types/onyx';
43-
import {useMoneyRequestReportContext} from './MoneyRequestReportContext';
4444
import MoneyRequestReportTableHeader from './MoneyRequestReportTableHeader';
4545
import SearchMoneyRequestReportEmptyState from './SearchMoneyRequestReportEmptyState';
4646
import {setActiveTransactionThreadIDs} from './TransactionThreadReportIDRepository';
@@ -137,18 +137,36 @@ function MoneyRequestReportTransactionList({
137137
const {bind} = useHover();
138138
const {isMouseDownOnInput, setMouseUp} = useMouseContext();
139139

140-
const {selectedTransactionsID, setSelectedTransactionsID, toggleTransaction, isTransactionSelected} = useMoneyRequestReportContext();
140+
const {selectedTransactionIDs, setSelectedTransactions, clearSelectedTransactions} = useSearchContext();
141141
const {selectionMode} = useMobileSelectionMode();
142142

143+
const toggleTransaction = useCallback(
144+
(transactionID: string) => {
145+
let newSelectedTransactionIDs = selectedTransactionIDs;
146+
if (selectedTransactionIDs.includes(transactionID)) {
147+
newSelectedTransactionIDs = selectedTransactionIDs.filter((t) => t !== transactionID);
148+
} else {
149+
newSelectedTransactionIDs = [...selectedTransactionIDs, transactionID];
150+
}
151+
setSelectedTransactions(newSelectedTransactionIDs);
152+
},
153+
[setSelectedTransactions, selectedTransactionIDs],
154+
);
155+
156+
const isTransactionSelected = useCallback((transactionID: string) => selectedTransactionIDs.includes(transactionID), [selectedTransactionIDs]);
157+
143158
useFocusEffect(
144159
useCallback(() => {
145160
return () => {
146161
if (navigationRef?.getRootState()?.routes.at(-1)?.name === NAVIGATORS.RIGHT_MODAL_NAVIGATOR) {
147162
return;
148163
}
149-
setSelectedTransactionsID([]);
164+
clearSelectedTransactions(true);
150165
};
151-
}, [setSelectedTransactionsID]),
166+
// We don't need to run the effect on change of clearSelectedTransactions on every focus.
167+
// eslint-disable-next-line react-compiler/react-compiler
168+
// eslint-disable-next-line react-hooks/exhaustive-deps
169+
}, []),
152170
);
153171

154172
const handleMouseLeave = (e: React.MouseEvent<Element, MouseEvent>) => {
@@ -216,15 +234,15 @@ function MoneyRequestReportTransactionList({
216234
<View style={[styles.dFlex, styles.flexRow, styles.pv2, styles.pr4, StyleUtils.getPaddingLeft(variables.w12)]}>
217235
<Checkbox
218236
onPress={() => {
219-
if (selectedTransactionsID.length !== 0) {
220-
setSelectedTransactionsID([]);
237+
if (selectedTransactionIDs.length !== 0) {
238+
clearSelectedTransactions(true);
221239
} else {
222-
setSelectedTransactionsID(transactions.filter((t) => !isTransactionPendingDelete(t)).map((t) => t.transactionID));
240+
setSelectedTransactions(transactions.filter((t) => !isTransactionPendingDelete(t)).map((t) => t.transactionID));
223241
}
224242
}}
225243
accessibilityLabel={CONST.ROLE.CHECKBOX}
226-
isIndeterminate={selectedTransactionsID.length > 0 && selectedTransactionsID.length !== transactions.length}
227-
isChecked={selectedTransactionsID.length === transactions.length}
244+
isIndeterminate={selectedTransactionIDs.length > 0 && selectedTransactionIDs.length !== transactions.length}
245+
isChecked={selectedTransactionIDs.length === transactions.length}
228246
/>
229247
{isMediumScreenWidth && <Text style={[styles.textStrong, styles.ph3]}>{translate('workspace.people.selectAll')}</Text>}
230248
</View>

0 commit comments

Comments
 (0)