Skip to content

Commit 6663f9a

Browse files
committed
Merge branch 'perf/split-report-metadata' of https://github.com/callstack-internal/Expensify-App into perf/split-report-metadata
2 parents 96e214d + c65fe51 commit 6663f9a

4 files changed

Lines changed: 104 additions & 25 deletions

File tree

src/components/MoneyRequestReportView/MoneyRequestReportNavigation.tsx

Lines changed: 77 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,69 @@
11
import React, {useEffect} from 'react';
22
import {View} from 'react-native';
3+
import type {OnyxEntry} from 'react-native-onyx';
34
import PrevNextButtons from '@components/PrevNextButtons';
45
import Text from '@components/Text';
6+
import useOnyx from '@hooks/useOnyx';
57
import useSearchSections from '@hooks/useSearchSections';
68
import useThemeStyles from '@hooks/useThemeStyles';
79
import Navigation from '@navigation/Navigation';
810
import {saveLastSearchParams} from '@userActions/ReportNavigation';
911
import {search} from '@userActions/Search';
1012
import CONST from '@src/CONST';
13+
import ONYXKEYS from '@src/ONYXKEYS';
14+
import type {SearchResults} from '@src/types/onyx';
15+
import type LastSearchParams from '@src/types/onyx/ReportNavigation';
1116

1217
type MoneyRequestReportNavigationProps = {
1318
reportID?: string;
1419
shouldDisplayNarrowVersion: boolean;
1520
};
1621

