Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 8 additions & 5 deletions src/components/MoneyReportHeader.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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';

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

BTW I don't think we should connect this component to Search context since this component is used in the Inbox flow, not Search. I think we need to keep changes contained to the Search components since this is a Search feature

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@luacmartins When the user opens the RHP from Search and performs actions like approving or paying a money request, we will need to use the hash to update the search results.

import SettlementButton from './SettlementButton';

type MoneyReportHeaderProps = {
Expand Down Expand Up @@ -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;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

question to @luacmartins In general why do we have to push search hash to these functions which didn't use them before?
In what contexts does the backend need to use hash?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If I'm correct, the backend is not using the hash, this is purely used by the frontend to build optimistic data


const getCanIOUBePaid = useCallback(
(onlyShowPayElsewhere = false) => canIOUBePaidAction(moneyRequestReport, chatReport, policy, transaction ? [transaction] : undefined, onlyShowPayElsewhere),
Expand Down Expand Up @@ -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);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@mkzie2 an we determine if the Expense Report is being opened from Search? If so, would it be possible to use payMoneyRequestOnSearch instead?

}
},
[chatReport, isAnyTransactionOnHold, isDelegateAccessRestricted, moneyRequestReport],
[chatReport, hash, isAnyTransactionOnHold, isDelegateAccessRestricted, moneyRequestReport],
);

const confirmApproval = () => {
Expand All @@ -249,7 +252,7 @@ function MoneyReportHeader({policy, report: moneyRequestReport, transactionThrea
} else if (isAnyTransactionOnHold) {
setIsHoldMenuVisible(true);
} else {
approveMoneyRequest(moneyRequestReport, true);
approveMoneyRequest(moneyRequestReport, true, hash);
}
};

Expand Down Expand Up @@ -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}
/>
</View>
Expand Down Expand Up @@ -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}
/>
)}
Expand Down
17 changes: 15 additions & 2 deletions src/components/OpacityView.tsx
Original file line number Diff line number Diff line change
@@ -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';

Expand Down Expand Up @@ -32,6 +34,8 @@ type OpacityViewProps = {

/** Whether the view needs to be rendered offscreen (for Android only) */
needsOffscreenAlphaCompositing?: boolean;

shouldAnimateOnRemove?: boolean;
};

