Skip to content

Commit

Permalink
Merge pull request #44068 from callstack-internal/hur/improve-native-…
Browse files Browse the repository at this point in the history
…app-startup

perf: improve native apps startup
  • Loading branch information
mountiny authored Jul 8, 2024
2 parents ca92ae4 + bdba40b commit aae4922
Show file tree
Hide file tree
Showing 30 changed files with 274 additions and 419 deletions.
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);
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]);

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

0 comments on commit aae4922

Please sign in to comment.