Skip to content

Commit ccf5583

Browse files
authored
Merge pull request #73731 from software-mansion-labs/nav/swrhp
Create super wide RHP modal to display expense reports when accessed via Reports page or report previews
2 parents 9748320 + d9f30dc commit ccf5583

42 files changed

Lines changed: 1089 additions & 252 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

src/ROUTES.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,17 @@ const ROUTES = {
8383
return getUrlWithBackToParam(baseRoute, backTo);
8484
},
8585
},
86+
87+
EXPENSE_REPORT_RHP: {
88+
route: 'e/:reportID',
89+
getRoute: ({reportID, backTo}: {reportID: string; backTo?: string}) => {
90+
const baseRoute = `e/${reportID}` as const;
91+
92+
// eslint-disable-next-line no-restricted-syntax -- Legacy route generation
93+
return getUrlWithBackToParam(baseRoute, backTo);
94+
},
95+
},
96+
8697
SEARCH_REPORT_VERIFY_ACCOUNT: {
8798
route: `search/view/:reportID/${VERIFY_ACCOUNT}`,
8899
getRoute: (reportID: string) => `search/view/${reportID}/${VERIFY_ACCOUNT}` as const,

src/SCREENS.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -244,6 +244,8 @@ const SCREENS = {
244244
TRANSACTION_DUPLICATE: 'TransactionDuplicate',
245245
TRAVEL: 'Travel',
246246
SEARCH_REPORT: 'SearchReport',
247+
SEARCH_REPORT_ACTIONS: 'SearchReportActions',
248+
SEARCH_MONEY_REQUEST_REPORT: 'SearchMoneyRequestReport',
247249
SEARCH_ADVANCED_FILTERS: 'SearchAdvancedFilters',
248250
SEARCH_SAVED_SEARCH: 'SearchSavedSearch',
249251
SETTINGS_CATEGORIES: 'SettingsCategories',
@@ -259,8 +261,10 @@ const SCREENS = {
259261
REPORT_CHANGE_APPROVER: 'Report_Change_Approver',
260262
REPORT_VERIFY_ACCOUNT: 'Report_Verify_Account',
261263
MERGE_TRANSACTION: 'MergeTransaction',
264+
EXPENSE_REPORT: 'ExpenseReport',
262265
DOMAIN: 'Domain',
263266
},
267+
EXPENSE_REPORT_RHP: 'Expense_Report_RHP',
264268
PUBLIC_CONSOLE_DEBUG: 'Console_Debug',
265269
SIGN_IN_WITH_APPLE_DESKTOP: 'AppleSignInDesktop',
266270
SIGN_IN_WITH_GOOGLE_DESKTOP: 'GoogleSignInDesktop',

src/components/MoneyReportHeader.tsx

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ import Log from '@libs/Log';
4242
import {getThreadReportIDsForTransactions, getTotalAmountForIOUReportPreviewButton} from '@libs/MoneyRequestReportUtils';
4343
import Navigation from '@libs/Navigation/Navigation';
4444
import type {PlatformStackRouteProp} from '@libs/Navigation/PlatformStackNavigation/types';
45-
import type {ReportsSplitNavigatorParamList, SearchFullscreenNavigatorParamList, SearchReportParamList} from '@libs/Navigation/types';
45+
import type {ReportsSplitNavigatorParamList, SearchMoneyRequestReportParamList, SearchReportParamList} from '@libs/Navigation/types';
4646
import {buildOptimisticNextStepForPreventSelfApprovalsEnabled, buildOptimisticNextStepForStrictPolicyRuleViolations} from '@libs/NextStepUtils';
4747
import type {KYCFlowEvent, TriggerKYCFlow} from '@libs/PaymentUtils';
4848
import {selectPaymentType} from '@libs/PaymentUtils';
@@ -185,7 +185,7 @@ function MoneyReportHeader({
185185
const shouldDisplayNarrowVersion = shouldUseNarrowLayout || isMediumScreenWidth;
186186
const route = useRoute<
187187
| PlatformStackRouteProp<ReportsSplitNavigatorParamList, typeof SCREENS.REPORT>
188-
| PlatformStackRouteProp<SearchFullscreenNavigatorParamList, typeof SCREENS.SEARCH.MONEY_REQUEST_REPORT>
188+
| PlatformStackRouteProp<SearchMoneyRequestReportParamList, typeof SCREENS.SEARCH.MONEY_REQUEST_REPORT>
189189
| PlatformStackRouteProp<SearchReportParamList, typeof SCREENS.SEARCH.REPORT_RHP>
190190
>();
191191
const {login: currentUserLogin, accountID, email} = useCurrentUserPersonalDetails();
@@ -352,9 +352,9 @@ function MoneyReportHeader({
352352
const {selectedTransactionIDs, removeTransaction, clearSelectedTransactions, currentSearchQueryJSON, currentSearchKey, currentSearchHash} = useSearchContext();
353353
const shouldCalculateTotals = useSearchShouldCalculateTotals(currentSearchKey, currentSearchQueryJSON?.similarSearchHash, true);
354354

355-
const {wideRHPRouteKeys} = useContext(WideRHPContext);
355+
const {wideRHPRouteKeys, superWideRHPRouteKeys} = useContext(WideRHPContext);
356356
const [network] = useOnyx(ONYXKEYS.NETWORK, {canBeMissing: true});
357-
const shouldDisplayNarrowMoreButton = !shouldDisplayNarrowVersion || (wideRHPRouteKeys.length > 0 && !isSmallScreenWidth);
357+
const shouldDisplayNarrowMoreButton = !shouldDisplayNarrowVersion || ((wideRHPRouteKeys.length > 0 || superWideRHPRouteKeys.length > 0) && !isSmallScreenWidth);
358358

359359
const showExportProgressModal = useCallback(() => {
360360
return showConfirmModal({
@@ -1392,7 +1392,7 @@ function MoneyReportHeader({
13921392

13931393
const showNextStepBar = shouldShowNextStep && !!optimisticNextStep?.message?.length;
13941394
const showNextStepSkeleton = shouldShowNextStep && !optimisticNextStep && !!isLoadingInitialReportActions && !isOffline;
1395-
const shouldShowMoreContent = showNextStepBar || showNextStepSkeleton || !!statusBarProps || isReportInSearch;
1395+
const shouldShowMoreContent = showNextStepBar || showNextStepSkeleton || !!statusBarProps || (isReportInSearch && !shouldDisplayNarrowMoreButton);
13961396

13971397
return (
13981398
<View style={[styles.pt0, styles.borderBottom]}>
@@ -1433,6 +1433,12 @@ function MoneyReportHeader({
14331433
/>
14341434
</View>
14351435
)}
1436+
{isReportInSearch && (
1437+
<MoneyRequestReportNavigation
1438+
reportID={moneyRequestReport?.reportID}
1439+
shouldDisplayNarrowVersion={!shouldDisplayNarrowMoreButton}
1440+
/>
1441+
)}
14361442
</View>
14371443
)}
14381444
</HeaderWithBackButton>
@@ -1477,7 +1483,7 @@ function MoneyReportHeader({
14771483
/>
14781484
)}
14791485
</View>
1480-
{isReportInSearch && (
1486+
{isReportInSearch && !shouldDisplayNarrowMoreButton && (
14811487
<MoneyRequestReportNavigation
14821488
reportID={moneyRequestReport?.reportID}
14831489
shouldDisplayNarrowVersion={shouldDisplayNarrowVersion}

src/components/MoneyRequestReportView/MoneyRequestReportActionsList.tsx

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import {isUserValidatedSelector} from '@selectors/Account';
55
import {accountIDSelector} from '@selectors/Session';
66
import {tierNameSelector} from '@selectors/UserWallet';
77
import isEmpty from 'lodash/isEmpty';
8-
import React, {useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState} from 'react';
8+
import React, {useCallback, useContext, useEffect, useLayoutEffect, useMemo, useRef, useState} from 'react';
99
import type {NativeScrollEvent, NativeSyntheticEvent} from 'react-native';
1010
import {DeviceEventEmitter, InteractionManager, View} from 'react-native';
1111
import type {OnyxEntry} from 'react-native-onyx';
@@ -20,6 +20,7 @@ import {PressableWithFeedback} from '@components/Pressable';
2020
import ScrollView from '@components/ScrollView';
2121
import {useSearchContext} from '@components/Search/SearchContext';
2222
import Text from '@components/Text';
23+
import {WideRHPContext} from '@components/WideRHPContextProvider';
2324
import useLoadReportActions from '@hooks/useLoadReportActions';
2425
import useLocalize from '@hooks/useLocalize';
2526
import useMobileSelectionMode from '@hooks/useMobileSelectionMode';
@@ -165,7 +166,10 @@ function MoneyRequestReportActionsList({
165166
const isReportArchived = useReportIsArchived(reportID);
166167
const canPerformWriteAction = canUserPerformWriteAction(report, isReportArchived);
167168

168-
const {shouldUseNarrowLayout} = useResponsiveLayout();
169+
// eslint-disable-next-line rulesdir/prefer-shouldUseNarrowLayout-instead-of-isSmallScreenWidth
170+
const {shouldUseNarrowLayout: shouldUseNarrowLayoutByDefault, isSmallScreenWidth} = useResponsiveLayout();
171+
const {superWideRHPRouteKeys} = useContext(WideRHPContext);
172+
const shouldUseNarrowLayout = shouldUseNarrowLayoutByDefault && (superWideRHPRouteKeys.length === 0 || isSmallScreenWidth);
169173

170174
const [session] = useOnyx(ONYXKEYS.SESSION, {canBeMissing: false});
171175
const [offlineModalVisible, setOfflineModalVisible] = useState(false);

src/components/MoneyRequestReportView/MoneyRequestReportTransactionItem.tsx

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
1-
import React, {useEffect, useMemo, useRef} from 'react';
1+
import React, {useContext, useEffect, useMemo, useRef} from 'react';
22
import type {View} from 'react-native';
33
import {getButtonRole} from '@components/Button/utils';
44
import OfflineWithFeedback from '@components/OfflineWithFeedback';
55
import {PressableWithFeedback} from '@components/Pressable';
66
import type {SearchColumnType, TableColumnSize} from '@components/Search/types';
77
import {getExpenseHeaders} from '@components/SelectionListWithSections/SearchTableHeader';
88
import TransactionItemRow from '@components/TransactionItemRow';
9+
import {WideRHPContext} from '@components/WideRHPContextProvider';
910
import useAnimatedHighlightStyle from '@hooks/useAnimatedHighlightStyle';
1011
import useLocalize from '@hooks/useLocalize';
1112
import useResponsiveLayout from '@hooks/useResponsiveLayout';
@@ -86,7 +87,9 @@ function MoneyRequestReportTransactionItem({
8687
const {translate} = useLocalize();
8788
const styles = useThemeStyles();
8889
// eslint-disable-next-line rulesdir/prefer-shouldUseNarrowLayout-instead-of-isSmallScreenWidth
89-
const {isSmallScreenWidth, isMediumScreenWidth, isLargeScreenWidth, shouldUseNarrowLayout} = useResponsiveLayout();
90+
const {isSmallScreenWidth, isMediumScreenWidth, isLargeScreenWidth, shouldUseNarrowLayout: shouldUseNarrowLayoutByDefault} = useResponsiveLayout();
91+
const {superWideRHPRouteKeys} = useContext(WideRHPContext);
92+
const shouldUseNarrowLayout = shouldUseNarrowLayoutByDefault && (superWideRHPRouteKeys.length === 0 || isSmallScreenWidth);
9093
const theme = useTheme();
9194
const isPendingDelete = isTransactionPendingDelete(transaction);
9295
const pendingAction = getTransactionPendingAction(transaction);

src/components/MoneyRequestReportView/MoneyRequestReportTransactionList.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -161,7 +161,9 @@ function MoneyRequestReportTransactionList({
161161
const StyleUtils = useStyleUtils();
162162
const {translate, localeCompare} = useLocalize();
163163
// eslint-disable-next-line rulesdir/prefer-shouldUseNarrowLayout-instead-of-isSmallScreenWidth
164-
const {shouldUseNarrowLayout, isSmallScreenWidth, isMediumScreenWidth} = useResponsiveLayout();
164+
const {shouldUseNarrowLayout: shouldUseNarrowLayoutByDefault, isSmallScreenWidth, isMediumScreenWidth} = useResponsiveLayout();
165+
const {superWideRHPRouteKeys} = useContext(WideRHPContext);
166+
const shouldUseNarrowLayout = shouldUseNarrowLayoutByDefault && (superWideRHPRouteKeys.length === 0 || isSmallScreenWidth);
165167
const {markReportIDAsExpense} = useContext(WideRHPContext);
166168
const [isModalVisible, setIsModalVisible] = useState(false);
167169
const [selectedTransactionID, setSelectedTransactionID] = useState<string>('');

src/components/MoneyRequestReportView/MoneyRequestReportView.tsx

Lines changed: 74 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,23 @@
11
import {PortalHost} from '@gorhom/portal';
22
import React, {useCallback, useMemo} from 'react';
3-
import {InteractionManager, View} from 'react-native';
3+
// We use Animated for all functionality related to wide RHP to make it easier
4+
// to interact with react-navigation components (e.g., CardContainer, interpolator), which also use Animated.
5+
// eslint-disable-next-line no-restricted-imports
6+
import {Animated, InteractionManager, ScrollView, View} from 'react-native';
47
import type {OnyxEntry} from 'react-native-onyx';
58
import HeaderGap from '@components/HeaderGap';
69
import MoneyReportHeader from '@components/MoneyReportHeader';
710
import MoneyRequestHeader from '@components/MoneyRequestHeader';
811
import OfflineWithFeedback from '@components/OfflineWithFeedback';
12+
import MoneyRequestReceiptView from '@components/ReportActionItem/MoneyRequestReceiptView';
913
import ReportActionsSkeletonView from '@components/ReportActionsSkeletonView';
1014
import ReportHeaderSkeletonView from '@components/ReportHeaderSkeletonView';
1115
import useNetwork from '@hooks/useNetwork';
1216
import useNewTransactions from '@hooks/useNewTransactions';
1317
import useOnyx from '@hooks/useOnyx';
1418
import usePaginatedReportActions from '@hooks/usePaginatedReportActions';
1519
import useParentReportAction from '@hooks/useParentReportAction';
20+
import useResponsiveLayout from '@hooks/useResponsiveLayout';
1621
import useThemeStyles from '@hooks/useThemeStyles';
1722
import useTransactionsAndViolationsForReport from '@hooks/useTransactionsAndViolationsForReport';
1823
import {removeFailedReport} from '@libs/actions/Report';
@@ -56,6 +61,16 @@ function goBackFromSearchMoneyRequest() {
5661
const rootState = navigationRef.getRootState();
5762
const lastRoute = rootState.routes.at(-1);
5863

64+
if (!lastRoute) {
65+
Log.hmmm('[goBackFromSearchMoneyRequest()] No last route found in root state.');
66+
return;
67+
}
68+
69+
if (lastRoute?.name === NAVIGATORS.RIGHT_MODAL_NAVIGATOR) {
70+
Navigation.goBack();
71+
return;
72+
}
73+
5974
if (lastRoute?.name !== NAVIGATORS.SEARCH_FULLSCREEN_NAVIGATOR) {
6075
Log.hmmm('[goBackFromSearchMoneyRequest()] goBackFromSearchMoneyRequest was called from a different navigator than SearchFullscreenNavigator.');
6176
return;
@@ -84,6 +99,10 @@ function MoneyRequestReportView({report, policy, reportMetadata, shouldDisplayRe
8499
const styles = useThemeStyles();
85100
const {isOffline} = useNetwork();
86101

102+
// eslint-disable-next-line rulesdir/prefer-shouldUseNarrowLayout-instead-of-isSmallScreenWidth
103+
const {isSmallScreenWidth} = useResponsiveLayout();
104+
105+
const [allReports] = useOnyx(ONYXKEYS.COLLECTION.REPORT, {canBeMissing: false});
87106
const reportID = report?.reportID;
88107
const [isLoadingApp] = useOnyx(ONYXKEYS.IS_LOADING_APP, {canBeMissing: true});
89108
const [isComposerFullSize = false] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT_IS_COMPOSER_FULL_SIZE}${reportID}`, {canBeMissing: true});
@@ -129,6 +148,9 @@ function MoneyRequestReportView({report, policy, reportMetadata, shouldDisplayRe
129148
const isEmptyTransactionReport = visibleTransactions && visibleTransactions.length === 0 && transactionThreadReportID === undefined;
130149
const shouldDisplayMoneyRequestActionsList = !!isEmptyTransactionReport || shouldDisplayReportTableView(report, visibleTransactions ?? []);
131150

151+
const [transactionThreadReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${transactionThreadReportID}`, {canBeMissing: true});
152+
const shouldShowWideRHPReceipt = visibleTransactions.length === 1 && !isSmallScreenWidth && !!transactionThreadReport;
153+
132154
const reportHeaderView = useMemo(
133155
() =>
134156
isTransactionThreadView ? (
@@ -211,47 +233,61 @@ function MoneyRequestReportView({report, policy, reportMetadata, shouldDisplayRe
211233
>
212234
<HeaderGap />
213235
{reportHeaderView}
214-
<View style={[styles.overflowHidden, styles.justifyContentEnd, styles.flex1]}>
215-
{shouldDisplayMoneyRequestActionsList ? (
216-
<MoneyRequestReportActionsList
217-
report={report}
218-
policy={policy}
219-
transactions={visibleTransactions}
220-
hasPendingDeletionTransaction={hasPendingDeletionTransaction}
221-
newTransactions={newTransactions}
222-
reportActions={reportActions}
223-
violations={allReportViolations}
224-
hasOlderActions={hasOlderActions}
225-
hasNewerActions={hasNewerActions}
226-
showReportActionsLoadingState={isLoadingInitialReportActions && !reportMetadata?.hasOnceLoadedReportActions}
227-
/>
228-
) : (
229-
<ReportActionsView
230-
report={report}
231-
reportActions={reportActions}
232-
isLoadingInitialReportActions={reportMetadata?.isLoadingInitialReportActions}
233-
hasNewerActions={hasNewerActions}
234-
hasOlderActions={hasOlderActions}
235-
parentReportAction={parentReportAction}
236-
transactionThreadReportID={transactionThreadReportID}
237-
/>
236+
<View style={[styles.flex1, styles.flexRow]}>
237+
{shouldShowWideRHPReceipt && (
238+
<Animated.View style={styles.wideRHPMoneyRequestReceiptViewContainer}>
239+
<ScrollView contentContainerStyle={styles.wideRHPMoneyRequestReceiptViewScrollViewContainer}>
240+
<MoneyRequestReceiptView
241+
allReports={allReports}
242+
report={transactionThreadReport}
243+
fillSpace
244+
isDisplayedInWideRHP
245+
/>
246+
</ScrollView>
247+
</Animated.View>
238248
)}
239-
{shouldDisplayReportFooter ? (
240-
<>
241-
<ReportFooter
249+
<View style={[[styles.flex1, styles.justifyContentEnd, styles.overflowHidden]]}>
250+
{shouldDisplayMoneyRequestActionsList ? (
251+
<MoneyRequestReportActionsList
242252
report={report}
243-
reportMetadata={reportMetadata}
244253
policy={policy}
245-
pendingAction={reportPendingAction}
246-
isComposerFullSize={!!isComposerFullSize}
247-
lastReportAction={lastReportAction}
248-
reportTransactions={transactions}
249-
// If the report is from the 'Send Money' flow, we add the comment to the `iou` report because for these we don't combine reportActions even if there is a single transaction (they always have a single transaction)
250-
transactionThreadReportID={isSentMoneyReport ? undefined : transactionThreadReportID}
254+
transactions={visibleTransactions}
255+
hasPendingDeletionTransaction={hasPendingDeletionTransaction}
256+
newTransactions={newTransactions}
257+
reportActions={reportActions}
258+
violations={allReportViolations}
259+
hasOlderActions={hasOlderActions}
260+
hasNewerActions={hasNewerActions}
261+
showReportActionsLoadingState={isLoadingInitialReportActions && !reportMetadata?.hasOnceLoadedReportActions}
262+
/>
263+
) : (
264+
<ReportActionsView
265+
report={report}
266+
reportActions={reportActions}
267+
isLoadingInitialReportActions={reportMetadata?.isLoadingInitialReportActions}
268+
hasNewerActions={hasNewerActions}
269+
hasOlderActions={hasOlderActions}
270+
parentReportAction={parentReportAction}
271+
transactionThreadReportID={transactionThreadReportID}
251272
/>
252-
<PortalHost name="suggestions" />
253-
</>
254-
) : null}
273+
)}
274+
{shouldDisplayReportFooter ? (
275+
<>
276+
<ReportFooter
277+
report={report}
278+
reportMetadata={reportMetadata}
279+
policy={policy}
280+
pendingAction={reportPendingAction}
281+
isComposerFullSize={!!isComposerFullSize}
282+
lastReportAction={lastReportAction}
283+
reportTransactions={transactions}
284+
// If the report is from the 'Send Money' flow, we add the comment to the `iou` report because for these we don't combine reportActions even if there is a single transaction (they always have a single transaction)
285+
transactionThreadReportID={isSentMoneyReport ? undefined : transactionThreadReportID}
286+
/>
287+
<PortalHost name="suggestions" />
288+
</>
289+
) : null}
290+
</View>
255291
</View>
256292
</OfflineWithFeedback>
257293
</View>

0 commit comments

Comments
 (0)