From 627bdf2ce86072b9b531ebfa389e2ce481b2e545 Mon Sep 17 00:00:00 2001 From: Georgia Monahan Date: Wed, 21 Feb 2024 11:58:29 -0800 Subject: [PATCH 001/213] Remove unnecssary calls to OpenPrivatePersonalDetailsPage --- .../Profile/PersonalDetails/AddressPage.js | 10 ------ .../PersonalDetails/DateOfBirthPage.js | 9 ----- .../Profile/PersonalDetails/LegalNamePage.js | 8 ----- src/pages/settings/Profile/ProfilePage.js | 36 +++++++------------ 4 files changed, 12 insertions(+), 51 deletions(-) diff --git a/src/pages/settings/Profile/PersonalDetails/AddressPage.js b/src/pages/settings/Profile/PersonalDetails/AddressPage.js index 55d221085fcb..b8bbf0d4fad2 100644 --- a/src/pages/settings/Profile/PersonalDetails/AddressPage.js +++ b/src/pages/settings/Profile/PersonalDetails/AddressPage.js @@ -3,12 +3,9 @@ import PropTypes from 'prop-types'; import React, {useCallback, useEffect, useMemo, useState} from 'react'; import {withOnyx} from 'react-native-onyx'; import AddressForm from '@components/AddressForm'; -import FullscreenLoadingIndicator from '@components/FullscreenLoadingIndicator'; import HeaderWithBackButton from '@components/HeaderWithBackButton'; import ScreenWrapper from '@components/ScreenWrapper'; import useLocalize from '@hooks/useLocalize'; -import usePrivatePersonalDetails from '@hooks/usePrivatePersonalDetails'; -import useThemeStyles from '@hooks/useThemeStyles'; import Navigation from '@libs/Navigation/Navigation'; import * as PersonalDetails from '@userActions/PersonalDetails'; import ONYXKEYS from '@src/ONYXKEYS'; @@ -59,13 +56,10 @@ function updateAddress(values) { } function AddressPage({privatePersonalDetails, route}) { - const styles = useThemeStyles(); - usePrivatePersonalDetails(); const {translate} = useLocalize(); const address = useMemo(() => lodashGet(privatePersonalDetails, 'address') || {}, [privatePersonalDetails]); const countryFromUrl = lodashGet(route, 'params.country'); const [currentCountry, setCurrentCountry] = useState(address.country); - const isLoadingPersonalDetails = lodashGet(privatePersonalDetails, 'isLoading', true); const [street1, street2] = (address.street || '').split('\n'); const [state, setState] = useState(address.state); const [city, setCity] = useState(address.city); @@ -123,9 +117,6 @@ function AddressPage({privatePersonalDetails, route}) { shouldShowBackButton onBackButtonPress={() => Navigation.goBack()} /> - {isLoadingPersonalDetails ? ( - - ) : ( - )} ); } diff --git a/src/pages/settings/Profile/PersonalDetails/DateOfBirthPage.js b/src/pages/settings/Profile/PersonalDetails/DateOfBirthPage.js index 943d9fe0bab7..7861b2486c49 100644 --- a/src/pages/settings/Profile/PersonalDetails/DateOfBirthPage.js +++ b/src/pages/settings/Profile/PersonalDetails/DateOfBirthPage.js @@ -1,16 +1,13 @@ import {subYears} from 'date-fns'; -import lodashGet from 'lodash/get'; import PropTypes from 'prop-types'; import React, {useCallback} from 'react'; import {withOnyx} from 'react-native-onyx'; import DatePicker from '@components/DatePicker'; import FormProvider from '@components/Form/FormProvider'; import InputWrapper from '@components/Form/InputWrapper'; -import FullscreenLoadingIndicator from '@components/FullscreenLoadingIndicator'; import HeaderWithBackButton from '@components/HeaderWithBackButton'; import ScreenWrapper from '@components/ScreenWrapper'; import withLocalize, {withLocalizePropTypes} from '@components/withLocalize'; -import usePrivatePersonalDetails from '@hooks/usePrivatePersonalDetails'; import useThemeStyles from '@hooks/useThemeStyles'; import compose from '@libs/compose'; import Navigation from '@libs/Navigation/Navigation'; @@ -39,8 +36,6 @@ const defaultProps = { function DateOfBirthPage({translate, privatePersonalDetails}) { const styles = useThemeStyles(); - usePrivatePersonalDetails(); - const isLoadingPersonalDetails = lodashGet(privatePersonalDetails, 'isLoading', true); /** * @param {Object} values @@ -71,9 +66,6 @@ function DateOfBirthPage({translate, privatePersonalDetails}) { title={translate('common.dob')} onBackButtonPress={() => Navigation.goBack()} /> - {isLoadingPersonalDetails ? ( - - ) : ( - )} ); } diff --git a/src/pages/settings/Profile/PersonalDetails/LegalNamePage.js b/src/pages/settings/Profile/PersonalDetails/LegalNamePage.js index 0e81ea5194c1..5152982a3c6f 100644 --- a/src/pages/settings/Profile/PersonalDetails/LegalNamePage.js +++ b/src/pages/settings/Profile/PersonalDetails/LegalNamePage.js @@ -6,12 +6,10 @@ import {withOnyx} from 'react-native-onyx'; import _ from 'underscore'; import FormProvider from '@components/Form/FormProvider'; import InputWrapper from '@components/Form/InputWrapper'; -import FullscreenLoadingIndicator from '@components/FullscreenLoadingIndicator'; import HeaderWithBackButton from '@components/HeaderWithBackButton'; import ScreenWrapper from '@components/ScreenWrapper'; import TextInput from '@components/TextInput'; import withLocalize, {withLocalizePropTypes} from '@components/withLocalize'; -import usePrivatePersonalDetails from '@hooks/usePrivatePersonalDetails'; import useThemeStyles from '@hooks/useThemeStyles'; import compose from '@libs/compose'; import * as ErrorUtils from '@libs/ErrorUtils'; @@ -47,10 +45,8 @@ const updateLegalName = (values) => { function LegalNamePage(props) { const styles = useThemeStyles(); - usePrivatePersonalDetails(); const legalFirstName = lodashGet(props.privatePersonalDetails, 'legalFirstName', ''); const legalLastName = lodashGet(props.privatePersonalDetails, 'legalLastName', ''); - const isLoadingPersonalDetails = lodashGet(props.privatePersonalDetails, 'isLoading', true); const validate = useCallback((values) => { const errors = {}; @@ -90,9 +86,6 @@ function LegalNamePage(props) { title={props.translate('privatePersonalDetails.legalName')} onBackButtonPress={() => Navigation.goBack()} /> - {isLoadingPersonalDetails ? ( - - ) : ( - )} ); } diff --git a/src/pages/settings/Profile/ProfilePage.js b/src/pages/settings/Profile/ProfilePage.js index 2872fae5b511..7572db87f4b9 100755 --- a/src/pages/settings/Profile/ProfilePage.js +++ b/src/pages/settings/Profile/ProfilePage.js @@ -4,7 +4,6 @@ import React, {useEffect} from 'react'; import {ScrollView, View} from 'react-native'; import {withOnyx} from 'react-native-onyx'; import _ from 'underscore'; -import FullscreenLoadingIndicator from '@components/FullscreenLoadingIndicator'; import HeaderWithBackButton from '@components/HeaderWithBackButton'; import * as Illustrations from '@components/Icon/Illustrations'; import MenuItemWithTopDescription from '@components/MenuItemWithTopDescription'; @@ -13,9 +12,6 @@ import Section from '@components/Section'; import withCurrentUserPersonalDetails, {withCurrentUserPersonalDetailsDefaultProps, withCurrentUserPersonalDetailsPropTypes} from '@components/withCurrentUserPersonalDetails'; import withLocalize, {withLocalizePropTypes} from '@components/withLocalize'; import withWindowDimensions, {windowDimensionsPropTypes} from '@components/withWindowDimensions'; -import usePrivatePersonalDetails from '@hooks/usePrivatePersonalDetails'; -import useStyleUtils from '@hooks/useStyleUtils'; -import useTheme from '@hooks/useTheme'; import useThemeStyles from '@hooks/useThemeStyles'; import useWindowDimensions from '@hooks/useWindowDimensions'; import compose from '@libs/compose'; @@ -82,9 +78,7 @@ const defaultProps = { }; function ProfilePage(props) { - const theme = useTheme(); const styles = useThemeStyles(); - const StyleUtils = useStyleUtils(); const getPronouns = () => { let pronounsKey = lodashGet(props.currentUserPersonalDetails, 'pronouns', ''); if (pronounsKey.startsWith(CONST.PRONOUNS.PREFIX)) { @@ -100,10 +94,8 @@ function ProfilePage(props) { const contactMethodBrickRoadIndicator = UserUtils.getLoginListBrickRoadIndicator(props.loginList); const emojiCode = lodashGet(props, 'currentUserPersonalDetails.status.emojiCode', ''); const {isSmallScreenWidth} = useWindowDimensions(); - usePrivatePersonalDetails(); const privateDetails = props.privatePersonalDetails || {}; const legalName = `${privateDetails.legalFirstName || ''} ${privateDetails.legalLastName || ''}`.trim(); - const isLoadingPersonalDetails = lodashGet(props.privatePersonalDetails, 'isLoading', true); const publicOptions = [ { @@ -198,22 +190,18 @@ function ProfilePage(props) { childrenStyles={styles.pt3} titleStyles={styles.accountSettingsSectionTitle} > - {isLoadingPersonalDetails ? ( - - ) : ( - <> - {_.map(privateOptions, (detail, index) => ( - Navigation.navigate(detail.pageRoute)} - /> - ))} - - )} + <> + {_.map(privateOptions, (detail, index) => ( + Navigation.navigate(detail.pageRoute)} + /> + ))} + From e5b5536a176f3c113760cfa24888111bf8945c38 Mon Sep 17 00:00:00 2001 From: Georgia Monahan Date: Wed, 21 Feb 2024 12:04:06 -0800 Subject: [PATCH 002/213] remaining uses --- src/components/CardPreview.tsx | 2 -- src/hooks/usePrivatePersonalDetails.ts | 20 ------------------- .../settings/Wallet/ReportCardLostPage.js | 2 -- .../Wallet/WalletPage/CardDetails.tsx | 2 -- 4 files changed, 26 deletions(-) delete mode 100644 src/hooks/usePrivatePersonalDetails.ts diff --git a/src/components/CardPreview.tsx b/src/components/CardPreview.tsx index 3ac56d6b26a8..acdf8a92fb5e 100644 --- a/src/components/CardPreview.tsx +++ b/src/components/CardPreview.tsx @@ -3,7 +3,6 @@ import {View} from 'react-native'; import type {OnyxEntry} from 'react-native-onyx'; import {withOnyx} from 'react-native-onyx'; import ExpensifyCardImage from '@assets/images/expensify-card.svg'; -import usePrivatePersonalDetails from '@hooks/usePrivatePersonalDetails'; import useThemeStyles from '@hooks/useThemeStyles'; import variables from '@styles/variables'; import ONYXKEYS from '@src/ONYXKEYS'; @@ -22,7 +21,6 @@ type CardPreviewProps = CardPreviewOnyxProps; function CardPreview({privatePersonalDetails, session}: CardPreviewProps) { const styles = useThemeStyles(); - usePrivatePersonalDetails(); const {legalFirstName, legalLastName} = privatePersonalDetails ?? {}; const cardHolder = legalFirstName && legalLastName ? `${legalFirstName} ${legalLastName}` : session?.email ?? ''; diff --git a/src/hooks/usePrivatePersonalDetails.ts b/src/hooks/usePrivatePersonalDetails.ts deleted file mode 100644 index f17600e9878f..000000000000 --- a/src/hooks/usePrivatePersonalDetails.ts +++ /dev/null @@ -1,20 +0,0 @@ -import {useContext, useEffect} from 'react'; -import {NetworkContext} from '@components/OnyxProvider'; -import * as PersonalDetails from '@userActions/PersonalDetails'; - -/** - * Hook for fetching private personal details - */ -export default function usePrivatePersonalDetails() { - const network = useContext(NetworkContext); - - useEffect(() => { - const personalDetails = PersonalDetails.getPrivatePersonalDetails(); - // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing - if (network?.isOffline || (Boolean(personalDetails) && personalDetails?.isLoading !== undefined)) { - return; - } - - PersonalDetails.openPersonalDetails(); - }, [network?.isOffline]); -} diff --git a/src/pages/settings/Wallet/ReportCardLostPage.js b/src/pages/settings/Wallet/ReportCardLostPage.js index b78c0b6bdc21..ff33fbd73099 100644 --- a/src/pages/settings/Wallet/ReportCardLostPage.js +++ b/src/pages/settings/Wallet/ReportCardLostPage.js @@ -11,7 +11,6 @@ import SingleOptionSelector from '@components/SingleOptionSelector'; import Text from '@components/Text'; import useLocalize from '@hooks/useLocalize'; import usePrevious from '@hooks/usePrevious'; -import usePrivatePersonalDetails from '@hooks/usePrivatePersonalDetails'; import useThemeStyles from '@hooks/useThemeStyles'; import * as CardUtils from '@libs/CardUtils'; import Navigation from '@libs/Navigation/Navigation'; @@ -91,7 +90,6 @@ function ReportCardLostPage({ formData, }) { const styles = useThemeStyles(); - usePrivatePersonalDetails(); const domainCards = CardUtils.getDomainCards(cardList)[domain]; const physicalCard = CardUtils.findPhysicalCard(domainCards); diff --git a/src/pages/settings/Wallet/WalletPage/CardDetails.tsx b/src/pages/settings/Wallet/WalletPage/CardDetails.tsx index 77458384c214..4cce60cfa5c0 100644 --- a/src/pages/settings/Wallet/WalletPage/CardDetails.tsx +++ b/src/pages/settings/Wallet/WalletPage/CardDetails.tsx @@ -7,7 +7,6 @@ import MenuItemWithTopDescription from '@components/MenuItemWithTopDescription'; import PressableWithDelayToggle from '@components/Pressable/PressableWithDelayToggle'; import TextLink from '@components/TextLink'; import useLocalize from '@hooks/useLocalize'; -import usePrivatePersonalDetails from '@hooks/usePrivatePersonalDetails'; import useThemeStyles from '@hooks/useThemeStyles'; import Clipboard from '@libs/Clipboard'; import Navigation from '@libs/Navigation/Navigation'; @@ -47,7 +46,6 @@ type CardDetailsProps = CardDetailsOnyxProps & { function CardDetails({pan = '', expiration = '', cvv = '', privatePersonalDetails, domain}: CardDetailsProps) { const styles = useThemeStyles(); - usePrivatePersonalDetails(); const {translate} = useLocalize(); const handleCopyToClipboard = () => { From ed012d5ad9ae9effc8af71235dd9c296ead00120 Mon Sep 17 00:00:00 2001 From: Georgia Monahan Date: Wed, 21 Feb 2024 12:09:52 -0800 Subject: [PATCH 003/213] prettier --- .../Profile/PersonalDetails/AddressPage.js | 24 +++---- .../PersonalDetails/DateOfBirthPage.js | 34 +++++----- .../Profile/PersonalDetails/LegalNamePage.js | 66 +++++++++---------- 3 files changed, 62 insertions(+), 62 deletions(-) diff --git a/src/pages/settings/Profile/PersonalDetails/AddressPage.js b/src/pages/settings/Profile/PersonalDetails/AddressPage.js index b8bbf0d4fad2..89650f978e05 100644 --- a/src/pages/settings/Profile/PersonalDetails/AddressPage.js +++ b/src/pages/settings/Profile/PersonalDetails/AddressPage.js @@ -117,18 +117,18 @@ function AddressPage({privatePersonalDetails, route}) { shouldShowBackButton onBackButtonPress={() => Navigation.goBack()} /> - + ); } diff --git a/src/pages/settings/Profile/PersonalDetails/DateOfBirthPage.js b/src/pages/settings/Profile/PersonalDetails/DateOfBirthPage.js index 7861b2486c49..ba7db33bb070 100644 --- a/src/pages/settings/Profile/PersonalDetails/DateOfBirthPage.js +++ b/src/pages/settings/Profile/PersonalDetails/DateOfBirthPage.js @@ -66,23 +66,23 @@ function DateOfBirthPage({translate, privatePersonalDetails}) { title={translate('common.dob')} onBackButtonPress={() => Navigation.goBack()} /> - - - + + + ); } diff --git a/src/pages/settings/Profile/PersonalDetails/LegalNamePage.js b/src/pages/settings/Profile/PersonalDetails/LegalNamePage.js index 5152982a3c6f..ec1a48148687 100644 --- a/src/pages/settings/Profile/PersonalDetails/LegalNamePage.js +++ b/src/pages/settings/Profile/PersonalDetails/LegalNamePage.js @@ -86,39 +86,39 @@ function LegalNamePage(props) { title={props.translate('privatePersonalDetails.legalName')} onBackButtonPress={() => Navigation.goBack()} /> - - - - - - - - + + + + + + + + ); } From 8410d344f4c9de1a45410d76dcb6572da3e190a7 Mon Sep 17 00:00:00 2001 From: Georgia Monahan Date: Wed, 21 Feb 2024 16:31:59 -0800 Subject: [PATCH 004/213] fix LegalNamePage loading --- .../Profile/PersonalDetails/LegalNamePage.js | 80 +++++++++++-------- 1 file changed, 45 insertions(+), 35 deletions(-) diff --git a/src/pages/settings/Profile/PersonalDetails/LegalNamePage.js b/src/pages/settings/Profile/PersonalDetails/LegalNamePage.js index ec1a48148687..0bd7f4b70d34 100644 --- a/src/pages/settings/Profile/PersonalDetails/LegalNamePage.js +++ b/src/pages/settings/Profile/PersonalDetails/LegalNamePage.js @@ -6,6 +6,7 @@ import {withOnyx} from 'react-native-onyx'; import _ from 'underscore'; import FormProvider from '@components/Form/FormProvider'; import InputWrapper from '@components/Form/InputWrapper'; +import FullscreenLoadingIndicator from '@components/FullscreenLoadingIndicator'; import HeaderWithBackButton from '@components/HeaderWithBackButton'; import ScreenWrapper from '@components/ScreenWrapper'; import TextInput from '@components/TextInput'; @@ -20,15 +21,16 @@ import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import INPUT_IDS from '@src/types/form/LegalNameForm'; + const propTypes = { /* Onyx Props */ - - /** User's private personal details */ privatePersonalDetails: PropTypes.shape({ legalFirstName: PropTypes.string, legalLastName: PropTypes.string, }), + isLoadingApp: PropTypes.bool, + ...withLocalizePropTypes, }; @@ -37,6 +39,7 @@ const defaultProps = { legalFirstName: '', legalLastName: '', }, + isLoadingApp: true, }; const updateLegalName = (values) => { @@ -86,39 +89,43 @@ function LegalNamePage(props) { title={props.translate('privatePersonalDetails.legalName')} onBackButtonPress={() => Navigation.goBack()} /> - - - - - - - - + {props.isLoadingApp ? ( + + ) : ( + + + + + + + + + )} ); } @@ -133,5 +140,8 @@ export default compose( privatePersonalDetails: { key: ONYXKEYS.PRIVATE_PERSONAL_DETAILS, }, + isLoadingApp: { + key: ONYXKEYS.IS_LOADING_APP, + }, }), )(LegalNamePage); From e33dc8554f4a0cecc3cdb26bed85e7c7432b3dca Mon Sep 17 00:00:00 2001 From: Georgia Monahan Date: Wed, 21 Feb 2024 16:36:19 -0800 Subject: [PATCH 005/213] fix direct link loading for AddressPage --- .../Profile/PersonalDetails/AddressPage.js | 39 ++++++++++++------- 1 file changed, 26 insertions(+), 13 deletions(-) diff --git a/src/pages/settings/Profile/PersonalDetails/AddressPage.js b/src/pages/settings/Profile/PersonalDetails/AddressPage.js index 89650f978e05..2df72e1958bc 100644 --- a/src/pages/settings/Profile/PersonalDetails/AddressPage.js +++ b/src/pages/settings/Profile/PersonalDetails/AddressPage.js @@ -3,9 +3,11 @@ import PropTypes from 'prop-types'; import React, {useCallback, useEffect, useMemo, useState} from 'react'; import {withOnyx} from 'react-native-onyx'; import AddressForm from '@components/AddressForm'; +import FullscreenLoadingIndicator from '@components/FullscreenLoadingIndicator'; import HeaderWithBackButton from '@components/HeaderWithBackButton'; import ScreenWrapper from '@components/ScreenWrapper'; import useLocalize from '@hooks/useLocalize'; +import useThemeStyles from '@hooks/useThemeStyles'; import Navigation from '@libs/Navigation/Navigation'; import * as PersonalDetails from '@userActions/PersonalDetails'; import ONYXKEYS from '@src/ONYXKEYS'; @@ -25,6 +27,8 @@ const propTypes = { }), }), + isLoadingApp: PropTypes.bool, + /** Route from navigation */ route: PropTypes.shape({ /** Params from the route */ @@ -45,6 +49,7 @@ const defaultProps = { country: '', }, }, + isLoadingApp: true, }; /** @@ -55,7 +60,8 @@ function updateAddress(values) { PersonalDetails.updateAddress(values.addressLine1.trim(), values.addressLine2.trim(), values.city.trim(), values.state.trim(), values.zipPostCode.trim().toUpperCase(), values.country); } -function AddressPage({privatePersonalDetails, route}) { +function AddressPage({privatePersonalDetails, route, isLoadingApp}) { + const styles = useThemeStyles(); const {translate} = useLocalize(); const address = useMemo(() => lodashGet(privatePersonalDetails, 'address') || {}, [privatePersonalDetails]); const countryFromUrl = lodashGet(route, 'params.country'); @@ -117,18 +123,22 @@ function AddressPage({privatePersonalDetails, route}) { shouldShowBackButton onBackButtonPress={() => Navigation.goBack()} /> - + {isLoadingApp ? ( + + ) : ( + + )} ); } @@ -141,4 +151,7 @@ export default withOnyx({ privatePersonalDetails: { key: ONYXKEYS.PRIVATE_PERSONAL_DETAILS, }, + isLoadingApp: { + key: ONYXKEYS.IS_LOADING_APP, + }, })(AddressPage); From 3d31a5e28963a52fb252d4b2c84285366740ad8e Mon Sep 17 00:00:00 2001 From: Georgia Monahan Date: Wed, 21 Feb 2024 16:42:23 -0800 Subject: [PATCH 006/213] Loading pages --- .../PersonalDetails/DateOfBirthPage.js | 47 ++++++++++++------- src/pages/settings/Profile/ProfilePage.js | 36 +++++++++----- 2 files changed, 53 insertions(+), 30 deletions(-) diff --git a/src/pages/settings/Profile/PersonalDetails/DateOfBirthPage.js b/src/pages/settings/Profile/PersonalDetails/DateOfBirthPage.js index ba7db33bb070..b10c768e3df2 100644 --- a/src/pages/settings/Profile/PersonalDetails/DateOfBirthPage.js +++ b/src/pages/settings/Profile/PersonalDetails/DateOfBirthPage.js @@ -5,6 +5,7 @@ import {withOnyx} from 'react-native-onyx'; import DatePicker from '@components/DatePicker'; import FormProvider from '@components/Form/FormProvider'; import InputWrapper from '@components/Form/InputWrapper'; +import FullscreenLoadingIndicator from '@components/FullscreenLoadingIndicator'; import HeaderWithBackButton from '@components/HeaderWithBackButton'; import ScreenWrapper from '@components/ScreenWrapper'; import withLocalize, {withLocalizePropTypes} from '@components/withLocalize'; @@ -25,6 +26,8 @@ const propTypes = { dob: PropTypes.string, }), + isLoadingApp: PropTypes.bool, + ...withLocalizePropTypes, }; @@ -32,9 +35,10 @@ const defaultProps = { privatePersonalDetails: { dob: '', }, + isLoadingApp: true, }; -function DateOfBirthPage({translate, privatePersonalDetails}) { +function DateOfBirthPage({translate, privatePersonalDetails, isLoadingApp}) { const styles = useThemeStyles(); /** @@ -66,23 +70,27 @@ function DateOfBirthPage({translate, privatePersonalDetails}) { title={translate('common.dob')} onBackButtonPress={() => Navigation.goBack()} /> - - - + {isLoadingApp ? ( + + ) : ( + + + + )} ); } @@ -97,5 +105,8 @@ export default compose( privatePersonalDetails: { key: ONYXKEYS.PRIVATE_PERSONAL_DETAILS, }, + isLoadingApp: { + key: ONYXKEYS.IS_LOADING_APP, + }, }), )(DateOfBirthPage); diff --git a/src/pages/settings/Profile/ProfilePage.js b/src/pages/settings/Profile/ProfilePage.js index 7572db87f4b9..fd8b15404a42 100755 --- a/src/pages/settings/Profile/ProfilePage.js +++ b/src/pages/settings/Profile/ProfilePage.js @@ -4,6 +4,7 @@ import React, {useEffect} from 'react'; import {ScrollView, View} from 'react-native'; import {withOnyx} from 'react-native-onyx'; import _ from 'underscore'; +import FullscreenLoadingIndicator from '@components/FullscreenLoadingIndicator'; import HeaderWithBackButton from '@components/HeaderWithBackButton'; import * as Illustrations from '@components/Icon/Illustrations'; import MenuItemWithTopDescription from '@components/MenuItemWithTopDescription'; @@ -14,6 +15,8 @@ import withLocalize, {withLocalizePropTypes} from '@components/withLocalize'; import withWindowDimensions, {windowDimensionsPropTypes} from '@components/withWindowDimensions'; import useThemeStyles from '@hooks/useThemeStyles'; import useWindowDimensions from '@hooks/useWindowDimensions'; +import useStyleUtils from '@hooks/useStyleUtils'; +import useTheme from '@hooks/useTheme'; import compose from '@libs/compose'; import {translatableTextPropTypes} from '@libs/Localize'; import Navigation from '@libs/Navigation/Navigation'; @@ -78,7 +81,9 @@ const defaultProps = { }; function ProfilePage(props) { + const theme = useTheme(); const styles = useThemeStyles(); + const StyleUtils = useStyleUtils(); const getPronouns = () => { let pronounsKey = lodashGet(props.currentUserPersonalDetails, 'pronouns', ''); if (pronounsKey.startsWith(CONST.PRONOUNS.PREFIX)) { @@ -190,18 +195,22 @@ function ProfilePage(props) { childrenStyles={styles.pt3} titleStyles={styles.accountSettingsSectionTitle} > - <> - {_.map(privateOptions, (detail, index) => ( - Navigation.navigate(detail.pageRoute)} - /> - ))} - + {props.isLoadingApp ? ( + + ) : ( + <> + {_.map(privateOptions, (detail, index) => ( + Navigation.navigate(detail.pageRoute)} + /> + ))} + + )} @@ -227,5 +236,8 @@ export default compose( user: { key: ONYXKEYS.USER, }, + isLoadingApp: { + key: ONYXKEYS.IS_LOADING_APP, + }, }), )(ProfilePage); From 26d1f573504218836f360c6b65d19cdf3855007e Mon Sep 17 00:00:00 2001 From: Georgia Monahan Date: Wed, 21 Feb 2024 16:45:21 -0800 Subject: [PATCH 007/213] prettier --- src/pages/settings/Profile/PersonalDetails/LegalNamePage.js | 1 - src/pages/settings/Profile/ProfilePage.js | 4 ++-- src/types/onyx/PrivatePersonalDetails.ts | 3 --- 3 files changed, 2 insertions(+), 6 deletions(-) diff --git a/src/pages/settings/Profile/PersonalDetails/LegalNamePage.js b/src/pages/settings/Profile/PersonalDetails/LegalNamePage.js index 0bd7f4b70d34..f79882620077 100644 --- a/src/pages/settings/Profile/PersonalDetails/LegalNamePage.js +++ b/src/pages/settings/Profile/PersonalDetails/LegalNamePage.js @@ -21,7 +21,6 @@ import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import INPUT_IDS from '@src/types/form/LegalNameForm'; - const propTypes = { /* Onyx Props */ privatePersonalDetails: PropTypes.shape({ diff --git a/src/pages/settings/Profile/ProfilePage.js b/src/pages/settings/Profile/ProfilePage.js index fd8b15404a42..ed2c47f0ad4b 100755 --- a/src/pages/settings/Profile/ProfilePage.js +++ b/src/pages/settings/Profile/ProfilePage.js @@ -13,10 +13,10 @@ import Section from '@components/Section'; import withCurrentUserPersonalDetails, {withCurrentUserPersonalDetailsDefaultProps, withCurrentUserPersonalDetailsPropTypes} from '@components/withCurrentUserPersonalDetails'; import withLocalize, {withLocalizePropTypes} from '@components/withLocalize'; import withWindowDimensions, {windowDimensionsPropTypes} from '@components/withWindowDimensions'; -import useThemeStyles from '@hooks/useThemeStyles'; -import useWindowDimensions from '@hooks/useWindowDimensions'; import useStyleUtils from '@hooks/useStyleUtils'; import useTheme from '@hooks/useTheme'; +import useThemeStyles from '@hooks/useThemeStyles'; +import useWindowDimensions from '@hooks/useWindowDimensions'; import compose from '@libs/compose'; import {translatableTextPropTypes} from '@libs/Localize'; import Navigation from '@libs/Navigation/Navigation'; diff --git a/src/types/onyx/PrivatePersonalDetails.ts b/src/types/onyx/PrivatePersonalDetails.ts index 4d0dedf16ea7..3c0110416404 100644 --- a/src/types/onyx/PrivatePersonalDetails.ts +++ b/src/types/onyx/PrivatePersonalDetails.ts @@ -14,9 +14,6 @@ type PrivatePersonalDetails = { /** User's home address */ address?: Address; - - /** Whether we are loading the data via the API */ - isLoading?: boolean; }; export default PrivatePersonalDetails; From 5b33277fc7c14a597ad173170926a35c447f4ef0 Mon Sep 17 00:00:00 2001 From: Georgia Monahan Date: Wed, 21 Feb 2024 17:19:57 -0800 Subject: [PATCH 008/213] remove unused function --- src/libs/actions/PersonalDetails.ts | 38 ----------------------------- 1 file changed, 38 deletions(-) diff --git a/src/libs/actions/PersonalDetails.ts b/src/libs/actions/PersonalDetails.ts index 5ae37bb85f10..f976003dea66 100644 --- a/src/libs/actions/PersonalDetails.ts +++ b/src/libs/actions/PersonalDetails.ts @@ -239,43 +239,6 @@ function updateSelectedTimezone(selectedTimezone: SelectedTimezone) { Navigation.goBack(ROUTES.SETTINGS_TIMEZONE); } -/** - * Fetches additional personal data like legal name, date of birth, address - */ -function openPersonalDetails() { - const optimisticData: OnyxUpdate[] = [ - { - onyxMethod: Onyx.METHOD.MERGE, - key: ONYXKEYS.PRIVATE_PERSONAL_DETAILS, - value: { - isLoading: true, - }, - }, - ]; - - const successData: OnyxUpdate[] = [ - { - onyxMethod: Onyx.METHOD.MERGE, - key: ONYXKEYS.PRIVATE_PERSONAL_DETAILS, - value: { - isLoading: false, - }, - }, - ]; - - const failureData: OnyxUpdate[] = [ - { - onyxMethod: Onyx.METHOD.MERGE, - key: ONYXKEYS.PRIVATE_PERSONAL_DETAILS, - value: { - isLoading: false, - }, - }, - ]; - - API.read(READ_COMMANDS.OPEN_PERSONAL_DETAILS, {}, {optimisticData, successData, failureData}); -} - /** * Fetches public profile info about a given user. * The API will only return the accountID, displayName, and avatar for the user @@ -456,7 +419,6 @@ export { clearAvatarErrors, deleteAvatar, getPrivatePersonalDetails, - openPersonalDetails, openPublicProfilePage, updateAddress, updateAutomaticTimezone, From d968e0b8b36d1678ea9fd302e5f09efe5f6619e1 Mon Sep 17 00:00:00 2001 From: Georgia Monahan Date: Wed, 21 Feb 2024 17:45:47 -0800 Subject: [PATCH 009/213] add default props to profilepage --- src/pages/settings/Profile/ProfilePage.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/pages/settings/Profile/ProfilePage.js b/src/pages/settings/Profile/ProfilePage.js index ed2c47f0ad4b..fe317a1fddc9 100755 --- a/src/pages/settings/Profile/ProfilePage.js +++ b/src/pages/settings/Profile/ProfilePage.js @@ -57,6 +57,8 @@ const propTypes = { }), }), + isLoadingApp: PropTypes.bool, + ...withLocalizePropTypes, ...windowDimensionsPropTypes, ...withCurrentUserPersonalDetailsPropTypes, @@ -78,6 +80,7 @@ const defaultProps = { country: '', }, }, + isLoadingApp: true, }; function ProfilePage(props) { From 8fb16bcb56fe480983b032e4cf1375b48e22c18b Mon Sep 17 00:00:00 2001 From: Jakub Butkiewicz Date: Fri, 23 Feb 2024 10:52:30 +0100 Subject: [PATCH 010/213] ref: migrate AddressForm to TS --- src/components/AddressSearch/types.ts | 2 +- src/components/Form/InputWrapper.tsx | 3 +- src/pages/ReimbursementAccount/AddressForm.js | 163 ------------------ .../ReimbursementAccount/AddressForm.tsx | 128 ++++++++++++++ 4 files changed, 131 insertions(+), 165 deletions(-) delete mode 100644 src/pages/ReimbursementAccount/AddressForm.js create mode 100644 src/pages/ReimbursementAccount/AddressForm.tsx diff --git a/src/components/AddressSearch/types.ts b/src/components/AddressSearch/types.ts index 9b4254a9bc45..25e8187d1542 100644 --- a/src/components/AddressSearch/types.ts +++ b/src/components/AddressSearch/types.ts @@ -94,4 +94,4 @@ type AddressSearchProps = { type IsCurrentTargetInsideContainerType = (event: FocusEvent | NativeSyntheticEvent, containerRef: RefObject) => boolean; -export type {CurrentLocationButtonProps, AddressSearchProps, RenamedInputKeysProps, IsCurrentTargetInsideContainerType}; +export type {CurrentLocationButtonProps, AddressSearchProps, RenamedInputKeysProps, IsCurrentTargetInsideContainerType, StreetValue}; diff --git a/src/components/Form/InputWrapper.tsx b/src/components/Form/InputWrapper.tsx index 4d55de008516..0f8152f6cb89 100644 --- a/src/components/Form/InputWrapper.tsx +++ b/src/components/Form/InputWrapper.tsx @@ -4,6 +4,7 @@ import type {AnimatedTextInputRef} from '@components/RNTextInput'; import RoomNameInput from '@components/RoomNameInput'; import TextInput from '@components/TextInput'; import {canUseTouchScreen} from '@libs/DeviceCapabilities'; +import type {State} from '@libs/Navigation/types'; import FormContext from './FormContext'; import type {InputComponentBaseProps, InputComponentValueProps, ValidInputs, ValueTypeKey} from './types'; @@ -57,7 +58,7 @@ function computeComponentSpecificRegistrationParams({ type InputWrapperProps = ComponentPropsWithoutRef & InputComponentValueProps & { InputComponent: TInput; - inputID: string; + inputID: string | State; isFocused?: boolean; /** diff --git a/src/pages/ReimbursementAccount/AddressForm.js b/src/pages/ReimbursementAccount/AddressForm.js deleted file mode 100644 index dc95d62b6afa..000000000000 --- a/src/pages/ReimbursementAccount/AddressForm.js +++ /dev/null @@ -1,163 +0,0 @@ -import PropTypes from 'prop-types'; -import React from 'react'; -import {View} from 'react-native'; -import AddressSearch from '@components/AddressSearch'; -import InputWrapper from '@components/Form/InputWrapper'; -import StatePicker from '@components/StatePicker'; -import TextInput from '@components/TextInput'; -import useThemeStyles from '@hooks/useThemeStyles'; -import CONST from '@src/CONST'; - -const propTypes = { - /** Translate key for Street name */ - streetTranslationKey: PropTypes.string.isRequired, - - /** Callback fired when a field changes. Passes args as {[fieldName]: val} */ - onFieldChange: PropTypes.func, - - /** Default values */ - defaultValues: PropTypes.shape({ - /** Address street field */ - street: PropTypes.string, - - /** Address city field */ - city: PropTypes.string, - - /** Address state field */ - state: PropTypes.string, - - /** Address zip code field */ - zipCode: PropTypes.string, - }), - - /** Form values */ - values: PropTypes.shape({ - /** Address street field */ - street: PropTypes.string, - - /** Address city field */ - city: PropTypes.string, - - /** Address state field */ - state: PropTypes.string, - - /** Address zip code field */ - zipCode: PropTypes.string, - }), - - /** Any errors that can arise from form validation */ - errors: PropTypes.shape({ - street: PropTypes.bool, - city: PropTypes.bool, - state: PropTypes.bool, - zipCode: PropTypes.bool, - }), - - /** The map for inputID of the inputs */ - inputKeys: PropTypes.shape({ - street: PropTypes.string, - city: PropTypes.string, - state: PropTypes.string, - zipCode: PropTypes.string, - }), - - /** Saves a draft of the input value when used in a form */ - shouldSaveDraft: PropTypes.bool, - - /** Returns translated string for given locale and phrase */ - translate: PropTypes.func.isRequired, -}; - -const defaultProps = { - values: { - street: undefined, - city: undefined, - state: undefined, - zipCode: undefined, - }, - defaultValues: { - street: undefined, - city: undefined, - state: undefined, - zipCode: undefined, - }, - errors: {}, - inputKeys: { - street: '', - city: '', - state: '', - zipCode: '', - }, - shouldSaveDraft: false, - onFieldChange: () => {}, -}; - -function AddressForm(props) { - const styles = useThemeStyles(); - return ( - <> - - - - props.onFieldChange({city: value})} - errorText={props.errors.city ? 'bankAccount.error.addressCity' : ''} - containerStyles={[styles.mt6]} - /> - - - props.onFieldChange({state: value})} - errorText={props.errors.state ? 'bankAccount.error.addressState' : ''} - /> - - props.onFieldChange({zipCode: value})} - errorText={props.errors.zipCode ? 'bankAccount.error.zipCode' : ''} - maxLength={CONST.BANK_ACCOUNT.MAX_LENGTH.ZIP_CODE} - hint={['common.zipCodeExampleFormat', {zipSampleFormat: CONST.COUNTRY_ZIP_REGEX_DATA.US.samples}]} - containerStyles={[styles.mt3]} - /> - - ); -} - -AddressForm.propTypes = propTypes; -AddressForm.defaultProps = defaultProps; -AddressForm.displayName = 'AddressForm'; -export default AddressForm; diff --git a/src/pages/ReimbursementAccount/AddressForm.tsx b/src/pages/ReimbursementAccount/AddressForm.tsx new file mode 100644 index 000000000000..dfadaa3d55b9 --- /dev/null +++ b/src/pages/ReimbursementAccount/AddressForm.tsx @@ -0,0 +1,128 @@ +import React from 'react'; +import {View} from 'react-native'; +import AddressSearch from '@components/AddressSearch'; +import InputWrapper from '@components/Form/InputWrapper'; +import StatePicker from '@components/StatePicker'; +import TextInput from '@components/TextInput'; +import useLocalize from '@hooks/useLocalize'; +import useThemeStyles from '@hooks/useThemeStyles'; +import type {State} from '@libs/Navigation/types'; +import CONST from '@src/CONST'; +import type {TranslationPaths} from '@src/languages/types'; + +type Address = { + /** Address street field */ + street: string; + + /** Address city field */ + city: string; + + /** Address state field */ + state: State; + + /** Address zip code field */ + zipCode: string; + + /** Address street2 field */ + street2: string; + + /** Address latitude field */ + lat: string; + + /** Address longitude field */ + lng: string; +}; + +type AddressError = Record; + +type AddressFormProps = { + /** Translate key for Street name */ + streetTranslationKey: TranslationPaths; + + /** Callback fired when a field changes. Passes args as {[fieldName]: val} */ + onFieldChange?: (value: T) => void; + + /** Default values */ + defaultValues?: Address; + + /** Form values */ + values?: Address; + + /** Any errors that can arise from form validation */ + errors?: AddressError; + + /** The map for inputID of the inputs */ + inputKeys?: Address; + + /** Saves a draft of the input value when used in a form */ + shouldSaveDraft?: boolean; +}; + +function AddressForm({shouldSaveDraft = false, defaultValues, values, errors, inputKeys, onFieldChange = () => {}, streetTranslationKey}: AddressFormProps) { + const {translate} = useLocalize(); + const styles = useThemeStyles(); + return ( + <> + + + + onFieldChange({city: value})} + errorText={errors?.city ? 'bankAccount.error.addressCity' : ''} + containerStyles={[styles.mt6]} + /> + + + onFieldChange({state: value})} + errorText={errors?.state ? 'bankAccount.error.addressState' : ''} + /> + + onFieldChange({zipCode: value})} + errorText={errors?.zipCode ? 'bankAccount.error.zipCode' : ''} + maxLength={CONST.BANK_ACCOUNT.MAX_LENGTH.ZIP_CODE} + hint={['common.zipCodeExampleFormat', {zipSampleFormat: CONST.COUNTRY_ZIP_REGEX_DATA.US.samples}]} + containerStyles={[styles.mt3]} + /> + + ); +} + +AddressForm.displayName = 'AddressForm'; +export default AddressForm; From 06d3b9a6221c7848e8eff858b243a51ff3b1bc2f Mon Sep 17 00:00:00 2001 From: Jakub Butkiewicz Date: Fri, 23 Feb 2024 17:07:08 +0100 Subject: [PATCH 011/213] ref: move WorkspaceResetBanAccountModal to TS, fixes for AddressForm --- src/components/AddressSearch/index.tsx | 2 +- src/components/AddressSearch/types.ts | 5 +-- src/components/Form/types.ts | 4 ++- .../ReimbursementAccount/AddressForm.tsx | 4 +-- ....js => WorkspaceResetBankAccountModal.tsx} | 35 +++++++++---------- 5 files changed, 25 insertions(+), 25 deletions(-) rename src/pages/workspace/{WorkspaceResetBankAccountModal.js => WorkspaceResetBankAccountModal.tsx} (68%) diff --git a/src/components/AddressSearch/index.tsx b/src/components/AddressSearch/index.tsx index 89e87eeebe54..5b24fb3fa847 100644 --- a/src/components/AddressSearch/index.tsx +++ b/src/components/AddressSearch/index.tsx @@ -48,7 +48,7 @@ function AddressSearch( street: 'addressStreet', street2: 'addressStreet2', city: 'addressCity', - state: 'addressState', + state: 'addressState' as State, zipCode: 'addressZipCode', lat: 'addressLat', lng: 'addressLng', diff --git a/src/components/AddressSearch/types.ts b/src/components/AddressSearch/types.ts index 25e8187d1542..d459f97d0d2e 100644 --- a/src/components/AddressSearch/types.ts +++ b/src/components/AddressSearch/types.ts @@ -2,6 +2,7 @@ import type {RefObject} from 'react'; import type {NativeSyntheticEvent, StyleProp, TextInputFocusEventData, View, ViewStyle} from 'react-native'; import type {Place} from 'react-native-google-places-autocomplete'; import type {MaybePhraseKey} from '@libs/Localize'; +import {State} from '@libs/Navigation/types'; import type Locale from '@src/types/onyx/Locale'; type CurrentLocationButtonProps = { @@ -16,7 +17,7 @@ type RenamedInputKeysProps = { street: string; street2: string; city: string; - state: string; + state: State; lat: string; lng: string; zipCode: string; @@ -77,7 +78,7 @@ type AddressSearchProps = { predefinedPlaces?: Place[]; /** A map of inputID key names */ - renamedInputKeys: RenamedInputKeysProps; + renamedInputKeys?: RenamedInputKeysProps; /** Maximum number of characters allowed in search input */ maxInputLength?: number; diff --git a/src/components/Form/types.ts b/src/components/Form/types.ts index ae98978ffcad..5360da93d6fe 100644 --- a/src/components/Form/types.ts +++ b/src/components/Form/types.ts @@ -11,6 +11,7 @@ import type SingleChoiceQuestion from '@components/SingleChoiceQuestion'; import type StatePicker from '@components/StatePicker'; import type TextInput from '@components/TextInput'; import type ValuePicker from '@components/ValuePicker'; +import type {State} from '@libs/Navigation/types'; import type BusinessTypePicker from '@pages/ReimbursementAccount/BusinessInfo/substeps/TypeBusiness/BusinessTypePicker'; import type {TranslationPaths} from '@src/languages/types'; import type {OnyxFormKey, OnyxValues} from '@src/ONYXKEYS'; @@ -36,11 +37,12 @@ type ValidInputs = | typeof StatePicker | typeof ValuePicker; -type ValueTypeKey = 'string' | 'boolean' | 'date'; +type ValueTypeKey = 'string' | 'boolean' | 'date' | 'state'; type ValueTypeMap = { string: string; boolean: boolean; date: Date; + state: State; }; type FormValue = ValueOf; diff --git a/src/pages/ReimbursementAccount/AddressForm.tsx b/src/pages/ReimbursementAccount/AddressForm.tsx index dfadaa3d55b9..4f618982c737 100644 --- a/src/pages/ReimbursementAccount/AddressForm.tsx +++ b/src/pages/ReimbursementAccount/AddressForm.tsx @@ -98,8 +98,8 @@ function AddressForm({shouldSaveDraft = false, defaultValues, values, errors, in InputComponent={StatePicker} inputID={inputKeys?.state ?? 'stateInput'} shouldSaveDraft={shouldSaveDraft} - value={values?.state ?? ''} - defaultValue={defaultValues?.state ?? ''} + value={values?.state} + defaultValue={defaultValues?.state} onInputChange={(value) => onFieldChange({state: value})} errorText={errors?.state ? 'bankAccount.error.addressState' : ''} /> diff --git a/src/pages/workspace/WorkspaceResetBankAccountModal.js b/src/pages/workspace/WorkspaceResetBankAccountModal.tsx similarity index 68% rename from src/pages/workspace/WorkspaceResetBankAccountModal.js rename to src/pages/workspace/WorkspaceResetBankAccountModal.tsx index f98077a546ca..19428408c609 100644 --- a/src/pages/workspace/WorkspaceResetBankAccountModal.js +++ b/src/pages/workspace/WorkspaceResetBankAccountModal.tsx @@ -1,34 +1,32 @@ -import lodashGet from 'lodash/get'; -import PropTypes from 'prop-types'; import React from 'react'; +import type {OnyxEntry} from 'react-native-onyx'; import {withOnyx} from 'react-native-onyx'; import ConfirmModal from '@components/ConfirmModal'; import Text from '@components/Text'; import useLocalize from '@hooks/useLocalize'; import useThemeStyles from '@hooks/useThemeStyles'; import BankAccount from '@libs/models/BankAccount'; -import * as ReimbursementAccountProps from '@pages/ReimbursementAccount/reimbursementAccountPropTypes'; import * as BankAccounts from '@userActions/BankAccounts'; import ONYXKEYS from '@src/ONYXKEYS'; +import type * as OnyxTypes from '@src/types/onyx'; -const propTypes = { - /** Reimbursement account data */ - reimbursementAccount: ReimbursementAccountProps.reimbursementAccountPropTypes.isRequired, - +type WorkspaceResetBankAccountModalOnyxProps = { /** Session info for the currently logged in user. */ - session: PropTypes.shape({ - /** Currently logged in user email */ - email: PropTypes.string, - }).isRequired, + session: OnyxEntry; +}; + +type WorkspaceResetBankAccountModalProps = WorkspaceResetBankAccountModalOnyxProps & { + /** Reimbursement account data */ + reimbursementAccount: OnyxTypes.ReimbursementAccount; }; -function WorkspaceResetBankAccountModal({reimbursementAccount, session}) { +function WorkspaceResetBankAccountModal({reimbursementAccount, session}: WorkspaceResetBankAccountModalProps) { const styles = useThemeStyles(); const {translate} = useLocalize(); - const achData = lodashGet(reimbursementAccount, 'achData') || {}; - const isInOpenState = achData.state === BankAccount.STATE.OPEN; - const bankAccountID = achData.bankAccountID; - const bankShortName = `${achData.addressName || ''} ${(achData.accountNumber || '').slice(-4)}`; + const achData = reimbursementAccount?.achData; + const isInOpenState = achData?.state === BankAccount.STATE.OPEN; + const bankAccountID = achData?.bankAccountID; + const bankShortName = `${achData?.addressName ?? ''} ${(achData?.accountNumber ?? '').slice(-4)}`; return ( BankAccounts.resetFreePlanBankAccount(bankAccountID, session, achData.policyID)} + onConfirm={() => BankAccounts.resetFreePlanBankAccount(bankAccountID ?? -1, session ?? {}, achData?.policyID ?? '')} shouldShowCancelButton isVisible /> @@ -56,9 +54,8 @@ function WorkspaceResetBankAccountModal({reimbursementAccount, session}) { } WorkspaceResetBankAccountModal.displayName = 'WorkspaceResetBankAccountModal'; -WorkspaceResetBankAccountModal.propTypes = propTypes; -export default withOnyx({ +export default withOnyx({ session: { key: ONYXKEYS.SESSION, }, From 9f40e79ff1daa83355ca3689f7b6b6d320813cd8 Mon Sep 17 00:00:00 2001 From: Jakub Butkiewicz Date: Fri, 23 Feb 2024 17:11:11 +0100 Subject: [PATCH 012/213] ref: move RequestorStep to TS --- .../PersonalInfo/PersonalInfo.tsx | 3 ++- .../{RequestorStep.js => RequestorStep.tsx} | 18 +++++++++--------- 2 files changed, 11 insertions(+), 10 deletions(-) rename src/pages/ReimbursementAccount/{RequestorStep.js => RequestorStep.tsx} (56%) diff --git a/src/pages/ReimbursementAccount/PersonalInfo/PersonalInfo.tsx b/src/pages/ReimbursementAccount/PersonalInfo/PersonalInfo.tsx index ce37cd4aa4e2..b259a4ee7e3c 100644 --- a/src/pages/ReimbursementAccount/PersonalInfo/PersonalInfo.tsx +++ b/src/pages/ReimbursementAccount/PersonalInfo/PersonalInfo.tsx @@ -1,3 +1,4 @@ +import type {RefAttributes} from 'react'; import React, {forwardRef, useCallback, useMemo} from 'react'; import {View} from 'react-native'; import type {OnyxEntry} from 'react-native-onyx'; @@ -95,7 +96,7 @@ function PersonalInfo({reimbursementAccount, reimbursementAccountDraft, onBackBu PersonalInfo.displayName = 'PersonalInfo'; -export default withOnyx({ +export default withOnyx & PersonalInfoProps, PersonalInfoOnyxProps>({ // @ts-expect-error: ONYXKEYS.REIMBURSEMENT_ACCOUNT is conflicting with ONYXKEYS.FORMS.REIMBURSEMENT_ACCOUNT_FORM reimbursementAccount: { key: ONYXKEYS.REIMBURSEMENT_ACCOUNT, diff --git a/src/pages/ReimbursementAccount/RequestorStep.js b/src/pages/ReimbursementAccount/RequestorStep.tsx similarity index 56% rename from src/pages/ReimbursementAccount/RequestorStep.js rename to src/pages/ReimbursementAccount/RequestorStep.tsx index a8060cb1ae90..ef2901b1370a 100644 --- a/src/pages/ReimbursementAccount/RequestorStep.js +++ b/src/pages/ReimbursementAccount/RequestorStep.tsx @@ -1,17 +1,18 @@ -import PropTypes from 'prop-types'; -import React from 'react'; +import type {ForwardedRef} from 'react'; +import React, {forwardRef} from 'react'; +import type {View} from 'react-native'; import PersonalInfo from './PersonalInfo/PersonalInfo'; import VerifyIdentity from './VerifyIdentity/VerifyIdentity'; -const propTypes = { +type RequestorStepProps = { /** Goes to the previous step */ - onBackButtonPress: PropTypes.func.isRequired, + onBackButtonPress: () => void; /** If we should show Onfido flow */ - shouldShowOnfido: PropTypes.bool.isRequired, + shouldShowOnfido: boolean; }; -const RequestorStep = React.forwardRef(({shouldShowOnfido, onBackButtonPress}, ref) => { +function RequestorStep({shouldShowOnfido, onBackButtonPress}: RequestorStepProps, ref: ForwardedRef) { if (shouldShowOnfido) { return ; } @@ -22,9 +23,8 @@ const RequestorStep = React.forwardRef(({shouldShowOnfido, onBackButtonPress}, r onBackButtonPress={onBackButtonPress} /> ); -}); +} -RequestorStep.propTypes = propTypes; RequestorStep.displayName = 'RequestorStep'; -export default RequestorStep; +export default forwardRef(RequestorStep); From 3125cbe5f54a4f6708f78aa2a6b5e8cf102db7df Mon Sep 17 00:00:00 2001 From: Jakub Butkiewicz Date: Fri, 23 Feb 2024 17:35:11 +0100 Subject: [PATCH 013/213] ref: remove RequestorOnfidoStep because its not used anywhere --- .../RequestorOnfidoStep.js | 103 ------------------ 1 file changed, 103 deletions(-) delete mode 100644 src/pages/ReimbursementAccount/RequestorOnfidoStep.js diff --git a/src/pages/ReimbursementAccount/RequestorOnfidoStep.js b/src/pages/ReimbursementAccount/RequestorOnfidoStep.js deleted file mode 100644 index 8cca56779059..000000000000 --- a/src/pages/ReimbursementAccount/RequestorOnfidoStep.js +++ /dev/null @@ -1,103 +0,0 @@ -import lodashGet from 'lodash/get'; -import PropTypes from 'prop-types'; -import React from 'react'; -import {ScrollView} from 'react-native'; -import {withOnyx} from 'react-native-onyx'; -import FullPageOfflineBlockingView from '@components/BlockingViews/FullPageOfflineBlockingView'; -import HeaderWithBackButton from '@components/HeaderWithBackButton'; -import Onfido from '@components/Onfido'; -import ScreenWrapper from '@components/ScreenWrapper'; -import useLocalize from '@hooks/useLocalize'; -import useThemeStyles from '@hooks/useThemeStyles'; -import Growl from '@libs/Growl'; -import * as BankAccounts from '@userActions/BankAccounts'; -import CONST from '@src/CONST'; -import ONYXKEYS from '@src/ONYXKEYS'; -import * as ReimbursementAccountProps from './reimbursementAccountPropTypes'; - -const propTypes = { - /** The bank account currently in setup */ - reimbursementAccount: ReimbursementAccountProps.reimbursementAccountPropTypes.isRequired, - - /** Goes to the previous step */ - onBackButtonPress: PropTypes.func.isRequired, - - /** The token required to initialize the Onfido SDK */ - onfidoToken: PropTypes.string, - - /** The application ID for our Onfido instance */ - onfidoApplicantID: PropTypes.string, -}; - -const defaultProps = { - onfidoToken: null, - onfidoApplicantID: null, -}; - -const HEADER_STEP_COUNTER = {step: 3, total: 5}; -const ONFIDO_ERROR_DISPLAY_DURATION = 10000; - -function RequestorOnfidoStep({onBackButtonPress, reimbursementAccount, onfidoToken, onfidoApplicantID}) { - const styles = useThemeStyles(); - const {translate} = useLocalize(); - - const submitOnfidoData = (onfidoData) => { - BankAccounts.verifyIdentityForBankAccount(lodashGet(reimbursementAccount, 'achData.bankAccountID', 0), { - ...onfidoData, - applicantID: onfidoApplicantID, - }); - BankAccounts.updateReimbursementAccountDraft({isOnfidoSetupComplete: true}); - }; - - const handleOnfidoError = () => { - // In case of any unexpected error we log it to the server, show a growl, and return the user back to the requestor step so they can try again. - Growl.error(translate('onfidoStep.genericError'), ONFIDO_ERROR_DISPLAY_DURATION); - BankAccounts.clearOnfidoToken(); - BankAccounts.goToWithdrawalAccountSetupStep(CONST.BANK_ACCOUNT.STEP.REQUESTOR); - }; - - const handleOnfidoUserExit = () => { - BankAccounts.clearOnfidoToken(); - BankAccounts.goToWithdrawalAccountSetupStep(CONST.BANK_ACCOUNT.STEP.REQUESTOR); - }; - - return ( - - - - - - - - - ); -} - -RequestorOnfidoStep.displayName = 'RequestorOnfidoStep'; -RequestorOnfidoStep.propTypes = propTypes; -RequestorOnfidoStep.defaultProps = defaultProps; -RequestorOnfidoStep.displayName = 'RequestorOnfidoStep'; - -export default withOnyx({ - onfidoToken: { - key: ONYXKEYS.ONFIDO_TOKEN, - }, - onfidoApplicantID: { - key: ONYXKEYS.ONFIDO_APPLICANT_ID, - }, -})(RequestorOnfidoStep); From 08a5d4e9bf532c22d540dde87984a1375596c951 Mon Sep 17 00:00:00 2001 From: Jakub Butkiewicz Date: Fri, 23 Feb 2024 21:10:25 +0100 Subject: [PATCH 014/213] ref: move ReimbursementAccount page and did few adjustments --- src/components/AddressSearch/index.tsx | 2 +- src/libs/Navigation/types.ts | 4 +- src/libs/actions/BankAccounts.ts | 2 +- ...ntPage.js => ReimbursementAccountPage.tsx} | 263 +++++++++--------- src/types/onyx/ReimbursementAccount.ts | 2 + 5 files changed, 137 insertions(+), 136 deletions(-) rename src/pages/ReimbursementAccount/{ReimbursementAccountPage.js => ReimbursementAccountPage.tsx} (74%) diff --git a/src/components/AddressSearch/index.tsx b/src/components/AddressSearch/index.tsx index 5b24fb3fa847..89e87eeebe54 100644 --- a/src/components/AddressSearch/index.tsx +++ b/src/components/AddressSearch/index.tsx @@ -48,7 +48,7 @@ function AddressSearch( street: 'addressStreet', street2: 'addressStreet2', city: 'addressCity', - state: 'addressState' as State, + state: 'addressState', zipCode: 'addressZipCode', lat: 'addressLat', lng: 'addressLng', diff --git a/src/libs/Navigation/types.ts b/src/libs/Navigation/types.ts index 4fd218d4fd42..bf3dfc573845 100644 --- a/src/libs/Navigation/types.ts +++ b/src/libs/Navigation/types.ts @@ -365,8 +365,8 @@ type AddPersonalBankAccountNavigatorParamList = { type ReimbursementAccountNavigatorParamList = { [SCREENS.REIMBURSEMENT_ACCOUNT_ROOT]: { - stepToOpen: string; - policyID: string; + stepToOpen?: string; + policyID?: string; }; }; diff --git a/src/libs/actions/BankAccounts.ts b/src/libs/actions/BankAccounts.ts index 30dd03b6e780..e162ce9c30ed 100644 --- a/src/libs/actions/BankAccounts.ts +++ b/src/libs/actions/BankAccounts.ts @@ -66,7 +66,7 @@ function openPlaidView() { clearPlaid().then(() => ReimbursementAccount.setBankAccountSubStep(CONST.BANK_ACCOUNT.SETUP_TYPE.PLAID)); } -function setPlaidEvent(eventName: string) { +function setPlaidEvent(eventName: string | null) { Onyx.set(ONYXKEYS.PLAID_CURRENT_EVENT, eventName); } diff --git a/src/pages/ReimbursementAccount/ReimbursementAccountPage.js b/src/pages/ReimbursementAccount/ReimbursementAccountPage.tsx similarity index 74% rename from src/pages/ReimbursementAccount/ReimbursementAccountPage.js rename to src/pages/ReimbursementAccount/ReimbursementAccountPage.tsx index cc416a5cfbdd..978c70ef6af7 100644 --- a/src/pages/ReimbursementAccount/ReimbursementAccountPage.js +++ b/src/pages/ReimbursementAccount/ReimbursementAccountPage.tsx @@ -1,10 +1,12 @@ +import {RouteProp} from '@react-navigation/native'; import Str from 'expensify-common/lib/str'; -import lodashGet from 'lodash/get'; +import lodashPick from 'lodash/pick'; import PropTypes from 'prop-types'; import React, {useEffect, useRef, useState} from 'react'; import {View} from 'react-native'; +import type {OnyxCollection, OnyxEntry} from 'react-native-onyx'; import {withOnyx} from 'react-native-onyx'; -import _ from 'underscore'; +import type {ValueOf} from 'type-fest'; import FullPageNotFoundView from '@components/BlockingViews/FullPageNotFoundView'; import HeaderWithBackButton from '@components/HeaderWithBackButton'; import ReimbursementAccountLoadingIndicator from '@components/ReimbursementAccountLoadingIndicator'; @@ -18,13 +20,19 @@ import compose from '@libs/compose'; import getPlaidOAuthReceivedRedirectURI from '@libs/getPlaidOAuthReceivedRedirectURI'; import BankAccount from '@libs/models/BankAccount'; import Navigation from '@libs/Navigation/Navigation'; +import {ReimbursementAccountNavigatorParamList} from '@libs/Navigation/types'; import * as PolicyUtils from '@libs/PolicyUtils'; import shouldReopenOnfido from '@libs/shouldReopenOnfido'; -import withPolicy from '@pages/workspace/withPolicy'; +import withPolicy, {WithPolicyProps} from '@pages/workspace/withPolicy'; import * as BankAccounts from '@userActions/BankAccounts'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; +import type {Route} from '@src/ROUTES'; import ROUTES from '@src/ROUTES'; +import SCREENS from '@src/SCREENS'; +import type * as OnyxTypes from '@src/types/onyx'; +import {ACHData} from '@src/types/onyx/ReimbursementAccount'; +import {isEmptyObject} from '@src/types/utils/EmptyObject'; import ACHContractStep from './ACHContractStep'; import BankAccountStep from './BankAccountStep'; import BeneficialOwnersStep from './BeneficialOwnersStep'; @@ -36,73 +44,78 @@ import reimbursementAccountDraftPropTypes from './ReimbursementAccountDraftPropT import * as ReimbursementAccountProps from './reimbursementAccountPropTypes'; import RequestorStep from './RequestorStep'; -const propTypes = { +type ReimbursementAccountDraft = { + bankAccountID: number; + + /** Props needed for BankAccountStep */ + accountNumber: string; + routingNumber: string; + acceptTerms: boolean; + plaidAccountID: string; + mask: string; + + /** Props needed for CompanyStep */ + companyName: string; + addressStreet: string; + addressCity: string; + addressState: string; + addressZipCode: string; + companyPhone: string; + website: string; + companyTaxID: string; + incorporationType: string; + incorporationDate: string | Date; + incorporationState: string; + hasNoConnectionToCannabis: boolean; + + /** Props needed for RequestorStep */ + firstName: string; + lastName: string; + requestorAddressStreet: string; + requestorAddressCity: string; + requestorAddressState: string; + requestorAddressZipCode: string; + dob: string | Date; + ssnLast4: string; + isOnfidoSetupComplete: boolean; + + /** Props needed for ACHContractStep */ + ownsMoreThan25Percent: boolean; + hasOtherBeneficialOwners: boolean; + acceptTermsAndConditions: boolean; + certifyTrueInformation: boolean; + beneficialOwners: string; + beneficialOwnerKeys: string[]; +}; + +type ReimbursementAccountOnyxProps = { /** Plaid SDK token to use to initialize the widget */ - plaidLinkToken: PropTypes.string, + plaidLinkToken?: OnyxEntry; /** Plaid SDK current event */ - plaidCurrentEvent: PropTypes.string, - - /** ACH data for the withdrawal account actively being set up */ - reimbursementAccount: ReimbursementAccountProps.reimbursementAccountPropTypes, - - /** The draft values of the bank account being setup */ - reimbursementAccountDraft: reimbursementAccountDraftPropTypes, - - /** The token required to initialize the Onfido SDK */ - onfidoToken: PropTypes.string, - - /** Policy values needed in the component */ - policy: PropTypes.shape({ - name: PropTypes.string, - }), + plaidCurrentEvent?: OnyxEntry; /** Indicated whether the app is loading */ - isLoadingApp: PropTypes.bool, + isLoadingApp: OnyxEntry; /** Holds information about the users account that is logging in */ - account: PropTypes.shape({ - /** Whether a sign on form is loading (being submitted) */ - isLoading: PropTypes.bool, - }), + account: OnyxEntry; /** Current session for the user */ - session: PropTypes.shape({ - /** User login */ - email: PropTypes.string, - }), - - /** Route object from navigation */ - route: PropTypes.shape({ - /** Params that are passed into the route */ - params: PropTypes.shape({ - /** A step to navigate to if we need to drop the user into a specific point in the flow */ - stepToOpen: PropTypes.string, - policyID: PropTypes.string, - }), - }), -}; + session: OnyxEntry; -const defaultProps = { - reimbursementAccount: ReimbursementAccountProps.reimbursementAccountDefaultProps, - reimbursementAccountDraft: {}, - onfidoToken: '', - policy: {}, - plaidLinkToken: '', - plaidCurrentEvent: '', - isLoadingApp: false, - account: {}, - session: { - email: null, - }, - route: { - params: { - stepToOpen: '', - policyID: '', - }, - }, + /** ACH data for the withdrawal account actively being set up */ + reimbursementAccount: OnyxEntry; + + /** The draft values of the bank account being setup */ + reimbursementAccountDraft: OnyxEntry; + + /** The token required to initialize the Onfido SDK */ + onfidoToken: OnyxEntry; }; +type ReimbursementAccountPageProps = WithPolicyProps & ReimbursementAccountOnyxProps & RouteProp; + const ROUTE_NAMES = { COMPANY: 'company', PERSONAL_INFORMATION: 'personal-information', @@ -116,11 +129,9 @@ const ROUTE_NAMES = { /** * We can pass stepToOpen in the URL to force which step to show. * Mainly needed when user finished the flow in verifying state, and Ops ask them to modify some fields from a specific step. - * @param {Object} route - * @returns {String} */ -function getStepToOpenFromRouteParams(route) { - switch (lodashGet(route, ['params', 'stepToOpen'], '')) { +function getStepToOpenFromRouteParams(route: RouteProp): ValueOf | '' { + switch (route.params.stepToOpen ?? '') { case ROUTE_NAMES.NEW: return CONST.BANK_ACCOUNT.STEP.BANK_ACCOUNT; case ROUTE_NAMES.COMPANY: @@ -140,11 +151,7 @@ function getStepToOpenFromRouteParams(route) { } } -/** - * @param {String} currentStep - * @returns {String} - */ -function getRouteForCurrentStep(currentStep) { +function getRouteForCurrentStep(currentStep: ValueOf): ValueOf { switch (currentStep) { case CONST.BANK_ACCOUNT.STEP.COMPANY: return ROUTE_NAMES.COMPANY; @@ -164,7 +171,18 @@ function getRouteForCurrentStep(currentStep) { } } -function ReimbursementAccountPage({reimbursementAccount, route, onfidoToken, policy, account, isLoadingApp, session, plaidLinkToken, plaidCurrentEvent, reimbursementAccountDraft}) { +function ReimbursementAccountPage({ + reimbursementAccount, + route, + onfidoToken = '', + policy, + account, + isLoadingApp = false, + session, + plaidLinkToken = '', + plaidCurrentEvent = '', + reimbursementAccountDraft, +}: ReimbursementAccountPageProps) { /** The SetupWithdrawalAccount flow allows us to continue the flow from various points depending on where the user left off. This view will refer to the achData as the single source of truth to determine which route to @@ -172,26 +190,19 @@ function ReimbursementAccountPage({reimbursementAccount, route, onfidoToken, pol mounts which will set the achData.currentStep after the account data is fetched and overwrite the logical next step. */ - const achData = lodashGet(reimbursementAccount, 'achData', {}); + const achData = reimbursementAccount?.achData; - /** - * @param {Array} fieldNames - * - * @returns {Object} - */ - function getBankAccountFields(fieldNames) { + function getBankAccountFields(fieldNames: T[]): Pick { + // @ts-expect-error -- Pick is more acurate type in this case because lodashPick returns Partial return { - ..._.pick(lodashGet(reimbursementAccount, 'achData'), ...fieldNames), + ...lodashPick(reimbursementAccount?.achData, ...fieldNames), }; } /** * Returns selected bank account fields based on field names provided. - * - * @param {string} step - * @returns {Array} */ - function getFieldsForStep(step) { + function getFieldsForStep(step: string): string[] { switch (step) { case CONST.BANK_ACCOUNT.STEP.BANK_ACCOUNT: return ['routingNumber', 'accountNumber', 'bankName', 'plaidAccountID', 'plaidAccessToken', 'isSavings']; @@ -218,21 +229,21 @@ function ReimbursementAccountPage({reimbursementAccount, route, onfidoToken, pol /** * Returns true if a VBBA exists in any state other than OPEN or LOCKED - * @returns {Boolean} + */ - function hasInProgressVBBA() { - return achData.bankAccountID && achData.state !== BankAccount.STATE.OPEN && achData.state !== BankAccount.STATE.LOCKED; + function hasInProgressVBBA(): boolean { + return !!achData?.bankAccountID && achData?.state !== BankAccount.STATE.OPEN && achData?.state !== BankAccount.STATE.LOCKED; } /* * Calculates the state used to show the "Continue with setup" view. If a bank account setup is already in progress and * no specific further step was passed in the url we'll show the workspace bank account reset modal if the user wishes to start over */ - function getShouldShowContinueSetupButtonInitialValue() { + function getShouldShowContinueSetupButtonInitialValue(): boolean { if (!hasInProgressVBBA()) { // Since there is no VBBA in progress, we won't need to show the component ContinueBankAccountSetup return false; } - return achData.state === BankAccount.STATE.PENDING || _.contains([CONST.BANK_ACCOUNT.STEP.BANK_ACCOUNT, ''], getStepToOpenFromRouteParams(route)); + return achData?.state === BankAccount.STATE.PENDING || [CONST.BANK_ACCOUNT.STEP.BANK_ACCOUNT, ''].includes(getStepToOpenFromRouteParams(route)); } /** @@ -244,14 +255,14 @@ function ReimbursementAccountPage({reimbursementAccount, route, onfidoToken, pol which acts similarly to `componentDidUpdate` when the `reimbursementAccount` dependency changes. */ const [hasACHDataBeenLoaded, setHasACHDataBeenLoaded] = useState( - reimbursementAccount !== ReimbursementAccountProps.reimbursementAccountDefaultProps && _.has(reimbursementAccount, 'achData.currentStep'), + reimbursementAccount !== ReimbursementAccountProps.reimbursementAccountDefaultProps && reimbursementAccount.achData && 'currentStep' in reimbursementAccount.achData, ); const [shouldShowContinueSetupButton, setShouldShowContinueSetupButton] = useState(hasACHDataBeenLoaded ? getShouldShowContinueSetupButtonInitialValue() : false); - const currentStep = achData.currentStep || CONST.BANK_ACCOUNT.STEP.BANK_ACCOUNT; - const policyName = lodashGet(policy, 'name', ''); - const policyID = lodashGet(route.params, 'policyID', ''); + const currentStep = achData?.currentStep ?? CONST.BANK_ACCOUNT.STEP.BANK_ACCOUNT; + const policyName = policy?.name ?? ''; + const policyID = route.params.policyID ?? ''; const styles = useThemeStyles(); const {translate} = useLocalize(); const {isOffline} = useNetwork(); @@ -261,17 +272,17 @@ function ReimbursementAccountPage({reimbursementAccount, route, onfidoToken, pol /** * Retrieve verified business bank account currently being set up. - * @param {boolean} ignoreLocalCurrentStep Pass true if you want the last "updated" view (from db), not the last "viewed" view (from onyx). + * @param ignoreLocalCurrentStep Pass true if you want the last "updated" view (from db), not the last "viewed" view (from onyx). */ - function fetchData(ignoreLocalCurrentStep) { + function fetchData(ignoreLocalCurrentStep?: boolean) { // Show loader right away, as optimisticData might be set only later in case multiple calls are in the queue BankAccounts.setReimbursementAccountLoading(true); // We can specify a step to navigate to by using route params when the component mounts. // We want to use the same stepToOpen variable when the network state changes because we can be redirected to a different step when the account refreshes. const stepToOpen = getStepToOpenFromRouteParams(route); - const subStep = achData.subStep || ''; - const localCurrentStep = achData.currentStep || ''; + const subStep = achData?.subStep ?? ''; + const localCurrentStep = achData?.currentStep ?? ''; BankAccounts.openReimbursementAccountPage(stepToOpen, subStep, ignoreLocalCurrentStep ? '' : localCurrentStep, policyID); } @@ -304,7 +315,7 @@ function ReimbursementAccountPage({reimbursementAccount, route, onfidoToken, pol if ( prevReimbursementAccount && prevReimbursementAccount.pendingAction === CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE && - reimbursementAccount.pendingAction !== prevReimbursementAccount.pendingAction + reimbursementAccount?.pendingAction !== prevReimbursementAccount.pendingAction ) { setShouldShowContinueSetupButton(hasInProgressVBBA()); } @@ -316,7 +327,7 @@ function ReimbursementAccountPage({reimbursementAccount, route, onfidoToken, pol const currentStepRouteParam = getStepToOpenFromRouteParams(route); if (currentStepRouteParam === currentStep) { // If the user is connecting online with plaid, reset any bank account errors so we don't persist old data from a potential previous connection - if (currentStep === CONST.BANK_ACCOUNT.STEP.BANK_ACCOUNT && achData.subStep === CONST.BANK_ACCOUNT.SETUP_TYPE.PLAID) { + if (currentStep === CONST.BANK_ACCOUNT.STEP.BANK_ACCOUNT && achData?.subStep === CONST.BANK_ACCOUNT.SETUP_TYPE.PLAID) { BankAccounts.hideBankAccountErrors(); } @@ -325,7 +336,7 @@ function ReimbursementAccountPage({reimbursementAccount, route, onfidoToken, pol } // Update the data that is returned from back-end to draft value - const draftStep = reimbursementAccount.draftStep; + const draftStep = reimbursementAccount?.draftStep; if (draftStep) { BankAccounts.updateReimbursementAccountDraft(getBankAccountFields(getFieldsForStep(draftStep))); } @@ -337,9 +348,9 @@ function ReimbursementAccountPage({reimbursementAccount, route, onfidoToken, pol BankAccounts.hideBankAccountErrors(); } - const backTo = lodashGet(route.params, 'backTo'); + const backTo = route.params.backTo; // eslint-disable-next-line no-shadow - const policyID = lodashGet(route.params, 'policyID'); + const policyID = route.params.policyID; Navigation.navigate(ROUTES.BANK_ACCOUNT_WITH_STEP_TO_OPEN.getRoute(getRouteForCurrentStep(currentStep), policyID, backTo)); }, @@ -354,17 +365,11 @@ function ReimbursementAccountPage({reimbursementAccount, route, onfidoToken, pol fetchData(true); }; - /** - * @param {String} fieldName - * @param {String} defaultValue - * - * @returns {String} - */ - const getDefaultStateForField = (fieldName, defaultValue = '') => lodashGet(reimbursementAccount, ['achData', fieldName], defaultValue); + const getDefaultStateForField = (fieldName: keyof ACHData, defaultValue = ''): string | number | boolean | string[] => reimbursementAccount?.achData?.[fieldName] ?? defaultValue; const goBack = () => { - const subStep = achData.subStep; - const shouldShowOnfido = onfidoToken && !achData.isOnfidoSetupComplete; + const subStep = achData?.subStep; + const shouldShowOnfido = onfidoToken && !achData?.isOnfidoSetupComplete; switch (currentStep) { case CONST.BANK_ACCOUNT.STEP.BANK_ACCOUNT: @@ -401,9 +406,9 @@ function ReimbursementAccountPage({reimbursementAccount, route, onfidoToken, pol break; case CONST.BANK_ACCOUNT.STEP.VALIDATION: - if (_.contains([BankAccount.STATE.VERIFYING, BankAccount.STATE.SETUP], achData.state)) { + if ([BankAccount.STATE.VERIFYING, BankAccount.STATE.SETUP].some((value) => value === achData?.state)) { BankAccounts.goToWithdrawalAccountSetupStep(CONST.BANK_ACCOUNT.STEP.ACH_CONTRACT); - } else if (!isOffline && achData.state === BankAccount.STATE.PENDING) { + } else if (!isOffline && achData?.state === BankAccount.STATE.PENDING) { setShouldShowContinueSetupButton(true); } else { Navigation.goBack(); @@ -415,19 +420,16 @@ function ReimbursementAccountPage({reimbursementAccount, route, onfidoToken, pol } }; - const isLoading = (isLoadingApp || account.isLoading || reimbursementAccount.isLoading) && (!plaidCurrentEvent || plaidCurrentEvent === CONST.BANK_ACCOUNT.PLAID.EVENTS_NAME.EXIT); + const isLoading = (!!isLoadingApp || !!account?.isLoading || reimbursementAccount?.isLoading) && (!plaidCurrentEvent || plaidCurrentEvent === CONST.BANK_ACCOUNT.PLAID.EVENTS_NAME.EXIT); const shouldShowOfflineLoader = !( isOffline && - _.contains( - [ - CONST.BANK_ACCOUNT.STEP.BANK_ACCOUNT, - CONST.BANK_ACCOUNT.STEP.COMPANY, - CONST.BANK_ACCOUNT.STEP.REQUESTOR, - CONST.BANK_ACCOUNT.STEP.BENEFICIAL_OWNERS, - CONST.BANK_ACCOUNT.STEP.ACH_CONTRACT, - ], - currentStep, - ) + [ + CONST.BANK_ACCOUNT.STEP.BANK_ACCOUNT, + CONST.BANK_ACCOUNT.STEP.COMPANY, + CONST.BANK_ACCOUNT.STEP.REQUESTOR, + CONST.BANK_ACCOUNT.STEP.BENEFICIAL_OWNERS, + CONST.BANK_ACCOUNT.STEP.ACH_CONTRACT, + ].some((value) => value === currentStep) ); // Show loading indicator when page is first time being opened and props.reimbursementAccount yet to be loaded from the server @@ -443,16 +445,16 @@ function ReimbursementAccountPage({reimbursementAccount, route, onfidoToken, pol Navigation.goBack(ROUTES.SETTINGS_WORKSPACES)} - subtitleKey={_.isEmpty(policy) ? undefined : 'workspace.common.notAuthorized'} + subtitleKey={isEmptyObject(policy) ? undefined : 'workspace.common.notAuthorized'} /> ); } let errorText; - const userHasPhonePrimaryEmail = Str.endsWith(session.email, CONST.SMS.DOMAIN); - const throttledDate = lodashGet(reimbursementAccount, 'throttledDate', ''); - const hasUnsupportedCurrency = lodashGet(policy, 'outputCurrency', '') !== CONST.CURRENCY.USD; + const userHasPhonePrimaryEmail = Str.endsWith(session?.email ?? '', CONST.SMS.DOMAIN); + const throttledDate = reimbursementAccount?.throttledDate ?? ''; + const hasUnsupportedCurrency = (policy?.outputCurrency ?? '') !== CONST.CURRENCY.USD; if (userHasPhonePrimaryEmail) { errorText = translate('bankAccount.hasPhoneLoginError'); @@ -510,7 +512,7 @@ function ReimbursementAccountPage({reimbursementAccount, route, onfidoToken, pol } if (currentStep === CONST.BANK_ACCOUNT.STEP.REQUESTOR) { - const shouldShowOnfido = onfidoToken && !achData.isOnfidoSetupComplete; + const shouldShowOnfido = onfidoToken && !achData?.isOnfidoSetupComplete; return ( ({ reimbursementAccount: { key: ONYXKEYS.REIMBURSEMENT_ACCOUNT, }, @@ -572,6 +572,5 @@ export default compose( account: { key: ONYXKEYS.ACCOUNT, }, - }), - withPolicy, -)(ReimbursementAccountPage); + })(ReimbursementAccountPage), +); diff --git a/src/types/onyx/ReimbursementAccount.ts b/src/types/onyx/ReimbursementAccount.ts index 173200ba681a..db56aa027b13 100644 --- a/src/types/onyx/ReimbursementAccount.ts +++ b/src/types/onyx/ReimbursementAccount.ts @@ -38,6 +38,8 @@ type ACHData = BeneficialOwnersStepProps & /** Policy ID of the workspace the bank account is being set up on */ policyID?: string; + + isOnfidoSetupComplete?: boolean; }; type ReimbursementAccount = { From 8c181882037f6dad85f7443fec2ca655e489190d Mon Sep 17 00:00:00 2001 From: Jakub Butkiewicz Date: Mon, 26 Feb 2024 11:55:21 +0100 Subject: [PATCH 015/213] ref: move ACHContractStep to TS --- .../{ACHContractStep.js => ACHContractStep.tsx} | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) rename src/pages/ReimbursementAccount/{ACHContractStep.js => ACHContractStep.tsx} (61%) diff --git a/src/pages/ReimbursementAccount/ACHContractStep.js b/src/pages/ReimbursementAccount/ACHContractStep.tsx similarity index 61% rename from src/pages/ReimbursementAccount/ACHContractStep.js rename to src/pages/ReimbursementAccount/ACHContractStep.tsx index f2c2e5af85de..bf6013840db1 100644 --- a/src/pages/ReimbursementAccount/ACHContractStep.js +++ b/src/pages/ReimbursementAccount/ACHContractStep.tsx @@ -1,16 +1,14 @@ -import PropTypes from 'prop-types'; import React from 'react'; import CompleteVerification from './CompleteVerification/CompleteVerification'; -const propTypes = { +type ACHContractStepProps = { /** Goes to the previous step */ - onBackButtonPress: PropTypes.func.isRequired, + onBackButtonPress: () => void; }; -function ACHContractStep({onBackButtonPress}) { +function ACHContractStep({onBackButtonPress}: ACHContractStepProps) { return ; } -ACHContractStep.propTypes = propTypes; ACHContractStep.displayName = 'ACHContractStep'; export default ACHContractStep; From 1ca6fb0e7458d9bba69e5493434e57a37b21137b Mon Sep 17 00:00:00 2001 From: Jakub Butkiewicz Date: Mon, 26 Feb 2024 12:27:51 +0100 Subject: [PATCH 016/213] ref: move BankAccountStep to TS --- src/libs/Navigation/types.ts | 1 + src/libs/getPlaidDesktopMessage/types.ts | 4 +- ...BankAccountStep.js => BankAccountStep.tsx} | 146 +++++++++--------- .../BankInfo/BankInfo.tsx | 3 +- .../EnableBankAccount/EnableBankAccount.tsx | 2 +- .../ReimbursementAccountPage.tsx | 36 ++--- .../WorkspaceResetBankAccountModal.tsx | 2 +- 7 files changed, 90 insertions(+), 104 deletions(-) rename src/pages/ReimbursementAccount/{BankAccountStep.js => BankAccountStep.tsx} (65%) diff --git a/src/libs/Navigation/types.ts b/src/libs/Navigation/types.ts index bf3dfc573845..ea7934637697 100644 --- a/src/libs/Navigation/types.ts +++ b/src/libs/Navigation/types.ts @@ -367,6 +367,7 @@ type ReimbursementAccountNavigatorParamList = { [SCREENS.REIMBURSEMENT_ACCOUNT_ROOT]: { stepToOpen?: string; policyID?: string; + backTo?: Routes; }; }; diff --git a/src/libs/getPlaidDesktopMessage/types.ts b/src/libs/getPlaidDesktopMessage/types.ts index 12a13737339c..95a6254e997b 100644 --- a/src/libs/getPlaidDesktopMessage/types.ts +++ b/src/libs/getPlaidDesktopMessage/types.ts @@ -1,3 +1,5 @@ -type GetPlaidDesktopMessage = () => string | undefined; +import type {TranslationPaths} from '@src/languages/types'; + +type GetPlaidDesktopMessage = () => TranslationPaths | undefined; export default GetPlaidDesktopMessage; diff --git a/src/pages/ReimbursementAccount/BankAccountStep.js b/src/pages/ReimbursementAccount/BankAccountStep.tsx similarity index 65% rename from src/pages/ReimbursementAccount/BankAccountStep.js rename to src/pages/ReimbursementAccount/BankAccountStep.tsx index 75ae02587486..a2d836e59be6 100644 --- a/src/pages/ReimbursementAccount/BankAccountStep.js +++ b/src/pages/ReimbursementAccount/BankAccountStep.tsx @@ -1,7 +1,6 @@ -import lodashGet from 'lodash/get'; -import PropTypes from 'prop-types'; import React from 'react'; import {ScrollView, View} from 'react-native'; +import type {OnyxEntry} from 'react-native-onyx'; import {withOnyx} from 'react-native-onyx'; import Button from '@components/Button'; import HeaderWithBackButton from '@components/HeaderWithBackButton'; @@ -14,10 +13,9 @@ import ScreenWrapper from '@components/ScreenWrapper'; import Section from '@components/Section'; import Text from '@components/Text'; import TextLink from '@components/TextLink'; -import withLocalize from '@components/withLocalize'; +import useLocalize from '@hooks/useLocalize'; import useTheme from '@hooks/useTheme'; import useThemeStyles from '@hooks/useThemeStyles'; -import compose from '@libs/compose'; import getPlaidDesktopMessage from '@libs/getPlaidDesktopMessage'; import variables from '@styles/variables'; import * as BankAccounts from '@userActions/BankAccounts'; @@ -29,59 +27,59 @@ import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; import INPUT_IDS from '@src/types/form/ReimbursementAccountForm'; +import type * as OnyxTypes from '@src/types/onyx'; import BankInfo from './BankInfo/BankInfo'; -import StepPropTypes from './StepPropTypes'; -const propTypes = { - ...StepPropTypes, +type BankAccountStepOnyxProps = { + /** Object with various information about the user */ + user: OnyxEntry; + /** If the plaid button has been disabled */ + isPlaidDisabled: OnyxEntry; +}; + +type BankAccountStepProps = BankAccountStepOnyxProps & { /** The OAuth URI + stateID needed to re-initialize the PlaidLink after the user logs into their bank */ - receivedRedirectURI: PropTypes.string, + receivedRedirectURI?: string | null; /** During the OAuth flow we need to use the plaidLink token that we initially connected with */ - plaidLinkOAuthToken: PropTypes.string, - - /** Object with various information about the user */ - user: PropTypes.shape({ - /** Is the user account validated? */ - validated: PropTypes.bool, - }), - - /** If the plaid button has been disabled */ - isPlaidDisabled: PropTypes.bool, + plaidLinkOAuthToken?: OnyxEntry; /* The workspace name */ - policyName: PropTypes.string, + policyName?: string; /* The workspace ID */ - policyID: PropTypes.string, -}; + policyID?: string; + + /** The bank account currently in setup */ + reimbursementAccount: OnyxEntry; -const defaultProps = { - receivedRedirectURI: null, - plaidLinkOAuthToken: '', - user: {}, - isPlaidDisabled: false, - policyName: '', - policyID: '', + /** Goes to the previous step */ + onBackButtonPress: () => void; }; const bankInfoStepKeys = INPUT_IDS.BANK_INFO_STEP; -function BankAccountStep(props) { +function BankAccountStep({ + plaidLinkOAuthToken = '', + policyID = '', + policyName = '', + user, + receivedRedirectURI, + reimbursementAccount, + onBackButtonPress, + isPlaidDisabled, +}: BankAccountStepProps) { const theme = useTheme(); const styles = useThemeStyles(); - let subStep = lodashGet(props.reimbursementAccount, 'achData.subStep', ''); - const shouldReinitializePlaidLink = props.plaidLinkOAuthToken && props.receivedRedirectURI && subStep !== CONST.BANK_ACCOUNT.SUBSTEP.MANUAL; + const {translate} = useLocalize(); + let subStep = reimbursementAccount?.achData?.subStep ?? ''; + const shouldReinitializePlaidLink = plaidLinkOAuthToken && receivedRedirectURI && subStep !== CONST.BANK_ACCOUNT.SUBSTEP.MANUAL; if (shouldReinitializePlaidLink) { subStep = CONST.BANK_ACCOUNT.SETUP_TYPE.PLAID; } const plaidDesktopMessage = getPlaidDesktopMessage(); - const bankAccountRoute = `${CONFIG.EXPENSIFY.NEW_EXPENSIFY_URL}${ROUTES.BANK_ACCOUNT_WITH_STEP_TO_OPEN.getRoute( - 'new', - props.policyID, - ROUTES.WORKSPACE_INITIAL.getRoute(props.policyID), - )}`; + const bankAccountRoute = `${CONFIG.EXPENSIFY.NEW_EXPENSIFY_URL}${ROUTES.BANK_ACCOUNT_WITH_STEP_TO_OPEN.getRoute('new', policyID, ROUTES.WORKSPACE_INITIAL.getRoute(policyID))}`; const removeExistingBankAccountDetails = () => { const bankAccountData = { @@ -99,8 +97,8 @@ function BankAccountStep(props) { if (subStep === CONST.BANK_ACCOUNT.SETUP_TYPE.PLAID || subStep === CONST.BANK_ACCOUNT.SETUP_TYPE.MANUAL) { return ( ); } @@ -112,49 +110,48 @@ function BankAccountStep(props) { > - +
- - {props.translate('bankAccount.toGetStarted')} + + {translate('bankAccount.toGetStarted')} - {Boolean(plaidDesktopMessage) && ( + {!!plaidDesktopMessage && ( - {props.translate(plaidDesktopMessage)} + {translate(plaidDesktopMessage)} )}
- {!props.user.validated && ( + {!user?.validated && ( - {props.translate('bankAccount.validateAccountError.phrase1')} + {translate('bankAccount.validateAccountError.phrase1')} Session.signOutAndRedirectToSignIn()} > - {props.translate('bankAccount.validateAccountError.phrase2')} + {translate('bankAccount.validateAccountError.phrase2')} . )} - {props.translate('common.privacy')} + {translate('common.privacy')} Link.openExternalLink('https://community.expensify.com/discussion/5677/deep-dive-how-expensify-protects-your-information/')} style={[styles.flexRow, styles.alignItemsCenter]} - accessibilityLabel={props.translate('bankAccount.yourDataIsSecure')} + accessibilityLabel={translate('bankAccount.yourDataIsSecure')} > - {props.translate('bankAccount.yourDataIsSecure')} + {translate('bankAccount.yourDataIsSecure')} - + ({ + user: { + key: ONYXKEYS.USER, + }, + isPlaidDisabled: { + key: ONYXKEYS.IS_PLAID_DISABLED, + }, +})(BankAccountStep); diff --git a/src/pages/ReimbursementAccount/BankInfo/BankInfo.tsx b/src/pages/ReimbursementAccount/BankInfo/BankInfo.tsx index bb352acd4732..12ba39744eb2 100644 --- a/src/pages/ReimbursementAccount/BankInfo/BankInfo.tsx +++ b/src/pages/ReimbursementAccount/BankInfo/BankInfo.tsx @@ -31,13 +31,12 @@ type BankInfoOnyxProps = { /** The draft values of the bank account being setup */ reimbursementAccountDraft: OnyxEntry; - - policyID: string; }; type BankInfoProps = BankInfoOnyxProps & { /** Goes to the previous step */ onBackButtonPress: () => void; + policyID: string; }; const BANK_INFO_STEP_KEYS = INPUT_IDS.BANK_INFO_STEP; diff --git a/src/pages/ReimbursementAccount/EnableBankAccount/EnableBankAccount.tsx b/src/pages/ReimbursementAccount/EnableBankAccount/EnableBankAccount.tsx index fd2f05493098..d14b00afe5f1 100644 --- a/src/pages/ReimbursementAccount/EnableBankAccount/EnableBankAccount.tsx +++ b/src/pages/ReimbursementAccount/EnableBankAccount/EnableBankAccount.tsx @@ -29,7 +29,7 @@ type EnableBankAccountOnyxProps = { type EnableBankAccountProps = EnableBankAccountOnyxProps & { /** Bank account currently in setup */ - reimbursementAccount: ReimbursementAccount; + reimbursementAccount: OnyxEntry; /** Method to trigger when pressing back button of the header */ onBackButtonPress: () => void; diff --git a/src/pages/ReimbursementAccount/ReimbursementAccountPage.tsx b/src/pages/ReimbursementAccount/ReimbursementAccountPage.tsx index 978c70ef6af7..72059b2fe8d3 100644 --- a/src/pages/ReimbursementAccount/ReimbursementAccountPage.tsx +++ b/src/pages/ReimbursementAccount/ReimbursementAccountPage.tsx @@ -1,10 +1,9 @@ -import {RouteProp} from '@react-navigation/native'; +import type {RouteProp} from '@react-navigation/native'; import Str from 'expensify-common/lib/str'; import lodashPick from 'lodash/pick'; -import PropTypes from 'prop-types'; import React, {useEffect, useRef, useState} from 'react'; import {View} from 'react-native'; -import type {OnyxCollection, OnyxEntry} from 'react-native-onyx'; +import type {OnyxEntry} from 'react-native-onyx'; import {withOnyx} from 'react-native-onyx'; import type {ValueOf} from 'type-fest'; import FullPageNotFoundView from '@components/BlockingViews/FullPageNotFoundView'; @@ -16,22 +15,21 @@ import useLocalize from '@hooks/useLocalize'; import useNetwork from '@hooks/useNetwork'; import usePrevious from '@hooks/usePrevious'; import useThemeStyles from '@hooks/useThemeStyles'; -import compose from '@libs/compose'; import getPlaidOAuthReceivedRedirectURI from '@libs/getPlaidOAuthReceivedRedirectURI'; import BankAccount from '@libs/models/BankAccount'; import Navigation from '@libs/Navigation/Navigation'; -import {ReimbursementAccountNavigatorParamList} from '@libs/Navigation/types'; +import type {ReimbursementAccountNavigatorParamList} from '@libs/Navigation/types'; import * as PolicyUtils from '@libs/PolicyUtils'; import shouldReopenOnfido from '@libs/shouldReopenOnfido'; -import withPolicy, {WithPolicyProps} from '@pages/workspace/withPolicy'; +import type {WithPolicyProps} from '@pages/workspace/withPolicy'; +import withPolicy from '@pages/workspace/withPolicy'; import * as BankAccounts from '@userActions/BankAccounts'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; -import type {Route} from '@src/ROUTES'; import ROUTES from '@src/ROUTES'; -import SCREENS from '@src/SCREENS'; +import type SCREENS from '@src/SCREENS'; import type * as OnyxTypes from '@src/types/onyx'; -import {ACHData} from '@src/types/onyx/ReimbursementAccount'; +import type {ACHData} from '@src/types/onyx/ReimbursementAccount'; import {isEmptyObject} from '@src/types/utils/EmptyObject'; import ACHContractStep from './ACHContractStep'; import BankAccountStep from './BankAccountStep'; @@ -40,7 +38,6 @@ import CompanyStep from './CompanyStep'; import ConnectBankAccount from './ConnectBankAccount/ConnectBankAccount'; import ContinueBankAccountSetup from './ContinueBankAccountSetup'; import EnableBankAccount from './EnableBankAccount/EnableBankAccount'; -import reimbursementAccountDraftPropTypes from './ReimbursementAccountDraftPropTypes'; import * as ReimbursementAccountProps from './reimbursementAccountPropTypes'; import RequestorStep from './RequestorStep'; @@ -181,7 +178,6 @@ function ReimbursementAccountPage({ session, plaidLinkToken = '', plaidCurrentEvent = '', - reimbursementAccountDraft, }: ReimbursementAccountPageProps) { /** The SetupWithdrawalAccount flow allows us to continue the flow from various points depending on where the @@ -255,14 +251,14 @@ function ReimbursementAccountPage({ which acts similarly to `componentDidUpdate` when the `reimbursementAccount` dependency changes. */ const [hasACHDataBeenLoaded, setHasACHDataBeenLoaded] = useState( - reimbursementAccount !== ReimbursementAccountProps.reimbursementAccountDefaultProps && reimbursementAccount.achData && 'currentStep' in reimbursementAccount.achData, + reimbursementAccount !== ReimbursementAccountProps.reimbursementAccountDefaultProps && reimbursementAccount?.achData && 'currentStep' in reimbursementAccount?.achData, ); const [shouldShowContinueSetupButton, setShouldShowContinueSetupButton] = useState(hasACHDataBeenLoaded ? getShouldShowContinueSetupButtonInitialValue() : false); const currentStep = achData?.currentStep ?? CONST.BANK_ACCOUNT.STEP.BANK_ACCOUNT; const policyName = policy?.name ?? ''; - const policyID = route.params.policyID ?? ''; + const policyID = route.params?.policyID ?? ''; const styles = useThemeStyles(); const {translate} = useLocalize(); const {isOffline} = useNetwork(); @@ -305,7 +301,7 @@ function ReimbursementAccountPage({ } if (!hasACHDataBeenLoaded) { - if (reimbursementAccount !== ReimbursementAccountProps.reimbursementAccountDefaultProps && reimbursementAccount.isLoading === false) { + if (reimbursementAccount !== ReimbursementAccountProps.reimbursementAccountDefaultProps && reimbursementAccount?.isLoading === false) { setShouldShowContinueSetupButton(getShouldShowContinueSetupButtonInitialValue()); setHasACHDataBeenLoaded(true); } @@ -348,9 +344,7 @@ function ReimbursementAccountPage({ BankAccounts.hideBankAccountErrors(); } - const backTo = route.params.backTo; - // eslint-disable-next-line no-shadow - const policyID = route.params.policyID; + const backTo = route.params?.backTo; Navigation.navigate(ROUTES.BANK_ACCOUNT_WITH_STEP_TO_OPEN.getRoute(getRouteForCurrentStep(currentStep), policyID, backTo)); }, @@ -365,8 +359,6 @@ function ReimbursementAccountPage({ fetchData(true); }; - const getDefaultStateForField = (fieldName: keyof ACHData, defaultValue = ''): string | number | boolean | string[] => reimbursementAccount?.achData?.[fieldName] ?? defaultValue; - const goBack = () => { const subStep = achData?.subStep; const shouldShowOnfido = onfidoToken && !achData?.isOnfidoSetupComplete; @@ -439,7 +431,7 @@ function ReimbursementAccountPage({ return ; } - if (!isLoading && (_.isEmpty(policy) || !PolicyUtils.isPolicyAdmin(policy))) { + if (!isLoading && (isEmptyObject(policy) || !PolicyUtils.isPolicyAdmin(policy))) { return ( @@ -574,3 +564,5 @@ export default withPolicy( }, })(ReimbursementAccountPage), ); + +export type {ReimbursementAccountDraft}; diff --git a/src/pages/workspace/WorkspaceResetBankAccountModal.tsx b/src/pages/workspace/WorkspaceResetBankAccountModal.tsx index 19428408c609..d471b89ece7a 100644 --- a/src/pages/workspace/WorkspaceResetBankAccountModal.tsx +++ b/src/pages/workspace/WorkspaceResetBankAccountModal.tsx @@ -17,7 +17,7 @@ type WorkspaceResetBankAccountModalOnyxProps = { type WorkspaceResetBankAccountModalProps = WorkspaceResetBankAccountModalOnyxProps & { /** Reimbursement account data */ - reimbursementAccount: OnyxTypes.ReimbursementAccount; + reimbursementAccount: OnyxEntry; }; function WorkspaceResetBankAccountModal({reimbursementAccount, session}: WorkspaceResetBankAccountModalProps) { From ceb9fab5def5d62d3e380beb7f95d0819608d527 Mon Sep 17 00:00:00 2001 From: Jakub Butkiewicz Date: Mon, 26 Feb 2024 12:29:10 +0100 Subject: [PATCH 017/213] ref: move CompanyStep to TS --- .../{CompanyStep.js => CompanyStep.tsx} | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) rename src/pages/ReimbursementAccount/{CompanyStep.js => CompanyStep.tsx} (58%) diff --git a/src/pages/ReimbursementAccount/CompanyStep.js b/src/pages/ReimbursementAccount/CompanyStep.tsx similarity index 58% rename from src/pages/ReimbursementAccount/CompanyStep.js rename to src/pages/ReimbursementAccount/CompanyStep.tsx index ea814ff37943..4c9af53938b3 100644 --- a/src/pages/ReimbursementAccount/CompanyStep.js +++ b/src/pages/ReimbursementAccount/CompanyStep.tsx @@ -1,17 +1,14 @@ -import PropTypes from 'prop-types'; import React from 'react'; import BusinessInfo from './BusinessInfo/BusinessInfo'; -const propTypes = { +type CompanyStepProps = { /** Goes to the previous step */ - onBackButtonPress: PropTypes.func.isRequired, + onBackButtonPress: () => void; }; - -function CompanyStep({onBackButtonPress}) { +function CompanyStep({onBackButtonPress}: CompanyStepProps) { return ; } -CompanyStep.propTypes = propTypes; CompanyStep.displayName = 'CompanyStep'; export default CompanyStep; From 646a2e51028986b76d141d7158f8b045252d3dd2 Mon Sep 17 00:00:00 2001 From: Jakub Butkiewicz Date: Mon, 26 Feb 2024 13:47:52 +0100 Subject: [PATCH 018/213] ref: move ContinueBankAccountStep, ExampleCheck to TS, started working on IdentityForm migration --- ...tSetup.js => ContinueBankAccountSetup.tsx} | 62 +++--- .../{ExampleCheck.js => ExampleCheck.tsx} | 0 .../ReimbursementAccount/IdentityForm.js | 209 ------------------ .../ReimbursementAccount/IdentityForm.tsx | 112 ++++++++++ .../ReimbursementAccountPage.tsx | 2 +- 5 files changed, 141 insertions(+), 244 deletions(-) rename src/pages/ReimbursementAccount/{ContinueBankAccountSetup.js => ContinueBankAccountSetup.tsx} (57%) rename src/pages/ReimbursementAccount/{ExampleCheck.js => ExampleCheck.tsx} (100%) delete mode 100644 src/pages/ReimbursementAccount/IdentityForm.js create mode 100644 src/pages/ReimbursementAccount/IdentityForm.tsx diff --git a/src/pages/ReimbursementAccount/ContinueBankAccountSetup.js b/src/pages/ReimbursementAccount/ContinueBankAccountSetup.tsx similarity index 57% rename from src/pages/ReimbursementAccount/ContinueBankAccountSetup.js rename to src/pages/ReimbursementAccount/ContinueBankAccountSetup.tsx index d1ac0989ae38..8b410a52789c 100644 --- a/src/pages/ReimbursementAccount/ContinueBankAccountSetup.js +++ b/src/pages/ReimbursementAccount/ContinueBankAccountSetup.tsx @@ -1,8 +1,6 @@ -import lodashGet from 'lodash/get'; -import PropTypes from 'prop-types'; import React from 'react'; import {ScrollView} from 'react-native'; -import _ from 'underscore'; +import type {OnyxEntry} from 'react-native-onyx'; import Button from '@components/Button'; import HeaderWithBackButton from '@components/HeaderWithBackButton'; import * as Expensicons from '@components/Icon/Expensicons'; @@ -12,88 +10,84 @@ import OfflineWithFeedback from '@components/OfflineWithFeedback'; import ScreenWrapper from '@components/ScreenWrapper'; import Section from '@components/Section'; import Text from '@components/Text'; -import withLocalize, {withLocalizePropTypes} from '@components/withLocalize'; +import useLocalize from '@hooks/useLocalize'; import useThemeStyles from '@hooks/useThemeStyles'; import WorkspaceResetBankAccountModal from '@pages/workspace/WorkspaceResetBankAccountModal'; import * as BankAccounts from '@userActions/BankAccounts'; import CONST from '@src/CONST'; -import * as ReimbursementAccountProps from './reimbursementAccountPropTypes'; +import type * as OnyxTypes from '@src/types/onyx'; +import {isEmptyObject} from '@src/types/utils/EmptyObject'; -const propTypes = { +type ContinueBankAccountStepProps = { /** Bank account currently in setup */ - reimbursementAccount: ReimbursementAccountProps.reimbursementAccountPropTypes.isRequired, + reimbursementAccount: OnyxEntry; /** Callback to continue to the next step of the setup */ - continue: PropTypes.func.isRequired, + continueFunction: () => void; /* The workspace name */ - policyName: PropTypes.string, + policyName?: string; /** Goes to the previous step */ - onBackButtonPress: PropTypes.func.isRequired, - - ...withLocalizePropTypes, + onBackButtonPress: () => void; }; -const defaultProps = {policyName: ''}; - -function ContinueBankAccountSetup(props) { +function ContinueBankAccountSetup({policyName = '', onBackButtonPress, reimbursementAccount, continueFunction}: ContinueBankAccountStepProps) { const styles = useThemeStyles(); - const errors = lodashGet(props.reimbursementAccount, 'errors', {}); - const pendingAction = lodashGet(props.reimbursementAccount, 'pendingAction', null); + const {translate} = useLocalize(); + const errors = reimbursementAccount?.errors ?? {}; + const pendingAction = reimbursementAccount?.pendingAction ?? null; return (
- {props.translate('workspace.bankAccount.youreAlmostDone')} + {translate('workspace.bankAccount.youreAlmostDone')}
- {props.reimbursementAccount.shouldShowResetModal && } + {reimbursementAccount?.shouldShowResetModal && }
); } -ContinueBankAccountSetup.propTypes = propTypes; -ContinueBankAccountSetup.defaultProps = defaultProps; ContinueBankAccountSetup.displayName = 'ContinueBankAccountSetup'; -export default withLocalize(ContinueBankAccountSetup); +export default ContinueBankAccountSetup; diff --git a/src/pages/ReimbursementAccount/ExampleCheck.js b/src/pages/ReimbursementAccount/ExampleCheck.tsx similarity index 100% rename from src/pages/ReimbursementAccount/ExampleCheck.js rename to src/pages/ReimbursementAccount/ExampleCheck.tsx diff --git a/src/pages/ReimbursementAccount/IdentityForm.js b/src/pages/ReimbursementAccount/IdentityForm.js deleted file mode 100644 index 1c53511723af..000000000000 --- a/src/pages/ReimbursementAccount/IdentityForm.js +++ /dev/null @@ -1,209 +0,0 @@ -import {subYears} from 'date-fns'; -import PropTypes from 'prop-types'; -import React from 'react'; -import {View} from 'react-native'; -import _ from 'underscore'; -import DatePicker from '@components/DatePicker'; -import InputWrapper from '@components/Form/InputWrapper'; -import TextInput from '@components/TextInput'; -import useThemeStyles from '@hooks/useThemeStyles'; -import CONST from '@src/CONST'; -import AddressForm from './AddressForm'; - -const propTypes = { - /** Style for wrapping View */ - style: PropTypes.oneOfType([PropTypes.arrayOf(PropTypes.object), PropTypes.object]), - - /** Form values */ - values: PropTypes.shape({ - /** First name field */ - firstName: PropTypes.string, - - /** Last name field */ - lastName: PropTypes.string, - - /** Address street field */ - street: PropTypes.string, - - /** Address city field */ - city: PropTypes.string, - - /** Address state field */ - state: PropTypes.string, - - /** Address zip code field */ - zipCode: PropTypes.string, - - /** Date of birth field */ - dob: PropTypes.oneOfType([PropTypes.string, PropTypes.instanceOf(Date)]), - - /** Last 4 digits of SSN */ - ssnLast4: PropTypes.string, - }), - - /** Default values */ - defaultValues: PropTypes.shape({ - /** First name field */ - firstName: PropTypes.string, - - /** Last name field */ - lastName: PropTypes.string, - - /** Address street field */ - street: PropTypes.string, - - /** Address city field */ - city: PropTypes.string, - - /** Address state field */ - state: PropTypes.string, - - /** Address zip code field */ - zipCode: PropTypes.string, - - /** Date of birth field */ - dob: PropTypes.oneOfType([PropTypes.string, PropTypes.instanceOf(Date)]), - - /** Last 4 digits of SSN */ - ssnLast4: PropTypes.string, - }), - - /** Any errors that can arise from form validation */ - errors: PropTypes.objectOf(PropTypes.bool), - - /** The map for inputID of the inputs */ - inputKeys: PropTypes.shape({ - firstName: PropTypes.string, - lastName: PropTypes.string, - dob: PropTypes.string, - ssnLast4: PropTypes.string, - street: PropTypes.string, - city: PropTypes.string, - state: PropTypes.string, - zipCode: PropTypes.string, - }), - - /** Saves a draft of the input value when used in a form */ - shouldSaveDraft: PropTypes.bool, - - /** Returns translated string for given locale and phrase */ - translate: PropTypes.func.isRequired, -}; - -const defaultProps = { - style: {}, - values: { - firstName: undefined, - lastName: undefined, - street: undefined, - city: undefined, - state: undefined, - zipCode: undefined, - dob: undefined, - ssnLast4: undefined, - }, - defaultValues: { - firstName: undefined, - lastName: undefined, - street: undefined, - city: undefined, - state: undefined, - zipCode: undefined, - dob: undefined, - ssnLast4: undefined, - }, - errors: {}, - inputKeys: { - firstName: '', - lastName: '', - street: '', - city: '', - state: '', - zipCode: '', - dob: '', - ssnLast4: '', - }, - shouldSaveDraft: false, -}; - -function IdentityForm(props) { - const styles = useThemeStyles(); - // dob field has multiple validations/errors, we are handling it temporarily like this. - const dobErrorText = (props.errors.dob ? 'bankAccount.error.dob' : '') || (props.errors.dobAge ? 'bankAccount.error.age' : ''); - const identityFormInputKeys = ['firstName', 'lastName', 'dob', 'ssnLast4']; - - const minDate = subYears(new Date(), CONST.DATE_BIRTH.MAX_AGE); - const maxDate = subYears(new Date(), CONST.DATE_BIRTH.MIN_AGE_FOR_PAYMENT); - - return ( - - - - - - - - - - - - - - ); -} - -IdentityForm.propTypes = propTypes; -IdentityForm.defaultProps = defaultProps; -IdentityForm.displayName = 'IdentityForm'; -export default IdentityForm; diff --git a/src/pages/ReimbursementAccount/IdentityForm.tsx b/src/pages/ReimbursementAccount/IdentityForm.tsx new file mode 100644 index 000000000000..16560f363400 --- /dev/null +++ b/src/pages/ReimbursementAccount/IdentityForm.tsx @@ -0,0 +1,112 @@ +import {subYears} from 'date-fns'; +import React from 'react'; +import type {StyleProp, ViewStyle} from 'react-native'; +import {View} from 'react-native'; +import DatePicker from '@components/DatePicker'; +import InputWrapper from '@components/Form/InputWrapper'; +import TextInput from '@components/TextInput'; +import useLocalize from '@hooks/useLocalize'; +import useThemeStyles from '@hooks/useThemeStyles'; +import CONST from '@src/CONST'; +import AddressForm from './AddressForm'; + +type IdentityFormProps = { + /** Style for wrapping View */ + style?: StyleProp; + + /** Form values */ + values: any; + + /** Default values */ + defaultValues: any; + + /** Any errors that can arise from form validation */ + errors: Record; + + /** The map for inputID of the inputs */ + inputKeys: any; + + /** Saves a draft of the input value when used in a form */ + shouldSaveDraft?: boolean; +}; + +function IdentityForm({shouldSaveDraft = false, errors, values, inputKeys, defaultValues, style}: IdentityFormProps) { + const styles = useThemeStyles(); + const {translate} = useLocalize(); + // dob field has multiple validations/errors, we are handling it temporarily like this. + const dobErrorText = (errors?.dob ? 'bankAccount.error.dob' : '') || (errors?.dobAge ? 'bankAccount.error.age' : ''); + const identityFormInputKeys = ['firstName', 'lastName', 'dob', 'ssnLast4']; + + const minDate = subYears(new Date(), CONST.DATE_BIRTH.MAX_AGE); + const maxDate = subYears(new Date(), CONST.DATE_BIRTH.MIN_AGE_FOR_PAYMENT); + + return ( + + + + + + + + + + + + + + ); +} + +IdentityForm.displayName = 'IdentityForm'; +export default IdentityForm; diff --git a/src/pages/ReimbursementAccount/ReimbursementAccountPage.tsx b/src/pages/ReimbursementAccount/ReimbursementAccountPage.tsx index 72059b2fe8d3..8a53d5ca729f 100644 --- a/src/pages/ReimbursementAccount/ReimbursementAccountPage.tsx +++ b/src/pages/ReimbursementAccount/ReimbursementAccountPage.tsx @@ -475,7 +475,7 @@ function ReimbursementAccountPage({ return ( { Navigation.goBack(); From 67f3da477942708cbee08b3c0aff463fe9f3fdf7 Mon Sep 17 00:00:00 2001 From: Jakub Butkiewicz Date: Tue, 27 Feb 2024 08:55:40 +0100 Subject: [PATCH 019/213] fix: typecheck --- src/components/AddressSearch/index.tsx | 3 +- src/components/AddressSearch/types.ts | 2 +- src/components/Form/types.ts | 2 +- src/libs/Navigation/types.ts | 2 +- .../ReimbursementAccount/AddressForm.tsx | 2 +- .../ReimbursementAccount/IdentityForm.tsx | 112 ------------------ .../ReimbursementAccountDraftPropTypes.js | 45 ------- .../ReimbursementAccountPage.tsx | 20 ++-- .../ReimbursementAccount/StepPropTypes.js | 20 ---- 9 files changed, 13 insertions(+), 195 deletions(-) delete mode 100644 src/pages/ReimbursementAccount/IdentityForm.tsx delete mode 100644 src/pages/ReimbursementAccount/ReimbursementAccountDraftPropTypes.js delete mode 100644 src/pages/ReimbursementAccount/StepPropTypes.js diff --git a/src/components/AddressSearch/index.tsx b/src/components/AddressSearch/index.tsx index 8ad26e5a7c46..0b9498e34aa2 100644 --- a/src/components/AddressSearch/index.tsx +++ b/src/components/AddressSearch/index.tsx @@ -6,6 +6,7 @@ import {GooglePlacesAutocomplete} from 'react-native-google-places-autocomplete' import type {GooglePlaceData, GooglePlaceDetail} from 'react-native-google-places-autocomplete'; import FullScreenLoadingIndicator from '@components/FullscreenLoadingIndicator'; import LocationErrorMessage from '@components/LocationErrorMessage'; +import type {State} from '@components/StatePicker/StateSelectorModal'; import Text from '@components/Text'; import TextInput from '@components/TextInput'; import useLocalize from '@hooks/useLocalize'; @@ -48,7 +49,7 @@ function AddressSearch( street: 'addressStreet', street2: 'addressStreet2', city: 'addressCity', - state: 'addressState', + state: 'addressState' as State, zipCode: 'addressZipCode', lat: 'addressLat', lng: 'addressLng', diff --git a/src/components/AddressSearch/types.ts b/src/components/AddressSearch/types.ts index c08191dd2489..0d3f8c1a972e 100644 --- a/src/components/AddressSearch/types.ts +++ b/src/components/AddressSearch/types.ts @@ -1,8 +1,8 @@ import type {RefObject} from 'react'; import type {NativeSyntheticEvent, StyleProp, TextInputFocusEventData, View, ViewStyle} from 'react-native'; import type {Place} from 'react-native-google-places-autocomplete'; +import type {State} from '@components/StatePicker/StateSelectorModal'; import type {MaybePhraseKey} from '@libs/Localize'; -import {State} from '@libs/Navigation/types'; import type Locale from '@src/types/onyx/Locale'; type CurrentLocationButtonProps = { diff --git a/src/components/Form/types.ts b/src/components/Form/types.ts index b509f89e0414..40b4fcece482 100644 --- a/src/components/Form/types.ts +++ b/src/components/Form/types.ts @@ -10,9 +10,9 @@ import type Picker from '@components/Picker'; import type RadioButtons from '@components/RadioButtons'; import type SingleChoiceQuestion from '@components/SingleChoiceQuestion'; import type StatePicker from '@components/StatePicker'; +import type {State} from '@components/StatePicker/StateSelectorModal'; import type TextInput from '@components/TextInput'; import type ValuePicker from '@components/ValuePicker'; -import type {State} from '@libs/Navigation/types'; import type BusinessTypePicker from '@pages/ReimbursementAccount/BusinessInfo/substeps/TypeBusiness/BusinessTypePicker'; import type {TranslationPaths} from '@src/languages/types'; import type {OnyxFormKey, OnyxValues} from '@src/ONYXKEYS'; diff --git a/src/libs/Navigation/types.ts b/src/libs/Navigation/types.ts index 9be03966e214..939a386751a5 100644 --- a/src/libs/Navigation/types.ts +++ b/src/libs/Navigation/types.ts @@ -392,8 +392,8 @@ type AddPersonalBankAccountNavigatorParamList = { type ReimbursementAccountNavigatorParamList = { [SCREENS.REIMBURSEMENT_ACCOUNT_ROOT]: { stepToOpen?: string; - policyID?: string; backTo?: Routes; + policyID?: string; }; }; diff --git a/src/pages/ReimbursementAccount/AddressForm.tsx b/src/pages/ReimbursementAccount/AddressForm.tsx index 4f618982c737..77019a5536e2 100644 --- a/src/pages/ReimbursementAccount/AddressForm.tsx +++ b/src/pages/ReimbursementAccount/AddressForm.tsx @@ -3,10 +3,10 @@ import {View} from 'react-native'; import AddressSearch from '@components/AddressSearch'; import InputWrapper from '@components/Form/InputWrapper'; import StatePicker from '@components/StatePicker'; +import type {State} from '@components/StatePicker/StateSelectorModal'; import TextInput from '@components/TextInput'; import useLocalize from '@hooks/useLocalize'; import useThemeStyles from '@hooks/useThemeStyles'; -import type {State} from '@libs/Navigation/types'; import CONST from '@src/CONST'; import type {TranslationPaths} from '@src/languages/types'; diff --git a/src/pages/ReimbursementAccount/IdentityForm.tsx b/src/pages/ReimbursementAccount/IdentityForm.tsx deleted file mode 100644 index 16560f363400..000000000000 --- a/src/pages/ReimbursementAccount/IdentityForm.tsx +++ /dev/null @@ -1,112 +0,0 @@ -import {subYears} from 'date-fns'; -import React from 'react'; -import type {StyleProp, ViewStyle} from 'react-native'; -import {View} from 'react-native'; -import DatePicker from '@components/DatePicker'; -import InputWrapper from '@components/Form/InputWrapper'; -import TextInput from '@components/TextInput'; -import useLocalize from '@hooks/useLocalize'; -import useThemeStyles from '@hooks/useThemeStyles'; -import CONST from '@src/CONST'; -import AddressForm from './AddressForm'; - -type IdentityFormProps = { - /** Style for wrapping View */ - style?: StyleProp; - - /** Form values */ - values: any; - - /** Default values */ - defaultValues: any; - - /** Any errors that can arise from form validation */ - errors: Record; - - /** The map for inputID of the inputs */ - inputKeys: any; - - /** Saves a draft of the input value when used in a form */ - shouldSaveDraft?: boolean; -}; - -function IdentityForm({shouldSaveDraft = false, errors, values, inputKeys, defaultValues, style}: IdentityFormProps) { - const styles = useThemeStyles(); - const {translate} = useLocalize(); - // dob field has multiple validations/errors, we are handling it temporarily like this. - const dobErrorText = (errors?.dob ? 'bankAccount.error.dob' : '') || (errors?.dobAge ? 'bankAccount.error.age' : ''); - const identityFormInputKeys = ['firstName', 'lastName', 'dob', 'ssnLast4']; - - const minDate = subYears(new Date(), CONST.DATE_BIRTH.MAX_AGE); - const maxDate = subYears(new Date(), CONST.DATE_BIRTH.MIN_AGE_FOR_PAYMENT); - - return ( - - - - - - - - - - - - - - ); -} - -IdentityForm.displayName = 'IdentityForm'; -export default IdentityForm; diff --git a/src/pages/ReimbursementAccount/ReimbursementAccountDraftPropTypes.js b/src/pages/ReimbursementAccount/ReimbursementAccountDraftPropTypes.js deleted file mode 100644 index 97bd1c508775..000000000000 --- a/src/pages/ReimbursementAccount/ReimbursementAccountDraftPropTypes.js +++ /dev/null @@ -1,45 +0,0 @@ -import PropTypes from 'prop-types'; - -export default PropTypes.shape({ - bankAccountID: PropTypes.number, - - /** Props needed for BankAccountStep */ - accountNumber: PropTypes.string, - routingNumber: PropTypes.string, - acceptTerms: PropTypes.bool, - plaidAccountID: PropTypes.string, - mask: PropTypes.string, - - /** Props needed for CompanyStep */ - companyName: PropTypes.string, - addressStreet: PropTypes.string, - addressCity: PropTypes.string, - addressState: PropTypes.string, - addressZipCode: PropTypes.string, - companyPhone: PropTypes.string, - website: PropTypes.string, - companyTaxID: PropTypes.string, - incorporationType: PropTypes.string, - incorporationDate: PropTypes.oneOfType([PropTypes.string, PropTypes.instanceOf(Date)]), - incorporationState: PropTypes.string, - hasNoConnectionToCannabis: PropTypes.bool, - - /** Props needed for RequestorStep */ - firstName: PropTypes.string, - lastName: PropTypes.string, - requestorAddressStreet: PropTypes.string, - requestorAddressCity: PropTypes.string, - requestorAddressState: PropTypes.string, - requestorAddressZipCode: PropTypes.string, - dob: PropTypes.oneOfType([PropTypes.string, PropTypes.instanceOf(Date)]), - ssnLast4: PropTypes.string, - isOnfidoSetupComplete: PropTypes.bool, - - /** Props needed for ACHContractStep */ - ownsMoreThan25Percent: PropTypes.bool, - hasOtherBeneficialOwners: PropTypes.bool, - acceptTermsAndConditions: PropTypes.bool, - certifyTrueInformation: PropTypes.bool, - beneficialOwners: PropTypes.string, - beneficialOwnerKeys: PropTypes.arrayOf(PropTypes.string), -}); diff --git a/src/pages/ReimbursementAccount/ReimbursementAccountPage.tsx b/src/pages/ReimbursementAccount/ReimbursementAccountPage.tsx index 289af249494b..a2de4d75f0a0 100644 --- a/src/pages/ReimbursementAccount/ReimbursementAccountPage.tsx +++ b/src/pages/ReimbursementAccount/ReimbursementAccountPage.tsx @@ -21,7 +21,7 @@ import Navigation from '@libs/Navigation/Navigation'; import type {ReimbursementAccountNavigatorParamList} from '@libs/Navigation/types'; import * as PolicyUtils from '@libs/PolicyUtils'; import shouldReopenOnfido from '@libs/shouldReopenOnfido'; -import type {WithPolicyProps} from '@pages/workspace/withPolicy'; +import type {WithPolicyOnyxProps} from '@pages/workspace/withPolicy'; import withPolicy from '@pages/workspace/withPolicy'; import * as BankAccounts from '@userActions/BankAccounts'; import CONST from '@src/CONST'; @@ -29,7 +29,6 @@ import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; import type SCREENS from '@src/SCREENS'; import type * as OnyxTypes from '@src/types/onyx'; -import type {ACHData} from '@src/types/onyx/ReimbursementAccount'; import {isEmptyObject} from '@src/types/utils/EmptyObject'; import ACHContractStep from './ACHContractStep'; import BankAccountStep from './BankAccountStep'; @@ -87,10 +86,10 @@ type ReimbursementAccountDraft = { type ReimbursementAccountOnyxProps = { /** Plaid SDK token to use to initialize the widget */ - plaidLinkToken?: OnyxEntry; + plaidLinkToken: OnyxEntry; /** Plaid SDK current event */ - plaidCurrentEvent?: OnyxEntry; + plaidCurrentEvent: OnyxEntry; /** Indicated whether the app is loading */ isLoadingApp: OnyxEntry; @@ -104,15 +103,12 @@ type ReimbursementAccountOnyxProps = { /** ACH data for the withdrawal account actively being set up */ reimbursementAccount: OnyxEntry; - /** The draft values of the bank account being setup */ - reimbursementAccountDraft: OnyxEntry; - /** The token required to initialize the Onfido SDK */ onfidoToken: OnyxEntry; }; -type ReimbursementAccountPageProps = WithPolicyProps & ReimbursementAccountOnyxProps & RouteProp; - +type ReimbursementAccountPageProps = WithPolicyOnyxProps & + ReimbursementAccountOnyxProps & {route: RouteProp}; const ROUTE_NAMES = { COMPANY: 'company', PERSONAL_INFORMATION: 'personal-information', @@ -188,7 +184,7 @@ function ReimbursementAccountPage({ */ const achData = reimbursementAccount?.achData; - function getBankAccountFields(fieldNames: T[]): Pick { + function getBankAccountFields(fieldNames: string[]): string[] { // @ts-expect-error -- Pick is more acurate type in this case because lodashPick returns Partial return { ...lodashPick(reimbursementAccount?.achData, ...fieldNames), @@ -539,12 +535,10 @@ ReimbursementAccountPage.displayName = 'ReimbursementAccountPage'; export default withPolicy( withOnyx({ + // @ts-expect-error: ONYXKEYS.REIMBURSEMENT_ACCOUNT is conflicting with ONYXKEYS.FORMS.REIMBURSEMENT_ACCOUNT_FORM reimbursementAccount: { key: ONYXKEYS.REIMBURSEMENT_ACCOUNT, }, - reimbursementAccountDraft: { - key: ONYXKEYS.FORMS.REIMBURSEMENT_ACCOUNT_FORM_DRAFT, - }, session: { key: ONYXKEYS.SESSION, }, diff --git a/src/pages/ReimbursementAccount/StepPropTypes.js b/src/pages/ReimbursementAccount/StepPropTypes.js deleted file mode 100644 index 04399230a1ab..000000000000 --- a/src/pages/ReimbursementAccount/StepPropTypes.js +++ /dev/null @@ -1,20 +0,0 @@ -import PropTypes from 'prop-types'; -import {withLocalizePropTypes} from '@components/withLocalize'; -import reimbursementAccountDraftPropTypes from './ReimbursementAccountDraftPropTypes'; -import * as ReimbursementAccountProps from './reimbursementAccountPropTypes'; - -export default { - /** The bank account currently in setup */ - reimbursementAccount: ReimbursementAccountProps.reimbursementAccountPropTypes.isRequired, - - /** The draft values of the bank account being setup */ - reimbursementAccountDraft: reimbursementAccountDraftPropTypes.isRequired, - - /** Goes to the previous step */ - onBackButtonPress: PropTypes.func.isRequired, - - /** Get a field value from Onyx reimbursementAccountDraft or reimbursementAccount */ - getDefaultStateForField: PropTypes.func.isRequired, - - ...withLocalizePropTypes, -}; From 093fe8f420301b119a3054099ed1e8135cd3af45 Mon Sep 17 00:00:00 2001 From: Jakub Butkiewicz Date: Tue, 27 Feb 2024 09:38:59 +0100 Subject: [PATCH 020/213] fix: typecheck --- src/components/AddressSearch/types.ts | 15 +++++++-------- src/components/Form/types.ts | 4 +--- src/pages/ReimbursementAccount/AddressForm.tsx | 12 +++++++----- .../AddressUBO.tsx | 1 - .../BusinessInfo/substeps/AddressBusiness.tsx | 1 - .../PersonalInfo/substeps/Address.tsx | 1 - src/pages/workspace/withPolicy.tsx | 6 +++--- 7 files changed, 18 insertions(+), 22 deletions(-) diff --git a/src/components/AddressSearch/types.ts b/src/components/AddressSearch/types.ts index 0d3f8c1a972e..53a80a357702 100644 --- a/src/components/AddressSearch/types.ts +++ b/src/components/AddressSearch/types.ts @@ -1,7 +1,6 @@ import type {RefObject} from 'react'; import type {NativeSyntheticEvent, StyleProp, TextInputFocusEventData, View, ViewStyle} from 'react-native'; import type {Place} from 'react-native-google-places-autocomplete'; -import type {State} from '@components/StatePicker/StateSelectorModal'; import type {MaybePhraseKey} from '@libs/Localize'; import type Locale from '@src/types/onyx/Locale'; @@ -14,13 +13,13 @@ type CurrentLocationButtonProps = { }; type RenamedInputKeysProps = { - street: string; - street2: string; - city: string; - state: State; - lat: string; - lng: string; - zipCode: string; + street?: string; + street2?: string; + city?: string; + state?: string; + lat?: string; + lng?: string; + zipCode?: string; address?: string; country?: string; }; diff --git a/src/components/Form/types.ts b/src/components/Form/types.ts index 40b4fcece482..37d0f730c9e9 100644 --- a/src/components/Form/types.ts +++ b/src/components/Form/types.ts @@ -10,7 +10,6 @@ import type Picker from '@components/Picker'; import type RadioButtons from '@components/RadioButtons'; import type SingleChoiceQuestion from '@components/SingleChoiceQuestion'; import type StatePicker from '@components/StatePicker'; -import type {State} from '@components/StatePicker/StateSelectorModal'; import type TextInput from '@components/TextInput'; import type ValuePicker from '@components/ValuePicker'; import type BusinessTypePicker from '@pages/ReimbursementAccount/BusinessInfo/substeps/TypeBusiness/BusinessTypePicker'; @@ -39,12 +38,11 @@ type ValidInputs = | typeof ValuePicker | typeof RadioButtons; -type ValueTypeKey = 'string' | 'boolean' | 'date' | 'state'; +type ValueTypeKey = 'string' | 'boolean' | 'date'; type ValueTypeMap = { string: string; boolean: boolean; date: Date; - state: State; }; type FormValue = ValueOf; diff --git a/src/pages/ReimbursementAccount/AddressForm.tsx b/src/pages/ReimbursementAccount/AddressForm.tsx index 77019a5536e2..527824322fc8 100644 --- a/src/pages/ReimbursementAccount/AddressForm.tsx +++ b/src/pages/ReimbursementAccount/AddressForm.tsx @@ -24,13 +24,13 @@ type Address = { zipCode: string; /** Address street2 field */ - street2: string; + street2?: string; /** Address latitude field */ - lat: string; + lat?: string; /** Address longitude field */ - lng: string; + lng?: string; }; type AddressError = Record; @@ -43,7 +43,7 @@ type AddressFormProps = { onFieldChange?: (value: T) => void; /** Default values */ - defaultValues?: Address; + defaultValues?: Partial>; /** Form values */ values?: Address; @@ -52,7 +52,7 @@ type AddressFormProps = { errors?: AddressError; /** The map for inputID of the inputs */ - inputKeys?: Address; + inputKeys?: Partial>; /** Saves a draft of the input value when used in a form */ shouldSaveDraft?: boolean; @@ -126,3 +126,5 @@ function AddressForm({shouldSaveDraft = false, defaultValues, values, errors, in AddressForm.displayName = 'AddressForm'; export default AddressForm; + +export type {Address}; diff --git a/src/pages/ReimbursementAccount/BeneficialOwnerInfo/substeps/BeneficialOwnerDetailsFormSubsteps/AddressUBO.tsx b/src/pages/ReimbursementAccount/BeneficialOwnerInfo/substeps/BeneficialOwnerDetailsFormSubsteps/AddressUBO.tsx index 999710310224..17344a72e7c2 100644 --- a/src/pages/ReimbursementAccount/BeneficialOwnerInfo/substeps/BeneficialOwnerDetailsFormSubsteps/AddressUBO.tsx +++ b/src/pages/ReimbursementAccount/BeneficialOwnerInfo/substeps/BeneficialOwnerDetailsFormSubsteps/AddressUBO.tsx @@ -77,7 +77,6 @@ function AddressUBO({reimbursementAccountDraft, onNext, isEditing, beneficialOwn diff --git a/src/pages/ReimbursementAccount/BusinessInfo/substeps/AddressBusiness.tsx b/src/pages/ReimbursementAccount/BusinessInfo/substeps/AddressBusiness.tsx index 354620f5e46a..f932a4e924aa 100644 --- a/src/pages/ReimbursementAccount/BusinessInfo/substeps/AddressBusiness.tsx +++ b/src/pages/ReimbursementAccount/BusinessInfo/substeps/AddressBusiness.tsx @@ -81,7 +81,6 @@ function AddressBusiness({reimbursementAccount, onNext, isEditing}: AddressBusin diff --git a/src/pages/ReimbursementAccount/PersonalInfo/substeps/Address.tsx b/src/pages/ReimbursementAccount/PersonalInfo/substeps/Address.tsx index 824ed950bd9f..ae842fbf4624 100644 --- a/src/pages/ReimbursementAccount/PersonalInfo/substeps/Address.tsx +++ b/src/pages/ReimbursementAccount/PersonalInfo/substeps/Address.tsx @@ -79,7 +79,6 @@ function Address({reimbursementAccount, onNext, isEditing}: AddressProps) { {translate('common.noPO')} >; +type WorkspaceParamList = BottomTabNavigatorParamList & CentralPaneNavigatorParamList & SettingsNavigatorParamList & ReimbursementAccountNavigatorParamList; +type PolicyRoute = RouteProp | typeof SCREENS.REIMBURSEMENT_ACCOUNT_ROOT>; function getPolicyIDFromRoute(route: PolicyRoute): string { return route?.params?.policyID ?? ''; From ee1949b83053a9ab41c6333af9c49a1651e8e0ec Mon Sep 17 00:00:00 2001 From: Jakub Butkiewicz Date: Tue, 27 Feb 2024 20:48:32 +0100 Subject: [PATCH 021/213] fix: small adjustment --- src/components/AddressSearch/index.tsx | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/components/AddressSearch/index.tsx b/src/components/AddressSearch/index.tsx index 0b9498e34aa2..8ad26e5a7c46 100644 --- a/src/components/AddressSearch/index.tsx +++ b/src/components/AddressSearch/index.tsx @@ -6,7 +6,6 @@ import {GooglePlacesAutocomplete} from 'react-native-google-places-autocomplete' import type {GooglePlaceData, GooglePlaceDetail} from 'react-native-google-places-autocomplete'; import FullScreenLoadingIndicator from '@components/FullscreenLoadingIndicator'; import LocationErrorMessage from '@components/LocationErrorMessage'; -import type {State} from '@components/StatePicker/StateSelectorModal'; import Text from '@components/Text'; import TextInput from '@components/TextInput'; import useLocalize from '@hooks/useLocalize'; @@ -49,7 +48,7 @@ function AddressSearch( street: 'addressStreet', street2: 'addressStreet2', city: 'addressCity', - state: 'addressState' as State, + state: 'addressState', zipCode: 'addressZipCode', lat: 'addressLat', lng: 'addressLng', From a3dbead38f6cf4102592c327858a8dc931ae719f Mon Sep 17 00:00:00 2001 From: Jakub Butkiewicz Date: Wed, 28 Feb 2024 09:33:10 +0100 Subject: [PATCH 022/213] fix: lint --- .../ReimbursementAccountPage.tsx | 108 ++++++++---------- 1 file changed, 50 insertions(+), 58 deletions(-) diff --git a/src/pages/ReimbursementAccount/ReimbursementAccountPage.tsx b/src/pages/ReimbursementAccount/ReimbursementAccountPage.tsx index a2de4d75f0a0..0dae9b99ed11 100644 --- a/src/pages/ReimbursementAccount/ReimbursementAccountPage.tsx +++ b/src/pages/ReimbursementAccount/ReimbursementAccountPage.tsx @@ -278,75 +278,67 @@ function ReimbursementAccountPage({ BankAccounts.openReimbursementAccountPage(stepToOpen, subStep, ignoreLocalCurrentStep ? '' : localCurrentStep, policyID); } - useEffect( - () => { - fetchData(); - return () => { - BankAccounts.clearReimbursementAccount(); - }; - }, - // eslint-disable-next-line react-hooks/exhaustive-deps - [], - ); // The empty dependency array ensures this runs only once after the component mounts. - - useEffect( - () => { - // Check for network change from offline to online - if (prevIsOffline && !isOffline && prevReimbursementAccount && prevReimbursementAccount.pendingAction !== CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE) { - fetchData(); - } + useEffect(() => { + fetchData(); + return () => { + BankAccounts.clearReimbursementAccount(); + }; + }, []); // The empty dependency array ensures this runs only once after the component mounts. - if (!hasACHDataBeenLoaded) { - if (reimbursementAccount !== ReimbursementAccountProps.reimbursementAccountDefaultProps && reimbursementAccount?.isLoading === false) { - setShouldShowContinueSetupButton(getShouldShowContinueSetupButtonInitialValue()); - setHasACHDataBeenLoaded(true); - } - return; - } + useEffect(() => { + // Check for network change from offline to online + if (prevIsOffline && !isOffline && prevReimbursementAccount && prevReimbursementAccount.pendingAction !== CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE) { + fetchData(); + } - if ( - prevReimbursementAccount && - prevReimbursementAccount.pendingAction === CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE && - reimbursementAccount?.pendingAction !== prevReimbursementAccount.pendingAction - ) { - setShouldShowContinueSetupButton(hasInProgressVBBA()); + if (!hasACHDataBeenLoaded) { + if (reimbursementAccount !== ReimbursementAccountProps.reimbursementAccountDefaultProps && reimbursementAccount?.isLoading === false) { + setShouldShowContinueSetupButton(getShouldShowContinueSetupButtonInitialValue()); + setHasACHDataBeenLoaded(true); } + return; + } - if (shouldShowContinueSetupButton) { - return; - } + if ( + prevReimbursementAccount && + prevReimbursementAccount.pendingAction === CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE && + reimbursementAccount?.pendingAction !== prevReimbursementAccount.pendingAction + ) { + setShouldShowContinueSetupButton(hasInProgressVBBA()); + } - const currentStepRouteParam = getStepToOpenFromRouteParams(route); - if (currentStepRouteParam === currentStep) { - // If the user is connecting online with plaid, reset any bank account errors so we don't persist old data from a potential previous connection - if (currentStep === CONST.BANK_ACCOUNT.STEP.BANK_ACCOUNT && achData?.subStep === CONST.BANK_ACCOUNT.SETUP_TYPE.PLAID) { - BankAccounts.hideBankAccountErrors(); - } + if (shouldShowContinueSetupButton) { + return; + } - // The route is showing the correct step, no need to update the route param or clear errors. - return; + const currentStepRouteParam = getStepToOpenFromRouteParams(route); + if (currentStepRouteParam === currentStep) { + // If the user is connecting online with plaid, reset any bank account errors so we don't persist old data from a potential previous connection + if (currentStep === CONST.BANK_ACCOUNT.STEP.BANK_ACCOUNT && achData?.subStep === CONST.BANK_ACCOUNT.SETUP_TYPE.PLAID) { + BankAccounts.hideBankAccountErrors(); } - // Update the data that is returned from back-end to draft value - const draftStep = reimbursementAccount?.draftStep; - if (draftStep) { - BankAccounts.updateReimbursementAccountDraft(getBankAccountFields(getFieldsForStep(draftStep))); - } + // The route is showing the correct step, no need to update the route param or clear errors. + return; + } - if (currentStepRouteParam !== '') { - // When we click "Connect bank account", we load the page without the current step param, if there - // was an error when we tried to disconnect or start over, we want the user to be able to see the error, - // so we don't clear it. We only want to clear the errors if we are moving between steps. - BankAccounts.hideBankAccountErrors(); - } + // Update the data that is returned from back-end to draft value + const draftStep = reimbursementAccount?.draftStep; + if (draftStep) { + BankAccounts.updateReimbursementAccountDraft(getBankAccountFields(getFieldsForStep(draftStep))); + } - const backTo = route.params?.backTo; + if (currentStepRouteParam !== '') { + // When we click "Connect bank account", we load the page without the current step param, if there + // was an error when we tried to disconnect or start over, we want the user to be able to see the error, + // so we don't clear it. We only want to clear the errors if we are moving between steps. + BankAccounts.hideBankAccountErrors(); + } - Navigation.navigate(ROUTES.BANK_ACCOUNT_WITH_STEP_TO_OPEN.getRoute(getRouteForCurrentStep(currentStep), policyID, backTo)); - }, - // eslint-disable-next-line react-hooks/exhaustive-deps - [isOffline, reimbursementAccount, route, hasACHDataBeenLoaded, shouldShowContinueSetupButton], - ); + const backTo = route.params?.backTo; + + Navigation.navigate(ROUTES.BANK_ACCOUNT_WITH_STEP_TO_OPEN.getRoute(getRouteForCurrentStep(currentStep), policyID, backTo)); + }, [isOffline, reimbursementAccount, route, hasACHDataBeenLoaded, shouldShowContinueSetupButton]); const continueFunction = () => { BankAccounts.setBankAccountSubStep(CONST.BANK_ACCOUNT.SETUP_TYPE.MANUAL).then(() => { From 55630d67e86b9df471dd1ad03373703e96a55e80 Mon Sep 17 00:00:00 2001 From: Jakub Butkiewicz Date: Wed, 28 Feb 2024 14:23:13 +0100 Subject: [PATCH 023/213] fix: lint --- src/pages/ReimbursementAccount/ReimbursementAccountPage.tsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/pages/ReimbursementAccount/ReimbursementAccountPage.tsx b/src/pages/ReimbursementAccount/ReimbursementAccountPage.tsx index 0dae9b99ed11..db3d2bb05481 100644 --- a/src/pages/ReimbursementAccount/ReimbursementAccountPage.tsx +++ b/src/pages/ReimbursementAccount/ReimbursementAccountPage.tsx @@ -247,7 +247,7 @@ function ReimbursementAccountPage({ which acts similarly to `componentDidUpdate` when the `reimbursementAccount` dependency changes. */ const [hasACHDataBeenLoaded, setHasACHDataBeenLoaded] = useState( - reimbursementAccount !== ReimbursementAccountProps.reimbursementAccountDefaultProps && reimbursementAccount?.achData && 'currentStep' in reimbursementAccount?.achData, + reimbursementAccount !== ReimbursementAccountProps.reimbursementAccountDefaultProps && reimbursementAccount?.achData && 'currentStep' in reimbursementAccount.achData, ); const [shouldShowContinueSetupButton, setShouldShowContinueSetupButton] = useState(hasACHDataBeenLoaded ? getShouldShowContinueSetupButtonInitialValue() : false); @@ -283,6 +283,7 @@ function ReimbursementAccountPage({ return () => { BankAccounts.clearReimbursementAccount(); }; + // eslint-disable-next-line react-hooks/exhaustive-deps }, []); // The empty dependency array ensures this runs only once after the component mounts. useEffect(() => { @@ -338,6 +339,7 @@ function ReimbursementAccountPage({ const backTo = route.params?.backTo; Navigation.navigate(ROUTES.BANK_ACCOUNT_WITH_STEP_TO_OPEN.getRoute(getRouteForCurrentStep(currentStep), policyID, backTo)); + // eslint-disable-next-line react-hooks/exhaustive-deps }, [isOffline, reimbursementAccount, route, hasACHDataBeenLoaded, shouldShowContinueSetupButton]); const continueFunction = () => { From dd90ad3838c36a0d8fb73f37266007770bcd727b Mon Sep 17 00:00:00 2001 From: Georgia Monahan Date: Wed, 28 Feb 2024 14:41:04 +0000 Subject: [PATCH 024/213] use hook --- src/hooks/usePrivatePersonalDetails.ts | 20 ++++++++++++ src/libs/actions/PersonalDetails.ts | 38 +++++++++++++++++++++++ src/pages/settings/Profile/ProfilePage.js | 2 ++ 3 files changed, 60 insertions(+) create mode 100644 src/hooks/usePrivatePersonalDetails.ts diff --git a/src/hooks/usePrivatePersonalDetails.ts b/src/hooks/usePrivatePersonalDetails.ts new file mode 100644 index 000000000000..f17600e9878f --- /dev/null +++ b/src/hooks/usePrivatePersonalDetails.ts @@ -0,0 +1,20 @@ +import {useContext, useEffect} from 'react'; +import {NetworkContext} from '@components/OnyxProvider'; +import * as PersonalDetails from '@userActions/PersonalDetails'; + +/** + * Hook for fetching private personal details + */ +export default function usePrivatePersonalDetails() { + const network = useContext(NetworkContext); + + useEffect(() => { + const personalDetails = PersonalDetails.getPrivatePersonalDetails(); + // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing + if (network?.isOffline || (Boolean(personalDetails) && personalDetails?.isLoading !== undefined)) { + return; + } + + PersonalDetails.openPersonalDetails(); + }, [network?.isOffline]); +} diff --git a/src/libs/actions/PersonalDetails.ts b/src/libs/actions/PersonalDetails.ts index f976003dea66..5ae37bb85f10 100644 --- a/src/libs/actions/PersonalDetails.ts +++ b/src/libs/actions/PersonalDetails.ts @@ -239,6 +239,43 @@ function updateSelectedTimezone(selectedTimezone: SelectedTimezone) { Navigation.goBack(ROUTES.SETTINGS_TIMEZONE); } +/** + * Fetches additional personal data like legal name, date of birth, address + */ +function openPersonalDetails() { + const optimisticData: OnyxUpdate[] = [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: ONYXKEYS.PRIVATE_PERSONAL_DETAILS, + value: { + isLoading: true, + }, + }, + ]; + + const successData: OnyxUpdate[] = [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: ONYXKEYS.PRIVATE_PERSONAL_DETAILS, + value: { + isLoading: false, + }, + }, + ]; + + const failureData: OnyxUpdate[] = [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: ONYXKEYS.PRIVATE_PERSONAL_DETAILS, + value: { + isLoading: false, + }, + }, + ]; + + API.read(READ_COMMANDS.OPEN_PERSONAL_DETAILS, {}, {optimisticData, successData, failureData}); +} + /** * Fetches public profile info about a given user. * The API will only return the accountID, displayName, and avatar for the user @@ -419,6 +456,7 @@ export { clearAvatarErrors, deleteAvatar, getPrivatePersonalDetails, + openPersonalDetails, openPublicProfilePage, updateAddress, updateAutomaticTimezone, diff --git a/src/pages/settings/Profile/ProfilePage.js b/src/pages/settings/Profile/ProfilePage.js index fe317a1fddc9..e0b4edaa205d 100755 --- a/src/pages/settings/Profile/ProfilePage.js +++ b/src/pages/settings/Profile/ProfilePage.js @@ -17,6 +17,7 @@ import useStyleUtils from '@hooks/useStyleUtils'; import useTheme from '@hooks/useTheme'; import useThemeStyles from '@hooks/useThemeStyles'; import useWindowDimensions from '@hooks/useWindowDimensions'; +import usePrivatePersonalDetails from '@hooks/usePrivatePersonalDetails'; import compose from '@libs/compose'; import {translatableTextPropTypes} from '@libs/Localize'; import Navigation from '@libs/Navigation/Navigation'; @@ -87,6 +88,7 @@ function ProfilePage(props) { const theme = useTheme(); const styles = useThemeStyles(); const StyleUtils = useStyleUtils(); + usePrivatePersonalDetails(); const getPronouns = () => { let pronounsKey = lodashGet(props.currentUserPersonalDetails, 'pronouns', ''); if (pronounsKey.startsWith(CONST.PRONOUNS.PREFIX)) { From bd533880c46d97445c0ed4c792c1757b4cd6da89 Mon Sep 17 00:00:00 2001 From: Jakub Butkiewicz Date: Thu, 29 Feb 2024 12:30:37 +0100 Subject: [PATCH 025/213] fix: resolve comments --- .../ReimbursementAccount/AddressForm.tsx | 32 +++---------------- .../BankInfo/BankInfo.tsx | 2 ++ .../ContinueBankAccountSetup.tsx | 2 +- src/pages/workspace/withPolicy.tsx | 4 +-- src/types/onyx/PrivatePersonalDetails.ts | 7 ++-- src/types/onyx/ReimbursementAccount.ts | 1 + 6 files changed, 16 insertions(+), 32 deletions(-) diff --git a/src/pages/ReimbursementAccount/AddressForm.tsx b/src/pages/ReimbursementAccount/AddressForm.tsx index 527824322fc8..ea2f7304aa1e 100644 --- a/src/pages/ReimbursementAccount/AddressForm.tsx +++ b/src/pages/ReimbursementAccount/AddressForm.tsx @@ -9,29 +9,7 @@ import useLocalize from '@hooks/useLocalize'; import useThemeStyles from '@hooks/useThemeStyles'; import CONST from '@src/CONST'; import type {TranslationPaths} from '@src/languages/types'; - -type Address = { - /** Address street field */ - street: string; - - /** Address city field */ - city: string; - - /** Address state field */ - state: State; - - /** Address zip code field */ - zipCode: string; - - /** Address street2 field */ - street2?: string; - - /** Address latitude field */ - lat?: string; - - /** Address longitude field */ - lng?: string; -}; +import type {Address} from '@src/types/onyx/PrivatePersonalDetails'; type AddressError = Record; @@ -43,7 +21,7 @@ type AddressFormProps = { onFieldChange?: (value: T) => void; /** Default values */ - defaultValues?: Partial>; + defaultValues?: Address; /** Form values */ values?: Address; @@ -52,7 +30,7 @@ type AddressFormProps = { errors?: AddressError; /** The map for inputID of the inputs */ - inputKeys?: Partial>; + inputKeys?: Address; /** Saves a draft of the input value when used in a form */ shouldSaveDraft?: boolean; @@ -69,7 +47,7 @@ function AddressForm({shouldSaveDraft = false, defaultValues, values, errors, in inputID={inputKeys?.street ?? 'streetInput'} shouldSaveDraft={shouldSaveDraft} label={translate(streetTranslationKey)} - containerStyles={[styles.mt6]} + containerStyles={styles.mt6} value={values?.street} defaultValue={defaultValues?.street} onInputChange={onFieldChange} @@ -98,7 +76,7 @@ function AddressForm({shouldSaveDraft = false, defaultValues, values, errors, in InputComponent={StatePicker} inputID={inputKeys?.state ?? 'stateInput'} shouldSaveDraft={shouldSaveDraft} - value={values?.state} + value={values?.state as State} defaultValue={defaultValues?.state} onInputChange={(value) => onFieldChange({state: value})} errorText={errors?.state ? 'bankAccount.error.addressState' : ''} diff --git a/src/pages/ReimbursementAccount/BankInfo/BankInfo.tsx b/src/pages/ReimbursementAccount/BankInfo/BankInfo.tsx index a4c8fc727d23..04c9dab4a315 100644 --- a/src/pages/ReimbursementAccount/BankInfo/BankInfo.tsx +++ b/src/pages/ReimbursementAccount/BankInfo/BankInfo.tsx @@ -36,6 +36,8 @@ type BankInfoOnyxProps = { type BankInfoProps = BankInfoOnyxProps & { /** Goes to the previous step */ onBackButtonPress: () => void; + + /** Current Policy ID */ policyID: string; }; diff --git a/src/pages/ReimbursementAccount/ContinueBankAccountSetup.tsx b/src/pages/ReimbursementAccount/ContinueBankAccountSetup.tsx index 8b410a52789c..d26eeec2f049 100644 --- a/src/pages/ReimbursementAccount/ContinueBankAccountSetup.tsx +++ b/src/pages/ReimbursementAccount/ContinueBankAccountSetup.tsx @@ -69,7 +69,7 @@ function ContinueBankAccountSetup({policyName = '', onBackButtonPress, reimburse shouldShowRightIcon large success - isDisabled={Boolean(pendingAction) || !isEmptyObject(errors)} + isDisabled={!!pendingAction || !isEmptyObject(errors)} /> | typeof SCREENS.REIMBURSEMENT_ACCOUNT_ROOT>; +type NavigatorsParamList = BottomTabNavigatorParamList & CentralPaneNavigatorParamList & SettingsNavigatorParamList & ReimbursementAccountNavigatorParamList; +type PolicyRoute = RouteProp | typeof SCREENS.REIMBURSEMENT_ACCOUNT_ROOT>; function getPolicyIDFromRoute(route: PolicyRoute): string { return route?.params?.policyID ?? ''; diff --git a/src/types/onyx/PrivatePersonalDetails.ts b/src/types/onyx/PrivatePersonalDetails.ts index 780e3f71b61d..64805d30495d 100644 --- a/src/types/onyx/PrivatePersonalDetails.ts +++ b/src/types/onyx/PrivatePersonalDetails.ts @@ -3,8 +3,9 @@ type Address = { street2?: string; city: string; state: string; - zip: string; - country: string; + zip?: string; + country?: string; + zipCode?: string; }; type PrivatePersonalDetails = { @@ -21,3 +22,5 @@ type PrivatePersonalDetails = { }; export default PrivatePersonalDetails; + +export type {Address}; diff --git a/src/types/onyx/ReimbursementAccount.ts b/src/types/onyx/ReimbursementAccount.ts index 2ec1aa57c437..224937d97bfa 100644 --- a/src/types/onyx/ReimbursementAccount.ts +++ b/src/types/onyx/ReimbursementAccount.ts @@ -36,6 +36,7 @@ type ACHData = Partial Date: Thu, 29 Feb 2024 13:13:02 +0100 Subject: [PATCH 026/213] fix: typecheck --- src/components/AddressSearch/index.tsx | 5 +++-- src/components/AddressSearch/types.ts | 19 ++++--------------- src/types/onyx/PrivatePersonalDetails.ts | 3 +++ 3 files changed, 10 insertions(+), 17 deletions(-) diff --git a/src/components/AddressSearch/index.tsx b/src/components/AddressSearch/index.tsx index 4f21f4aa1dc3..e2fe918de189 100644 --- a/src/components/AddressSearch/index.tsx +++ b/src/components/AddressSearch/index.tsx @@ -19,9 +19,10 @@ import type {GeolocationErrorCodeType} from '@libs/getCurrentPosition/getCurrent import * as GooglePlacesUtils from '@libs/GooglePlacesUtils'; import variables from '@styles/variables'; import CONST from '@src/CONST'; +import type {Address} from '@src/types/onyx/PrivatePersonalDetails'; import CurrentLocationButton from './CurrentLocationButton'; import isCurrentTargetInsideContainer from './isCurrentTargetInsideContainer'; -import type {AddressSearchProps, RenamedInputKeysProps} from './types'; +import type {AddressSearchProps} from './types'; // The error that's being thrown below will be ignored until we fork the // react-native-google-places-autocomplete repo and replace the @@ -212,7 +213,7 @@ function AddressSearch( if (inputID) { Object.entries(values).forEach(([key, inputValue]) => { - const inputKey = renamedInputKeys?.[key as keyof RenamedInputKeysProps] ?? key; + const inputKey = renamedInputKeys?.[key as keyof Address] ?? key; if (!inputKey) { return; } diff --git a/src/components/AddressSearch/types.ts b/src/components/AddressSearch/types.ts index 53a80a357702..ec7a2c61398b 100644 --- a/src/components/AddressSearch/types.ts +++ b/src/components/AddressSearch/types.ts @@ -3,6 +3,7 @@ import type {NativeSyntheticEvent, StyleProp, TextInputFocusEventData, View, Vie import type {Place} from 'react-native-google-places-autocomplete'; import type {MaybePhraseKey} from '@libs/Localize'; import type Locale from '@src/types/onyx/Locale'; +import {Address} from '@src/types/onyx/PrivatePersonalDetails'; type CurrentLocationButtonProps = { /** Callback that is called when the button is clicked */ @@ -12,18 +13,6 @@ type CurrentLocationButtonProps = { isDisabled?: boolean; }; -type RenamedInputKeysProps = { - street?: string; - street2?: string; - city?: string; - state?: string; - lat?: string; - lng?: string; - zipCode?: string; - address?: string; - country?: string; -}; - type OnPressProps = { address: string; lat: number; @@ -61,7 +50,7 @@ type AddressSearchProps = { defaultValue?: string; /** A callback function when the value of this field has changed */ - onInputChange?: (value: string | number | RenamedInputKeysProps | StreetValue, key?: string) => void; + onInputChange?: (value: string | number | Address | StreetValue, key?: string) => void; /** A callback function when an address has been auto-selected */ onPress?: (props: OnPressProps) => void; @@ -79,7 +68,7 @@ type AddressSearchProps = { predefinedPlaces?: Place[] | null; /** A map of inputID key names */ - renamedInputKeys?: RenamedInputKeysProps; + renamedInputKeys?: Address; /** Maximum number of characters allowed in search input */ maxInputLength?: number; @@ -96,4 +85,4 @@ type AddressSearchProps = { type IsCurrentTargetInsideContainerType = (event: FocusEvent | NativeSyntheticEvent, containerRef: RefObject) => boolean; -export type {CurrentLocationButtonProps, AddressSearchProps, RenamedInputKeysProps, IsCurrentTargetInsideContainerType, StreetValue}; +export type {CurrentLocationButtonProps, AddressSearchProps, IsCurrentTargetInsideContainerType, StreetValue}; diff --git a/src/types/onyx/PrivatePersonalDetails.ts b/src/types/onyx/PrivatePersonalDetails.ts index 64805d30495d..b6f357237505 100644 --- a/src/types/onyx/PrivatePersonalDetails.ts +++ b/src/types/onyx/PrivatePersonalDetails.ts @@ -6,6 +6,9 @@ type Address = { zip?: string; country?: string; zipCode?: string; + address?: string; + lat?: string; + lng?: string; }; type PrivatePersonalDetails = { From 6b82c5d762305d82526426c6dd2da9df16741759 Mon Sep 17 00:00:00 2001 From: Jakub Butkiewicz Date: Thu, 29 Feb 2024 14:15:05 +0100 Subject: [PATCH 027/213] fix: lint --- src/components/AddressSearch/types.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/AddressSearch/types.ts b/src/components/AddressSearch/types.ts index ec7a2c61398b..bc7acf3f7e40 100644 --- a/src/components/AddressSearch/types.ts +++ b/src/components/AddressSearch/types.ts @@ -3,7 +3,7 @@ import type {NativeSyntheticEvent, StyleProp, TextInputFocusEventData, View, Vie import type {Place} from 'react-native-google-places-autocomplete'; import type {MaybePhraseKey} from '@libs/Localize'; import type Locale from '@src/types/onyx/Locale'; -import {Address} from '@src/types/onyx/PrivatePersonalDetails'; +import type {Address} from '@src/types/onyx/PrivatePersonalDetails'; type CurrentLocationButtonProps = { /** Callback that is called when the button is clicked */ From 7f85aba23db53dd5e8c2e1d5e1b34d6975a38bbe Mon Sep 17 00:00:00 2001 From: Jakub Butkiewicz Date: Thu, 29 Feb 2024 14:55:05 +0100 Subject: [PATCH 028/213] fix: resolve comments --- .../ReimbursementAccountPage.tsx | 65 ++++--------------- src/types/form/ReimbursementAccountForm.ts | 1 + src/types/onyx/ReimbursementAccount.ts | 16 +++++ 3 files changed, 29 insertions(+), 53 deletions(-) diff --git a/src/pages/ReimbursementAccount/ReimbursementAccountPage.tsx b/src/pages/ReimbursementAccount/ReimbursementAccountPage.tsx index ee650379fc6c..bb0a36887df0 100644 --- a/src/pages/ReimbursementAccount/ReimbursementAccountPage.tsx +++ b/src/pages/ReimbursementAccount/ReimbursementAccountPage.tsx @@ -1,4 +1,5 @@ import type {RouteProp} from '@react-navigation/native'; +import type {StackScreenProps} from '@react-navigation/stack'; import Str from 'expensify-common/lib/str'; import lodashPick from 'lodash/pick'; import React, {useEffect, useRef, useState} from 'react'; @@ -28,9 +29,12 @@ import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; import type SCREENS from '@src/SCREENS'; +import type {InputID} from '@src/types/form/ReimbursementAccountForm'; import type * as OnyxTypes from '@src/types/onyx'; +import type {ACHData, BankAccountStep as TBankAccountStep} from '@src/types/onyx/ReimbursementAccount'; import {isEmptyObject} from '@src/types/utils/EmptyObject'; import ACHContractStep from './ACHContractStep'; +// eslint-disable-next-line @typescript-eslint/no-redeclare import BankAccountStep from './BankAccountStep'; import BeneficialOwnersStep from './BeneficialOwnersStep'; import CompanyStep from './CompanyStep'; @@ -40,50 +44,6 @@ import EnableBankAccount from './EnableBankAccount/EnableBankAccount'; import * as ReimbursementAccountProps from './reimbursementAccountPropTypes'; import RequestorStep from './RequestorStep'; -type ReimbursementAccountDraft = { - bankAccountID: number; - - /** Props needed for BankAccountStep */ - accountNumber: string; - routingNumber: string; - acceptTerms: boolean; - plaidAccountID: string; - mask: string; - - /** Props needed for CompanyStep */ - companyName: string; - addressStreet: string; - addressCity: string; - addressState: string; - addressZipCode: string; - companyPhone: string; - website: string; - companyTaxID: string; - incorporationType: string; - incorporationDate: string | Date; - incorporationState: string; - hasNoConnectionToCannabis: boolean; - - /** Props needed for RequestorStep */ - firstName: string; - lastName: string; - requestorAddressStreet: string; - requestorAddressCity: string; - requestorAddressState: string; - requestorAddressZipCode: string; - dob: string | Date; - ssnLast4: string; - isOnfidoSetupComplete: boolean; - - /** Props needed for ACHContractStep */ - ownsMoreThan25Percent: boolean; - hasOtherBeneficialOwners: boolean; - acceptTermsAndConditions: boolean; - certifyTrueInformation: boolean; - beneficialOwners: string; - beneficialOwnerKeys: string[]; -}; - type ReimbursementAccountOnyxProps = { /** Plaid SDK token to use to initialize the widget */ plaidLinkToken: OnyxEntry; @@ -108,7 +68,9 @@ type ReimbursementAccountOnyxProps = { }; type ReimbursementAccountPageProps = WithPolicyOnyxProps & - ReimbursementAccountOnyxProps & {route: RouteProp}; + ReimbursementAccountOnyxProps & + StackScreenProps; + const ROUTE_NAMES = { COMPANY: 'company', PERSONAL_INFORMATION: 'personal-information', @@ -123,8 +85,8 @@ const ROUTE_NAMES = { * We can pass stepToOpen in the URL to force which step to show. * Mainly needed when user finished the flow in verifying state, and Ops ask them to modify some fields from a specific step. */ -function getStepToOpenFromRouteParams(route: RouteProp): ValueOf | '' { - switch (route.params.stepToOpen ?? '') { +function getStepToOpenFromRouteParams(route: RouteProp): TBankAccountStep | '' { + switch (route.params.stepToOpen) { case ROUTE_NAMES.NEW: return CONST.BANK_ACCOUNT.STEP.BANK_ACCOUNT; case ROUTE_NAMES.COMPANY: @@ -144,7 +106,7 @@ function getStepToOpenFromRouteParams(route: RouteProp): ValueOf { +function getRouteForCurrentStep(currentStep: TBankAccountStep): ValueOf { switch (currentStep) { case CONST.BANK_ACCOUNT.STEP.COMPANY: return ROUTE_NAMES.COMPANY; @@ -184,8 +146,7 @@ function ReimbursementAccountPage({ */ const achData = reimbursementAccount?.achData; - function getBankAccountFields(fieldNames: string[]): string[] { - // @ts-expect-error -- Pick is more acurate type in this case because lodashPick returns Partial + function getBankAccountFields(fieldNames: T[]): Pick { return { ...lodashPick(reimbursementAccount?.achData, ...fieldNames), }; @@ -194,7 +155,7 @@ function ReimbursementAccountPage({ /** * Returns selected bank account fields based on field names provided. */ - function getFieldsForStep(step: string): string[] { + function getFieldsForStep(step: TBankAccountStep): InputID[] { switch (step) { case CONST.BANK_ACCOUNT.STEP.BANK_ACCOUNT: return ['routingNumber', 'accountNumber', 'bankName', 'plaidAccountID', 'plaidAccessToken', 'isSavings']; @@ -553,5 +514,3 @@ export default withPolicy( }, })(ReimbursementAccountPage), ); - -export type {ReimbursementAccountDraft}; diff --git a/src/types/form/ReimbursementAccountForm.ts b/src/types/form/ReimbursementAccountForm.ts index 7860d5a066f1..da6512b767f2 100644 --- a/src/types/form/ReimbursementAccountForm.ts +++ b/src/types/form/ReimbursementAccountForm.ts @@ -131,5 +131,6 @@ export type { BeneficialOwnersStepProps, ACHContractStepProps, ReimbursementAccountProps, + InputID, }; export default INPUT_IDS; diff --git a/src/types/onyx/ReimbursementAccount.ts b/src/types/onyx/ReimbursementAccount.ts index 224937d97bfa..ff704f856c1d 100644 --- a/src/types/onyx/ReimbursementAccount.ts +++ b/src/types/onyx/ReimbursementAccount.ts @@ -38,6 +38,22 @@ type ACHData = Partial Date: Thu, 29 Feb 2024 16:39:07 +0100 Subject: [PATCH 029/213] fix: resolve comments --- .../ContinueBankAccountSetup.tsx | 2 +- .../ReimbursementAccountPage.tsx | 1 - src/types/onyx/ReimbursementAccount.ts | 16 ++-------------- 3 files changed, 3 insertions(+), 16 deletions(-) diff --git a/src/pages/ReimbursementAccount/ContinueBankAccountSetup.tsx b/src/pages/ReimbursementAccount/ContinueBankAccountSetup.tsx index d26eeec2f049..e43029b3d84f 100644 --- a/src/pages/ReimbursementAccount/ContinueBankAccountSetup.tsx +++ b/src/pages/ReimbursementAccount/ContinueBankAccountSetup.tsx @@ -77,7 +77,7 @@ function ContinueBankAccountSetup({policyName = '', onBackButtonPress, reimburse onPress={() => BankAccounts.requestResetFreePlanBankAccount()} shouldShowRightIcon wrapperStyle={[styles.cardMenuItem]} - disabled={Boolean(pendingAction) || !isEmptyObject(errors)} + disabled={!!pendingAction || !isEmptyObject(errors)} /> diff --git a/src/pages/ReimbursementAccount/ReimbursementAccountPage.tsx b/src/pages/ReimbursementAccount/ReimbursementAccountPage.tsx index bb0a36887df0..c9f755f274e6 100644 --- a/src/pages/ReimbursementAccount/ReimbursementAccountPage.tsx +++ b/src/pages/ReimbursementAccount/ReimbursementAccountPage.tsx @@ -182,7 +182,6 @@ function ReimbursementAccountPage({ /** * Returns true if a VBBA exists in any state other than OPEN or LOCKED - */ function hasInProgressVBBA(): boolean { return !!achData?.bankAccountID && achData?.state !== BankAccount.STATE.OPEN && achData?.state !== BankAccount.STATE.LOCKED; diff --git a/src/types/onyx/ReimbursementAccount.ts b/src/types/onyx/ReimbursementAccount.ts index ff704f856c1d..b96c47729cd7 100644 --- a/src/types/onyx/ReimbursementAccount.ts +++ b/src/types/onyx/ReimbursementAccount.ts @@ -1,6 +1,6 @@ import type {ValueOf} from 'type-fest'; import type CONST from '@src/CONST'; -import type {ACHContractStepProps, BeneficialOwnersStepProps, CompanyStepProps, RequestorStepProps} from '@src/types/form/ReimbursementAccountForm'; +import type {ACHContractStepProps, BeneficialOwnersStepProps, CompanyStepProps, ReimbursementAccountProps, RequestorStepProps} from '@src/types/form/ReimbursementAccountForm'; import type {BankName} from './Bank'; import type * as OnyxCommon from './OnyxCommon'; @@ -8,7 +8,7 @@ type BankAccountStep = ValueOf; type BankAccountSubStep = ValueOf; -type ACHData = Partial & { +type ACHData = Partial & { /** Step of the setup flow that we are on. Determines which view is presented. */ currentStep?: BankAccountStep; @@ -41,19 +41,7 @@ type ACHData = Partial Date: Mon, 4 Mar 2024 11:58:28 +0000 Subject: [PATCH 030/213] remove weird merge file creation --- src/pages/settings/Profile/PersonalDetails/LegalNamePage.js | 0 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 src/pages/settings/Profile/PersonalDetails/LegalNamePage.js diff --git a/src/pages/settings/Profile/PersonalDetails/LegalNamePage.js b/src/pages/settings/Profile/PersonalDetails/LegalNamePage.js deleted file mode 100644 index e69de29bb2d1..000000000000 From cc9b06a2811369be4d5754c5e0b6a9d739f29708 Mon Sep 17 00:00:00 2001 From: Georgia Monahan Date: Mon, 4 Mar 2024 12:02:14 +0000 Subject: [PATCH 031/213] Clean up --- src/pages/ProfilePage.js | 2 -- src/pages/settings/Profile/ProfilePage.js | 4 ++-- src/pages/settings/Wallet/WalletPage/CardDetails.tsx | 4 ++-- 3 files changed, 4 insertions(+), 6 deletions(-) diff --git a/src/pages/ProfilePage.js b/src/pages/ProfilePage.js index 80d5515d45d3..cf05b8e4ab28 100755 --- a/src/pages/ProfilePage.js +++ b/src/pages/ProfilePage.js @@ -20,7 +20,6 @@ import ScreenWrapper from '@components/ScreenWrapper'; import Text from '@components/Text'; import UserDetailsTooltip from '@components/UserDetailsTooltip'; import withLocalize, {withLocalizePropTypes} from '@components/withLocalize'; -import usePrivatePersonalDetails from '@hooks/usePrivatePersonalDetails'; import useThemeStyles from '@hooks/useThemeStyles'; import compose from '@libs/compose'; import Navigation from '@libs/Navigation/Navigation'; @@ -94,7 +93,6 @@ const getPhoneNumber = (details) => { }; function ProfilePage(props) { - usePrivatePersonalDetails(); const styles = useThemeStyles(); const accountID = Number(lodashGet(props.route.params, 'accountID', 0)); const isCurrentUser = props.session.accountID === accountID; diff --git a/src/pages/settings/Profile/ProfilePage.js b/src/pages/settings/Profile/ProfilePage.js index 81fca0fab16c..34a1e15df15f 100755 --- a/src/pages/settings/Profile/ProfilePage.js +++ b/src/pages/settings/Profile/ProfilePage.js @@ -14,11 +14,11 @@ import Section from '@components/Section'; import withCurrentUserPersonalDetails, {withCurrentUserPersonalDetailsDefaultProps, withCurrentUserPersonalDetailsPropTypes} from '@components/withCurrentUserPersonalDetails'; import withLocalize, {withLocalizePropTypes} from '@components/withLocalize'; import withWindowDimensions, {windowDimensionsPropTypes} from '@components/withWindowDimensions'; +import usePrivatePersonalDetails from '@hooks/usePrivatePersonalDetails'; import useStyleUtils from '@hooks/useStyleUtils'; import useTheme from '@hooks/useTheme'; import useThemeStyles from '@hooks/useThemeStyles'; import useWindowDimensions from '@hooks/useWindowDimensions'; -import usePrivatePersonalDetails from '@hooks/usePrivatePersonalDetails'; import compose from '@libs/compose'; import {translatableTextPropTypes} from '@libs/Localize'; import Navigation from '@libs/Navigation/Navigation'; @@ -89,7 +89,6 @@ function ProfilePage(props) { const theme = useTheme(); const styles = useThemeStyles(); const StyleUtils = useStyleUtils(); - usePrivatePersonalDetails(); const getPronouns = () => { let pronounsKey = lodashGet(props.currentUserPersonalDetails, 'pronouns', ''); if (pronounsKey.startsWith(CONST.PRONOUNS.PREFIX)) { @@ -105,6 +104,7 @@ function ProfilePage(props) { const contactMethodBrickRoadIndicator = UserUtils.getLoginListBrickRoadIndicator(props.loginList); const emojiCode = lodashGet(props, 'currentUserPersonalDetails.status.emojiCode', ''); const {isSmallScreenWidth} = useWindowDimensions(); + usePrivatePersonalDetails(); const privateDetails = props.privatePersonalDetails || {}; const legalName = `${privateDetails.legalFirstName || ''} ${privateDetails.legalLastName || ''}`.trim(); diff --git a/src/pages/settings/Wallet/WalletPage/CardDetails.tsx b/src/pages/settings/Wallet/WalletPage/CardDetails.tsx index d5645e5f379f..77458384c214 100644 --- a/src/pages/settings/Wallet/WalletPage/CardDetails.tsx +++ b/src/pages/settings/Wallet/WalletPage/CardDetails.tsx @@ -7,8 +7,8 @@ import MenuItemWithTopDescription from '@components/MenuItemWithTopDescription'; import PressableWithDelayToggle from '@components/Pressable/PressableWithDelayToggle'; import TextLink from '@components/TextLink'; import useLocalize from '@hooks/useLocalize'; -import useThemeStyles from '@hooks/useThemeStyles'; import usePrivatePersonalDetails from '@hooks/usePrivatePersonalDetails'; +import useThemeStyles from '@hooks/useThemeStyles'; import Clipboard from '@libs/Clipboard'; import Navigation from '@libs/Navigation/Navigation'; import * as PersonalDetailsUtils from '@libs/PersonalDetailsUtils'; @@ -47,8 +47,8 @@ type CardDetailsProps = CardDetailsOnyxProps & { function CardDetails({pan = '', expiration = '', cvv = '', privatePersonalDetails, domain}: CardDetailsProps) { const styles = useThemeStyles(); - const {translate} = useLocalize(); usePrivatePersonalDetails(); + const {translate} = useLocalize(); const handleCopyToClipboard = () => { Clipboard.setString(pan); From c79637c6d47fb09b0eaa230954ebb59be5859ff5 Mon Sep 17 00:00:00 2001 From: Georgia Monahan Date: Mon, 4 Mar 2024 12:40:26 +0000 Subject: [PATCH 032/213] leave TS type --- src/types/onyx/PrivatePersonalDetails.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/types/onyx/PrivatePersonalDetails.ts b/src/types/onyx/PrivatePersonalDetails.ts index 7219bcaf4f84..5a9dae0a5523 100644 --- a/src/types/onyx/PrivatePersonalDetails.ts +++ b/src/types/onyx/PrivatePersonalDetails.ts @@ -18,6 +18,9 @@ type PrivatePersonalDetails = { /** User's home address */ address?: Address; + + /** Whether we are loading the data via the API */ + isLoading?: boolean; }; export default PrivatePersonalDetails; From 1bd4b0a9dd0a9598f7110af0707a18c4b7c1f851 Mon Sep 17 00:00:00 2001 From: Georgia Monahan Date: Tue, 5 Mar 2024 15:47:59 +0000 Subject: [PATCH 033/213] remove hook and calls to to get private personal details --- src/hooks/usePrivatePersonalDetails.ts | 20 ------------------- src/libs/actions/PersonalDetails.ts | 7 ------- .../Profile/PersonalDetails/AddressPage.tsx | 2 -- .../PersonalDetails/DateOfBirthPage.tsx | 2 -- .../Profile/PersonalDetails/LegalNamePage.tsx | 2 -- src/pages/settings/Profile/ProfilePage.js | 2 -- .../Wallet/WalletPage/CardDetails.tsx | 2 -- 7 files changed, 37 deletions(-) delete mode 100644 src/hooks/usePrivatePersonalDetails.ts diff --git a/src/hooks/usePrivatePersonalDetails.ts b/src/hooks/usePrivatePersonalDetails.ts deleted file mode 100644 index f17600e9878f..000000000000 --- a/src/hooks/usePrivatePersonalDetails.ts +++ /dev/null @@ -1,20 +0,0 @@ -import {useContext, useEffect} from 'react'; -import {NetworkContext} from '@components/OnyxProvider'; -import * as PersonalDetails from '@userActions/PersonalDetails'; - -/** - * Hook for fetching private personal details - */ -export default function usePrivatePersonalDetails() { - const network = useContext(NetworkContext); - - useEffect(() => { - const personalDetails = PersonalDetails.getPrivatePersonalDetails(); - // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing - if (network?.isOffline || (Boolean(personalDetails) && personalDetails?.isLoading !== undefined)) { - return; - } - - PersonalDetails.openPersonalDetails(); - }, [network?.isOffline]); -} diff --git a/src/libs/actions/PersonalDetails.ts b/src/libs/actions/PersonalDetails.ts index 5becaee1593c..1396ac17f047 100644 --- a/src/libs/actions/PersonalDetails.ts +++ b/src/libs/actions/PersonalDetails.ts @@ -446,17 +446,10 @@ function clearAvatarErrors() { }); } -/** - * Get private personal details value - */ -function getPrivatePersonalDetails(): OnyxEntry { - return privatePersonalDetails; -} export { clearAvatarErrors, deleteAvatar, - getPrivatePersonalDetails, openPersonalDetails, openPublicProfilePage, updateAddress, diff --git a/src/pages/settings/Profile/PersonalDetails/AddressPage.tsx b/src/pages/settings/Profile/PersonalDetails/AddressPage.tsx index 5552f1dd3b15..78ee6f969355 100644 --- a/src/pages/settings/Profile/PersonalDetails/AddressPage.tsx +++ b/src/pages/settings/Profile/PersonalDetails/AddressPage.tsx @@ -7,7 +7,6 @@ import FullscreenLoadingIndicator from '@components/FullscreenLoadingIndicator'; import HeaderWithBackButton from '@components/HeaderWithBackButton'; import ScreenWrapper from '@components/ScreenWrapper'; import useLocalize from '@hooks/useLocalize'; -import usePrivatePersonalDetails from '@hooks/usePrivatePersonalDetails'; import useThemeStyles from '@hooks/useThemeStyles'; import Navigation from '@libs/Navigation/Navigation'; import type {SettingsNavigatorParamList} from '@libs/Navigation/types'; @@ -45,7 +44,6 @@ function updateAddress(values: FormOnyxValues privatePersonalDetails?.address, [privatePersonalDetails]); const countryFromUrl = route.params?.country; diff --git a/src/pages/settings/Profile/PersonalDetails/DateOfBirthPage.tsx b/src/pages/settings/Profile/PersonalDetails/DateOfBirthPage.tsx index 34f351982416..e91093731c03 100644 --- a/src/pages/settings/Profile/PersonalDetails/DateOfBirthPage.tsx +++ b/src/pages/settings/Profile/PersonalDetails/DateOfBirthPage.tsx @@ -10,7 +10,6 @@ import FullscreenLoadingIndicator from '@components/FullscreenLoadingIndicator'; import HeaderWithBackButton from '@components/HeaderWithBackButton'; import ScreenWrapper from '@components/ScreenWrapper'; import useLocalize from '@hooks/useLocalize'; -import usePrivatePersonalDetails from '@hooks/usePrivatePersonalDetails'; import useThemeStyles from '@hooks/useThemeStyles'; import Navigation from '@libs/Navigation/Navigation'; import * as ValidationUtils from '@libs/ValidationUtils'; @@ -31,7 +30,6 @@ type DateOfBirthPageProps = DateOfBirthPageOnyxProps; function DateOfBirthPage({privatePersonalDetails, isLoadingApp = true}: DateOfBirthPageProps) { const {translate} = useLocalize(); const styles = useThemeStyles(); - usePrivatePersonalDetails(); /** * @returns An object containing the errors for each inputID diff --git a/src/pages/settings/Profile/PersonalDetails/LegalNamePage.tsx b/src/pages/settings/Profile/PersonalDetails/LegalNamePage.tsx index 776524b6794d..a957740e81d0 100644 --- a/src/pages/settings/Profile/PersonalDetails/LegalNamePage.tsx +++ b/src/pages/settings/Profile/PersonalDetails/LegalNamePage.tsx @@ -10,7 +10,6 @@ import HeaderWithBackButton from '@components/HeaderWithBackButton'; import ScreenWrapper from '@components/ScreenWrapper'; import TextInput from '@components/TextInput'; import useLocalize from '@hooks/useLocalize'; -import usePrivatePersonalDetails from '@hooks/usePrivatePersonalDetails'; import useThemeStyles from '@hooks/useThemeStyles'; import * as ErrorUtils from '@libs/ErrorUtils'; import Navigation from '@libs/Navigation/Navigation'; @@ -38,7 +37,6 @@ const updateLegalName = (values: PrivatePersonalDetails) => { function LegalNamePage({privatePersonalDetails, isLoadingApp = true}: LegalNamePageProps) { const styles = useThemeStyles(); const {translate} = useLocalize(); - usePrivatePersonalDetails(); const legalFirstName = privatePersonalDetails?.legalFirstName ?? ''; const legalLastName = privatePersonalDetails?.legalLastName ?? ''; diff --git a/src/pages/settings/Profile/ProfilePage.js b/src/pages/settings/Profile/ProfilePage.js index 34a1e15df15f..982123dc4d6c 100755 --- a/src/pages/settings/Profile/ProfilePage.js +++ b/src/pages/settings/Profile/ProfilePage.js @@ -14,7 +14,6 @@ import Section from '@components/Section'; import withCurrentUserPersonalDetails, {withCurrentUserPersonalDetailsDefaultProps, withCurrentUserPersonalDetailsPropTypes} from '@components/withCurrentUserPersonalDetails'; import withLocalize, {withLocalizePropTypes} from '@components/withLocalize'; import withWindowDimensions, {windowDimensionsPropTypes} from '@components/withWindowDimensions'; -import usePrivatePersonalDetails from '@hooks/usePrivatePersonalDetails'; import useStyleUtils from '@hooks/useStyleUtils'; import useTheme from '@hooks/useTheme'; import useThemeStyles from '@hooks/useThemeStyles'; @@ -104,7 +103,6 @@ function ProfilePage(props) { const contactMethodBrickRoadIndicator = UserUtils.getLoginListBrickRoadIndicator(props.loginList); const emojiCode = lodashGet(props, 'currentUserPersonalDetails.status.emojiCode', ''); const {isSmallScreenWidth} = useWindowDimensions(); - usePrivatePersonalDetails(); const privateDetails = props.privatePersonalDetails || {}; const legalName = `${privateDetails.legalFirstName || ''} ${privateDetails.legalLastName || ''}`.trim(); diff --git a/src/pages/settings/Wallet/WalletPage/CardDetails.tsx b/src/pages/settings/Wallet/WalletPage/CardDetails.tsx index 77458384c214..4cce60cfa5c0 100644 --- a/src/pages/settings/Wallet/WalletPage/CardDetails.tsx +++ b/src/pages/settings/Wallet/WalletPage/CardDetails.tsx @@ -7,7 +7,6 @@ import MenuItemWithTopDescription from '@components/MenuItemWithTopDescription'; import PressableWithDelayToggle from '@components/Pressable/PressableWithDelayToggle'; import TextLink from '@components/TextLink'; import useLocalize from '@hooks/useLocalize'; -import usePrivatePersonalDetails from '@hooks/usePrivatePersonalDetails'; import useThemeStyles from '@hooks/useThemeStyles'; import Clipboard from '@libs/Clipboard'; import Navigation from '@libs/Navigation/Navigation'; @@ -47,7 +46,6 @@ type CardDetailsProps = CardDetailsOnyxProps & { function CardDetails({pan = '', expiration = '', cvv = '', privatePersonalDetails, domain}: CardDetailsProps) { const styles = useThemeStyles(); - usePrivatePersonalDetails(); const {translate} = useLocalize(); const handleCopyToClipboard = () => { From d6f6d2f7ba087f98f829e63b0bbc1660e3f8aa9e Mon Sep 17 00:00:00 2001 From: Georgia Monahan Date: Thu, 7 Mar 2024 09:55:13 +0000 Subject: [PATCH 034/213] lint --- src/libs/actions/PersonalDetails.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/libs/actions/PersonalDetails.ts b/src/libs/actions/PersonalDetails.ts index 1396ac17f047..d08f3787942e 100644 --- a/src/libs/actions/PersonalDetails.ts +++ b/src/libs/actions/PersonalDetails.ts @@ -446,7 +446,6 @@ function clearAvatarErrors() { }); } - export { clearAvatarErrors, deleteAvatar, From 970229ab40d46d19fc801b1a07cf3772282716fb Mon Sep 17 00:00:00 2001 From: Georgia Monahan Date: Mon, 11 Mar 2024 14:40:38 +0000 Subject: [PATCH 035/213] fix lint --- src/libs/actions/PersonalDetails.ts | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/libs/actions/PersonalDetails.ts b/src/libs/actions/PersonalDetails.ts index d08f3787942e..6ebdd778e9b8 100644 --- a/src/libs/actions/PersonalDetails.ts +++ b/src/libs/actions/PersonalDetails.ts @@ -43,12 +43,6 @@ Onyx.connect({ callback: (val) => (allPersonalDetails = val), }); -let privatePersonalDetails: OnyxEntry = null; -Onyx.connect({ - key: ONYXKEYS.PRIVATE_PERSONAL_DETAILS, - callback: (val) => (privatePersonalDetails = val), -}); - function updatePronouns(pronouns: string) { if (currentUserAccountID) { const parameters: UpdatePronounsParams = {pronouns}; From b3bc58956f7d5a5f9ca4d03cc86b8aeb7129d6fe Mon Sep 17 00:00:00 2001 From: Georgia Monahan Date: Mon, 11 Mar 2024 15:43:05 +0000 Subject: [PATCH 036/213] TS error --- src/libs/actions/PersonalDetails.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/actions/PersonalDetails.ts b/src/libs/actions/PersonalDetails.ts index 6ebdd778e9b8..d91fea3543d0 100644 --- a/src/libs/actions/PersonalDetails.ts +++ b/src/libs/actions/PersonalDetails.ts @@ -23,7 +23,7 @@ import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; import type {DateOfBirthForm} from '@src/types/form'; -import type {PersonalDetails, PersonalDetailsList, PrivatePersonalDetails} from '@src/types/onyx'; +import type {PersonalDetails, PersonalDetailsList} from '@src/types/onyx'; import type {SelectedTimezone, Timezone} from '@src/types/onyx/PersonalDetails'; import * as Session from './Session'; From 7d89088b9d2aea80a0e9f5085e947e1e64a10131 Mon Sep 17 00:00:00 2001 From: Etotaziba Olei Date: Thu, 14 Mar 2024 07:46:09 +0100 Subject: [PATCH 037/213] add initial english strings --- src/languages/es.ts | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/languages/es.ts b/src/languages/es.ts index 267581f043ee..e8ecc9b6364a 100644 --- a/src/languages/es.ts +++ b/src/languages/es.ts @@ -1769,6 +1769,7 @@ export default { invoices: 'Enviar facturas', travel: 'Viajes', members: 'Miembros', + accounting: 'Accounting', plan: 'Plan', profile: 'Perfil', bankAccount: 'Cuenta bancaria', @@ -1908,6 +1909,12 @@ export default { invitedBySecondaryLogin: ({secondaryLogin}) => `Agregado por nombre de usuario secundario ${secondaryLogin}.`, membersListTitle: 'Directorio de todos los miembros del espacio de trabajo.', }, + accounting: { + title: 'Connections', + subtitle: 'Connect to your accounting system to code transactions with your chart of accounts, auto-match payments and keep your finances in sync.', + qbo: 'Quickbooks Online', + setup: 'Set up', + }, card: { header: 'Desbloquea Tarjetas Expensify gratis', headerWithEcard: '¡Tus tarjetas están listas!', From 3492e28b5e3b691e65315faaa19a1afdc0f54d25 Mon Sep 17 00:00:00 2001 From: Etotaziba Olei Date: Thu, 14 Mar 2024 07:46:24 +0100 Subject: [PATCH 038/213] add initial spanish strings --- src/languages/en.ts | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/languages/en.ts b/src/languages/en.ts index 7e442eee2236..2bd96e310b9a 100755 --- a/src/languages/en.ts +++ b/src/languages/en.ts @@ -1745,6 +1745,7 @@ export default { invoices: 'Invoices', travel: 'Travel', members: 'Members', + accounting: 'Accounting', plan: 'Plan', profile: 'Profile', bankAccount: 'Bank account', @@ -1918,6 +1919,12 @@ export default { invalidRateError: 'Please enter a valid rate', lowRateError: 'Rate must be greater than 0', }, + accounting: { + title: 'Connections', + subtitle: 'Connect to your accounting system to code transactions with your chart of accounts, auto-match payments and keep your finances in sync.', + qbo: 'Quickbooks Online', + setup: 'Set up', + }, bills: { manageYourBills: 'Manage your bills', askYourVendorsBeforeEmail: 'Ask your vendors to forward their invoices to ', From 6edddee6d9f8cdd6c0b71b89a416361139202e24 Mon Sep 17 00:00:00 2001 From: Etotaziba Olei Date: Thu, 14 Mar 2024 07:47:58 +0100 Subject: [PATCH 039/213] add accounting navigation param type --- src/libs/Navigation/types.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/libs/Navigation/types.ts b/src/libs/Navigation/types.ts index a1ec30fa303e..d1421addca47 100644 --- a/src/libs/Navigation/types.ts +++ b/src/libs/Navigation/types.ts @@ -530,6 +530,9 @@ type WorkspacesCentralPaneNavigatorParamList = { [SCREENS.WORKSPACE.MEMBERS]: { policyID: string; }; + [SCREENS.WORKSPACE.ACCOUNTING]: { + policyID: string; + }; [SCREENS.WORKSPACE.CATEGORIES]: { policyID: string; }; From 917451c66e6eb00df732a16902a62ffa3896248e Mon Sep 17 00:00:00 2001 From: Etotaziba Olei Date: Thu, 14 Mar 2024 07:49:24 +0100 Subject: [PATCH 040/213] add accounting to workspace screens --- src/SCREENS.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/SCREENS.ts b/src/SCREENS.ts index 4db5fd9115a5..ad9c6eab2137 100644 --- a/src/SCREENS.ts +++ b/src/SCREENS.ts @@ -213,6 +213,7 @@ const SCREENS = { INVOICES: 'Workspace_Invoices', TRAVEL: 'Workspace_Travel', MEMBERS: 'Workspace_Members', + ACCOUNTING: 'Workspace_Accounting', INVITE: 'Workspace_Invite', INVITE_MESSAGE: 'Workspace_Invite_Message', CATEGORIES: 'Workspace_Categories', From 7f529e502996987702a9f76ecfa89c9ff8ba5705 Mon Sep 17 00:00:00 2001 From: Etotaziba Olei Date: Thu, 14 Mar 2024 07:50:47 +0100 Subject: [PATCH 041/213] add workspace acoounting route --- src/ROUTES.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/ROUTES.ts b/src/ROUTES.ts index defb945ba8c2..9735b22cfd3d 100644 --- a/src/ROUTES.ts +++ b/src/ROUTES.ts @@ -549,6 +549,10 @@ const ROUTES = { route: 'settings/workspaces/:policyID/members', getRoute: (policyID: string) => `settings/workspaces/${policyID}/members` as const, }, + WORKSPACE_ACCOUNTING: { + route: 'settings/workspaces/:policyID/accounting', + getRoute: (policyID: string) => `settings/workspaces/${policyID}/accounting` as const, + }, WORKSPACE_CATEGORIES: { route: 'settings/workspaces/:policyID/categories', getRoute: (policyID: string) => `settings/workspaces/${policyID}/categories` as const, From e4ba2b03bb5892959969da2543467c9f8bcdab91 Mon Sep 17 00:00:00 2001 From: Etotaziba Olei Date: Thu, 14 Mar 2024 07:51:32 +0100 Subject: [PATCH 042/213] add workspace accounting to workspace setting navigator --- src/libs/Navigation/AppNavigator/ModalStackNavigators.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/libs/Navigation/AppNavigator/ModalStackNavigators.tsx b/src/libs/Navigation/AppNavigator/ModalStackNavigators.tsx index 7df1da23d068..ade6ca103701 100644 --- a/src/libs/Navigation/AppNavigator/ModalStackNavigators.tsx +++ b/src/libs/Navigation/AppNavigator/ModalStackNavigators.tsx @@ -196,6 +196,7 @@ const WorkspaceSettingsModalStackNavigator = createModalStackNavigator( [SCREENS.WORKSPACE.INVOICES]: () => require('../../../pages/workspace/invoices/WorkspaceInvoicesPage').default as React.ComponentType, [SCREENS.WORKSPACE.TRAVEL]: () => require('../../../pages/workspace/travel/WorkspaceTravelPage').default as React.ComponentType, [SCREENS.WORKSPACE.MEMBERS]: () => require('../../../pages/workspace/WorkspaceMembersPage').default as React.ComponentType, + [SCREENS.WORKSPACE.ACCOUNTING]: () => require('../../../pages/workspace/accounting/PolicyAccountingPage').default as React.ComponentType, [SCREENS.WORKSPACE.CATEGORIES]: () => require('../../../pages/workspace/categories/WorkspaceCategoriesPage').default as React.ComponentType, [SCREENS.WORKSPACE.MORE_FEATURES]: () => require('../../../pages/workspace/WorkspaceMoreFeaturesPage').default as React.ComponentType, [SCREENS.WORKSPACE.TAGS]: () => require('../../../pages/workspace/tags/WorkspaceTagsPage').default as React.ComponentType, From 4bfc37172c0faec2cd27266d618dd163b2e8dd3a Mon Sep 17 00:00:00 2001 From: Etotaziba Olei Date: Thu, 14 Mar 2024 07:52:15 +0100 Subject: [PATCH 043/213] add workspace accounting to link to screens --- src/libs/Navigation/linkingConfig/config.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/libs/Navigation/linkingConfig/config.ts b/src/libs/Navigation/linkingConfig/config.ts index 1461c27e03e0..c096cc5df04c 100644 --- a/src/libs/Navigation/linkingConfig/config.ts +++ b/src/libs/Navigation/linkingConfig/config.ts @@ -573,6 +573,9 @@ const config: LinkingOptions['config'] = { [SCREENS.WORKSPACE.MEMBERS]: { path: ROUTES.WORKSPACE_MEMBERS.route, }, + [SCREENS.WORKSPACE.ACCOUNTING]: { + path: ROUTES.WORKSPACE_ACCOUNTING.route, + }, [SCREENS.WORKSPACE.CATEGORIES]: { path: ROUTES.WORKSPACE_CATEGORIES.route, }, From e05a0b0ba60f55398709d30ab9383777ad3a3acf Mon Sep 17 00:00:00 2001 From: Etotaziba Olei Date: Thu, 14 Mar 2024 07:53:19 +0100 Subject: [PATCH 044/213] add workspace accounting Page --- .../accounting/PolicyAccountingPage.tsx | 91 +++++++++++++++++++ 1 file changed, 91 insertions(+) create mode 100644 src/pages/workspace/accounting/PolicyAccountingPage.tsx diff --git a/src/pages/workspace/accounting/PolicyAccountingPage.tsx b/src/pages/workspace/accounting/PolicyAccountingPage.tsx new file mode 100644 index 000000000000..da482ac7837c --- /dev/null +++ b/src/pages/workspace/accounting/PolicyAccountingPage.tsx @@ -0,0 +1,91 @@ +import React, {useMemo} from 'react'; +import {View} from 'react-native'; +import Button from '@components/Button'; +import HeaderWithBackButton from '@components/HeaderWithBackButton'; +import * as Expensicons from '@components/Icon/Expensicons'; +import * as Illustrations from '@components/Icon/Illustrations'; +import MenuItemList from '@components/MenuItemList'; +import ScreenWrapper from '@components/ScreenWrapper'; +import ScrollView from '@components/ScrollView'; +import Section from '@components/Section'; +import useLocalize from '@hooks/useLocalize'; +import useThemeStyles from '@hooks/useThemeStyles'; +import useWaitForNavigation from '@hooks/useWaitForNavigation'; +import useWindowDimensions from '@hooks/useWindowDimensions'; +import Navigation from '@libs/Navigation/Navigation'; +import type {TranslationPaths} from '@src/languages/types'; +import ROUTES from '@src/ROUTES'; + +function PolicyAccountingPage() { + const styles = useThemeStyles(); + const {translate} = useLocalize(); + const waitForNavigate = useWaitForNavigation(); + const {isSmallScreenWidth} = useWindowDimensions(); + + const menuItems = useMemo(() => { + const baseMenuItems = [ + { + translationKey: 'workspace.accounting.qbo', + icon: Expensicons.Shield, + action: waitForNavigate(() => Navigation.navigate(ROUTES.SETTINGS_2FA.getRoute())), + }, + ]; + + return baseMenuItems.map((item) => ({ + key: item.translationKey, + title: translate(item.translationKey as TranslationPaths), + icon: item.icon, + onPress: item.action, + shouldShowRightIcon: false, + + link: '', + shouldShowRightComponent: true, + rightComponent: ( +