Skip to content

Commit

Permalink
perf: use context for shared reports access
Browse files Browse the repository at this point in the history
  • Loading branch information
hurali97 committed Feb 13, 2024
1 parent 1f1867a commit 3088672
Show file tree
Hide file tree
Showing 10 changed files with 228 additions and 290 deletions.
4 changes: 4 additions & 0 deletions src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ import {KeyboardStateProvider} from './components/withKeyboardState';
import {WindowDimensionsProvider} from './components/withWindowDimensions';
import Expensify from './Expensify';
import useDefaultDragAndDrop from './hooks/useDefaultDragAndDrop';
import {OrderedReportIDsContextProvider} from './hooks/useOrderedReportIDs';
import {ReportsContextProvider} from './hooks/useReports';
import OnyxUpdateManager from './libs/actions/OnyxUpdateManager';
import * as Session from './libs/actions/Session';
import * as Environment from './libs/Environment/Environment';
Expand Down Expand Up @@ -79,6 +81,8 @@ function App({url}: AppProps) {
EnvironmentProvider,
CustomStatusBarAndBackgroundContextProvider,
ActiveWorkspaceContextProvider,
ReportsContextProvider,
OrderedReportIDsContextProvider,
]}
>
<CustomStatusBarAndBackground />
Expand Down
6 changes: 2 additions & 4 deletions src/components/LHNOptionsList/LHNOptionsList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {StyleSheet, View} from 'react-native';
import {withOnyx} from 'react-native-onyx';
import withCurrentReportID from '@components/withCurrentReportID';
import usePermissions from '@hooks/usePermissions';
import {useReports} from '@hooks/useReports';
import useThemeStyles from '@hooks/useThemeStyles';
import * as ReportActionsUtils from '@libs/ReportActionsUtils';
import variables from '@styles/variables';
Expand All @@ -22,7 +23,6 @@ function LHNOptionsList({
onSelectRow,
optionMode,
shouldDisableFocusOptions = false,
reports = {},
reportActions = {},
policy = {},
preferredLocale = CONST.LOCALES.DEFAULT,
Expand All @@ -33,6 +33,7 @@ function LHNOptionsList({
transactionViolations = {},
onFirstItemRendered = () => {},
}: LHNOptionsListProps) {
const reports = useReports();
const styles = useThemeStyles();
const {canUseViolations} = usePermissions();

Expand Down Expand Up @@ -134,9 +135,6 @@ LHNOptionsList.displayName = 'LHNOptionsList';

export default withCurrentReportID(
withOnyx<LHNOptionsListProps, LHNOptionsListOnyxProps>({
reports: {
key: ONYXKEYS.COLLECTION.REPORT,
},
reportActions: {
key: ONYXKEYS.COLLECTION.REPORT_ACTIONS,
},
Expand Down
3 changes: 0 additions & 3 deletions src/components/LHNOptionsList/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,6 @@ type LHNOptionsListOnyxProps = {
/** The policy which the user has access to and which the report could be tied to */
policy: OnyxCollection<Policy>;

/** All reports shared with the user */
reports: OnyxCollection<Report>;

/** Array of report actions for this report */
reportActions: OnyxCollection<ReportActions>;

Expand Down
145 changes: 145 additions & 0 deletions src/hooks/useOrderedReportIDs.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
import React, {createContext, useContext, useMemo} from 'react';
import {withOnyx} from 'react-native-onyx';
import type {OnyxCollection, OnyxEntry} from 'react-native-onyx';
import {getCurrentUserAccountID} from '@libs/actions/Report';
import {getPolicyMembersByIdWithoutCurrentUser} from '@libs/PolicyUtils';
import SidebarUtils from '@libs/SidebarUtils';
import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
import type {Beta, Policy, PolicyMembers, ReportAction, ReportActions, TransactionViolation} from '@src/types/onyx';
import type PriorityMode from '@src/types/onyx/PriorityMode';
import useActiveWorkspace from './useActiveWorkspace';
import useCurrentReportID from './useCurrentReportID';
import {useReports} from './useReports';

type OnyxProps = {
betas: OnyxEntry<Beta[]>;
policies: OnyxCollection<Policy>;
allReportActions: OnyxCollection<ReportAction[]>;
transactionViolations: OnyxCollection<TransactionViolation[]>;
policyMembers: OnyxCollection<PolicyMembers>;
priorityMode: OnyxEntry<PriorityMode>;
};

type WithOrderedReportIDsContextProviderProps = OnyxProps & {
children: React.ReactNode;
};

const OrderedReportIDsContext = createContext({});

function WithOrderedReportIDsContextProvider(props: WithOrderedReportIDsContextProviderProps) {
const chatReports = useReports();
const currentReportIDValue = useCurrentReportID();
const {activeWorkspaceID} = useActiveWorkspace();

const policyMemberAccountIDs = useMemo(
() => getPolicyMembersByIdWithoutCurrentUser(props.policyMembers, activeWorkspaceID, getCurrentUserAccountID()),
[activeWorkspaceID, props.policyMembers],
);

const optionListItems = useMemo(
() =>
SidebarUtils.getOrderedReportIDs(
null,
chatReports,
props.betas ?? [],
props.policies,
props.priorityMode,
props.allReportActions,
props.transactionViolations,
activeWorkspaceID,
policyMemberAccountIDs,
),
[chatReports, props.betas, props.policies, props.priorityMode, props.allReportActions, props.transactionViolations, activeWorkspaceID, policyMemberAccountIDs],
);

// We need to make sure the current report is in the list of reports, but we do not want
// to have to re-generate the list every time the currentReportID changes. To do that
// we first generate the list as if there was no current report, then here 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.
const optionListItemsWithCurrentReport = useMemo(() => {
if (currentReportIDValue?.currentReportID && !optionListItems.includes(currentReportIDValue.currentReportID)) {
return SidebarUtils.getOrderedReportIDs(
currentReportIDValue.currentReportID,
chatReports,
props.betas ?? [],
props.policies,
props.priorityMode,
props.allReportActions,
props.transactionViolations,
activeWorkspaceID,
policyMemberAccountIDs,
);
}
return optionListItems;
}, [
activeWorkspaceID,
chatReports,
currentReportIDValue?.currentReportID,
optionListItems,
policyMemberAccountIDs,
props.allReportActions,
props.betas,
props.policies,
props.priorityMode,
props.transactionViolations,
]);

return <OrderedReportIDsContext.Provider value={optionListItemsWithCurrentReport}>{props.children}</OrderedReportIDsContext.Provider>;
}

const reportActionsSelector = (reportActions: OnyxEntry<ReportActions>) => {
if (!reportActions) {
return [];
}

return Object.values(reportActions).map((reportAction) => {
const {reportActionID, actionName, originalMessage} = reportAction ?? {};
const decision = reportAction?.message?.[0]?.moderationDecision?.decision;
return {
reportActionID,
actionName,
originalMessage,
message: [
{
moderationDecision: {decision},
},
],
};
});
};

const OrderedReportIDsContextProvider = withOnyx<WithOrderedReportIDsContextProviderProps, OnyxProps>({
priorityMode: {
key: ONYXKEYS.NVP_PRIORITY_MODE,
initialValue: CONST.PRIORITY_MODE.DEFAULT,
},
betas: {
key: ONYXKEYS.BETAS,
initialValue: [],
},
allReportActions: {
key: ONYXKEYS.COLLECTION.REPORT_ACTIONS,
// @ts-expect-error Need some help in determining the correct type for this selector
selector: (actions) => reportActionsSelector(actions),
initialValue: {},
},
policies: {
key: ONYXKEYS.COLLECTION.POLICY,
initialValue: {},
},
policyMembers: {
key: ONYXKEYS.COLLECTION.POLICY_MEMBERS,
},
transactionViolations: {
key: ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS,
initialValue: {},
},
})(WithOrderedReportIDsContextProvider);

function useOrderedReportIDs() {
return useContext(OrderedReportIDsContext);
}

export {OrderedReportIDsContextProvider, OrderedReportIDsContext, useOrderedReportIDs};
44 changes: 44 additions & 0 deletions src/hooks/useReports.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import React, {createContext, useContext, useEffect, useMemo, useState} from 'react';
import Onyx from 'react-native-onyx';
import type {OnyxCollection} from 'react-native-onyx';
import ONYXKEYS from '@src/ONYXKEYS';
import type {Report} from '@src/types/onyx';

type Reports = OnyxCollection<Report>;
type ReportsContextValue = Reports;

type ReportsContextProviderProps = {
children: React.ReactNode;
};

const ReportsContext = createContext<ReportsContextValue>(null);

function ReportsContextProvider(props: ReportsContextProviderProps) {
const [reports, setReports] = useState<Reports>(null);

useEffect(() => {
// eslint-disable-next-line rulesdir/prefer-onyx-connect-in-libs
const connID = Onyx.connect({
key: ONYXKEYS.COLLECTION.REPORT,
waitForCollectionCallback: true,
callback: (val) => {
setReports(val);
},
});
return () => {
Onyx.disconnect(connID);
};
}, []);

const contextValue = useMemo(() => reports ?? {}, [reports]);

return <ReportsContext.Provider value={contextValue}>{props.children}</ReportsContext.Provider>;
}

function useReports() {
return useContext(ReportsContext);
}

ReportsContextProvider.displayName = 'ReportsContextProvider';

export {ReportsContextProvider, ReportsContext, useReports};
13 changes: 4 additions & 9 deletions src/libs/Navigation/AppNavigator/ReportScreenIDSetter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import type {OnyxCollection, OnyxEntry} from 'react-native-onyx';
import {withOnyx} from 'react-native-onyx';
import useActiveWorkspace from '@hooks/useActiveWorkspace';
import usePermissions from '@hooks/usePermissions';
import {useReports} from '@hooks/useReports';
import {getPolicyMembersByIdWithoutCurrentUser} from '@libs/PolicyUtils';
import * as ReportUtils from '@libs/ReportUtils';
import * as App from '@userActions/App';
Expand All @@ -11,9 +12,6 @@ import type {Policy, PolicyMembers, Report, ReportMetadata} from '@src/types/ony
import type {ReportScreenWrapperProps} from './ReportScreenWrapper';

type ReportScreenIDSetterComponentProps = {
/** Available reports that would be displayed in this navigator */
reports: OnyxCollection<Report>;

/** The policies which the user has access to */
policies: OnyxCollection<Policy>;

Expand Down Expand Up @@ -59,9 +57,10 @@ const getLastAccessedReportID = (
};

// This wrapper is reponsible for opening the last accessed report if there is no reportID specified in the route params
function ReportScreenIDSetter({route, reports, policies, policyMembers = {}, navigation, isFirstTimeNewExpensifyUser = false, reportMetadata, accountID}: ReportScreenIDSetterProps) {
function ReportScreenIDSetter({route, policies, policyMembers = {}, navigation, isFirstTimeNewExpensifyUser = false, reportMetadata, accountID}: ReportScreenIDSetterProps) {
const {canUseDefaultRooms} = usePermissions();
const {activeWorkspaceID} = useActiveWorkspace();
const reports = useReports();

useEffect(() => {
// Don't update if there is a reportID in the params already
Expand All @@ -83,7 +82,7 @@ function ReportScreenIDSetter({route, reports, policies, policyMembers = {}, nav
!canUseDefaultRooms,
policies,
isFirstTimeNewExpensifyUser,
!!reports?.params?.openOnAdminRoom,
!!route?.params?.openOnAdminRoom,
reportMetadata,
activeWorkspaceID,
policyMemberAccountIDs,
Expand All @@ -106,10 +105,6 @@ function ReportScreenIDSetter({route, reports, policies, policyMembers = {}, nav
ReportScreenIDSetter.displayName = 'ReportScreenIDSetter';

export default withOnyx<ReportScreenIDSetterProps, ReportScreenIDSetterComponentProps>({
reports: {
key: ONYXKEYS.COLLECTION.REPORT,
allowStaleData: true,
},
policies: {
key: ONYXKEYS.COLLECTION.POLICY,
allowStaleData: true,
Expand Down
Loading

0 comments on commit 3088672

Please sign in to comment.