Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

perf: improve native apps startup #44068

Merged
Merged
Show file tree
Hide file tree
Changes from 40 commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
cce6158
perf: use single report connection
hurali97 Jun 20, 2024
98bc82c
perf: use single policy connection
hurali97 Jun 20, 2024
36cb171
perf: reduce re-renders by memoizing policyMemberAccountIDs and expli…
hurali97 Jun 20, 2024
1761567
perf: use policies from useReportIDs
hurali97 Jun 20, 2024
5ec39eb
perf: avoid translating same value repeatedly
hurali97 Jun 20, 2024
ada9673
fix: types
hurali97 Jun 20, 2024
87bbd8b
fix: update imports
hurali97 Jun 20, 2024
839d7a2
refactor: remove unrelated changes
hurali97 Jun 20, 2024
1ebfe98
fix: linting
hurali97 Jun 20, 2024
76ba035
fix: prettier
hurali97 Jun 20, 2024
11bef46
fix: prettier
hurali97 Jun 20, 2024
c22fcec
fix: unit tests
hurali97 Jun 21, 2024
6ff507c
Merge branch 'main' of https://github.com/callstack-internal/Expensif…
hurali97 Jun 21, 2024
2b8154c
fix: typecheck
hurali97 Jun 21, 2024
cf111d9
fix: unit test
hurali97 Jun 21, 2024
aa41959
fix: failing reassure test
hurali97 Jun 21, 2024
ba9b00c
fix: add support for testing dynamic imports
hurali97 Jun 21, 2024
58c81a0
refactor: update imports
hurali97 Jun 24, 2024
c7b03ab
perf: use single connection for report
hurali97 Jun 24, 2024
10644c4
feat: add support for reports name map
hurali97 Jun 24, 2024
36e7688
feat: add jsdocs
hurali97 Jun 24, 2024
6137686
fix: typecheck
hurali97 Jun 24, 2024
c0ba178
Merge branch 'main' of https://github.com/callstack-internal/Expensif…
hurali97 Jun 24, 2024
942e38c
fix: use getAllReports
hurali97 Jun 24, 2024
af352b1
Merge branch 'main' of https://github.com/callstack-internal/Expensif…
hurali97 Jun 25, 2024
d26fa48
Merge branch 'main' of https://github.com/callstack-internal/Expensif…
hurali97 Jun 25, 2024
6441df0
refactor: remove unused code
hurali97 Jun 25, 2024
39ad610
Merge branch 'main' of https://github.com/callstack-internal/Expensif…
hurali97 Jun 25, 2024
06fee84
refactor: use shorthand condition
hurali97 Jun 25, 2024
dfcc973
Merge branch 'main' of https://github.com/callstack-internal/Expensif…
hurali97 Jun 25, 2024
a469535
fix: prettier
hurali97 Jun 26, 2024
8dde364
Merge branch 'main' of https://github.com/callstack-internal/Expensif…
hurali97 Jun 26, 2024
9180ec0
fix: use reportID with collection identifier
hurali97 Jun 26, 2024
b250882
Merge branch 'main' of https://github.com/callstack-internal/Expensif…
hurali97 Jun 27, 2024
212287b
Merge branch 'main' of https://github.com/callstack-internal/Expensif…
hurali97 Jun 28, 2024
5760ee8
Merge branch 'main' of https://github.com/callstack-internal/Expensif…
hurali97 Jul 2, 2024
611e585
fix: use correct reportID
hurali97 Jul 5, 2024
461762b
Merge branch 'main' of https://github.com/callstack-internal/Expensif…
hurali97 Jul 5, 2024
8a17369
fix: apply prettier
hurali97 Jul 5, 2024
4bcbb19
Merge branch 'main' of https://github.com/callstack-internal/Expensif…
hurali97 Jul 8, 2024
bdba40b
fix: use correct reportID
hurali97 Jul 8, 2024
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
4 changes: 2 additions & 2 deletions .github/workflows/reassurePerformanceTests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -48,12 +48,12 @@ jobs:
git fetch origin "$BASELINE_BRANCH" --no-tags --depth=1
git switch "$BASELINE_BRANCH"
npm install --force
npx reassure --baseline
NODE_OPTIONS=--experimental-vm-modules npx reassure --baseline
git switch --force --detach -
git merge --no-commit --allow-unrelated-histories "$BASELINE_BRANCH" -X ours
git checkout --ours .
npm install --force
npx reassure --branch
NODE_OPTIONS=--experimental-vm-modules npx reassure --branch

