diff --git a/src/components/MoneyReportHeader.tsx b/src/components/MoneyReportHeader.tsx
index 9c47dd0da547..aada3e312353 100644
--- a/src/components/MoneyReportHeader.tsx
+++ b/src/components/MoneyReportHeader.tsx
@@ -80,6 +80,7 @@ import MoneyRequestHeaderStatusBar from './MoneyRequestHeaderStatusBar';
import type {ActionHandledType} from './ProcessMoneyReportHoldMenu';
import ProcessMoneyReportHoldMenu from './ProcessMoneyReportHoldMenu';
import ExportWithDropdownMenu from './ReportActionItem/ExportWithDropdownMenu';
+import {useSearchContext} from './Search/SearchContext';
import SettlementButton from './SettlementButton';
type MoneyReportHeaderProps = {
@@ -163,6 +164,8 @@ function MoneyReportHeader({policy, report: moneyRequestReport, transactionThrea
const isPayAtEndExpense = isPayAtEndExpenseTransactionUtils(transaction);
const isArchivedReport = isArchivedReportWithID(moneyRequestReport?.reportID);
const [archiveReason] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${moneyRequestReport?.reportID}`, {selector: getArchiveReason});
+ const {currentSearchHash, isAllStatus} = useSearchContext();
+ const hash = isAllStatus ? undefined : currentSearchHash;
const getCanIOUBePaid = useCallback(
(onlyShowPayElsewhere = false) => canIOUBePaidAction(moneyRequestReport, chatReport, policy, transaction ? [transaction] : undefined, onlyShowPayElsewhere),
@@ -236,10 +239,10 @@ function MoneyReportHeader({policy, report: moneyRequestReport, transactionThrea
} else if (isInvoiceReport(moneyRequestReport)) {
payInvoice(type, chatReport, moneyRequestReport, payAsBusiness);
} else {
- payMoneyRequest(type, chatReport, moneyRequestReport, true);
+ payMoneyRequest(type, chatReport, moneyRequestReport, true, hash);
}
},
- [chatReport, isAnyTransactionOnHold, isDelegateAccessRestricted, moneyRequestReport],
+ [chatReport, hash, isAnyTransactionOnHold, isDelegateAccessRestricted, moneyRequestReport],
);
const confirmApproval = () => {
@@ -249,7 +252,7 @@ function MoneyReportHeader({policy, report: moneyRequestReport, transactionThrea
} else if (isAnyTransactionOnHold) {
setIsHoldMenuVisible(true);
} else {
- approveMoneyRequest(moneyRequestReport, true);
+ approveMoneyRequest(moneyRequestReport, true, hash);
}
};
@@ -418,7 +421,7 @@ function MoneyReportHeader({policy, report: moneyRequestReport, transactionThrea
success={isWaitingForSubmissionFromCurrentUser}
text={translate('common.submit')}
style={[styles.mnw120, styles.pv2, styles.pr0]}
- onPress={() => submitReport(moneyRequestReport)}
+ onPress={() => submitReport(moneyRequestReport, hash)}
isDisabled={shouldDisableSubmitButton}
/>
@@ -479,7 +482,7 @@ function MoneyReportHeader({policy, report: moneyRequestReport, transactionThrea
success={isWaitingForSubmissionFromCurrentUser}
text={translate('common.submit')}
style={[styles.flex1, styles.pr0]}
- onPress={() => submitReport(moneyRequestReport)}
+ onPress={() => submitReport(moneyRequestReport, hash)}
isDisabled={shouldDisableSubmitButton}
/>
)}
diff --git a/src/components/OpacityView.tsx b/src/components/OpacityView.tsx
index 6c7aa26d05ba..f13d703e77e8 100644
--- a/src/components/OpacityView.tsx
+++ b/src/components/OpacityView.tsx
@@ -1,7 +1,9 @@
-import React from 'react';
+import React, {useState} from 'react';
import type {StyleProp, ViewStyle} from 'react-native';
import type {AnimatedStyle} from 'react-native-reanimated';
import Animated, {useAnimatedStyle, useSharedValue, withTiming} from 'react-native-reanimated';
+import useExitingAnimation from '@hooks/useExitingAnimation';
+import useThemeStyles from '@hooks/useThemeStyles';
import shouldRenderOffscreen from '@libs/shouldRenderOffscreen';
import variables from '@styles/variables';
@@ -32,6 +34,8 @@ type OpacityViewProps = {
/** Whether the view needs to be rendered offscreen (for Android only) */
needsOffscreenAlphaCompositing?: boolean;
+
+ shouldAnimateOnRemove?: boolean;
};
function OpacityView({
@@ -41,8 +45,13 @@ function OpacityView({
style = [],
dimmingValue = variables.hoverDimValue,
needsOffscreenAlphaCompositing = false,
+ shouldAnimateOnRemove,
}: OpacityViewProps) {
const opacity = useSharedValue(1);
+ const styles = useThemeStyles();
+ const [height, setHeight] = useState(0);
+ const Exiting = useExitingAnimation(height);
+
const opacityStyle = useAnimatedStyle(() => ({
opacity: opacity.get(),
}));
@@ -53,8 +62,12 @@ function OpacityView({
return (
{
+ setHeight(e.nativeEvent.layout.height);
+ }}
>
{children}
diff --git a/src/components/Pressable/PressableWithFeedback.tsx b/src/components/Pressable/PressableWithFeedback.tsx
index 10e6ac7bbca6..8a79a9337cf4 100644
--- a/src/components/Pressable/PressableWithFeedback.tsx
+++ b/src/components/Pressable/PressableWithFeedback.tsx
@@ -37,6 +37,8 @@ type PressableWithFeedbackProps = PressableProps & {
/** The color of the underlay that will show through when the Pressable is active. */
underlayColor?: Color;
+
+ shouldAnimateOnRemove?: boolean;
};
function PressableWithFeedback(
@@ -47,6 +49,7 @@ function PressableWithFeedback(
pressDimmingValue = variables.pressDimValue,
hoverDimmingValue = variables.hoverDimValue,
dimAnimationDuration,
+ shouldAnimateOnRemove,
...rest
}: PressableWithFeedbackProps,
ref: PressableRef,
@@ -61,6 +64,7 @@ function PressableWithFeedback(
dimAnimationDuration={dimAnimationDuration}
style={wrapperStyle}
needsOffscreenAlphaCompositing={needsOffscreenAlphaCompositing}
+ shouldAnimateOnRemove={shouldAnimateOnRemove}
>
;
@@ -60,6 +61,8 @@ function ProcessMoneyReportHoldMenu({
}: ProcessMoneyReportHoldMenuProps) {
const {translate} = useLocalize();
const isApprove = requestType === CONST.IOU.REPORT_ACTION_TYPE.APPROVE;
+ const {currentSearchHash, isAllStatus} = useSearchContext();
+ const hash = isAllStatus ? undefined : currentSearchHash;
// We need to use isSmallScreenWidth instead of shouldUseNarrowLayout to apply the correct modal type
// eslint-disable-next-line rulesdir/prefer-shouldUseNarrowLayout-instead-of-isSmallScreenWidth
const {isSmallScreenWidth} = useResponsiveLayout();
@@ -69,15 +72,15 @@ function ProcessMoneyReportHoldMenu({
if (startAnimation) {
startAnimation();
}
- IOU.approveMoneyRequest(moneyRequestReport, full);
- if (!full && isLinkedTransactionHeld(Navigation.getTopmostReportActionId() ?? '-1', moneyRequestReport?.reportID ?? '')) {
- Navigation.goBack(ROUTES.REPORT_WITH_ID.getRoute(moneyRequestReport?.reportID ?? ''));
+ approveMoneyRequest(moneyRequestReport, full, hash);
+ if (!full && isLinkedTransactionHeld(Navigation.getTopmostReportActionId(), moneyRequestReport?.reportID)) {
+ Navigation.goBack(ROUTES.REPORT_WITH_ID.getRoute(moneyRequestReport?.reportID));
}
} else if (chatReport && paymentType) {
if (startAnimation) {
startAnimation();
}
- IOU.payMoneyRequest(paymentType, chatReport, moneyRequestReport, full);
+ payMoneyRequest(paymentType, chatReport, moneyRequestReport, full, hash);
}
onClose();
};
diff --git a/src/components/Search/SearchContext.tsx b/src/components/Search/SearchContext.tsx
index 73091abf045b..f537f562afb6 100644
--- a/src/components/Search/SearchContext.tsx
+++ b/src/components/Search/SearchContext.tsx
@@ -8,6 +8,7 @@ import type {SearchContext, SelectedTransactions} from './types';
const defaultSearchContext = {
currentSearchHash: -1,
+ isAllStatus: false,
shouldTurnOffSelectionMode: false,
selectedTransactions: {},
selectedReports: [],
@@ -34,17 +35,21 @@ function getReportsFromSelectedTransactions(data: TransactionListItemType[] | Re
}
function SearchContextProvider({children}: ChildrenProps) {
- const [searchContextData, setSearchContextData] = useState>({
+ const [searchContextData, setSearchContextData] = useState<
+ Pick
+ >({
currentSearchHash: defaultSearchContext.currentSearchHash,
selectedTransactions: defaultSearchContext.selectedTransactions,
shouldTurnOffSelectionMode: false,
selectedReports: defaultSearchContext.selectedReports,
+ isAllStatus: defaultSearchContext.isAllStatus,
});
- const setCurrentSearchHash = useCallback((searchHash: number) => {
+ const setCurrentSearchHash = useCallback((searchHash: number, status: string | string[]) => {
setSearchContextData((prevState) => ({
...prevState,
currentSearchHash: searchHash,
+ isAllStatus: status === CONST.SEARCH.STATUS.EXPENSE.ALL,
}));
}, []);
diff --git a/src/components/Search/SearchPageHeader/SearchPageHeader.tsx b/src/components/Search/SearchPageHeader/SearchPageHeader.tsx
index a3b814650a23..5d0fccecc6df 100644
--- a/src/components/Search/SearchPageHeader/SearchPageHeader.tsx
+++ b/src/components/Search/SearchPageHeader/SearchPageHeader.tsx
@@ -60,7 +60,7 @@ function SearchPageHeader({queryJSON, searchName, searchRouterListVisible, hideS
// We need to use isSmallScreenWidth instead of shouldUseNarrowLayout to apply the correct modal type for the decision modal
// eslint-disable-next-line rulesdir/prefer-shouldUseNarrowLayout-instead-of-isSmallScreenWidth
const {shouldUseNarrowLayout, isSmallScreenWidth} = useResponsiveLayout();
- const {selectedTransactions, clearSelectedTransactions, selectedReports} = useSearchContext();
+ const {selectedTransactions, clearSelectedTransactions, selectedReports, isAllStatus} = useSearchContext();
const [selectionMode] = useOnyx(ONYXKEYS.MOBILE_SELECTION_MODE);
const personalDetails = usePersonalDetails();
const [reports] = useOnyx(ONYXKEYS.COLLECTION.REPORT);
@@ -141,7 +141,7 @@ function SearchPageHeader({queryJSON, searchName, searchRouterListVisible, hideS
const reportIDList = !selectedReports.length
? Object.values(selectedTransactions).map((transaction) => transaction.reportID)
: selectedReports?.filter((report) => !!report).map((report) => report.reportID) ?? [];
- approveMoneyRequestOnSearch(hash, reportIDList, transactionIDList);
+ approveMoneyRequestOnSearch(hash, reportIDList, isAllStatus, transactionIDList);
},
});
}
@@ -205,7 +205,7 @@ function SearchPageHeader({queryJSON, searchName, searchRouterListVisible, hideS
}))
) as PaymentData[];
- payMoneyRequestOnSearch(hash, paymentData, transactionIDList);
+ payMoneyRequestOnSearch(hash, paymentData, isAllStatus, transactionIDList);
},
});
}
@@ -319,6 +319,7 @@ function SearchPageHeader({queryJSON, searchName, searchRouterListVisible, hideS
selectedReports,
translate,
hash,
+ isAllStatus,
lastPaymentMethods,
status,
queryJSON,
diff --git a/src/components/Search/index.tsx b/src/components/Search/index.tsx
index ea87d595b965..bb8e0204e455 100644
--- a/src/components/Search/index.tsx
+++ b/src/components/Search/index.tsx
@@ -172,8 +172,8 @@ function Search({queryJSON, onSearchListScroll, isSearchScreenFocused, contentCo
useEffect(() => {
clearSelectedTransactions(hash);
- setCurrentSearchHash(hash);
- }, [hash, clearSelectedTransactions, setCurrentSearchHash]);
+ setCurrentSearchHash(hash, queryJSON.status);
+ }, [hash, clearSelectedTransactions, setCurrentSearchHash, queryJSON.status]);
const searchResults = currentSearchResults?.data ? currentSearchResults : lastNonEmptySearchResults;
const isSearchResultsEmpty = !searchResults?.data || isSearchResultsEmptyUtil(searchResults);
@@ -549,6 +549,7 @@ function Search({queryJSON, onSearchListScroll, isSearchScreenFocused, contentCo
shouldKeepFocusedItemAtTopOfViewableArea={type === CONST.SEARCH.DATA_TYPES.CHAT}
isScreenFocused={isSearchScreenFocused}
initialNumToRender={shouldUseNarrowLayout ? 5 : undefined}
+ shouldAnimateOnRemove
/>
);
}
diff --git a/src/components/Search/types.ts b/src/components/Search/types.ts
index 3dc408cc27c3..8cb4295e5c0c 100644
--- a/src/components/Search/types.ts
+++ b/src/components/Search/types.ts
@@ -61,9 +61,10 @@ type SearchStatus = ExpenseSearchStatus | InvoiceSearchStatus | TripSearchStatus
type SearchContext = {
currentSearchHash: number;
+ isAllStatus: boolean;
selectedTransactions: SelectedTransactions;
selectedReports: SelectedReports[];
- setCurrentSearchHash: (hash: number) => void;
+ setCurrentSearchHash: (hash: number, status: string | string[]) => void;
setSelectedTransactions: (selectedTransactions: SelectedTransactions, data: TransactionListItemType[] | ReportListItemType[] | ReportActionListItemType[]) => void;
clearSelectedTransactions: (hash?: number, shouldTurnOffSelectionMode?: boolean) => void;
shouldTurnOffSelectionMode: boolean;
diff --git a/src/components/SelectionList/BaseListItem.tsx b/src/components/SelectionList/BaseListItem.tsx
index 99b6f8d5c53e..912c77ac46f0 100644
--- a/src/components/SelectionList/BaseListItem.tsx
+++ b/src/components/SelectionList/BaseListItem.tsx
@@ -38,6 +38,7 @@ function BaseListItem({
onFocus = () => {},
hoverStyle,
onLongPressRow,
+ shouldAnimateOnRemove,
}: BaseListItemProps) {
const theme = useTheme();
const styles = useThemeStyles();
@@ -110,6 +111,7 @@ function BaseListItem({
onMouseLeave={handleMouseLeave}
tabIndex={item.tabIndex}
wrapperStyle={pressableWrapperStyle}
+ shouldAnimateOnRemove={shouldAnimateOnRemove}
>
(
listItemTitleStyles,
initialNumToRender = 12,
listItemTitleContainerStyles,
+ shouldAnimateOnRemove = false,
isScreenFocused = false,
shouldSubscribeToArrowKeyEvents = true,
}: BaseSelectionListProps,
@@ -576,6 +577,7 @@ function BaseSelectionList(
shouldHighlightSelectedItem={shouldHighlightSelectedItem}
singleExecution={singleExecution}
titleContainerStyles={listItemTitleContainerStyles}
+ shouldAnimateOnRemove={shouldAnimateOnRemove}
/>
);
diff --git a/src/components/SelectionList/BaseSelectionListItemRenderer.tsx b/src/components/SelectionList/BaseSelectionListItemRenderer.tsx
index 987a72e025c1..a7de36c18c38 100644
--- a/src/components/SelectionList/BaseSelectionListItemRenderer.tsx
+++ b/src/components/SelectionList/BaseSelectionListItemRenderer.tsx
@@ -15,6 +15,7 @@ type BaseSelectionListItemRendererProps = Omit['singleExecution'];
titleStyles?: StyleProp;
titleContainerStyles?: StyleProp;
+ shouldAnimateOnRemove?: boolean;
};
function BaseSelectionListItemRenderer({
@@ -44,6 +45,7 @@ function BaseSelectionListItemRenderer({
titleStyles,
singleExecution,
titleContainerStyles,
+ shouldAnimateOnRemove,
}: BaseSelectionListItemRendererProps) {
const handleOnCheckboxPress = () => {
if (isReportListItemType(item)) {
@@ -94,6 +96,7 @@ function BaseSelectionListItemRenderer({
wrapperStyle={wrapperStyle}
titleStyles={titleStyles}
titleContainerStyles={titleContainerStyles}
+ shouldAnimateOnRemove={shouldAnimateOnRemove}
/>
{item.footerContent && item.footerContent}
>
diff --git a/src/components/SelectionList/Search/ReportListItem.tsx b/src/components/SelectionList/Search/ReportListItem.tsx
index 7a573412871b..b708a034273c 100644
--- a/src/components/SelectionList/Search/ReportListItem.tsx
+++ b/src/components/SelectionList/Search/ReportListItem.tsx
@@ -63,6 +63,7 @@ function ReportListItem({
onFocus,
onLongPressRow,
shouldSyncFocus,
+ shouldAnimateOnRemove,
}: ReportListItemProps) {
const reportItem = item as unknown as ReportListItemType;
@@ -70,7 +71,7 @@ function ReportListItem({
const styles = useThemeStyles();
const {isLargeScreenWidth} = useResponsiveLayout();
const StyleUtils = useStyleUtils();
- const {currentSearchHash} = useSearchContext();
+ const {currentSearchHash, isAllStatus} = useSearchContext();
const animatedHighlightStyle = useAnimatedHighlightStyle({
borderRadius: variables.componentBorderRadius,
@@ -95,7 +96,7 @@ function ReportListItem({
];
const handleOnButtonPress = () => {
- handleActionButtonPress(currentSearchHash, reportItem, () => onSelectRow(item));
+ handleActionButtonPress(currentSearchHash, reportItem, () => onSelectRow(item), isAllStatus);
};
const openReportInRHP = (transactionItem: TransactionListItemType) => {
@@ -132,6 +133,7 @@ function ReportListItem({
onLongPressRow={onLongPressRow}
shouldSyncFocus={shouldSyncFocus}
isLoading={reportItem.isActionLoading}
+ shouldAnimateOnRemove={shouldAnimateOnRemove}
/>
);
}
@@ -155,6 +157,7 @@ function ReportListItem({
shouldSyncFocus={shouldSyncFocus}
hoverStyle={item.isSelected && styles.activeComponentBG}
pressableWrapperStyle={[styles.mh5, animatedHighlightStyle]}
+ shouldAnimateOnRemove={shouldAnimateOnRemove}
>
{!isLargeScreenWidth && (
diff --git a/src/components/SelectionList/Search/TransactionListItem.tsx b/src/components/SelectionList/Search/TransactionListItem.tsx
index 2582f1fb23cc..c742068a8499 100644
--- a/src/components/SelectionList/Search/TransactionListItem.tsx
+++ b/src/components/SelectionList/Search/TransactionListItem.tsx
@@ -22,13 +22,14 @@ function TransactionListItem({
onLongPressRow,
shouldSyncFocus,
isLoading,
+ shouldAnimateOnRemove,
}: TransactionListItemProps) {
const transactionItem = item as unknown as TransactionListItemType;
const styles = useThemeStyles();
const theme = useTheme();
const {isLargeScreenWidth} = useResponsiveLayout();
- const {currentSearchHash} = useSearchContext();
+ const {currentSearchHash, isAllStatus} = useSearchContext();
const listItemPressableStyle = [
styles.selectionListPressableItemWrapper,
@@ -71,12 +72,13 @@ function TransactionListItem({
shouldSyncFocus={shouldSyncFocus}
hoverStyle={item.isSelected && styles.activeComponentBG}
pressableWrapperStyle={[styles.mh5, animatedHighlightStyle]}
+ shouldAnimateOnRemove={shouldAnimateOnRemove}
>
{
- handleActionButtonPress(currentSearchHash, transactionItem, () => onSelectRow(item));
+ handleActionButtonPress(currentSearchHash, transactionItem, () => onSelectRow(item), isAllStatus);
}}
onCheckboxPress={() => onCheckboxPress?.(item)}
isDisabled={!!isDisabled}
diff --git a/src/components/SelectionList/types.ts b/src/components/SelectionList/types.ts
index 3eb63ae97242..1f82a86814c3 100644
--- a/src/components/SelectionList/types.ts
+++ b/src/components/SelectionList/types.ts
@@ -315,6 +315,8 @@ type ListItemProps = CommonListItemProps & {
/** Styles applid for the title container of the list item */
titleContainerStyles?: StyleProp;
+
+ shouldAnimateOnRemove?: boolean;
};
type BaseListItemProps = CommonListItemProps & {
@@ -331,6 +333,7 @@ type BaseListItemProps = CommonListItemProps & {
hoverStyle?: StyleProp;
/** Errors that this user may contain */
shouldDisplayRBR?: boolean;
+ shouldAnimateOnRemove?: boolean;
};
type UserListItemProps = ListItemProps & {
@@ -666,6 +669,9 @@ type BaseSelectionListProps = Partial & {
/** Initial number of items to render */
initialNumToRender?: number;
+ /** Whether to have an animation when an item is removed from the list */
+ shouldAnimateOnRemove?: boolean;
+
/** Whether the screen is focused or not. (useIsFocused state does not work in tab screens, e.g. SearchPageBottomTab) */
isScreenFocused?: boolean;
} & TRightHandSideComponent;
diff --git a/src/hooks/useExitingAnimation/index.ts b/src/hooks/useExitingAnimation/index.ts
new file mode 100644
index 000000000000..b08d26eec894
--- /dev/null
+++ b/src/hooks/useExitingAnimation/index.ts
@@ -0,0 +1,40 @@
+import {useMemo} from 'react';
+import {Easing, Keyframe} from 'react-native-reanimated';
+import type {ReanimatedKeyframe} from 'react-native-reanimated/lib/typescript/layoutReanimation/animationBuilder/Keyframe';
+import type UseExitingAnimation from './type';
+
+const easing = Easing.bezier(0.76, 0.0, 0.24, 1.0);
+
+/**
+ * Due to issues with react-native-reanimated Keyframes the easing type doesn't account for bezier functions
+ * and we also need to use internal .build() function to make the easing apply on each mount.
+ *
+ * This causes problems with both eslint & Typescript and is going to be fixed in react-native-reanimated 3.17 with these PRs merged:
+ * https://github.com/software-mansion/react-native-reanimated/pull/6960
+ * https://github.com/software-mansion/react-native-reanimated/pull/6958
+ *
+ * Once that's added we can apply our changes to files in BottomDockedModal/Backdrop/*.tsx and BottomDockedModal/Container/*.tsx
+ */
+
+/* eslint-disable @typescript-eslint/no-unsafe-call */
+const useExitingAnimation: UseExitingAnimation = (height: number) => {
+ const Exiting = useMemo(() => {
+ const SlideOut = new Keyframe({
+ from: {
+ height,
+ },
+ to: {
+ height: '0',
+ // @ts-expect-error Types mismatch in reanimated, should to be fixed in 3.17
+ easing,
+ },
+ });
+
+ // @ts-expect-error Internal function used to fix easing issue, should to be fixed in 3.17
+ return SlideOut.duration(300).build() as ReanimatedKeyframe;
+ }, [height]);
+
+ return Exiting;
+};
+
+export default useExitingAnimation;
diff --git a/src/hooks/useExitingAnimation/index.web.ts b/src/hooks/useExitingAnimation/index.web.ts
new file mode 100644
index 000000000000..2c50d1585ec5
--- /dev/null
+++ b/src/hooks/useExitingAnimation/index.web.ts
@@ -0,0 +1,37 @@
+import {useMemo} from 'react';
+import {Easing, Keyframe} from 'react-native-reanimated';
+import type UseExitingAnimation from './type';
+
+const easing = Easing.bezier(0.76, 0.0, 0.24, 1.0);
+
+/**
+ * Due to issues with react-native-reanimated Keyframes the easing type doesn't account for bezier functions
+ * and we also need to use internal .build() function to make the easing apply on each mount.
+ *
+ * This causes problems with both eslint & Typescript and is going to be fixed in react-native-reanimated 3.17 with these PRs merged:
+ * https://github.com/software-mansion/react-native-reanimated/pull/6960
+ * https://github.com/software-mansion/react-native-reanimated/pull/6958
+ *
+ * Once that's added we can apply our changes to files in BottomDockedModal/Backdrop/*.tsx and BottomDockedModal/Container/*.tsx
+ */
+
+const useExitingAnimation: UseExitingAnimation = (height: number) => {
+ const Exiting = useMemo(() => {
+ const SlideOut = new Keyframe({
+ from: {
+ height,
+ },
+ to: {
+ height: '0',
+ // @ts-expect-error Types mismatch in reanimated, should to be fixed in 3.17
+ easing,
+ },
+ });
+
+ return SlideOut.duration(300);
+ }, [height]);
+
+ return Exiting;
+};
+
+export default useExitingAnimation;
diff --git a/src/hooks/useExitingAnimation/type.ts b/src/hooks/useExitingAnimation/type.ts
new file mode 100644
index 000000000000..1f20cecd738d
--- /dev/null
+++ b/src/hooks/useExitingAnimation/type.ts
@@ -0,0 +1,5 @@
+import type {ReanimatedKeyframe} from 'react-native-reanimated/lib/typescript/layoutReanimation/animationBuilder/Keyframe';
+
+type UseExitingAnimation = (height: number) => ReanimatedKeyframe;
+
+export default UseExitingAnimation;
diff --git a/src/libs/ReportActionsUtils.ts b/src/libs/ReportActionsUtils.ts
index f60a4bb2e668..afdf76d160ef 100644
--- a/src/libs/ReportActionsUtils.ts
+++ b/src/libs/ReportActionsUtils.ts
@@ -1583,7 +1583,7 @@ function getDismissedViolationMessageText(originalMessage: ReportAction report.transactions.length);
}
/**
diff --git a/src/libs/actions/IOU.ts b/src/libs/actions/IOU.ts
index 6f4596b0e597..73ab08bc6834 100644
--- a/src/libs/actions/IOU.ts
+++ b/src/libs/actions/IOU.ts
@@ -4165,6 +4165,60 @@ function updateMoneyRequestDistanceRate(
API.write(WRITE_COMMANDS.UPDATE_MONEY_REQUEST_DISTANCE_RATE, paramsWithoutTaxUpdated, onyxData);
}
+function createSearchOnyxData(hash: number, reportID: string | undefined, iouTransactionID?: string) {
+ const transactionIDList = getReportTransactions(reportID).map((transaction) => transaction.transactionID);
+ const optimisticData: OnyxUpdate[] = [
+ {
+ onyxMethod: Onyx.METHOD.MERGE,
+ key: `${ONYXKEYS.COLLECTION.SNAPSHOT}${hash}`,
+ value: {
+ data: iouTransactionID
+ ? {
+ [`${ONYXKEYS.COLLECTION.TRANSACTION}${iouTransactionID}`]: {isActionLoading: true},
+ }
+ : {
+ [`${ONYXKEYS.COLLECTION.REPORT}${reportID}`]: {isActionLoading: true},
+ },
+ },
+ },
+ ];
+ const successData: OnyxUpdate[] = [
+ {
+ onyxMethod: Onyx.METHOD.MERGE,
+ key: `${ONYXKEYS.COLLECTION.SNAPSHOT}${hash}`,
+ value: {
+ data: iouTransactionID
+ ? {
+ [`${ONYXKEYS.COLLECTION.REPORT}${reportID}`]: null,
+ }
+ : {
+ [`${ONYXKEYS.COLLECTION.REPORT}${reportID}`]: null,
+ ...(Object.fromEntries(
+ (transactionIDList ?? []).map((transactionID) => [`${ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`, null]),
+ ) as Partial),
+ },
+ },
+ },
+ ];
+ const failureData: OnyxUpdate[] = [
+ {
+ onyxMethod: Onyx.METHOD.MERGE,
+ key: `${ONYXKEYS.COLLECTION.SNAPSHOT}${hash}`,
+ value: {
+ data: iouTransactionID
+ ? {}
+ : {
+ [`${ONYXKEYS.COLLECTION.REPORT}${reportID}`]: {
+ errors: getMicroSecondOnyxErrorWithTranslationKey('common.genericErrorMessage'),
+ isActionLoading: false,
+ },
+ },
+ },
+ },
+ ];
+ return {optimisticData, successData, failureData};
+}
+
const getConvertTrackedExpenseInformation = (
transactionID: string | undefined,
actionableWhisperReportActionID: string | undefined,
@@ -6788,7 +6842,7 @@ function cleanUpMoneyRequest(transactionID: string, reportAction: OnyxTypes.Repo
* @param isSingleTransactionView - whether we are in the transaction thread report
* @return the url to navigate back once the money request is deleted
*/
-function deleteMoneyRequest(transactionID: string | undefined, reportAction: OnyxTypes.ReportAction, isSingleTransactionView = false) {
+function deleteMoneyRequest(transactionID: string | undefined, reportAction: OnyxTypes.ReportAction, isSingleTransactionView = false, hash?: number) {
if (!transactionID) {
return;
}
@@ -7099,6 +7153,13 @@ function deleteMoneyRequest(transactionID: string | undefined, reportAction: Ony
});
}
+ if (hash) {
+ const searchOnyxData = createSearchOnyxData(hash, iouReport?.reportID, transactionID);
+ optimisticData.push(...searchOnyxData.optimisticData);
+ successData.push(...searchOnyxData.successData);
+ failureData.push(...searchOnyxData.failureData);
+ }
+
const parameters: DeleteMoneyRequestParams = {
transactionID,
reportActionID: reportAction.reportActionID,
@@ -7757,6 +7818,7 @@ function getPayMoneyRequestParams(
paymentMethodType: PaymentMethodType,
full: boolean,
payAsBusiness?: boolean,
+ hash?: number,
): PayMoneyRequestData {
const isInvoiceReport = isInvoiceReportReportUtils(iouReport);
const activePolicy = getPolicy(activePolicyID);
@@ -8019,6 +8081,13 @@ function getPayMoneyRequestParams(
optimisticHoldReportExpenseActionIDs = JSON.stringify(holdReportOnyxData.optimisticHoldReportExpenseActionIDs);
}
+ if (hash) {
+ const searchOnyxData = createSearchOnyxData(hash, iouReport?.reportID);
+ optimisticData.push(...searchOnyxData.optimisticData);
+ successData.push(...searchOnyxData.successData);
+ failureData.push(...searchOnyxData.failureData);
+ }
+
return {
params: {
iouReportID: iouReport?.reportID,
@@ -8250,7 +8319,7 @@ function getNextApproverAccountID(report: OnyxEntry, isUnappro
return getAccountIDsByLogins([nextApproverEmail]).at(0);
}
-function approveMoneyRequest(expenseReport: OnyxEntry, full?: boolean) {
+function approveMoneyRequest(expenseReport: OnyxEntry, full?: boolean, hash?: number) {
if (!expenseReport) {
return;
}
@@ -8270,9 +8339,10 @@ function approveMoneyRequest(expenseReport: OnyxEntry, full?:
const approvalChain = getApprovalChain(getPolicy(expenseReport.policyID), expenseReport);
- const predictedNextStatus = isLastApprover(approvalChain) ? CONST.REPORT.STATUS_NUM.APPROVED : CONST.REPORT.STATUS_NUM.SUBMITTED;
- const predictedNextState = isLastApprover(approvalChain) ? CONST.REPORT.STATE_NUM.APPROVED : CONST.REPORT.STATE_NUM.SUBMITTED;
- const managerID = isLastApprover(approvalChain) ? expenseReport.managerID : getNextApproverAccountID(expenseReport);
+ const predictedReportApproved = isLastApprover(approvalChain);
+ const predictedNextStatus = predictedReportApproved ? CONST.REPORT.STATUS_NUM.APPROVED : CONST.REPORT.STATUS_NUM.SUBMITTED;
+ const predictedNextState = predictedReportApproved ? CONST.REPORT.STATE_NUM.APPROVED : CONST.REPORT.STATE_NUM.SUBMITTED;
+ const managerID = predictedReportApproved ? expenseReport.managerID : getNextApproverAccountID(expenseReport);
const optimisticNextStep = buildNextStep(expenseReport, predictedNextStatus);
const chatReport = getReportOrDraftReport(expenseReport.chatReportID);
@@ -8405,6 +8475,13 @@ function approveMoneyRequest(expenseReport: OnyxEntry, full?:
optimisticHoldReportExpenseActionIDs = JSON.stringify(holdReportOnyxData.optimisticHoldReportExpenseActionIDs);
}
+ if (hash && predictedReportApproved) {
+ const searchOnyxData = createSearchOnyxData(hash, expenseReport.reportID);
+ optimisticData.push(...searchOnyxData.optimisticData);
+ successData.push(...searchOnyxData.successData);
+ failureData.push(...searchOnyxData.failureData);
+ }
+
const parameters: ApproveMoneyRequestParams = {
reportID: expenseReport.reportID,
approvedReportActionID: optimisticApprovedReportAction.reportActionID,
@@ -8417,7 +8494,7 @@ function approveMoneyRequest(expenseReport: OnyxEntry, full?:
API.write(WRITE_COMMANDS.APPROVE_MONEY_REQUEST, parameters, {optimisticData, successData, failureData});
}
-function unapproveExpenseReport(expenseReport: OnyxEntry) {
+function unapproveExpenseReport(expenseReport: OnyxEntry, hash?: number) {
if (isEmptyObject(expenseReport)) {
return;
}
@@ -8522,6 +8599,13 @@ function unapproveExpenseReport(expenseReport: OnyxEntry) {
});
}
+ if (hash) {
+ const searchOnyxData = createSearchOnyxData(hash, expenseReport.reportID);
+ optimisticData.push(...searchOnyxData.optimisticData);
+ successData.push(...searchOnyxData.successData);
+ failureData.push(...searchOnyxData.failureData);
+ }
+
const parameters: UnapproveExpenseReportParams = {
reportID: expenseReport.reportID,
reportActionID: optimisticUnapprovedReportAction.reportActionID,
@@ -8530,7 +8614,7 @@ function unapproveExpenseReport(expenseReport: OnyxEntry) {
API.write(WRITE_COMMANDS.UNAPPROVE_EXPENSE_REPORT, parameters, {optimisticData, successData, failureData});
}
-function submitReport(expenseReport: OnyxTypes.Report) {
+function submitReport(expenseReport: OnyxTypes.Report, hash?: number) {
if (expenseReport.policyID && shouldRestrictUserBillableActions(expenseReport.policyID)) {
Navigation.navigate(ROUTES.RESTRICTED_ACTION.getRoute(expenseReport.policyID));
return;
@@ -8654,6 +8738,13 @@ function submitReport(expenseReport: OnyxTypes.Report) {
});
}
+ if (hash) {
+ const searchOnyxData = createSearchOnyxData(hash, expenseReport.reportID);
+ optimisticData.push(...searchOnyxData.optimisticData);
+ successData.push(...searchOnyxData.successData);
+ failureData.push(...searchOnyxData.failureData);
+ }
+
const parameters: SubmitReportParams = {
reportID: expenseReport.reportID,
managerAccountID: getSubmitToAccountID(policy, expenseReport) ?? expenseReport.managerID,
@@ -8663,7 +8754,7 @@ function submitReport(expenseReport: OnyxTypes.Report) {
API.write(WRITE_COMMANDS.SUBMIT_REPORT, parameters, {optimisticData, successData, failureData});
}
-function cancelPayment(expenseReport: OnyxEntry, chatReport: OnyxTypes.Report, backTo?: Route) {
+function cancelPayment(expenseReport: OnyxEntry, chatReport: OnyxTypes.Report, backTo?: Route, hash?: number) {
if (isEmptyObject(expenseReport)) {
return;
}
@@ -8796,6 +8887,13 @@ function cancelPayment(expenseReport: OnyxEntry, chatReport: O
value: buildNextStep(expenseReport, CONST.REPORT.STATUS_NUM.REIMBURSED),
});
+ if (hash) {
+ const searchOnyxData = createSearchOnyxData(hash, expenseReport?.reportID);
+ optimisticData.push(...searchOnyxData.optimisticData);
+ successData.push(...searchOnyxData.successData);
+ failureData.push(...searchOnyxData.failureData);
+ }
+
API.write(
WRITE_COMMANDS.CANCEL_PAYMENT,
{
@@ -8849,7 +8947,7 @@ function completePaymentOnboarding(paymentSelected: ValueOf, full = true) {
+function payMoneyRequest(paymentType: PaymentMethodType, chatReport: OnyxTypes.Report, iouReport: OnyxEntry, full = true, hash?: number) {
if (chatReport.policyID && shouldRestrictUserBillableActions(chatReport.policyID)) {
Navigation.navigate(ROUTES.RESTRICTED_ACTION.getRoute(chatReport.policyID));
return;
@@ -8859,7 +8957,7 @@ function payMoneyRequest(paymentType: PaymentMethodType, chatReport: OnyxTypes.R
completePaymentOnboarding(paymentSelected);
const recipient = {accountID: iouReport?.ownerAccountID ?? CONST.DEFAULT_NUMBER_ID};
- const {params, optimisticData, successData, failureData} = getPayMoneyRequestParams(chatReport, iouReport, recipient, paymentType, full);
+ const {params, optimisticData, successData, failureData} = getPayMoneyRequestParams(chatReport, iouReport, recipient, paymentType, full, undefined, hash);
// For now, we need to call the PayMoneyRequestWithWallet API since PayMoneyRequest was not updated to work with
// Expensify Wallets.
diff --git a/src/libs/actions/Search.ts b/src/libs/actions/Search.ts
index ef1ddb998f63..395d5b096834 100644
--- a/src/libs/actions/Search.ts
+++ b/src/libs/actions/Search.ts
@@ -48,7 +48,7 @@ Onyx.connect({
waitForCollectionCallback: true,
});
-function handleActionButtonPress(hash: number, item: TransactionListItemType | ReportListItemType, goToItem: () => void) {
+function handleActionButtonPress(hash: number, item: TransactionListItemType | ReportListItemType, goToItem: () => void, isAllStatus: boolean) {
// The transactionIDList is needed to handle actions taken on `status:all` where transactions on single expense reports can be approved/paid.
// We need the transactionID to display the loading indicator for that list item's action.
const transactionID = isTransactionListItemType(item) ? [item.transactionID] : undefined;
@@ -62,14 +62,14 @@ function handleActionButtonPress(hash: number, item: TransactionListItemType | R
switch (item.action) {
case CONST.SEARCH.ACTION_TYPES.PAY:
- getPayActionCallback(hash, item, goToItem);
+ getPayActionCallback(hash, item, goToItem, isAllStatus);
return;
case CONST.SEARCH.ACTION_TYPES.APPROVE:
- approveMoneyRequestOnSearch(hash, [item.reportID], transactionID);
+ approveMoneyRequestOnSearch(hash, [item.reportID], isAllStatus, transactionID);
return;
case CONST.SEARCH.ACTION_TYPES.SUBMIT: {
const policy = (allSnapshots?.[`${ONYXKEYS.COLLECTION.SNAPSHOT}${hash}`]?.data?.[`${ONYXKEYS.COLLECTION.POLICY}${item.policyID}`] ?? {}) as SearchPolicy;
- submitMoneyRequestOnSearch(hash, [item], [policy], transactionID);
+ submitMoneyRequestOnSearch(hash, [item], [policy], isAllStatus, transactionID);
return;
}
default:
@@ -91,7 +91,7 @@ function getLastPolicyPaymentMethod(policyID: string | undefined, lastPaymentMet
return lastPolicyPaymentMethod;
}
-function getPayActionCallback(hash: number, item: TransactionListItemType | ReportListItemType, goToItem: () => void) {
+function getPayActionCallback(hash: number, item: TransactionListItemType | ReportListItemType, goToItem: () => void, isAllStatus: boolean) {
const lastPolicyPaymentMethod = getLastPolicyPaymentMethod(item.policyID, lastPaymentMethod);
if (!lastPolicyPaymentMethod) {
@@ -104,13 +104,13 @@ function getPayActionCallback(hash: number, item: TransactionListItemType | Repo
const transactionID = isTransactionListItemType(item) ? [item.transactionID] : undefined;
if (lastPolicyPaymentMethod === CONST.IOU.PAYMENT_TYPE.ELSEWHERE) {
- payMoneyRequestOnSearch(hash, [{reportID: item.reportID, amount, paymentType: lastPolicyPaymentMethod}], transactionID);
+ payMoneyRequestOnSearch(hash, [{reportID: item.reportID, amount, paymentType: lastPolicyPaymentMethod}], isAllStatus, transactionID);
return;
}
const hasVBBA = !!allSnapshots?.[`${ONYXKEYS.COLLECTION.SNAPSHOT}${hash}`]?.data?.[`${ONYXKEYS.COLLECTION.POLICY}${item.policyID}`]?.achAccount?.bankAccountID;
if (hasVBBA) {
- payMoneyRequestOnSearch(hash, [{reportID: item.reportID, amount, paymentType: lastPolicyPaymentMethod}], transactionID);
+ payMoneyRequestOnSearch(hash, [{reportID: item.reportID, amount, paymentType: lastPolicyPaymentMethod}], isAllStatus, transactionID);
return;
}
@@ -281,7 +281,7 @@ function holdMoneyRequestOnSearch(hash: number, transactionIDList: string[], com
API.write(WRITE_COMMANDS.HOLD_MONEY_REQUEST_ON_SEARCH, {hash, transactionIDList, comment}, {optimisticData, finallyData});
}
-function submitMoneyRequestOnSearch(hash: number, reportList: SearchReport[], policy: SearchPolicy[], transactionIDList?: string[]) {
+function submitMoneyRequestOnSearch(hash: number, reportList: SearchReport[], policy: SearchPolicy[], isAllStatus: boolean, transactionIDList?: string[]) {
const createActionLoadingData = (isLoading: boolean): OnyxUpdate[] => [
{
onyxMethod: Onyx.METHOD.MERGE,
@@ -296,7 +296,23 @@ function submitMoneyRequestOnSearch(hash: number, reportList: SearchReport[], po
},
];
const optimisticData: OnyxUpdate[] = createActionLoadingData(true);
- const finallyData: OnyxUpdate[] = createActionLoadingData(false);
+ const successData: OnyxUpdate[] = isAllStatus
+ ? createActionLoadingData(false)
+ : [
+ {
+ onyxMethod: Onyx.METHOD.MERGE,
+ key: `${ONYXKEYS.COLLECTION.SNAPSHOT}${hash}`,
+ value: {
+ data: {
+ ...(Object.fromEntries(reportList.map((item) => [`${ONYXKEYS.COLLECTION.REPORT}${item.reportID}`, null])) as Partial),
+ ...(Object.fromEntries(
+ (transactionIDList ?? []).map((transactionID) => [`${ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`, null]),
+ ) as Partial),
+ },
+ },
+ },
+ ];
+ const failureData: OnyxUpdate[] = createActionLoadingData(false);
const report = (reportList.at(0) ?? {}) as SearchReport;
const parameters: SubmitReportParams = {
@@ -307,10 +323,10 @@ function submitMoneyRequestOnSearch(hash: number, reportList: SearchReport[], po
// The SubmitReport command is not 1:1:1 yet, which means creating a separate SubmitMoneyRequestOnSearch command is not feasible until https://github.com/Expensify/Expensify/issues/451223 is done.
// In the meantime, we'll call SubmitReport which works for a single expense only, so not bulk actions are possible.
- API.write(WRITE_COMMANDS.SUBMIT_REPORT, parameters, {optimisticData, finallyData});
+ API.write(WRITE_COMMANDS.SUBMIT_REPORT, parameters, {optimisticData, successData, failureData});
}
-function approveMoneyRequestOnSearch(hash: number, reportIDList: string[], transactionIDList?: string[]) {
+function approveMoneyRequestOnSearch(hash: number, reportIDList: string[], isAllStatus: boolean, transactionIDList?: string[]) {
const createOnyxData = (update: Partial | Partial): OnyxUpdate[] => [
{
onyxMethod: Onyx.METHOD.MERGE,
@@ -323,13 +339,28 @@ function approveMoneyRequestOnSearch(hash: number, reportIDList: string[], trans
},
];
const optimisticData: OnyxUpdate[] = createOnyxData({isActionLoading: true});
- const failureData: OnyxUpdate[] = createOnyxData({errors: getMicroSecondOnyxErrorWithTranslationKey('common.genericErrorMessage')});
- const finallyData: OnyxUpdate[] = createOnyxData({isActionLoading: false});
-
- API.write(WRITE_COMMANDS.APPROVE_MONEY_REQUEST_ON_SEARCH, {hash, reportIDList}, {optimisticData, failureData, finallyData});
+ const successData: OnyxUpdate[] = isAllStatus
+ ? createOnyxData({isActionLoading: false})
+ : [
+ {
+ onyxMethod: Onyx.METHOD.MERGE,
+ key: `${ONYXKEYS.COLLECTION.SNAPSHOT}${hash}`,
+ value: {
+ data: {
+ ...(Object.fromEntries(reportIDList.map((reportID) => [`${ONYXKEYS.COLLECTION.REPORT}${reportID}`, null])) as Partial),
+ ...(Object.fromEntries(
+ (transactionIDList ?? []).map((transactionID) => [`${ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`, null]),
+ ) as Partial),
+ },
+ },
+ },
+ ];
+ const failureData: OnyxUpdate[] = createOnyxData({errors: getMicroSecondOnyxErrorWithTranslationKey('common.genericErrorMessage'), isActionLoading: false});
+
+ API.write(WRITE_COMMANDS.APPROVE_MONEY_REQUEST_ON_SEARCH, {hash, reportIDList}, {optimisticData, failureData, successData});
}
-function payMoneyRequestOnSearch(hash: number, paymentData: PaymentData[], transactionIDList?: string[]) {
+function payMoneyRequestOnSearch(hash: number, paymentData: PaymentData[], isAllStatus: boolean, transactionIDList?: string[]) {
const createOnyxData = (update: Partial | Partial): OnyxUpdate[] => [
{
onyxMethod: Onyx.METHOD.MERGE,
@@ -343,14 +374,29 @@ function payMoneyRequestOnSearch(hash: number, paymentData: PaymentData[], trans
];
const optimisticData: OnyxUpdate[] = createOnyxData({isActionLoading: true});
- const failureData: OnyxUpdate[] = createOnyxData({errors: getMicroSecondOnyxErrorWithTranslationKey('common.genericErrorMessage')});
- const finallyData: OnyxUpdate[] = createOnyxData({isActionLoading: false});
+ const successData: OnyxUpdate[] = isAllStatus
+ ? createOnyxData({isActionLoading: false})
+ : [
+ {
+ onyxMethod: Onyx.METHOD.MERGE,
+ key: `${ONYXKEYS.COLLECTION.SNAPSHOT}${hash}`,
+ value: {
+ data: {
+ ...(Object.fromEntries(paymentData.map((item) => [`${ONYXKEYS.COLLECTION.REPORT}${item.reportID}`, null])) as Partial),
+ ...(Object.fromEntries(
+ (transactionIDList ?? []).map((transactionID) => [`${ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`, null]),
+ ) as Partial),
+ },
+ },
+ },
+ ];
+ const failureData: OnyxUpdate[] = createOnyxData({errors: getMicroSecondOnyxErrorWithTranslationKey('common.genericErrorMessage'), isActionLoading: false});
// eslint-disable-next-line rulesdir/no-api-side-effects-method
API.makeRequestWithSideEffects(
SIDE_EFFECT_REQUEST_COMMANDS.PAY_MONEY_REQUEST_ON_SEARCH,
{hash, paymentData: JSON.stringify(paymentData)},
- {optimisticData, failureData, finallyData},
+ {optimisticData, failureData, successData},
).then((response) => {
if (response?.jsonCode !== CONST.JSON_CODE.SUCCESS) {
return;
@@ -367,7 +413,18 @@ function unholdMoneyRequestOnSearch(hash: number, transactionIDList: string[]) {
function deleteMoneyRequestOnSearch(hash: number, transactionIDList: string[]) {
const {optimisticData, finallyData} = getOnyxLoadingData(hash);
- API.write(WRITE_COMMANDS.DELETE_MONEY_REQUEST_ON_SEARCH, {hash, transactionIDList}, {optimisticData, finallyData});
+ const successData: OnyxUpdate[] = [
+ {
+ onyxMethod: Onyx.METHOD.MERGE,
+ key: `${ONYXKEYS.COLLECTION.SNAPSHOT}${hash}`,
+ value: {
+ data: {
+ ...(Object.fromEntries(transactionIDList.map((transactionID) => [`${ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`, null])) as Partial),
+ },
+ },
+ },
+ ];
+ API.write(WRITE_COMMANDS.DELETE_MONEY_REQUEST_ON_SEARCH, {hash, transactionIDList}, {optimisticData, successData, finallyData});
}
type Params = Record;
diff --git a/src/pages/ReportDetailsPage.tsx b/src/pages/ReportDetailsPage.tsx
index f348445e3c91..6f96811b52d7 100644
--- a/src/pages/ReportDetailsPage.tsx
+++ b/src/pages/ReportDetailsPage.tsx
@@ -173,7 +173,7 @@ function ReportDetailsPage({policies, report, route, reportMetadata}: ReportDeta
const [parentReportNameValuePairs] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS}${report?.parentReportID || CONST.DEFAULT_NUMBER_ID}`);
/* eslint-enable @typescript-eslint/prefer-nullish-coalescing */
const {reportActions} = usePaginatedReportActions(report.reportID);
- const {currentSearchHash} = useSearchContext();
+ const {currentSearchHash, isAllStatus} = useSearchContext();
// We need to use isSmallScreenWidth instead of shouldUseNarrowLayout to apply the correct modal type for the decision modal
// eslint-disable-next-line rulesdir/prefer-shouldUseNarrowLayout-instead-of-isSmallScreenWidth
@@ -348,8 +348,8 @@ function ReportDetailsPage({policies, report, route, reportMetadata}: ReportDeta
return;
}
Navigation.dismissModal();
- unapproveExpenseReport(moneyRequestReport);
- }, [isMoneyRequestExported, moneyRequestReport, isDelegateAccessRestricted]);
+ unapproveExpenseReport(moneyRequestReport, isAllStatus ? undefined : currentSearchHash);
+ }, [isDelegateAccessRestricted, isMoneyRequestExported, moneyRequestReport, isAllStatus, currentSearchHash]);
const shouldShowLeaveButton = canLeaveChat(report, policy);
const shouldShowGoToWorkspace = shouldShowPolicy(policy, false, session?.email) && !policy?.isJoinRequestPending;
@@ -387,9 +387,9 @@ function ReportDetailsPage({policies, report, route, reportMetadata}: ReportDeta
return;
}
- cancelPaymentAction(moneyRequestReport, chatReport, backTo);
+ cancelPaymentAction(moneyRequestReport, chatReport, backTo, isAllStatus ? undefined : currentSearchHash);
setIsConfirmModalVisible(false);
- }, [moneyRequestReport, chatReport, backTo]);
+ }, [moneyRequestReport, chatReport, backTo, isAllStatus, currentSearchHash]);
const menuItems: ReportDetailsPageMenuItem[] = useMemo(() => {
const items: ReportDetailsPageMenuItem[] = [];
@@ -922,9 +922,9 @@ function ReportDetailsPage({policies, report, route, reportMetadata}: ReportDeta
if (isTrackExpense) {
deleteTrackExpense(moneyRequestReport?.reportID, iouTransactionID, requestParentReportAction, isSingleTransactionView);
} else {
- deleteMoneyRequest(iouTransactionID, requestParentReportAction, isSingleTransactionView);
+ deleteMoneyRequest(iouTransactionID, requestParentReportAction, isSingleTransactionView, currentSearchHash);
}
- }, [caseID, iouTransactionID, isSingleTransactionView, moneyRequestReport?.reportID, report, requestParentReportAction]);
+ }, [caseID, currentSearchHash, iouTransactionID, isSingleTransactionView, moneyRequestReport?.reportID, report, requestParentReportAction]);
// A flag to indicate whether the user chose to delete the transaction or not
const isTransactionDeleted = useRef(false);
diff --git a/tests/unit/Search/handleActionButtonPressTest.ts b/tests/unit/Search/handleActionButtonPressTest.ts
index 69af0e83849a..28552ae03aab 100644
--- a/tests/unit/Search/handleActionButtonPressTest.ts
+++ b/tests/unit/Search/handleActionButtonPressTest.ts
@@ -205,13 +205,13 @@ describe('handleActionButtonPress', () => {
const searchHash = 1;
test('Should navigate to item when report has one transaction on hold', () => {
const goToItem = jest.fn(() => {});
- handleActionButtonPress(searchHash, mockReportItemWithHold, goToItem);
+ handleActionButtonPress(searchHash, mockReportItemWithHold, goToItem, false);
expect(goToItem).toHaveBeenCalledTimes(1);
});
test('Should not navigate to item when the hold is removed', () => {
const goToItem = jest.fn(() => {});
- handleActionButtonPress(searchHash, updatedMockReportItem, goToItem);
+ handleActionButtonPress(searchHash, updatedMockReportItem, goToItem, false);
expect(goToItem).toHaveBeenCalledTimes(0);
});
});