Skip to content
22 changes: 14 additions & 8 deletions src/App.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import CustomStatusBar from './components/CustomStatusBar';
import ErrorBoundary from './components/ErrorBoundary';
import Expensify from './Expensify';
import {LocaleContextProvider} from './components/withLocalize';
import OnyxProvider from './components/OnyxProvider';
import ComposeProviders from './components/ComposeProviders';

LogBox.ignoreLogs([
// Basically it means that if the app goes in the background and back to foreground on Android,
Expand All @@ -17,14 +19,18 @@ LogBox.ignoreLogs([
]);

const App = () => (
<SafeAreaProvider>
<LocaleContextProvider>
<CustomStatusBar />
<ErrorBoundary errorMessage="E.cash crash caught by error boundary">
<Expensify />
</ErrorBoundary>
</LocaleContextProvider>
</SafeAreaProvider>
<ComposeProviders
components={[
OnyxProvider,
SafeAreaProvider,
LocaleContextProvider,
]}
>
<CustomStatusBar />
<ErrorBoundary errorMessage="E.cash crash caught by error boundary">
<Expensify />
</ErrorBoundary>
</ComposeProviders>
);

App.displayName = 'App';
Expand Down
32 changes: 1 addition & 31 deletions src/Expensify.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,13 @@ import lodashGet from 'lodash/get';
import PropTypes from 'prop-types';
import React, {PureComponent} from 'react';
import {View, AppState} from 'react-native';
import Onyx, {withOnyx} from 'react-native-onyx';
import {withOnyx} from 'react-native-onyx';
import _ from 'underscore';

import BootSplash from './libs/BootSplash';
import listenToStorageEvents from './libs/listenToStorageEvents';
import * as ActiveClientManager from './libs/ActiveClientManager';
import ONYXKEYS from './ONYXKEYS';
import CONST from './CONST';
import NavigationRoot from './libs/Navigation/NavigationRoot';
import Log from './libs/Log';
import migrateOnyx from './libs/migrateOnyx';
import styles from './styles/styles';
import PushNotification from './libs/Notification/PushNotification';
Expand All @@ -24,33 +21,6 @@ import ROUTES from './ROUTES';
import StartupTimer from './libs/StartupTimer';
import {setRedirectToWorkspaceNewAfterSignIn} from './libs/actions/Session';

// Initialize the store when the app loads for the first time
Onyx.init({
keys: ONYXKEYS,
safeEvictionKeys: [ONYXKEYS.COLLECTION.REPORT_ACTIONS],
initialKeyStates: {

// Clear any loading and error messages so they do not appear on app startup
[ONYXKEYS.SESSION]: {loading: false, shouldShowComposeInput: true},
[ONYXKEYS.ACCOUNT]: CONST.DEFAULT_ACCOUNT_DATA,
[ONYXKEYS.NETWORK]: {isOffline: false},
[ONYXKEYS.IOU]: {
loading: false, error: false, creatingIOUTransaction: false, isRetrievingCurrency: false,
},
[ONYXKEYS.IS_SIDEBAR_LOADED]: false,
},
registerStorageEventListener: (onStorageEvent) => {
listenToStorageEvents(onStorageEvent);
},
});
Onyx.registerLogger(({level, message}) => {
if (level === 'alert') {
Log.alert(message, 0, {}, false);
} else {
Log.client(message);
}
});

const propTypes = {
/* Onyx Props */

Expand Down
22 changes: 22 additions & 0 deletions src/components/ComposeProviders.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import React from 'react';
import PropTypes from 'prop-types';

const propTypes = {
/** Provider components go here */
components: PropTypes.arrayOf(PropTypes.oneOfType([PropTypes.object, PropTypes.func])).isRequired,

/** Rendered child component */
children: PropTypes.node.isRequired,
};

const ComposeProviders = props => (
<>
{props.components.reduceRight((memo, Component) => (
<Component>{memo}</Component>
), props.children)}
</>
);

ComposeProviders.propTypes = propTypes;
ComposeProviders.displayName = 'ComposeProviders';
export default ComposeProviders;
73 changes: 73 additions & 0 deletions src/components/OnyxProvider.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import React from 'react';
import Onyx from 'react-native-onyx';
import PropTypes from 'prop-types';
import ONYXKEYS from '../ONYXKEYS';
import createOnyxContext from './createOnyxContext';
import ComposeProviders from './ComposeProviders';
import CONST from '../CONST';
import Log from '../libs/Log';
import listenToStorageEvents from '../libs/listenToStorageEvents';

// Initialize the store when the app loads for the first time
Onyx.init({
keys: ONYXKEYS,
safeEvictionKeys: [ONYXKEYS.COLLECTION.REPORT_ACTIONS],
initialKeyStates: {

// Clear any loading and error messages so they do not appear on app startup
[ONYXKEYS.SESSION]: {loading: false, shouldShowComposeInput: true},
[ONYXKEYS.ACCOUNT]: CONST.DEFAULT_ACCOUNT_DATA,
[ONYXKEYS.NETWORK]: {isOffline: false},
[ONYXKEYS.IOU]: {
loading: false, error: false, creatingIOUTransaction: false, isRetrievingCurrency: false,
},
[ONYXKEYS.IS_SIDEBAR_LOADED]: false,
},
registerStorageEventListener: (onStorageEvent) => {
listenToStorageEvents(onStorageEvent);
},
});
Onyx.registerLogger(({level, message}) => {
if (level === 'alert') {
Log.alert(message, 0, {}, false);
} else {
Log.client(message);
}
});

// Set up any providers for individual keys. This should only be used in cases where many components will subscribe to
// the same key (e.g. FlatList renderItem components)
const [withNetwork, NetworkProvider] = createOnyxContext(ONYXKEYS.NETWORK);
const [withPersonalDetails, PersonalDetailsProvider] = createOnyxContext(ONYXKEYS.PERSONAL_DETAILS);
const [
withReportActionsDrafts,
ReportActionsDraftsProvider,
] = createOnyxContext(ONYXKEYS.COLLECTION.REPORT_ACTIONS_DRAFTS);

const propTypes = {
/** Rendered child component */
children: PropTypes.node.isRequired,
};

const OnyxProvider = props => (
<ComposeProviders
components={[
NetworkProvider,
PersonalDetailsProvider,
ReportActionsDraftsProvider,
]}
>
{props.children}
</ComposeProviders>
);

OnyxProvider.displayName = 'OnyxProvider';
OnyxProvider.propTypes = propTypes;

export default OnyxProvider;

export {
withNetwork,
withPersonalDetails,
withReportActionsDrafts,
};
50 changes: 50 additions & 0 deletions src/components/createOnyxContext.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import React, {createContext, forwardRef} from 'react';
import PropTypes from 'prop-types';
import {withOnyx} from 'react-native-onyx';
import Str from 'expensify-common/lib/str';
import getComponentDisplayName from '../libs/getComponentDisplayName';

const propTypes = {
/** Rendered child component */
children: PropTypes.node.isRequired,
};

export default (onyxKeyName) => {
const Context = createContext();
const Provider = props => (
<Context.Provider value={props[onyxKeyName]}>
{props.children}
</Context.Provider>
);

Provider.propTypes = propTypes;
Provider.displayName = `${Str.UCFirst(onyxKeyName)}Provider`;

const ProviderWithOnyx = withOnyx({
[onyxKeyName]: {
key: onyxKeyName,
},
})(Provider);

const withOnyxKey = ({propName = onyxKeyName, transformValue = () => {}} = {}) => (WrappedComponent) => {
const Consumer = forwardRef((props, ref) => (
<Context.Consumer>
{(value) => {
const propsToPass = {
...props,
[propName]: transformValue ? transformValue(value, props) : value,
};
return (
// eslint-disable-next-line react/jsx-props-no-spreading
<WrappedComponent {...propsToPass} ref={ref} />
);
}}
</Context.Consumer>
));

Consumer.displayName = `with${Str.UCFirst(onyxKeyName)}(${getComponentDisplayName(WrappedComponent)})`;
return Consumer;
};

return [withOnyxKey, ProviderWithOnyx];
};
15 changes: 8 additions & 7 deletions src/pages/home/report/ReportActionItem.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import _ from 'underscore';
import lodashGet from 'lodash/get';
import React, {Component} from 'react';
import {View} from 'react-native';
import PropTypes from 'prop-types';
import {withOnyx} from 'react-native-onyx';
import CONST from '../../../CONST';
import ONYXKEYS from '../../../ONYXKEYS';
import ReportActionPropTypes from './ReportActionPropTypes';
Expand All @@ -23,6 +23,7 @@ import ControlSelection from '../../../libs/ControlSelection';
import canUseTouchScreen from '../../../libs/canUseTouchscreen';
import MiniReportActionContextMenu from './ContextMenu/MiniReportActionContextMenu';
import {isActiveReportAction, showContextMenu} from './ContextMenu/ReportActionContextMenu';
import {withReportActionsDrafts} from '../../../components/OnyxProvider';

const propTypes = {
/** The ID of the report this action is on. */
Expand Down Expand Up @@ -185,12 +186,12 @@ ReportActionItem.defaultProps = defaultProps;

export default compose(
withWindowDimensions,
withOnyx({
draftMessage: {
key: ({
reportID,
action,
}) => `${ONYXKEYS.COLLECTION.REPORT_ACTIONS_DRAFTS}${reportID}_${action.reportActionID}`,
withReportActionsDrafts({
propName: 'draftMessage',
transformValue: (drafts, props) => {
const {reportID, action} = props;
const draftKey = `${ONYXKEYS.COLLECTION.REPORT_ACTIONS_DRAFTS}${reportID}_${action.reportActionID}`;
return lodashGet(drafts, draftKey, '');
},
}),
)(ReportActionItem);
9 changes: 2 additions & 7 deletions src/pages/home/report/ReportActionItemMessage.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,10 @@ import React from 'react';
import {View} from 'react-native';
import PropTypes from 'prop-types';
import _ from 'underscore';
import {withOnyx} from 'react-native-onyx';
import ONYXKEYS from '../../../ONYXKEYS';
import styles from '../../../styles/styles';
import ReportActionItemFragment from './ReportActionItemFragment';
import ReportActionPropTypes from './ReportActionPropTypes';
import {withNetwork} from '../../../components/OnyxProvider';

const propTypes = {
/** The report action */
Expand Down Expand Up @@ -43,8 +42,4 @@ ReportActionItemMessage.propTypes = propTypes;
ReportActionItemMessage.defaultProps = defaultProps;
ReportActionItemMessage.displayName = 'ReportActionItemMessage';

export default withOnyx({
network: {
key: ONYXKEYS.NETWORK,
},
})(ReportActionItemMessage);
export default withNetwork()(ReportActionItemMessage);
9 changes: 2 additions & 7 deletions src/pages/home/report/ReportActionItemSingle.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import React from 'react';
import {View, Pressable} from 'react-native';
import {withOnyx} from 'react-native-onyx';
import PropTypes from 'prop-types';
import _ from 'underscore';
import Str from 'expensify-common/lib/str';
Expand All @@ -10,12 +9,12 @@ import styles from '../../../styles/styles';
import CONST from '../../../CONST';
import ReportActionItemDate from './ReportActionItemDate';
import Avatar from '../../../components/Avatar';
import ONYXKEYS from '../../../ONYXKEYS';
import personalDetailsPropType from '../../personalDetailsPropType';
import compose from '../../../libs/compose';
import withLocalize, {withLocalizePropTypes} from '../../../components/withLocalize';
import Navigation from '../../../libs/Navigation/Navigation';
import ROUTES from '../../../ROUTES';
import {withPersonalDetails} from '../../../components/OnyxProvider';

const propTypes = {
/** All the data of the action */
Expand Down Expand Up @@ -99,9 +98,5 @@ ReportActionItemSingle.displayName = 'ReportActionItemSingle';

export default compose(
withLocalize,
withOnyx({
personalDetails: {
key: ONYXKEYS.PERSONAL_DETAILS,
},
}),
withPersonalDetails(),
)(ReportActionItemSingle);