- name: Validate output.json
id: validateReassureOutput
Expand Down
56 changes: 9 additions & 47 deletions src/hooks/useReportIDs.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import type {OnyxEntry} from 'react-native-onyx';
import {useOnyx} from 'react-native-onyx';
import {getPolicyEmployeeListByIdWithoutCurrentUser} from '@libs/PolicyUtils';
import * as ReportActionsUtils from '@libs/ReportActionsUtils';
import * as ReportUtils from '@libs/ReportUtils';
import SidebarUtils from '@libs/SidebarUtils';
import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
Expand All @@ -13,7 +12,6 @@ import useActiveWorkspace from './useActiveWorkspace';
import useCurrentReportID from './useCurrentReportID';
import useCurrentUserPersonalDetails from './useCurrentUserPersonalDetails';

type ChatReportSelector = OnyxTypes.Report & {isUnreadWithMention: boolean};
type PolicySelector = Pick<OnyxTypes.Policy, 'type' | 'name' | 'avatarURL' | 'employeeList'>;
type ReportActionsSelector = Array<Pick<OnyxTypes.ReportAction, 'reportActionID' | 'actionName' | 'errors' | 'message' | 'originalMessage'>>;

Expand All @@ -25,56 +23,19 @@ type ReportIDsContextProviderProps = {
type ReportIDsContextValue = {
orderedReportIDs: string[];
currentReportID: string;
policyMemberAccountIDs: number[];
};

const ReportIDsContext = createContext<ReportIDsContextValue>({
orderedReportIDs: [],
currentReportID: '',
policyMemberAccountIDs: [],
});

/**
* This function (and the few below it), narrow down the data from Onyx to just the properties that we want to trigger a re-render of the component. This helps minimize re-rendering
* and makes the entire component more performant because it's not re-rendering when a bunch of properties change which aren't ever used in the UI.
*/
const chatReportSelector = (report: OnyxEntry<OnyxTypes.Report>): ChatReportSelector =>
(report && {
reportID: report.reportID,
participants: report.participants,
isPinned: report.isPinned,
isHidden: report.isHidden,
notificationPreference: report.notificationPreference,
errorFields: {
addWorkspaceRoom: report.errorFields?.addWorkspaceRoom,
},
lastMessageText: report.lastMessageText,
lastVisibleActionCreated: report.lastVisibleActionCreated,
iouReportID: report.iouReportID,
total: report.total,
nonReimbursableTotal: report.nonReimbursableTotal,
hasOutstandingChildRequest: report.hasOutstandingChildRequest,
isWaitingOnBankAccount: report.isWaitingOnBankAccount,
statusNum: report.statusNum,
stateNum: report.stateNum,
chatType: report.chatType,
type: report.type,
policyID: report.policyID,
visibility: report.visibility,
lastReadTime: report.lastReadTime,
// Needed for name sorting:
reportName: report.reportName,
policyName: report.policyName,
oldPolicyName: report.oldPolicyName,
// Other less obvious properites considered for sorting:
ownerAccountID: report.ownerAccountID,
currency: report.currency,
managerID: report.managerID,
// Other important less obivous properties for filtering:
parentReportActionID: report.parentReportActionID,
parentReportID: report.parentReportID,
isDeletedParentAction: report.isDeletedParentAction,
isUnreadWithMention: ReportUtils.isUnreadWithMention(report),
}) as ChatReportSelector;

const reportActionsSelector = (reportActions: OnyxEntry<OnyxTypes.ReportActions>): ReportActionsSelector =>
(reportActions &&
Object.values(reportActions)
Expand Down Expand Up @@ -118,7 +79,7 @@ function ReportIDsContextProvider({
currentReportIDForTests,
}: ReportIDsContextProviderProps) {
const [priorityMode] = useOnyx(ONYXKEYS.NVP_PRIORITY_MODE, {initialValue: CONST.PRIORITY_MODE.DEFAULT});
const [chatReports] = useOnyx(ONYXKEYS.COLLECTION.REPORT, {selector: chatReportSelector});
const [chatReports] = useOnyx(ONYXKEYS.COLLECTION.REPORT);
hurali97 marked this conversation as resolved.
Show resolved Hide resolved
const [policies] = useOnyx(ONYXKEYS.COLLECTION.POLICY, {selector: policySelector});
const [allReportActions] = useOnyx(ONYXKEYS.COLLECTION.REPORT_ACTIONS, {selector: reportActionsSelector});
const [transactionViolations] = useOnyx(ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS);
Expand All @@ -130,7 +91,7 @@ function ReportIDsContextProvider({
const derivedCurrentReportID = currentReportIDForTests ?? currentReportIDValue?.currentReportID;
const {activeWorkspaceID} = useActiveWorkspace();

const policyMemberAccountIDs = getPolicyEmployeeListByIdWithoutCurrentUser(policies, activeWorkspaceID, accountID);
const policyMemberAccountIDs = useMemo(() => getPolicyEmployeeListByIdWithoutCurrentUser(policies, activeWorkspaceID, accountID), [policies, activeWorkspaceID, accountID]);
mountiny marked this conversation as resolved.
Show resolved Hide resolved

const getOrderedReportIDs = useCallback(
(currentReportID?: string) =>
Expand All @@ -157,15 +118,16 @@ function ReportIDsContextProvider({
// we first generate the list as if there was no current report, then we check if
// the current report is missing from the list, which should very rarely happen. In this
// case we re-generate the list a 2nd time with the current report included.
if (derivedCurrentReportID && !orderedReportIDs.includes(derivedCurrentReportID)) {
return {orderedReportIDs: getOrderedReportIDs(derivedCurrentReportID), currentReportID: derivedCurrentReportID ?? '-1'};
if (derivedCurrentReportID && derivedCurrentReportID !== '-1' && orderedReportIDs.indexOf(derivedCurrentReportID) === -1) {
return {orderedReportIDs: getOrderedReportIDs(derivedCurrentReportID), currentReportID: derivedCurrentReportID ?? '-1', policyMemberAccountIDs};
}

return {
orderedReportIDs,
currentReportID: derivedCurrentReportID ?? '-1',
policyMemberAccountIDs,
};
}, [getOrderedReportIDs, orderedReportIDs, derivedCurrentReportID]);
}, [getOrderedReportIDs, orderedReportIDs, derivedCurrentReportID, policyMemberAccountIDs]);

return <ReportIDsContext.Provider value={contextValue}>{children}</ReportIDsContext.Provider>;
}
Expand All @@ -175,4 +137,4 @@ function useReportIDs() {
}

export {ReportIDsContext, ReportIDsContextProvider, policySelector, useReportIDs};
export type {ChatReportSelector, PolicySelector, ReportActionsSelector};
export type {PolicySelector, ReportActionsSelector};
13 changes: 4 additions & 9 deletions src/libs/DistanceRequestUtils.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,16 @@
import type {OnyxCollection, OnyxEntry} from 'react-native-onyx';
import type {OnyxEntry} from 'react-native-onyx';
import Onyx from 'react-native-onyx';
import type {LocaleContextProps} from '@components/LocaleContextProvider';
import type {RateAndUnit} from '@src/CONST';
import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
import type {LastSelectedDistanceRates, OnyxInputOrEntry, Report} from '@src/types/onyx';
import type {LastSelectedDistanceRates, OnyxInputOrEntry} from '@src/types/onyx';
import type {Unit} from '@src/types/onyx/Policy';
import type Policy from '@src/types/onyx/Policy';
import {isEmptyObject} from '@src/types/utils/EmptyObject';
import * as CurrencyUtils from './CurrencyUtils';
import * as PolicyUtils from './PolicyUtils';
import * as ReportConnection from './ReportConnection';
import * as ReportUtils from './ReportUtils';

type MileageRate = {
Expand All @@ -28,13 +29,6 @@ Onyx.connect({
},
});

let allReports: OnyxCollection<Report>;
Onyx.connect({
key: ONYXKEYS.COLLECTION.REPORT,
waitForCollectionCallback: true,
callback: (value) => (allReports = value),
});

const METERS_TO_KM = 0.001; // 1 kilometer is 1000 meters
const METERS_TO_MILES = 0.000621371; // There are approximately 0.000621371 miles in a meter

Expand Down Expand Up @@ -251,6 +245,7 @@ function convertToDistanceInMeters(distance: number, unit: Unit): number {
* Returns custom unit rate ID for the distance transaction
*/
function getCustomUnitRateID(reportID: string) {
const allReports = ReportConnection.getAllReports();
const report = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${reportID}`];
const parentReport = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${report?.parentReportID}`];
const policy = PolicyUtils.getPolicy(report?.policyID ?? parentReport?.policyID ?? '-1');
Expand Down
12 changes: 3 additions & 9 deletions src/libs/ModifiedExpenseMessage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,13 @@ import Onyx from 'react-native-onyx';
import type {OnyxCollection, OnyxEntry} from 'react-native-onyx';
import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
import type {PolicyTagList, Report, ReportAction} from '@src/types/onyx';
import type {PolicyTagList, ReportAction} from '@src/types/onyx';
import * as CurrencyUtils from './CurrencyUtils';
import DateUtils from './DateUtils';
import * as Localize from './Localize';
import * as PolicyUtils from './PolicyUtils';
import * as ReportActionsUtils from './ReportActionsUtils';
import * as ReportConnection from './ReportConnection';
import * as TransactionUtils from './TransactionUtils';

let allPolicyTags: OnyxCollection<PolicyTagList> = {};
Expand All @@ -23,13 +24,6 @@ Onyx.connect({
},
});

let allReports: OnyxCollection<Report>;
Onyx.connect({
key: ONYXKEYS.COLLECTION.REPORT,
waitForCollectionCallback: true,
callback: (value) => (allReports = value),
});

/**
* Builds the partial message fragment for a modified field on the expense.
*/
Expand Down Expand Up @@ -116,7 +110,7 @@ function getForReportAction(reportID: string | undefined, reportAction: OnyxEntr
return '';
}
const reportActionOriginalMessage = ReportActionsUtils.getOriginalMessage(reportAction);
const policyID = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${reportID}`]?.policyID ?? '-1';
const policyID = ReportConnection.getAllReports()?.[`${ONYXKEYS.COLLECTION.REPORT}${reportID}`]?.policyID ?? '-1';

const removalFragments: string[] = [];
const setFragments: string[] = [];
Expand Down
13 changes: 3 additions & 10 deletions src/libs/Navigation/Navigation.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import {findFocusedRoute} from '@react-navigation/core';
import type {EventArg, NavigationContainerEventMap} from '@react-navigation/native';
import {CommonActions, getPathFromState, StackActions} from '@react-navigation/native';
import type {OnyxCollection, OnyxEntry} from 'react-native-onyx';
import Onyx from 'react-native-onyx';
import type {OnyxEntry} from 'react-native-onyx';
import Log from '@libs/Log';
import {isCentralPaneName, removePolicyIDParamFromState} from '@libs/NavigationUtils';
import * as ReportConnection from '@libs/ReportConnection';
import * as ReportUtils from '@libs/ReportUtils';
import CONST from '@src/CONST';
import NAVIGATORS from '@src/NAVIGATORS';
Expand Down Expand Up @@ -35,13 +35,6 @@ let pendingRoute: Route | null = null;

let shouldPopAllStateOnUP = false;

let allReports: OnyxCollection<Report>;
Onyx.connect({
key: ONYXKEYS.COLLECTION.REPORT,
waitForCollectionCallback: true,
callback: (value) => (allReports = value),
});

/**
* Inform the navigation that next time user presses UP we should pop all the state back to LHN.
*/
Expand Down Expand Up @@ -69,7 +62,7 @@ const dismissModal = (reportID?: string, ref = navigationRef) => {
originalDismissModal(ref);
return;
}
const report = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${reportID}`];
const report = ReportConnection.getAllReports()?.[`${ONYXKEYS.COLLECTION.REPORT}${reportID}`];
originalDismissModalWithReport({reportID, ...report}, ref);
};
// Re-exporting the closeRHPFlow here to fill in default value for navigationRef. The closeRHPFlow isn't defined in this file to avoid cyclic dependencies.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,19 +1,19 @@
import Onyx from 'react-native-onyx';
import type {OnyxCollection} from 'react-native-onyx';
import applyOnyxUpdatesReliably from '@libs/actions/applyOnyxUpdatesReliably';
import * as ActiveClientManager from '@libs/ActiveClientManager';
import Log from '@libs/Log';
import Navigation from '@libs/Navigation/Navigation';
import type {ReportActionPushNotificationData} from '@libs/Notification/PushNotification/NotificationType';
import getPolicyEmployeeAccountIDs from '@libs/PolicyEmployeeListUtils';
import {extractPolicyIDFromPath} from '@libs/PolicyUtils';
import * as ReportConnection from '@libs/ReportConnection';
import {doesReportBelongToWorkspace} from '@libs/ReportUtils';
import Visibility from '@libs/Visibility';
import * as Modal from '@userActions/Modal';
import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
import ROUTES from '@src/ROUTES';
import type {OnyxUpdatesFromServer, Report} from '@src/types/onyx';
import type {OnyxUpdatesFromServer} from '@src/types/onyx';
import {isEmptyObject} from '@src/types/utils/EmptyObject';
import PushNotification from '..';

Expand All @@ -28,13 +28,6 @@ Onyx.connect({
},
});

let allReports: OnyxCollection<Report>;
Onyx.connect({
key: ONYXKEYS.COLLECTION.REPORT,
waitForCollectionCallback: true,
callback: (value) => (allReports = value),
});

function getLastUpdateIDAppliedToClient(): Promise<number> {
return new Promise((resolve) => {
Onyx.connect({
Expand Down Expand Up @@ -82,7 +75,7 @@ function navigateToReport({reportID, reportActionID}: ReportActionPushNotificati
Log.info('[PushNotification] Navigating to report', false, {reportID, reportActionID});

const policyID = lastVisitedPath && extractPolicyIDFromPath(lastVisitedPath);
const report = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${reportID}`];
const report = ReportConnection.getAllReports()?.[`${ONYXKEYS.COLLECTION.REPORT}${reportID}`];
const policyEmployeeAccountIDs = policyID ? getPolicyEmployeeAccountIDs(policyID) : [];
const reportBelongsToWorkspace = policyID && !isEmptyObject(report) && doesReportBelongToWorkspace(report, policyEmployeeAccountIDs, policyID);

Expand Down
21 changes: 7 additions & 14 deletions src/libs/OnyxAwareParser.ts
Original file line number Diff line number Diff line change
@@ -1,23 +1,12 @@
import {ExpensiMark} from 'expensify-common';
import Onyx from 'react-native-onyx';
import ONYXKEYS from '@src/ONYXKEYS';
import * as ReportConnection from './ReportConnection';

const parser = new ExpensiMark();

const reportIDToNameMap: Record<string, string> = {};
const accountIDToNameMap: Record<string, string> = {};

Onyx.connect({
key: ONYXKEYS.COLLECTION.REPORT,
callback: (report) => {
if (!report) {
return;
}

reportIDToNameMap[report.reportID] = report.reportName ?? report.displayName ?? report.reportID;
},
});

Onyx.connect({
key: ONYXKEYS.PERSONAL_DETAILS_LIST,
callback: (personalDetailsList) => {
Expand All @@ -37,7 +26,11 @@ function parseHtmlToMarkdown(
accountIDToName?: Record<string, string>,
cacheVideoAttributes?: (videoSource: string, videoAttrs: string) => void,
): string {
return parser.htmlToMarkdown(html, {reportIDToName: reportIDToName ?? reportIDToNameMap, accountIDToName: accountIDToName ?? accountIDToNameMap, cacheVideoAttributes});
return parser.htmlToMarkdown(html, {
reportIDToName: reportIDToName ?? ReportConnection.getAllReportsNameMap(),
accountIDToName: accountIDToName ?? accountIDToNameMap,
cacheVideoAttributes,
});
}

function parseHtmlToText(
Expand All @@ -46,7 +39,7 @@ function parseHtmlToText(
accountIDToName?: Record<string, string>,
cacheVideoAttributes?: (videoSource: string, videoAttrs: string) => void,
): string {
return parser.htmlToText(html, {reportIDToName: reportIDToName ?? reportIDToNameMap, accountIDToName: accountIDToName ?? accountIDToNameMap, cacheVideoAttributes});
return parser.htmlToText(html, {reportIDToName: reportIDToName ?? ReportConnection.getAllReportsNameMap(), accountIDToName: accountIDToName ?? accountIDToNameMap, cacheVideoAttributes});
}

export {parseHtmlToMarkdown, parseHtmlToText};
9 changes: 2 additions & 7 deletions src/libs/OptionsListUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ import * as PersonalDetailsUtils from './PersonalDetailsUtils';
import * as PhoneNumber from './PhoneNumber';
import * as PolicyUtils from './PolicyUtils';
import * as ReportActionUtils from './ReportActionsUtils';
import * as ReportConnection from './ReportConnection';
import * as ReportUtils from './ReportUtils';
import * as TaskUtils from './TaskUtils';
import * as TransactionUtils from './TransactionUtils';
Expand Down Expand Up @@ -339,13 +340,6 @@ Onyx.connect({
},
});

let allReports: OnyxCollection<Report> = {};
Onyx.connect({
key: ONYXKEYS.COLLECTION.REPORT,
waitForCollectionCallback: true,
callback: (value) => (allReports = value),
});

let allReportsDraft: OnyxCollection<Report>;
Onyx.connect({
key: ONYXKEYS.COLLECTION.REPORT_DRAFT,
Expand All @@ -357,6 +351,7 @@ Onyx.connect({
* Get the report or draft report given a reportID
*/
function getReportOrDraftReport(reportID: string | undefined): OnyxEntry<Report> {
const allReports = ReportConnection.getAllReports();
if (!allReports && !allReportsDraft) {
return undefined;
}
Expand Down
Loading
Loading