From abeb4a2d3aa19c78941e7cd845d4d045f646112e Mon Sep 17 00:00:00 2001 From: Marc Glasser Date: Fri, 30 Jul 2021 10:54:19 -1000 Subject: [PATCH 01/11] create context factory --- src/App.js | 11 +++++-- src/components/ComposeProviders.js | 12 +++++++ src/components/createOnyxContext.js | 32 +++++++++++++++++++ src/components/withNetwork.js | 6 ++++ .../home/report/ReportActionItemMessage.js | 9 ++---- 5 files changed, 61 insertions(+), 9 deletions(-) create mode 100644 src/components/ComposeProviders.js create mode 100644 src/components/createOnyxContext.js create mode 100644 src/components/withNetwork.js diff --git a/src/App.js b/src/App.js index 0ac8e0ac8e601..04bacae596a88 100644 --- a/src/App.js +++ b/src/App.js @@ -3,7 +3,9 @@ import {LogBox} from 'react-native'; import {SafeAreaProvider} from 'react-native-safe-area-context'; import CustomStatusBar from './components/CustomStatusBar'; import ErrorBoundary from './components/ErrorBoundary'; +import {NetworkProvider} from './components/withNetwork'; import Expensify from './Expensify'; +import ComposeProviders from './components/ComposeProviders'; LogBox.ignoreLogs([ // Basically it means that if the app goes in the background and back to foreground on Android, @@ -16,12 +18,17 @@ LogBox.ignoreLogs([ ]); const App = () => ( - + - + ); App.displayName = 'App'; diff --git a/src/components/ComposeProviders.js b/src/components/ComposeProviders.js new file mode 100644 index 0000000000000..5217e3c149fd7 --- /dev/null +++ b/src/components/ComposeProviders.js @@ -0,0 +1,12 @@ +import React from 'react'; + +const ComposeProviders = props => ( + <> + {props.components.reduceRight((memo, Component) => ( + {memo} + ), props.children)} + +); + +ComposeProviders.displayName = 'ComposeProviders'; +export default ComposeProviders; diff --git a/src/components/createOnyxContext.js b/src/components/createOnyxContext.js new file mode 100644 index 0000000000000..66fedcee315ec --- /dev/null +++ b/src/components/createOnyxContext.js @@ -0,0 +1,32 @@ +import React, {createContext} from 'react'; +import {withOnyx} from 'react-native-onyx'; + +export default (onyxKeyName) => { + const Context = createContext(); + const Provider = props => ( + + {props.children} + + ); + + const ProviderWithOnyx = withOnyx({ + key: onyxKeyName, + })(Provider); + + const withOnyxKey = WrappedComponent => props => ( + + {(value) => { + const propsToPass = {...props, [onyxKeyName]: value}; + return ( + // eslint-disable-next-line react/jsx-props-no-spreading + + ); + }} + + ); + + return { + withOnyxKey, + Provider: ProviderWithOnyx, + }; +}; diff --git a/src/components/withNetwork.js b/src/components/withNetwork.js new file mode 100644 index 0000000000000..72ab8106af6c8 --- /dev/null +++ b/src/components/withNetwork.js @@ -0,0 +1,6 @@ +import ONYXKEYS from '../ONYXKEYS'; +import createOnyxContext from './createOnyxContext'; + +const context = createOnyxContext(ONYXKEYS.NETWORK); +export const NetworkProvider = context.Provider; +export default context.withOnyxKey; diff --git a/src/pages/home/report/ReportActionItemMessage.js b/src/pages/home/report/ReportActionItemMessage.js index 7f727f84e33a0..8c459d02b5548 100644 --- a/src/pages/home/report/ReportActionItemMessage.js +++ b/src/pages/home/report/ReportActionItemMessage.js @@ -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/withNetwork'; const propTypes = { /** The report action */ @@ -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); From 867dc607c1cabe5c22df02841dc11c125d09ac19 Mon Sep 17 00:00:00 2001 From: Marc Glasser Date: Fri, 30 Jul 2021 11:44:07 -1000 Subject: [PATCH 02/11] add factory method for context --- src/App.js | 5 ++ src/components/createOnyxContext.js | 4 +- src/components/withPersonalDetails.js | 6 +++ src/components/withReportActionsDrafts.js | 6 +++ src/pages/home/report/ReportActionItem.js | 49 +++++++++++-------- .../home/report/ReportActionItemSingle.js | 9 +--- src/pages/home/sidebar/SidebarLinks.js | 5 +- 7 files changed, 53 insertions(+), 31 deletions(-) create mode 100644 src/components/withPersonalDetails.js create mode 100644 src/components/withReportActionsDrafts.js diff --git a/src/App.js b/src/App.js index 04bacae596a88..0747d1d45963a 100644 --- a/src/App.js +++ b/src/App.js @@ -4,6 +4,9 @@ import {SafeAreaProvider} from 'react-native-safe-area-context'; import CustomStatusBar from './components/CustomStatusBar'; import ErrorBoundary from './components/ErrorBoundary'; import {NetworkProvider} from './components/withNetwork'; +import {PersonalDetailsProvider} from './components/withPersonalDetails'; +import {ReportActionsDraftsProvider} from './components/withReportActionsDrafts'; + import Expensify from './Expensify'; import ComposeProviders from './components/ComposeProviders'; @@ -22,6 +25,8 @@ const App = () => ( components={[ NetworkProvider, SafeAreaProvider, + PersonalDetailsProvider, + ReportActionsDraftsProvider, ]} > diff --git a/src/components/createOnyxContext.js b/src/components/createOnyxContext.js index 66fedcee315ec..8aa81c43fb92a 100644 --- a/src/components/createOnyxContext.js +++ b/src/components/createOnyxContext.js @@ -10,7 +10,9 @@ export default (onyxKeyName) => { ); const ProviderWithOnyx = withOnyx({ - key: onyxKeyName, + [onyxKeyName]: { + key: onyxKeyName, + }, })(Provider); const withOnyxKey = WrappedComponent => props => ( diff --git a/src/components/withPersonalDetails.js b/src/components/withPersonalDetails.js new file mode 100644 index 0000000000000..dc86e98a63092 --- /dev/null +++ b/src/components/withPersonalDetails.js @@ -0,0 +1,6 @@ +import ONYXKEYS from '../ONYXKEYS'; +import createOnyxContext from './createOnyxContext'; + +const context = createOnyxContext(ONYXKEYS.PERSONAL_DETAILS); +export const PersonalDetailsProvider = context.Provider; +export default context.withOnyxKey; diff --git a/src/components/withReportActionsDrafts.js b/src/components/withReportActionsDrafts.js new file mode 100644 index 0000000000000..070e9412215cd --- /dev/null +++ b/src/components/withReportActionsDrafts.js @@ -0,0 +1,6 @@ +import ONYXKEYS from '../ONYXKEYS'; +import createOnyxContext from './createOnyxContext'; + +const context = createOnyxContext(ONYXKEYS.COLLECTION.REPORT_ACTIONS_DRAFTS); +export const ReportActionsDraftsProvider = context.Provider; +export default context.withOnyxKey; diff --git a/src/pages/home/report/ReportActionItem.js b/src/pages/home/report/ReportActionItem.js index 010f9e2998172..ab7326ffd11fa 100644 --- a/src/pages/home/report/ReportActionItem.js +++ b/src/pages/home/report/ReportActionItem.js @@ -2,7 +2,6 @@ import _ from 'underscore'; 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'; @@ -23,6 +22,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/withReportActionsDrafts'; const propTypes = { /** The ID of the report this action is on. */ @@ -47,13 +47,15 @@ const propTypes = { index: PropTypes.number.isRequired, /** Draft message - if this is set the comment is in 'edit' mode */ - draftMessage: PropTypes.string, + // eslint-disable-next-line react/require-default-props + [ONYXKEYS.COLLECTION.REPORT_ACTIONS_DRAFTS]: PropTypes.shape({}), ...windowDimensionsPropTypes, }; const defaultProps = { - draftMessage: '', + // eslint-disable-next-line react/default-props-match-prop-types + [ONYXKEYS.COLLECTION.REPORT_ACTIONS_DRAFTS]: {}, hasOutstandingIOU: false, }; @@ -70,7 +72,7 @@ class ReportActionItem extends Component { shouldComponentUpdate(nextProps, nextState) { return this.props.displayAsGroup !== nextProps.displayAsGroup - || this.props.draftMessage !== nextProps.draftMessage + || this.getDraftMessage() !== this.getDraftMessage(nextProps) || this.props.isMostRecentIOUReportAction !== nextProps.isMostRecentIOUReportAction || this.props.hasOutstandingIOU !== nextProps.hasOutstandingIOU || this.props.shouldDisplayNewIndicator !== nextProps.shouldDisplayNewIndicator @@ -78,6 +80,19 @@ class ReportActionItem extends Component { || this.state.isContextMenuActive !== nextState.isContextMenuActive; } + /** + * @param {Object} props + * @returns {String} + */ + getDraftMessage(props) { + const propsToUse = props || this.props; + const drafts = propsToUse[ONYXKEYS.COLLECTION.REPORT_ACTIONS_DRAFTS] || {}; + // eslint-disable-next-line max-len + const draftKey = `${ONYXKEYS.COLLECTION.REPORT_ACTIONS_DRAFTS}${propsToUse.reportID}_${propsToUse.action.reportActionID}`; + const draft = drafts[draftKey]; + return draft || ''; + } + /** * Show the ReportActionContextMenu modal popover. * @@ -86,7 +101,7 @@ class ReportActionItem extends Component { */ showPopover(event, selection) { // Block menu on the message being Edited - if (this.props.draftMessage) { + if (this.getDraftMessage()) { return; } showContextMenu( @@ -95,7 +110,7 @@ class ReportActionItem extends Component { this.popoverAnchor, this.props.reportID, this.props.action, - this.props.draftMessage, + this.getDraftMessage(), this.checkIfContextMenuActive, this.checkIfContextMenuActive, ); @@ -106,6 +121,7 @@ class ReportActionItem extends Component { } render() { + const draftMessage = this.getDraftMessage(); let children; if (this.props.action.actionName === CONST.REPORT.ACTIONS.TYPE.IOU) { children = ( @@ -116,12 +132,12 @@ class ReportActionItem extends Component { /> ); } else { - children = !this.props.draftMessage + children = !draftMessage ? : ( @@ -133,7 +149,7 @@ class ReportActionItem extends Component { onPressIn={() => this.props.isSmallScreenWidth && canUseTouchScreen() && ControlSelection.block()} onPressOut={() => ControlSelection.unblock()} onSecondaryInteraction={this.showPopover} - preventDefaultContentMenu={!this.props.draftMessage} + preventDefaultContentMenu={!draftMessage} > @@ -146,7 +162,7 @@ class ReportActionItem extends Component { style={getReportActionItemStyle( hovered || this.state.isContextMenuActive - || this.props.draftMessage, + || draftMessage, )} > {!this.props.displayAsGroup @@ -168,10 +184,10 @@ class ReportActionItem extends Component { isVisible={ hovered && !this.state.isContextMenuActive - && !this.props.draftMessage + && !draftMessage } - draftMessage={this.props.draftMessage} + draftMessage={draftMessage} /> )} @@ -185,12 +201,5 @@ ReportActionItem.defaultProps = defaultProps; export default compose( withWindowDimensions, - withOnyx({ - draftMessage: { - key: ({ - reportID, - action, - }) => `${ONYXKEYS.COLLECTION.REPORT_ACTIONS_DRAFTS}${reportID}_${action.reportActionID}`, - }, - }), + withReportActionsDrafts, )(ReportActionItem); diff --git a/src/pages/home/report/ReportActionItemSingle.js b/src/pages/home/report/ReportActionItemSingle.js index 84befd28f4c8d..2cf1e216e72a2 100644 --- a/src/pages/home/report/ReportActionItemSingle.js +++ b/src/pages/home/report/ReportActionItemSingle.js @@ -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'; @@ -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/withPersonalDetails'; const propTypes = { /** All the data of the action */ @@ -99,9 +98,5 @@ ReportActionItemSingle.displayName = 'ReportActionItemSingle'; export default compose( withLocalize, - withOnyx({ - personalDetails: { - key: ONYXKEYS.PERSONAL_DETAILS, - }, - }), + withPersonalDetails, )(ReportActionItemSingle); diff --git a/src/pages/home/sidebar/SidebarLinks.js b/src/pages/home/sidebar/SidebarLinks.js index d846b67e9e293..f6005738dd851 100644 --- a/src/pages/home/sidebar/SidebarLinks.js +++ b/src/pages/home/sidebar/SidebarLinks.js @@ -21,6 +21,7 @@ import {participantPropTypes} from './optionPropTypes'; import themeColors from '../../../styles/themes/default'; import withLocalize, {withLocalizePropTypes} from '../../../components/withLocalize'; import * as App from '../../../libs/actions/App'; +import withNetwork from '../../../components/withNetwork'; const propTypes = { /** Toggles the navigation menu open and closed */ @@ -192,6 +193,7 @@ SidebarLinks.defaultProps = defaultProps; export default compose( withLocalize, + withNetwork, withOnyx({ reports: { key: ONYXKEYS.COLLECTION.REPORT, @@ -205,9 +207,6 @@ export default compose( myPersonalDetails: { key: ONYXKEYS.MY_PERSONAL_DETAILS, }, - network: { - key: ONYXKEYS.NETWORK, - }, currentlyViewedReportID: { key: ONYXKEYS.CURRENTLY_VIEWED_REPORTID, }, From e7dbcffe5cd1c1d70014cd1b044a62d112a16671 Mon Sep 17 00:00:00 2001 From: Marc Glasser Date: Fri, 30 Jul 2021 12:43:21 -1000 Subject: [PATCH 03/11] Add OnyxProvider --- src/App.js | 9 +---- src/components/OnyxProvider.js | 39 +++++++++++++++++++ src/components/createOnyxContext.js | 5 +-- src/components/withNetwork.js | 6 --- src/components/withPersonalDetails.js | 6 --- src/components/withReportActionsDrafts.js | 6 --- src/pages/home/report/ReportActionItem.js | 2 +- .../home/report/ReportActionItemMessage.js | 2 +- .../home/report/ReportActionItemSingle.js | 2 +- 9 files changed, 45 insertions(+), 32 deletions(-) create mode 100644 src/components/OnyxProvider.js delete mode 100644 src/components/withNetwork.js delete mode 100644 src/components/withPersonalDetails.js delete mode 100644 src/components/withReportActionsDrafts.js diff --git a/src/App.js b/src/App.js index c44eb78398bed..2d6f3f8e7335a 100644 --- a/src/App.js +++ b/src/App.js @@ -3,11 +3,8 @@ import {LogBox} from 'react-native'; import {SafeAreaProvider} from 'react-native-safe-area-context'; import CustomStatusBar from './components/CustomStatusBar'; import ErrorBoundary from './components/ErrorBoundary'; -import {NetworkProvider} from './components/withNetwork'; import {LocaleContextProvider} from './components/withLocalize'; -import {PersonalDetailsProvider} from './components/withPersonalDetails'; -import {ReportActionsDraftsProvider} from './components/withReportActionsDrafts'; - +import OnyxProvider from './components/OnyxProvider'; import Expensify from './Expensify'; import ComposeProviders from './components/ComposeProviders'; @@ -24,11 +21,9 @@ LogBox.ignoreLogs([ const App = () => ( diff --git a/src/components/OnyxProvider.js b/src/components/OnyxProvider.js new file mode 100644 index 0000000000000..89231c9685905 --- /dev/null +++ b/src/components/OnyxProvider.js @@ -0,0 +1,39 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import ONYXKEYS from '../ONYXKEYS'; +import createOnyxContext from './createOnyxContext'; +import ComposeProviders from './ComposeProviders'; + +const [withNetwork, NetworkProvider] = createOnyxContext(ONYXKEYS.NETWORK); +const [withPersonalDetails, PersonalDetailsProvider] = createOnyxContext(ONYXKEYS.PERSONAL_DETAILS); +const [ + withReportActionsDrafts, + ReportActionsDraftsProvider, +] = createOnyxContext(ONYXKEYS.COLLECTION.REPORT_ACTIONS_DRAFTS); + +const propTypes = { + children: PropTypes.node.isRequired, +}; + +const OnyxProvider = props => ( + + {props.children} + +); + +OnyxProvider.displayName = 'OnyxProvider'; +OnyxProvider.propTypes = propTypes; + +export default OnyxProvider; + +export { + withNetwork, + withPersonalDetails, + withReportActionsDrafts, +}; diff --git a/src/components/createOnyxContext.js b/src/components/createOnyxContext.js index 8aa81c43fb92a..77a60a85335f9 100644 --- a/src/components/createOnyxContext.js +++ b/src/components/createOnyxContext.js @@ -27,8 +27,5 @@ export default (onyxKeyName) => { ); - return { - withOnyxKey, - Provider: ProviderWithOnyx, - }; + return [withOnyxKey, ProviderWithOnyx]; }; diff --git a/src/components/withNetwork.js b/src/components/withNetwork.js deleted file mode 100644 index 72ab8106af6c8..0000000000000 --- a/src/components/withNetwork.js +++ /dev/null @@ -1,6 +0,0 @@ -import ONYXKEYS from '../ONYXKEYS'; -import createOnyxContext from './createOnyxContext'; - -const context = createOnyxContext(ONYXKEYS.NETWORK); -export const NetworkProvider = context.Provider; -export default context.withOnyxKey; diff --git a/src/components/withPersonalDetails.js b/src/components/withPersonalDetails.js deleted file mode 100644 index dc86e98a63092..0000000000000 --- a/src/components/withPersonalDetails.js +++ /dev/null @@ -1,6 +0,0 @@ -import ONYXKEYS from '../ONYXKEYS'; -import createOnyxContext from './createOnyxContext'; - -const context = createOnyxContext(ONYXKEYS.PERSONAL_DETAILS); -export const PersonalDetailsProvider = context.Provider; -export default context.withOnyxKey; diff --git a/src/components/withReportActionsDrafts.js b/src/components/withReportActionsDrafts.js deleted file mode 100644 index 070e9412215cd..0000000000000 --- a/src/components/withReportActionsDrafts.js +++ /dev/null @@ -1,6 +0,0 @@ -import ONYXKEYS from '../ONYXKEYS'; -import createOnyxContext from './createOnyxContext'; - -const context = createOnyxContext(ONYXKEYS.COLLECTION.REPORT_ACTIONS_DRAFTS); -export const ReportActionsDraftsProvider = context.Provider; -export default context.withOnyxKey; diff --git a/src/pages/home/report/ReportActionItem.js b/src/pages/home/report/ReportActionItem.js index ab7326ffd11fa..797933a115318 100644 --- a/src/pages/home/report/ReportActionItem.js +++ b/src/pages/home/report/ReportActionItem.js @@ -22,7 +22,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/withReportActionsDrafts'; +import {withReportActionsDrafts} from '../../../components/OnyxProvider'; const propTypes = { /** The ID of the report this action is on. */ diff --git a/src/pages/home/report/ReportActionItemMessage.js b/src/pages/home/report/ReportActionItemMessage.js index 8c459d02b5548..bc06bc1368258 100644 --- a/src/pages/home/report/ReportActionItemMessage.js +++ b/src/pages/home/report/ReportActionItemMessage.js @@ -5,7 +5,7 @@ import _ from 'underscore'; import styles from '../../../styles/styles'; import ReportActionItemFragment from './ReportActionItemFragment'; import ReportActionPropTypes from './ReportActionPropTypes'; -import withNetwork from '../../../components/withNetwork'; +import {withNetwork} from '../../../components/OnyxProvider'; const propTypes = { /** The report action */ diff --git a/src/pages/home/report/ReportActionItemSingle.js b/src/pages/home/report/ReportActionItemSingle.js index 2cf1e216e72a2..2cb7281ac6146 100644 --- a/src/pages/home/report/ReportActionItemSingle.js +++ b/src/pages/home/report/ReportActionItemSingle.js @@ -14,7 +14,7 @@ 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/withPersonalDetails'; +import {withPersonalDetails} from '../../../components/OnyxProvider'; const propTypes = { /** All the data of the action */ From 451d06149841a7b7be25a2d651a2abb8351bd65f Mon Sep 17 00:00:00 2001 From: Marc Glasser Date: Fri, 30 Jul 2021 13:03:25 -1000 Subject: [PATCH 04/11] fix propTypes --- src/components/ComposeProviders.js | 10 ++++++++++ src/components/OnyxProvider.js | 1 + src/components/createOnyxContext.js | 10 ++++++++++ src/pages/home/sidebar/SidebarLinks.js | 5 +++-- 4 files changed, 24 insertions(+), 2 deletions(-) diff --git a/src/components/ComposeProviders.js b/src/components/ComposeProviders.js index 5217e3c149fd7..98dba9d8d2b73 100644 --- a/src/components/ComposeProviders.js +++ b/src/components/ComposeProviders.js @@ -1,4 +1,13 @@ import React from 'react'; +import PropTypes from 'prop-types'; + +const propTypes = { + /** Provider components go here */ + components: PropTypes.arrayOf(PropTypes.object).isRequired, + + /** Rendered child component */ + children: PropTypes.node.isRequired, +}; const ComposeProviders = props => ( <> @@ -8,5 +17,6 @@ const ComposeProviders = props => ( ); +ComposeProviders.propTypes = propTypes; ComposeProviders.displayName = 'ComposeProviders'; export default ComposeProviders; diff --git a/src/components/OnyxProvider.js b/src/components/OnyxProvider.js index 89231c9685905..a2d4f6c4ceaac 100644 --- a/src/components/OnyxProvider.js +++ b/src/components/OnyxProvider.js @@ -12,6 +12,7 @@ const [ ] = createOnyxContext(ONYXKEYS.COLLECTION.REPORT_ACTIONS_DRAFTS); const propTypes = { + /** Rendered child component */ children: PropTypes.node.isRequired, }; diff --git a/src/components/createOnyxContext.js b/src/components/createOnyxContext.js index 77a60a85335f9..aa5b3b0a14a84 100644 --- a/src/components/createOnyxContext.js +++ b/src/components/createOnyxContext.js @@ -1,6 +1,12 @@ import React, {createContext} from 'react'; +import PropTypes from 'prop-types'; import {withOnyx} from 'react-native-onyx'; +const propTypes = { + /** Rendered child component */ + children: PropTypes.node.isRequired, +}; + export default (onyxKeyName) => { const Context = createContext(); const Provider = props => ( @@ -9,6 +15,9 @@ export default (onyxKeyName) => { ); + Provider.propTypes = propTypes; + Provider.displayName = `${onyxKeyName.toUpperCase()}Provider`; + const ProviderWithOnyx = withOnyx({ [onyxKeyName]: { key: onyxKeyName, @@ -27,5 +36,6 @@ export default (onyxKeyName) => { ); + withOnyxKey.displayName = `with${onyxKeyName.toUpperCase()}`; return [withOnyxKey, ProviderWithOnyx]; }; diff --git a/src/pages/home/sidebar/SidebarLinks.js b/src/pages/home/sidebar/SidebarLinks.js index f6005738dd851..d846b67e9e293 100644 --- a/src/pages/home/sidebar/SidebarLinks.js +++ b/src/pages/home/sidebar/SidebarLinks.js @@ -21,7 +21,6 @@ import {participantPropTypes} from './optionPropTypes'; import themeColors from '../../../styles/themes/default'; import withLocalize, {withLocalizePropTypes} from '../../../components/withLocalize'; import * as App from '../../../libs/actions/App'; -import withNetwork from '../../../components/withNetwork'; const propTypes = { /** Toggles the navigation menu open and closed */ @@ -193,7 +192,6 @@ SidebarLinks.defaultProps = defaultProps; export default compose( withLocalize, - withNetwork, withOnyx({ reports: { key: ONYXKEYS.COLLECTION.REPORT, @@ -207,6 +205,9 @@ export default compose( myPersonalDetails: { key: ONYXKEYS.MY_PERSONAL_DETAILS, }, + network: { + key: ONYXKEYS.NETWORK, + }, currentlyViewedReportID: { key: ONYXKEYS.CURRENTLY_VIEWED_REPORTID, }, From 454f96b4f7b973994ffc7276496ccbb6ca65976d Mon Sep 17 00:00:00 2001 From: Marc Glasser Date: Fri, 30 Jul 2021 13:08:18 -1000 Subject: [PATCH 05/11] move Onyx.init() logic to the provider --- src/Expensify.js | 32 +------------------------------- src/components/OnyxProvider.js | 33 +++++++++++++++++++++++++++++++++ 2 files changed, 34 insertions(+), 31 deletions(-) diff --git a/src/Expensify.js b/src/Expensify.js index 53cef50574e63..60134552bde4b 100644 --- a/src/Expensify.js +++ b/src/Expensify.js @@ -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'; @@ -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 */ diff --git a/src/components/OnyxProvider.js b/src/components/OnyxProvider.js index a2d4f6c4ceaac..f88a5e078e31a 100644 --- a/src/components/OnyxProvider.js +++ b/src/components/OnyxProvider.js @@ -1,9 +1,42 @@ 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 [ From 3be6b85cfc5578a9d3f21e75f74b8ed63919e93f Mon Sep 17 00:00:00 2001 From: Marc Glasser Date: Fri, 30 Jul 2021 13:17:35 -1000 Subject: [PATCH 06/11] fix propTypes and displayName --- src/components/ComposeProviders.js | 2 +- src/components/createOnyxContext.js | 32 +++++++++++++++++------------ 2 files changed, 20 insertions(+), 14 deletions(-) diff --git a/src/components/ComposeProviders.js b/src/components/ComposeProviders.js index 98dba9d8d2b73..7cf9af7348d13 100644 --- a/src/components/ComposeProviders.js +++ b/src/components/ComposeProviders.js @@ -3,7 +3,7 @@ import PropTypes from 'prop-types'; const propTypes = { /** Provider components go here */ - components: PropTypes.arrayOf(PropTypes.object).isRequired, + components: PropTypes.arrayOf(PropTypes.oneOfType([PropTypes.object, PropTypes.func])).isRequired, /** Rendered child component */ children: PropTypes.node.isRequired, diff --git a/src/components/createOnyxContext.js b/src/components/createOnyxContext.js index aa5b3b0a14a84..54856c1e2a143 100644 --- a/src/components/createOnyxContext.js +++ b/src/components/createOnyxContext.js @@ -1,6 +1,8 @@ import React, {createContext} 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 */ @@ -16,7 +18,7 @@ export default (onyxKeyName) => { ); Provider.propTypes = propTypes; - Provider.displayName = `${onyxKeyName.toUpperCase()}Provider`; + Provider.displayName = `${Str.UCFirst(onyxKeyName)}Provider`; const ProviderWithOnyx = withOnyx({ [onyxKeyName]: { @@ -24,18 +26,22 @@ export default (onyxKeyName) => { }, })(Provider); - const withOnyxKey = WrappedComponent => props => ( - - {(value) => { - const propsToPass = {...props, [onyxKeyName]: value}; - return ( - // eslint-disable-next-line react/jsx-props-no-spreading - - ); - }} - - ); + const withOnyxKey = (WrappedComponent) => { + const Consumer = props => ( + + {(value) => { + const propsToPass = {...props, [onyxKeyName]: value}; + return ( + // eslint-disable-next-line react/jsx-props-no-spreading + + ); + }} + + ); + + Consumer.displayName = `with${Str.UCFirst(onyxKeyName)}(${getComponentDisplayName(WrappedComponent)})`; + return Consumer; + }; - withOnyxKey.displayName = `with${onyxKeyName.toUpperCase()}`; return [withOnyxKey, ProviderWithOnyx]; }; From 14c061d1b57cba6b0d5d9fdf8dfed867b70534c2 Mon Sep 17 00:00:00 2001 From: Marc Glasser Date: Mon, 2 Aug 2021 11:54:34 -1000 Subject: [PATCH 07/11] move this --- src/App.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/App.js b/src/App.js index 2d6f3f8e7335a..ae1b8860f6062 100644 --- a/src/App.js +++ b/src/App.js @@ -3,9 +3,9 @@ import {LogBox} from 'react-native'; import {SafeAreaProvider} from 'react-native-safe-area-context'; 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 Expensify from './Expensify'; import ComposeProviders from './components/ComposeProviders'; LogBox.ignoreLogs([ From dd7072d73b21d99377a10e025260bea34df45e5e Mon Sep 17 00:00:00 2001 From: Marc Glasser Date: Mon, 2 Aug 2021 18:50:35 -1000 Subject: [PATCH 08/11] Fix dumb linter and add better propTypes --- src/pages/home/report/ReportActionItem.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/pages/home/report/ReportActionItem.js b/src/pages/home/report/ReportActionItem.js index 797933a115318..26efc4b6c5a0a 100644 --- a/src/pages/home/report/ReportActionItem.js +++ b/src/pages/home/report/ReportActionItem.js @@ -47,15 +47,15 @@ const propTypes = { index: PropTypes.number.isRequired, /** Draft message - if this is set the comment is in 'edit' mode */ - // eslint-disable-next-line react/require-default-props - [ONYXKEYS.COLLECTION.REPORT_ACTIONS_DRAFTS]: PropTypes.shape({}), + // eslint-disable-next-line react/no-unused-prop-types + reportActionsDrafts_: PropTypes.objectOf(PropTypes.string), ...windowDimensionsPropTypes, }; const defaultProps = { // eslint-disable-next-line react/default-props-match-prop-types - [ONYXKEYS.COLLECTION.REPORT_ACTIONS_DRAFTS]: {}, + reportActionsDrafts_: {}, hasOutstandingIOU: false, }; From e66626e3f9ae95ed257bc0f5a4002d17f9a6fc6e Mon Sep 17 00:00:00 2001 From: Marc Glasser Date: Mon, 2 Aug 2021 18:52:27 -1000 Subject: [PATCH 09/11] remove unnecessary comment --- src/pages/home/report/ReportActionItem.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/pages/home/report/ReportActionItem.js b/src/pages/home/report/ReportActionItem.js index 26efc4b6c5a0a..e6c6d10c1336a 100644 --- a/src/pages/home/report/ReportActionItem.js +++ b/src/pages/home/report/ReportActionItem.js @@ -54,7 +54,6 @@ const propTypes = { }; const defaultProps = { - // eslint-disable-next-line react/default-props-match-prop-types reportActionsDrafts_: {}, hasOutstandingIOU: false, }; From 33408b80acfaf0e9b54aa59fe6fd247e188bab37 Mon Sep 17 00:00:00 2001 From: Marc Glasser Date: Tue, 3 Aug 2021 06:54:09 -1000 Subject: [PATCH 10/11] Fix propTypes and add transformValue --- src/components/createOnyxContext.js | 7 ++- src/pages/home/report/ReportActionItem.js | 47 ++++++++----------- .../home/report/ReportActionItemMessage.js | 2 +- .../home/report/ReportActionItemSingle.js | 2 +- 4 files changed, 27 insertions(+), 31 deletions(-) diff --git a/src/components/createOnyxContext.js b/src/components/createOnyxContext.js index 54856c1e2a143..e79ed10ddffac 100644 --- a/src/components/createOnyxContext.js +++ b/src/components/createOnyxContext.js @@ -26,11 +26,14 @@ export default (onyxKeyName) => { }, })(Provider); - const withOnyxKey = (WrappedComponent) => { + const withOnyxKey = ({propName = onyxKeyName, transformValue = () => {}} = {}) => (WrappedComponent) => { const Consumer = props => ( {(value) => { - const propsToPass = {...props, [onyxKeyName]: value}; + const propsToPass = { + ...props, + [propName]: transformValue ? transformValue(value, props) : value, + }; return ( // eslint-disable-next-line react/jsx-props-no-spreading diff --git a/src/pages/home/report/ReportActionItem.js b/src/pages/home/report/ReportActionItem.js index e6c6d10c1336a..397bed900f1c2 100644 --- a/src/pages/home/report/ReportActionItem.js +++ b/src/pages/home/report/ReportActionItem.js @@ -47,14 +47,13 @@ const propTypes = { index: PropTypes.number.isRequired, /** Draft message - if this is set the comment is in 'edit' mode */ - // eslint-disable-next-line react/no-unused-prop-types - reportActionsDrafts_: PropTypes.objectOf(PropTypes.string), + draftMessage: PropTypes.string, ...windowDimensionsPropTypes, }; const defaultProps = { - reportActionsDrafts_: {}, + draftMessage: '', hasOutstandingIOU: false, }; @@ -71,7 +70,7 @@ class ReportActionItem extends Component { shouldComponentUpdate(nextProps, nextState) { return this.props.displayAsGroup !== nextProps.displayAsGroup - || this.getDraftMessage() !== this.getDraftMessage(nextProps) + || this.props.draftMessage !== nextProps.draftMessage || this.props.isMostRecentIOUReportAction !== nextProps.isMostRecentIOUReportAction || this.props.hasOutstandingIOU !== nextProps.hasOutstandingIOU || this.props.shouldDisplayNewIndicator !== nextProps.shouldDisplayNewIndicator @@ -79,19 +78,6 @@ class ReportActionItem extends Component { || this.state.isContextMenuActive !== nextState.isContextMenuActive; } - /** - * @param {Object} props - * @returns {String} - */ - getDraftMessage(props) { - const propsToUse = props || this.props; - const drafts = propsToUse[ONYXKEYS.COLLECTION.REPORT_ACTIONS_DRAFTS] || {}; - // eslint-disable-next-line max-len - const draftKey = `${ONYXKEYS.COLLECTION.REPORT_ACTIONS_DRAFTS}${propsToUse.reportID}_${propsToUse.action.reportActionID}`; - const draft = drafts[draftKey]; - return draft || ''; - } - /** * Show the ReportActionContextMenu modal popover. * @@ -100,7 +86,7 @@ class ReportActionItem extends Component { */ showPopover(event, selection) { // Block menu on the message being Edited - if (this.getDraftMessage()) { + if (this.props.draftMessage) { return; } showContextMenu( @@ -109,7 +95,7 @@ class ReportActionItem extends Component { this.popoverAnchor, this.props.reportID, this.props.action, - this.getDraftMessage(), + this.props.draftMessage, this.checkIfContextMenuActive, this.checkIfContextMenuActive, ); @@ -120,7 +106,6 @@ class ReportActionItem extends Component { } render() { - const draftMessage = this.getDraftMessage(); let children; if (this.props.action.actionName === CONST.REPORT.ACTIONS.TYPE.IOU) { children = ( @@ -131,12 +116,12 @@ class ReportActionItem extends Component { /> ); } else { - children = !draftMessage + children = !this.props.draftMessage ? : ( @@ -148,7 +133,7 @@ class ReportActionItem extends Component { onPressIn={() => this.props.isSmallScreenWidth && canUseTouchScreen() && ControlSelection.block()} onPressOut={() => ControlSelection.unblock()} onSecondaryInteraction={this.showPopover} - preventDefaultContentMenu={!draftMessage} + preventDefaultContentMenu={!this.props.draftMessage} > @@ -161,7 +146,7 @@ class ReportActionItem extends Component { style={getReportActionItemStyle( hovered || this.state.isContextMenuActive - || draftMessage, + || this.props.draftMessage, )} > {!this.props.displayAsGroup @@ -183,10 +168,10 @@ class ReportActionItem extends Component { isVisible={ hovered && !this.state.isContextMenuActive - && !draftMessage + && !this.props.draftMessage } - draftMessage={draftMessage} + draftMessage={this.props.draftMessage} /> )} @@ -200,5 +185,13 @@ ReportActionItem.defaultProps = defaultProps; export default compose( withWindowDimensions, - withReportActionsDrafts, + withReportActionsDrafts({ + propName: 'draftMessage', + transformValue: (drafts, props) => { + const {reportID, action} = props; + const draftKey = `${ONYXKEYS.COLLECTION.REPORT_ACTIONS_DRAFTS}${reportID}_${action.reportActionID}`; + const draft = drafts[draftKey]; + return draft || ''; + }, + }), )(ReportActionItem); diff --git a/src/pages/home/report/ReportActionItemMessage.js b/src/pages/home/report/ReportActionItemMessage.js index bc06bc1368258..d57e63e0cdad7 100644 --- a/src/pages/home/report/ReportActionItemMessage.js +++ b/src/pages/home/report/ReportActionItemMessage.js @@ -42,4 +42,4 @@ ReportActionItemMessage.propTypes = propTypes; ReportActionItemMessage.defaultProps = defaultProps; ReportActionItemMessage.displayName = 'ReportActionItemMessage'; -export default withNetwork(ReportActionItemMessage); +export default withNetwork()(ReportActionItemMessage); diff --git a/src/pages/home/report/ReportActionItemSingle.js b/src/pages/home/report/ReportActionItemSingle.js index 2cb7281ac6146..7f05458a4af6b 100644 --- a/src/pages/home/report/ReportActionItemSingle.js +++ b/src/pages/home/report/ReportActionItemSingle.js @@ -98,5 +98,5 @@ ReportActionItemSingle.displayName = 'ReportActionItemSingle'; export default compose( withLocalize, - withPersonalDetails, + withPersonalDetails(), )(ReportActionItemSingle); From 5958d481ba7566cdae7374b1e9d29a76f22bf5f7 Mon Sep 17 00:00:00 2001 From: Marc Glasser Date: Wed, 4 Aug 2021 09:28:07 -1000 Subject: [PATCH 11/11] use forwardRef + lodashGet --- src/components/createOnyxContext.js | 8 ++++---- src/pages/home/report/ReportActionItem.js | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/components/createOnyxContext.js b/src/components/createOnyxContext.js index e79ed10ddffac..9304efd4a430b 100644 --- a/src/components/createOnyxContext.js +++ b/src/components/createOnyxContext.js @@ -1,4 +1,4 @@ -import React, {createContext} from 'react'; +import React, {createContext, forwardRef} from 'react'; import PropTypes from 'prop-types'; import {withOnyx} from 'react-native-onyx'; import Str from 'expensify-common/lib/str'; @@ -27,7 +27,7 @@ export default (onyxKeyName) => { })(Provider); const withOnyxKey = ({propName = onyxKeyName, transformValue = () => {}} = {}) => (WrappedComponent) => { - const Consumer = props => ( + const Consumer = forwardRef((props, ref) => ( {(value) => { const propsToPass = { @@ -36,11 +36,11 @@ export default (onyxKeyName) => { }; return ( // eslint-disable-next-line react/jsx-props-no-spreading - + ); }} - ); + )); Consumer.displayName = `with${Str.UCFirst(onyxKeyName)}(${getComponentDisplayName(WrappedComponent)})`; return Consumer; diff --git a/src/pages/home/report/ReportActionItem.js b/src/pages/home/report/ReportActionItem.js index 397bed900f1c2..641ad7904ebb7 100644 --- a/src/pages/home/report/ReportActionItem.js +++ b/src/pages/home/report/ReportActionItem.js @@ -1,4 +1,5 @@ import _ from 'underscore'; +import lodashGet from 'lodash/get'; import React, {Component} from 'react'; import {View} from 'react-native'; import PropTypes from 'prop-types'; @@ -190,8 +191,7 @@ export default compose( transformValue: (drafts, props) => { const {reportID, action} = props; const draftKey = `${ONYXKEYS.COLLECTION.REPORT_ACTIONS_DRAFTS}${reportID}_${action.reportActionID}`; - const draft = drafts[draftKey]; - return draft || ''; + return lodashGet(drafts, draftKey, ''); }, }), )(ReportActionItem);