function OpacityView({
Expand All @@ -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(),
}));
Expand All @@ -53,8 +62,12 @@ function OpacityView({

return (
<Animated.View
style={[opacityStyle, style]}
style={[opacityStyle, style, shouldAnimateOnRemove && styles.overflowHidden]}
needsOffscreenAlphaCompositing={shouldRenderOffscreen ? needsOffscreenAlphaCompositing : undefined}
exiting={shouldAnimateOnRemove ? Exiting : undefined}
onLayout={(e) => {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This component (OpacityView) is being used by PressableWithFeedback which is used all over the app in crucial places.
We cannot add an onLayout on this animated component, since it might substantially slow down the app. This will result in multiple setState being run for every pressable component, multiple times.

Is there any other way to add this animation, without affecting OpacityView?

setHeight(e.nativeEvent.layout.height);
}}
>
{children}
</Animated.View>
Expand Down
4 changes: 4 additions & 0 deletions src/components/Pressable/PressableWithFeedback.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand All @@ -47,6 +49,7 @@ function PressableWithFeedback(
pressDimmingValue = variables.pressDimValue,
hoverDimmingValue = variables.hoverDimValue,
dimAnimationDuration,
shouldAnimateOnRemove,
...rest
}: PressableWithFeedbackProps,
ref: PressableRef,
Expand All @@ -61,6 +64,7 @@ function PressableWithFeedback(
dimAnimationDuration={dimAnimationDuration}
style={wrapperStyle}
needsOffscreenAlphaCompositing={needsOffscreenAlphaCompositing}
shouldAnimateOnRemove={shouldAnimateOnRemove}
>
<GenericPressable
ref={ref}
Expand Down
13 changes: 8 additions & 5 deletions src/components/ProcessMoneyReportHoldMenu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,16 @@ import React, {useMemo} from 'react';
import type {OnyxEntry} from 'react-native-onyx';
import useLocalize from '@hooks/useLocalize';
import useResponsiveLayout from '@hooks/useResponsiveLayout';
import {approveMoneyRequest, payMoneyRequest} from '@libs/actions/IOU';
import Navigation from '@libs/Navigation/Navigation';
import {isLinkedTransactionHeld} from '@libs/ReportActionsUtils';
import * as IOU from '@userActions/IOU';
import CONST from '@src/CONST';
import ROUTES from '@src/ROUTES';
import type * as OnyxTypes from '@src/types/onyx';
import type {PaymentMethodType} from '@src/types/onyx/OriginalMessage';
import type DeepValueOf from '@src/types/utils/DeepValueOf';
import DecisionModal from './DecisionModal';
import {useSearchContext} from './Search/SearchContext';

type ActionHandledType = DeepValueOf<typeof CONST.IOU.REPORT_ACTION_TYPE.PAY | typeof CONST.IOU.REPORT_ACTION_TYPE.APPROVE>;

Expand Down Expand Up @@ -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();
Expand All @@ -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();
};
Expand Down
9 changes: 7 additions & 2 deletions src/components/Search/SearchContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import type {SearchContext, SelectedTransactions} from './types';

const defaultSearchContext = {
currentSearchHash: -1,
isAllStatus: false,
shouldTurnOffSelectionMode: false,
selectedTransactions: {},
selectedReports: [],
Expand All @@ -34,17 +35,21 @@ function getReportsFromSelectedTransactions(data: TransactionListItemType[] | Re
}

function SearchContextProvider({children}: ChildrenProps) {
const [searchContextData, setSearchContextData] = useState<Pick<SearchContext, 'currentSearchHash' | 'selectedTransactions' | 'shouldTurnOffSelectionMode' | 'selectedReports'>>({
const [searchContextData, setSearchContextData] = useState<
Pick<SearchContext, 'currentSearchHash' | 'selectedTransactions' | 'shouldTurnOffSelectionMode' | 'selectedReports' | 'isAllStatus'>
>({
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,
}));
}, []);

Expand Down
7 changes: 4 additions & 3 deletions src/components/Search/SearchPageHeader/SearchPageHeader.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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);
},
});
}
Expand Down Expand Up @@ -205,7 +205,7 @@ function SearchPageHeader({queryJSON, searchName, searchRouterListVisible, hideS
}))
) as PaymentData[];