17-
function MoneyRequestReportNavigation({reportID, shouldDisplayNarrowVersion}: MoneyRequestReportNavigationProps) {
22+
type SnapshotGuard = {
23+
hasMultiple: boolean;
24+
includesReport: boolean;
25+
};
26+
27+
const EMPTY_GUARD: SnapshotGuard = {hasMultiple: false, includesReport: false};
28+
29+
const selectIsExpenseReportSearch = (lastSearchQuery: OnyxEntry<LastSearchParams>): boolean => lastSearchQuery?.queryJSON?.type === CONST.SEARCH.DATA_TYPES.EXPENSE_REPORT;
30+
31+
const selectQueryHash = (lastSearchQuery: OnyxEntry<LastSearchParams>): number | undefined => lastSearchQuery?.queryJSON?.hash;
32+
33+
const buildSnapshotGuardSelector =
34+
(reportID: string | undefined) =>
35+
(snapshot: OnyxEntry<SearchResults>): SnapshotGuard => {
36+
const data = snapshot?.data;
37+
if (!data || !reportID) {
38+
return EMPTY_GUARD;
39+
}
40+
const prefix = ONYXKEYS.COLLECTION.REPORT;
41+
let count = 0;
42+
let includesReport = false;
43+
for (const key of Object.keys(data)) {
44+
if (!key.startsWith(prefix)) {
45+
continue;
46+
}
47+
count++;
48+
if (!includesReport && key.slice(prefix.length) === reportID) {
49+
includesReport = true;
50+
}
51+
if (count > 1 && includesReport) {
52+
break;
53+
}
54+
}
55+
return {hasMultiple: count > 1, includesReport};
56+
};
57+
58+
function MoneyRequestReportNavigationInner({reportID, shouldDisplayNarrowVersion}: MoneyRequestReportNavigationProps) {
1859
const {allReports, isSearchLoading, lastSearchQuery} = useSearchSections();
60+
const styles = useThemeStyles();
1961

20-
const type = lastSearchQuery?.queryJSON?.type;
2162
const currentIndex = allReports.indexOf(reportID);
2263
const allReportsCount = lastSearchQuery?.previousLengthOfResults ?? 0;
23-
2464
const hideNextButton = !lastSearchQuery?.hasMoreResults && currentIndex === allReports.length - 1;
2565
const hidePrevButton = currentIndex === 0;
26-
const styles = useThemeStyles();
27-
const isExpenseReportSearch = type === CONST.SEARCH.DATA_TYPES.EXPENSE_REPORT;
28-
const shouldDisplayNavigationArrows = isExpenseReportSearch && allReports && allReports.length > 1 && currentIndex !== -1 && !!lastSearchQuery?.queryJSON;
66+
const shouldDisplayNavigationArrows = allReports.length > 1 && currentIndex !== -1 && !!lastSearchQuery?.queryJSON;
2967

3068
useEffect(() => {
3169
if (!lastSearchQuery?.queryJSON) {
@@ -100,18 +138,40 @@ function MoneyRequestReportNavigation({reportID, shouldDisplayNarrowVersion}: Mo
100138
goToReportId(allReports.at(prevIndex));
101139
};
102140

141+
if (!shouldDisplayNavigationArrows) {
142+
return null;
143+
}
144+
145+
return (
146+
<View style={[styles.flexRow, styles.alignItemsCenter, styles.gap2]}>
147+
{!shouldDisplayNarrowVersion && <Text style={styles.mutedTextLabel}>{`${currentIndex + 1} of ${allReportsCount}`}</Text>}
148+
<PrevNextButtons
149+
isPrevButtonDisabled={hidePrevButton}
150+
isNextButtonDisabled={hideNextButton}
151+
onNext={goToNextReport}
152+
onPrevious={goToPrevReport}
153+
/>
154+
</View>
155+
);
156+
}
157+
158+
function MoneyRequestReportNavigation({reportID, shouldDisplayNarrowVersion}: MoneyRequestReportNavigationProps) {
159+
const [isExpenseReportSearch] = useOnyx(ONYXKEYS.REPORT_NAVIGATION_LAST_SEARCH_QUERY, {selector: selectIsExpenseReportSearch});
160+
const [hash] = useOnyx(ONYXKEYS.REPORT_NAVIGATION_LAST_SEARCH_QUERY, {selector: selectQueryHash});
161+
const snapshotGuardSelector = buildSnapshotGuardSelector(reportID);
162+
const [snapshotGuard = EMPTY_GUARD] = useOnyx(`${ONYXKEYS.COLLECTION.SNAPSHOT}${hash}`, {selector: snapshotGuardSelector});
163+
164+
const shouldMount = isExpenseReportSearch && snapshotGuard.hasMultiple && snapshotGuard.includesReport;
165+
166+
if (!shouldMount) {
167+
return null;
168+
}
169+
103170
return (
104-
shouldDisplayNavigationArrows && (
105-
<View style={[styles.flexRow, styles.alignItemsCenter, styles.gap2]}>
106-
{!shouldDisplayNarrowVersion && <Text style={styles.mutedTextLabel}>{`${currentIndex + 1} of ${allReportsCount}`}</Text>}
107-
<PrevNextButtons
108-
isPrevButtonDisabled={hidePrevButton}
109-
isNextButtonDisabled={hideNextButton}
110-
onNext={goToNextReport}
111-
onPrevious={goToPrevReport}
112-
/>
113-
</View>
114-
)
171+
<MoneyRequestReportNavigationInner
172+
reportID={reportID}
173+
shouldDisplayNarrowVersion={shouldDisplayNarrowVersion}
174+
/>
115175
);
116176
}
117177

src/libs/Reauthentication.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,13 @@ function reauthenticate(command = ''): Promise<boolean> {
130130
const partnerName = shouldUseNewPartnerName ? CONFIG.EXPENSIFY.PARTNER_NAME : CONFIG.EXPENSIFY.LEGACY_PARTNER_NAME;
131131
const partnerPassword = shouldUseNewPartnerName ? CONFIG.EXPENSIFY.PARTNER_PASSWORD : CONFIG.EXPENSIFY.LEGACY_PARTNER_PASSWORD;
132132

133+
if (account?.isSAMLRequired) {
134+
Log.info(`[Reauthenticate] Redirecting to Sign In because SAML is required`);
135+
setIsAuthenticating(false);
136+
redirectToSignIn(undefined, true);
137+
return false;
138+
}
139+
133140
// Prevent reauthentication if credentials are missing (e.g. after sign out)
134141
if (!credentials?.autoGeneratedLogin || !credentials?.autoGeneratedPassword) {
135142
Log.info('[Reauthenticate] No credentials available, redirecting to sign in');

src/libs/actions/SignInRedirect.ts

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import {clearAllPolicies} from './Policy/Policy';
1212
let currentShouldForceOffline: boolean | undefined;
1313
let currentIsUsingImportedState: boolean | undefined;
1414
let currentSessionAuthToken: string | undefined;
15+
let currentSessionEmail: string | undefined;
1516
let currentCredentialsValidateCode: string | undefined;
1617

1718
Onyx.connectWithoutView({
@@ -32,6 +33,7 @@ Onyx.connectWithoutView({
3233
key: ONYXKEYS.SESSION,
3334
callback: (session) => {
3435
currentSessionAuthToken = session?.authToken;
36+
currentSessionEmail = session?.email;
3537
},
3638
});
3739

@@ -42,7 +44,7 @@ Onyx.connectWithoutView({
4244
},
4345
});
4446

45-
function clearStorageAndRedirect(errorMessage?: string): Promise<void> {
47+
function clearStorageAndRedirect(errorMessage?: string, isSAMLReauthentication?: boolean): Promise<void> {
4648
// Under certain conditions, there are key-values we'd like to keep in storage even when a user is logged out.
4749
// We pass these into the clear() method in order to avoid having to reset them on a delayed tick and getting
4850
// flashes of unwanted default state.
@@ -79,6 +81,14 @@ function clearStorageAndRedirect(errorMessage?: string): Promise<void> {
7981
keysToPreserve.push(ONYXKEYS.ACCOUNT);
8082
}
8183

84+
// Mark the account as loading and set the login in credentials to trigger the `SAML_SIGN_IN` transition
85+
if (isSAMLReauthentication) {
86+
keysToPreserve.push(ONYXKEYS.CREDENTIALS);
87+
keysToPreserve.push(ONYXKEYS.ACCOUNT);
88+
Onyx.merge(ONYXKEYS.CREDENTIALS, {login: currentSessionEmail, autoGeneratedLogin: null, autoGeneratedPassword: null});
89+
Onyx.merge(ONYXKEYS.ACCOUNT, {isLoading: true});
90+
}
91+
8292
return Onyx.clear(keysToPreserve).then(() => {
8393
if (CONFIG.IS_HYBRID_APP) {
8494
resetSignInFlow();
@@ -108,10 +118,11 @@ function clearStorageAndRedirect(errorMessage?: string): Promise<void> {
108118
*
109119
* Normally this method would live in Session.js, but that would cause a circular dependency with Network.js.
110120
*
111-
* @param [errorMessage] error message to be displayed on the sign in page
121+
* @param errorMessage Error message to be displayed on the sign in page
122+
* @param isSAMLReauthentication Whether the redirection was triggered by reauthentication for SAML required account
112123
*/
113-
function redirectToSignIn(errorMessage?: string): Promise<void> {
114-
return clearStorageAndRedirect(errorMessage).then(() => {
124+
function redirectToSignIn(errorMessage?: string, isSAMLReauthentication?: boolean): Promise<void> {
125+
return clearStorageAndRedirect(errorMessage, isSAMLReauthentication).then(() => {
115126
clearSessionStorage();
116127
});
117128
}

src/pages/iou/request/step/confirmation/useExpenseSubmission.ts

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -218,11 +218,12 @@ function useExpenseSubmission(params: UseExpenseSubmissionParams) {
218218
const parentReportAction = useParentReportAction(viewTourTaskReport);
219219

220220
// Derived values from transaction
221+
const isTrackExpense = iouType === CONST.IOU.TYPE.TRACK;
221222
const isGPSDistanceRequest = isGPSDistanceRequestTransactionUtils(transaction);
222223
const customUnitRateID = getRateID(transaction) ?? '';
223224
const transactionDistance = isManualDistanceRequest || isOdometerDistanceRequest || isGPSDistanceRequest ? (transaction?.comment?.customUnit?.quantity ?? undefined) : undefined;
224225
const defaultTaxCode = getDefaultTaxCode(policy, transaction);
225-
const transactionTaxCode = isTaxTrackingEnabled(isPolicyExpenseChat || isUnreported, policy, isDistanceRequest, isPerDiemRequest, isTimeRequest)
226+
const transactionTaxCode = isTaxTrackingEnabled(isPolicyExpenseChat || isUnreported || isTrackExpense, policy, isDistanceRequest, isPerDiemRequest, isTimeRequest)
226227
? ((transaction?.taxCode ? transaction?.taxCode : defaultTaxCode) ?? '')
227228
: '';
228229
const transactionTaxAmount = transaction?.taxAmount ?? 0;
@@ -365,7 +366,7 @@ function useExpenseSubmission(params: UseExpenseSubmissionParams) {
365366
if (!participant || isEmptyObject(transaction.comment) || isEmptyObject(transaction.comment.customUnit)) {
366367
return;
367368
}
368-
if (iouType === CONST.IOU.TYPE.TRACK) {
369+
if (isTrackExpense) {
369370
submitPerDiemExpenseForSelfDM({
370371
selfDMReport,
371372
policy,
@@ -610,7 +611,7 @@ function useExpenseSubmission(params: UseExpenseSubmissionParams) {
610611

611612
// Telemetry spans (SPAN_SUBMIT_EXPENSE, SPAN_SUBMIT_TO_DESTINATION_VISIBLE)
612613
// are started by SubmitExpenseOrchestrator before calling createTransaction.
613-
if (iouType !== CONST.IOU.TYPE.TRACK && isDistanceRequest && !isMovingTransactionFromTrackExpense && !isUnreported) {
614+
if (!isTrackExpense && isDistanceRequest && !isMovingTransactionFromTrackExpense && !isUnreported) {
614615
createDistanceRequest(iouType === CONST.IOU.TYPE.SPLIT ? splitParticipants : selectedParticipantsArg, trimmedComment, shouldHandleNavigation);
615616
markSubmitExpenseEnd();
616617
return;
@@ -756,7 +757,7 @@ function useExpenseSubmission(params: UseExpenseSubmissionParams) {
756757
return;
757758
}
758759

759-
if (!isPerDiemRequest && (iouType === CONST.IOU.TYPE.TRACK || isCategorizingTrackExpense || isSharingTrackExpense)) {
760+
if (!isPerDiemRequest && (isTrackExpense || isCategorizingTrackExpense || isSharingTrackExpense)) {
760761
if (Object.values(receiptFiles).filter((receipt) => !!receipt).length && transaction) {
761762
// If the transaction amount is zero, then the money is being requested through the "Scan" flow and the GPS coordinates need to be included.
762763
if (transaction.amount === 0 && !isSharingTrackExpense && !isCategorizingTrackExpense && locationPermissionGranted) {

0 commit comments

Comments
 (0)