payMoneyRequestOnSearch(hash, paymentData, transactionIDList);
payMoneyRequestOnSearch(hash, paymentData, isAllStatus, transactionIDList);
},
});
}
Expand Down Expand Up @@ -319,6 +319,7 @@ function SearchPageHeader({queryJSON, searchName, searchRouterListVisible, hideS
selectedReports,
translate,
hash,
isAllStatus,
lastPaymentMethods,
status,
queryJSON,
Expand Down
5 changes: 3 additions & 2 deletions src/components/Search/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -549,6 +549,7 @@ function Search({queryJSON, onSearchListScroll, isSearchScreenFocused, contentCo
shouldKeepFocusedItemAtTopOfViewableArea={type === CONST.SEARCH.DATA_TYPES.CHAT}
isScreenFocused={isSearchScreenFocused}
initialNumToRender={shouldUseNarrowLayout ? 5 : undefined}
shouldAnimateOnRemove
/>
);
}
Expand Down
3 changes: 2 additions & 1 deletion src/components/Search/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
2 changes: 2 additions & 0 deletions src/components/SelectionList/BaseListItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ function BaseListItem<TItem extends ListItem>({
onFocus = () => {},
hoverStyle,
onLongPressRow,
shouldAnimateOnRemove,
}: BaseListItemProps<TItem>) {
const theme = useTheme();
const styles = useThemeStyles();
Expand Down Expand Up @@ -110,6 +111,7 @@ function BaseListItem<TItem extends ListItem>({
onMouseLeave={handleMouseLeave}
tabIndex={item.tabIndex}
wrapperStyle={pressableWrapperStyle}
shouldAnimateOnRemove={shouldAnimateOnRemove}
>
<View
testID={`${CONST.BASE_LIST_ITEM_TEST_ID}${item.keyForList}`}
Expand Down
2 changes: 2 additions & 0 deletions src/components/SelectionList/BaseSelectionList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,7 @@ function BaseSelectionList<TItem extends ListItem>(
listItemTitleStyles,
initialNumToRender = 12,
listItemTitleContainerStyles,
shouldAnimateOnRemove = false,
isScreenFocused = false,
shouldSubscribeToArrowKeyEvents = true,
}: BaseSelectionListProps<TItem>,
Expand Down Expand Up @@ -576,6 +577,7 @@ function BaseSelectionList<TItem extends ListItem>(
shouldHighlightSelectedItem={shouldHighlightSelectedItem}
singleExecution={singleExecution}
titleContainerStyles={listItemTitleContainerStyles}
shouldAnimateOnRemove={shouldAnimateOnRemove}
/>
</View>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ type BaseSelectionListItemRendererProps<TItem extends ListItem> = Omit<BaseListI
singleExecution: ReturnType<typeof useSingleExecution>['singleExecution'];
titleStyles?: StyleProp<TextStyle>;
titleContainerStyles?: StyleProp<ViewStyle>;
shouldAnimateOnRemove?: boolean;
};

function BaseSelectionListItemRenderer<TItem extends ListItem>({
Expand Down Expand Up @@ -44,6 +45,7 @@ function BaseSelectionListItemRenderer<TItem extends ListItem>({
titleStyles,
singleExecution,
titleContainerStyles,
shouldAnimateOnRemove,
}: BaseSelectionListItemRendererProps<TItem>) {
const handleOnCheckboxPress = () => {
if (isReportListItemType(item)) {
Expand Down Expand Up @@ -94,6 +96,7 @@ function BaseSelectionListItemRenderer<TItem extends ListItem>({
wrapperStyle={wrapperStyle}
titleStyles={titleStyles}
titleContainerStyles={titleContainerStyles}
shouldAnimateOnRemove={shouldAnimateOnRemove}
/>
{item.footerContent && item.footerContent}
</>
Expand Down
7 changes: 5 additions & 2 deletions src/components/SelectionList/Search/ReportListItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -63,14 +63,15 @@ function ReportListItem<TItem extends ListItem>({
onFocus,
onLongPressRow,
shouldSyncFocus,
shouldAnimateOnRemove,
}: ReportListItemProps<TItem>) {
const reportItem = item as unknown as ReportListItemType;

const theme = useTheme();
const styles = useThemeStyles();
const {isLargeScreenWidth} = useResponsiveLayout();
const StyleUtils = useStyleUtils();
const {currentSearchHash} = useSearchContext();
const {currentSearchHash, isAllStatus} = useSearchContext();

const animatedHighlightStyle = useAnimatedHighlightStyle({
borderRadius: variables.componentBorderRadius,
Expand All @@ -95,7 +96,7 @@ function ReportListItem<TItem extends ListItem>({
];

const handleOnButtonPress = () => {
handleActionButtonPress(currentSearchHash, reportItem, () => onSelectRow(item));
handleActionButtonPress(currentSearchHash, reportItem, () => onSelectRow(item), isAllStatus);
};

const openReportInRHP = (transactionItem: TransactionListItemType) => {
Expand Down Expand Up @@ -132,6 +133,7 @@ function ReportListItem<TItem extends ListItem>({
onLongPressRow={onLongPressRow}
shouldSyncFocus={shouldSyncFocus}
isLoading={reportItem.isActionLoading}
shouldAnimateOnRemove={shouldAnimateOnRemove}
/>
);
}
Expand All @@ -155,6 +157,7 @@ function ReportListItem<TItem extends ListItem>({
shouldSyncFocus={shouldSyncFocus}
hoverStyle={item.isSelected && styles.activeComponentBG}
pressableWrapperStyle={[styles.mh5, animatedHighlightStyle]}
shouldAnimateOnRemove={shouldAnimateOnRemove}
>
<View style={styles.flex1}>
{!isLargeScreenWidth && (
Expand Down
Loading