From cd8ca07937025d621e7fb6802526e2a64b08aff5 Mon Sep 17 00:00:00 2001 From: Yogesh Bhatt Date: Sat, 7 Oct 2023 12:53:52 +0530 Subject: [PATCH 001/500] Convert StatePicker modal to StateSelection page --- src/ROUTES.ts | 16 ++ .../StatePicker/StateSelectorModal.js | 112 -------------- src/components/StatePicker/index.js | 97 ------------- src/components/StateSelector.js | 87 +++++++++++ .../AppNavigator/ModalStackNavigators.js | 1 + src/libs/Navigation/linkingConfig.js | 4 + src/libs/getStateFromRoute.ts | 13 ++ src/pages/ReimbursementAccount/AddressForm.js | 4 +- src/pages/ReimbursementAccount/CompanyStep.js | 23 ++- .../ReimbursementAccountPage.js | 1 + .../Profile/PersonalDetails/AddressPage.js | 22 ++- .../PersonalDetails/StateSelectionPage.js | 137 ++++++++++++++++++ src/pages/settings/Wallet/AddDebitCardPage.js | 19 ++- src/stories/Form.stories.js | 6 +- 14 files changed, 317 insertions(+), 225 deletions(-) delete mode 100644 src/components/StatePicker/StateSelectorModal.js delete mode 100644 src/components/StatePicker/index.js create mode 100644 src/components/StateSelector.js create mode 100644 src/libs/getStateFromRoute.ts create mode 100644 src/pages/settings/Profile/PersonalDetails/StateSelectionPage.js diff --git a/src/ROUTES.ts b/src/ROUTES.ts index b2dafa643b22..642e8ec8a2d0 100644 --- a/src/ROUTES.ts +++ b/src/ROUTES.ts @@ -94,6 +94,22 @@ export default { return route; }, }, + SETTINGS_PERSONAL_DETAILS_ADDRESS_STATE: { + route: 'settings/profile/personal-details/address/state', + getRoute: (state: string, backTo?: string, label?: string, stateParamName = 'state') => { + let route = `settings/profile/personal-details/address/state?state=${state}`; + if (backTo) { + route += `&backTo=${encodeURIComponent(backTo)}`; + } + if (label) { + route += `&label=${encodeURIComponent(label)}`; + } + if (stateParamName) { + route += `&stateParamName=${encodeURIComponent(stateParamName)}`; + } + return route; + }, + }, SETTINGS_CONTACT_METHODS: 'settings/profile/contact-methods', SETTINGS_CONTACT_METHOD_DETAILS: { route: 'settings/profile/contact-methods/:contactMethod/details', diff --git a/src/components/StatePicker/StateSelectorModal.js b/src/components/StatePicker/StateSelectorModal.js deleted file mode 100644 index 378dcc4ebc8b..000000000000 --- a/src/components/StatePicker/StateSelectorModal.js +++ /dev/null @@ -1,112 +0,0 @@ -import _ from 'underscore'; -import React, {useMemo, useEffect} from 'react'; -import PropTypes from 'prop-types'; -import {CONST as COMMON_CONST} from 'expensify-common/lib/CONST'; -import CONST from '../../CONST'; -import Modal from '../Modal'; -import HeaderWithBackButton from '../HeaderWithBackButton'; -import SelectionList from '../SelectionList'; -import useLocalize from '../../hooks/useLocalize'; -import ScreenWrapper from '../ScreenWrapper'; -import styles from '../../styles/styles'; -import searchCountryOptions from '../../libs/searchCountryOptions'; -import StringUtils from '../../libs/StringUtils'; - -const propTypes = { - /** Whether the modal is visible */ - isVisible: PropTypes.bool.isRequired, - - /** State value selected */ - currentState: PropTypes.string, - - /** Function to call when the user selects a State */ - onStateSelected: PropTypes.func, - - /** Function to call when the user closes the State modal */ - onClose: PropTypes.func, - - /** The search value from the selection list */ - searchValue: PropTypes.string.isRequired, - - /** Function to call when the user types in the search input */ - setSearchValue: PropTypes.func.isRequired, - - /** Label to display on field */ - label: PropTypes.string, -}; - -const defaultProps = { - currentState: '', - onClose: () => {}, - onStateSelected: () => {}, - label: undefined, -}; - -function StateSelectorModal({currentState, isVisible, onClose, onStateSelected, searchValue, setSearchValue, label}) { - const {translate} = useLocalize(); - - useEffect(() => { - if (isVisible) { - return; - } - setSearchValue(''); - }, [isVisible, setSearchValue]); - - const countryStates = useMemo( - () => - _.map(_.keys(COMMON_CONST.STATES), (state) => { - const stateName = translate(`allStates.${state}.stateName`); - const stateISO = translate(`allStates.${state}.stateISO`); - return { - value: stateISO, - keyForList: stateISO, - text: stateName, - isSelected: currentState === stateISO, - searchValue: StringUtils.sanitizeString(`${stateISO}${stateName}`), - }; - }), - [translate, currentState], - ); - - const searchResults = searchCountryOptions(searchValue, countryStates); - const headerMessage = searchValue.trim() && !searchResults.length ? translate('common.noResultsFound') : ''; - - return ( - - - - - - - ); -} - -StateSelectorModal.propTypes = propTypes; -StateSelectorModal.defaultProps = defaultProps; -StateSelectorModal.displayName = 'StateSelectorModal'; - -export default StateSelectorModal; diff --git a/src/components/StatePicker/index.js b/src/components/StatePicker/index.js deleted file mode 100644 index f7f894af2a07..000000000000 --- a/src/components/StatePicker/index.js +++ /dev/null @@ -1,97 +0,0 @@ -import React, {useState} from 'react'; -import {View} from 'react-native'; -import PropTypes from 'prop-types'; -import _ from 'underscore'; -import {CONST as COMMON_CONST} from 'expensify-common/lib/CONST'; -import styles from '../../styles/styles'; -import MenuItemWithTopDescription from '../MenuItemWithTopDescription'; -import useLocalize from '../../hooks/useLocalize'; -import FormHelpMessage from '../FormHelpMessage'; -import StateSelectorModal from './StateSelectorModal'; -import refPropTypes from '../refPropTypes'; - -const propTypes = { - /** Error text to display */ - errorText: PropTypes.string, - - /** State to display */ - value: PropTypes.string, - - /** Callback to call when the input changes */ - onInputChange: PropTypes.func, - - /** A ref to forward to MenuItemWithTopDescription */ - forwardedRef: refPropTypes, - - /** Label to display on field */ - label: PropTypes.string, -}; - -const defaultProps = { - value: undefined, - forwardedRef: undefined, - errorText: '', - onInputChange: () => {}, - label: undefined, -}; - -function StatePicker({value, errorText, onInputChange, forwardedRef, label}) { - const {translate} = useLocalize(); - const [isPickerVisible, setIsPickerVisible] = useState(false); - const [searchValue, setSearchValue] = useState(''); - - const showPickerModal = () => { - setIsPickerVisible(true); - }; - - const hidePickerModal = () => { - setIsPickerVisible(false); - }; - - const updateStateInput = (state) => { - if (state.value !== value) { - onInputChange(state.value); - } - hidePickerModal(); - }; - - const title = value && _.keys(COMMON_CONST.STATES).includes(value) ? translate(`allStates.${value}.stateName`) : ''; - const descStyle = title.length === 0 ? styles.textNormal : null; - - return ( - - - - - - - - ); -} - -StatePicker.propTypes = propTypes; -StatePicker.defaultProps = defaultProps; -StatePicker.displayName = 'StatePicker'; - -export default React.forwardRef((props, ref) => ( - -)); diff --git a/src/components/StateSelector.js b/src/components/StateSelector.js new file mode 100644 index 000000000000..c004fb8bf375 --- /dev/null +++ b/src/components/StateSelector.js @@ -0,0 +1,87 @@ +import React, {useEffect} from 'react'; +import PropTypes from 'prop-types'; +import {View} from 'react-native'; +import _ from 'underscore'; +import {CONST as COMMON_CONST} from 'expensify-common/lib/CONST'; +import styles from '../styles/styles'; +import Navigation from '../libs/Navigation/Navigation'; +import ROUTES from '../ROUTES'; +import useLocalize from '../hooks/useLocalize'; +import MenuItemWithTopDescription from './MenuItemWithTopDescription'; +import FormHelpMessage from './FormHelpMessage'; + +const propTypes = { + /** Form error text. e.g when no country is selected */ + errorText: PropTypes.string, + + /** Callback called when the country changes. */ + onInputChange: PropTypes.func.isRequired, + + /** Current selected country */ + value: PropTypes.string, + + /** inputID used by the Form component */ + // eslint-disable-next-line react/no-unused-prop-types + inputID: PropTypes.string.isRequired, + + /** React ref being forwarded to the MenuItemWithTopDescription */ + forwardedRef: PropTypes.func, + + /** Label of state in the url */ + paramName: PropTypes.string, + + /** Label to display on field */ + label: PropTypes.string, +}; + +const defaultProps = { + errorText: '', + value: undefined, + forwardedRef: () => {}, + label: undefined, + paramName: 'state', +}; + +function StateSelector({errorText, value: stateCode, label, paramName, onInputChange, forwardedRef}) { + const {translate} = useLocalize(); + + const title = stateCode && _.keys(COMMON_CONST.STATES).includes(stateCode) ? translate(`allStates.${stateCode}.stateName`) : ''; + const descStyle = title.length === 0 ? styles.textNormal : null; + + useEffect(() => { + // This will cause the form to revalidate and remove any error related to country name + onInputChange(stateCode); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [stateCode]); + + return ( + + { + const activeRoute = Navigation.getActiveRoute(); + Navigation.navigate(ROUTES.SETTINGS_PERSONAL_DETAILS_ADDRESS_STATE.getRoute(stateCode, activeRoute, label, paramName)); + }} + /> + + + + + ); +} + +StateSelector.propTypes = propTypes; +StateSelector.defaultProps = defaultProps; +StateSelector.displayName = 'StateSelector'; + +export default React.forwardRef((props, ref) => ( + +)); diff --git a/src/libs/Navigation/AppNavigator/ModalStackNavigators.js b/src/libs/Navigation/AppNavigator/ModalStackNavigators.js index 6636702592c0..f682d438f270 100644 --- a/src/libs/Navigation/AppNavigator/ModalStackNavigators.js +++ b/src/libs/Navigation/AppNavigator/ModalStackNavigators.js @@ -127,6 +127,7 @@ const SettingsModalStackNavigator = createModalStackNavigator({ Settings_PersonalDetails_DateOfBirth: () => require('../../../pages/settings/Profile/PersonalDetails/DateOfBirthPage').default, Settings_PersonalDetails_Address: () => require('../../../pages/settings/Profile/PersonalDetails/AddressPage').default, Settings_PersonalDetails_Address_Country: () => require('../../../pages/settings/Profile/PersonalDetails/CountrySelectionPage').default, + Settings_PersonalDetails_Address_State: () => require('../../../pages/settings/Profile/PersonalDetails/StateSelectionPage').default, Settings_ContactMethods: () => require('../../../pages/settings/Profile/Contacts/ContactMethodsPage').default, Settings_ContactMethodDetails: () => require('../../../pages/settings/Profile/Contacts/ContactMethodDetailsPage').default, Settings_NewContactMethod: () => require('../../../pages/settings/Profile/Contacts/NewContactMethodPage').default, diff --git a/src/libs/Navigation/linkingConfig.js b/src/libs/Navigation/linkingConfig.js index bf069aba314e..1b39987e7257 100644 --- a/src/libs/Navigation/linkingConfig.js +++ b/src/libs/Navigation/linkingConfig.js @@ -155,6 +155,10 @@ export default { path: ROUTES.SETTINGS_PERSONAL_DETAILS_ADDRESS_COUNTRY.route, exact: true, }, + Settings_PersonalDetails_Address_State: { + path: ROUTES.SETTINGS_PERSONAL_DETAILS_ADDRESS_STATE.route, + exact: true, + }, Settings_TwoFactorAuth: { path: ROUTES.SETTINGS_2FA, exact: true, diff --git a/src/libs/getStateFromRoute.ts b/src/libs/getStateFromRoute.ts new file mode 100644 index 000000000000..ff01068eae3e --- /dev/null +++ b/src/libs/getStateFromRoute.ts @@ -0,0 +1,13 @@ +// eslint-disable-next-line you-dont-need-lodash-underscore/get +import lodashGet from 'lodash/get'; +import {CONST as COMMON_CONST} from 'expensify-common/lib/CONST'; + +type RouteProps = { + params?: Record; +}; + +export default function getStateFromRoute(route: RouteProps, stateParamName = 'state'): string { + const stateFromUrlTemp = lodashGet(route, `params.${stateParamName}`) as unknown as string; + // check if state is valid + return lodashGet(COMMON_CONST.STATES, stateFromUrlTemp) ? stateFromUrlTemp : ''; +} diff --git a/src/pages/ReimbursementAccount/AddressForm.js b/src/pages/ReimbursementAccount/AddressForm.js index d8fbc0290136..09e365b58c63 100644 --- a/src/pages/ReimbursementAccount/AddressForm.js +++ b/src/pages/ReimbursementAccount/AddressForm.js @@ -5,7 +5,7 @@ import TextInput from '../../components/TextInput'; import AddressSearch from '../../components/AddressSearch'; import styles from '../../styles/styles'; import CONST from '../../CONST'; -import StatePicker from '../../components/StatePicker'; +import StateSelector from '../../components/StateSelector'; const propTypes = { /** Translate key for Street name */ @@ -123,7 +123,7 @@ function AddressForm(props) { /> - - lodashGet(privatePersonalDetails, 'address') || {}, [privatePersonalDetails]); - const countryFromUrl = lodashGet(route, 'params.country'); + + const countryFromUrlTemp = lodashGet(route, 'params.country'); + // check if country is valid + const countryFromUrl = lodashGet(CONST.ALL_COUNTRIES, countryFromUrlTemp) ? countryFromUrlTemp : ''; + + const stateFromUrl = getStateFromRoute(route); const [currentCountry, setCurrentCountry] = useState(address.country); const zipSampleFormat = lodashGet(CONST.COUNTRY_ZIP_REGEX_DATA, [currentCountry, 'samples'], ''); const zipFormat = translate('common.zipCodeExampleFormat', {zipSampleFormat}); @@ -160,6 +166,13 @@ function AddressPage({privatePersonalDetails, route}) { handleAddressChange(countryFromUrl, 'country'); }, [countryFromUrl, handleAddressChange, currentCountry]); + useEffect(() => { + if (!stateFromUrl || stateFromUrl === state) { + return; + } + handleAddressChange(stateFromUrl, 'state'); + }, [state, handleAddressChange, stateFromUrl]); + return ( {isUSAForm ? ( - ) : ( diff --git a/src/pages/settings/Profile/PersonalDetails/StateSelectionPage.js b/src/pages/settings/Profile/PersonalDetails/StateSelectionPage.js new file mode 100644 index 000000000000..34364421b4ac --- /dev/null +++ b/src/pages/settings/Profile/PersonalDetails/StateSelectionPage.js @@ -0,0 +1,137 @@ +import React, {useState, useMemo, useCallback} from 'react'; +import PropTypes from 'prop-types'; +import _ from 'underscore'; +import {CONST as COMMON_CONST} from 'expensify-common/lib/CONST'; +import lodashGet from 'lodash/get'; +import Navigation from '../../../../libs/Navigation/Navigation'; +import ScreenWrapper from '../../../../components/ScreenWrapper'; +import HeaderWithBackButton from '../../../../components/HeaderWithBackButton'; +import SelectionList from '../../../../components/SelectionList'; +import searchCountryOptions from '../../../../libs/searchCountryOptions'; +import StringUtils from '../../../../libs/StringUtils'; +import useLocalize from '../../../../hooks/useLocalize'; +import styles from '../../../../styles/styles'; + +const propTypes = { + /** Route from navigation */ + route: PropTypes.shape({ + /** Params from the route */ + params: PropTypes.shape({ + /** Currently selected country */ + country: PropTypes.string, + + /** Route to navigate back after selecting a currency */ + backTo: PropTypes.string, + }), + }).isRequired, + + /** Navigation from react-navigation */ + navigation: PropTypes.shape({ + /** getState function retrieves the current navigation state from react-navigation's navigation property */ + getState: PropTypes.func.isRequired, + }).isRequired, +}; + +/** + * Appends or updates a query parameter in a given URL. + * + * @param {string} url - The original URL. + * @param {string} paramName - The name of the query parameter to append or update. + * @param {string} paramValue - The value of the query parameter to append or update. + * @returns {string} The updated URL with the appended or updated query parameter. + */ +function appendParam(url, paramName, paramValue) { + if (url.includes(`${paramName}=`)) { + // If parameter exists, replace it + const regex = new RegExp(`${paramName}=([^&]*)`); + return url.replace(regex, `${paramName}=${paramValue}`); + } + // If parameter doesn't exist, append it + const separator = url.includes('?') ? '&' : '?'; + return `${url}${separator}${paramName}=${paramValue}`; +} + +function StateSelectionPage({route, navigation}) { + const [searchValue, setSearchValue] = useState(''); + const {translate} = useLocalize(); + const currentState = lodashGet(route, 'params.state'); + const stateParamName = lodashGet(route, 'params.stateParamName') || 'state'; + const label = lodashGet(route, 'params.label'); + + const countryStates = useMemo( + () => + _.map(_.keys(COMMON_CONST.STATES), (state) => { + const stateName = translate(`allStates.${state}.stateName`); + const stateISO = translate(`allStates.${state}.stateISO`); + return { + value: stateISO, + keyForList: stateISO, + text: stateName, + isSelected: currentState === stateISO, + searchValue: StringUtils.sanitizeString(`${stateISO}${stateName}`), + }; + }), + [translate, currentState], + ); + + const searchResults = searchCountryOptions(searchValue, countryStates); + const headerMessage = searchValue.trim() && !searchResults.length ? translate('common.noResultsFound') : ''; + + const selectCountryState = useCallback( + (option) => { + const backTo = lodashGet(route, 'params.backTo', ''); + + // Check the navigation state and "backTo" parameter to decide navigation behavior + if (navigation.getState().routes.length === 1 && _.isEmpty(backTo)) { + // If there is only one route and "backTo" is empty, go back in navigation + Navigation.goBack(); + } else if (!_.isEmpty(backTo) && navigation.getState().routes.length === 1) { + // If "backTo" is not empty and there is only one route, go back to the specific route defined in "backTo" with a country parameter + Navigation.goBack(appendParam(backTo, stateParamName, option.value)); + } else { + // Otherwise, navigate to the specific route defined in "backTo" with a country parameter + Navigation.navigate(appendParam(backTo, stateParamName, option.value)); + } + }, + [route, navigation, stateParamName], + ); + + return ( + + { + const backTo = lodashGet(route, 'params.backTo', ''); + let backToRoute = ''; + + if (backTo) { + backToRoute = appendParam(backTo, stateParamName, currentState); + } + + Navigation.goBack(backToRoute); + }} + /> + + + + ); +} + +StateSelectionPage.displayName = 'StateSelectionPage'; +StateSelectionPage.propTypes = propTypes; + +export default StateSelectionPage; diff --git a/src/pages/settings/Wallet/AddDebitCardPage.js b/src/pages/settings/Wallet/AddDebitCardPage.js index e75c3b2c517e..201a4e763693 100644 --- a/src/pages/settings/Wallet/AddDebitCardPage.js +++ b/src/pages/settings/Wallet/AddDebitCardPage.js @@ -11,7 +11,6 @@ import useLocalize from '../../../hooks/useLocalize'; import * as PaymentMethods from '../../../libs/actions/PaymentMethods'; import * as ValidationUtils from '../../../libs/ValidationUtils'; import CheckboxWithLabel from '../../../components/CheckboxWithLabel'; -import StatePicker from '../../../components/StatePicker'; import TextInput from '../../../components/TextInput'; import CONST from '../../../CONST'; import ONYXKEYS from '../../../ONYXKEYS'; @@ -22,6 +21,8 @@ import ROUTES from '../../../ROUTES'; import usePrevious from '../../../hooks/usePrevious'; import NotFoundPage from '../../ErrorPage/NotFoundPage'; import Permissions from '../../../libs/Permissions'; +import StateSelector from '../../../components/StateSelector'; +import getStateFromRoute from '../../../libs/getStateFromRoute'; const propTypes = { /* Onyx Props */ @@ -31,6 +32,15 @@ const propTypes = { /** List of betas available to current user */ betas: PropTypes.arrayOf(PropTypes.string), + + /** Route from navigation */ + route: PropTypes.shape({ + /** Params from the route */ + params: PropTypes.shape({ + /** Currently selected country */ + country: PropTypes.string, + }), + }).isRequired, }; const defaultProps = { @@ -104,6 +114,8 @@ function DebitCardPage(props) { return ; } + const stateFromUrl = getStateFromRoute(props.route); + return ( nameOnCardRef.current && nameOnCardRef.current.focus()} @@ -182,7 +194,10 @@ function DebitCardPage(props) { containerStyles={[styles.mt4]} /> - + From 1284e59cf00621f0012d38187e0738b1e7c3e8dc Mon Sep 17 00:00:00 2001 From: Yogesh Bhatt Date: Sat, 7 Oct 2023 14:43:44 +0530 Subject: [PATCH 002/500] Incorporation state resets bugfix --- src/pages/ReimbursementAccount/CompanyStep.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/ReimbursementAccount/CompanyStep.js b/src/pages/ReimbursementAccount/CompanyStep.js index ef7674894445..4bac76dbd164 100644 --- a/src/pages/ReimbursementAccount/CompanyStep.js +++ b/src/pages/ReimbursementAccount/CompanyStep.js @@ -262,7 +262,7 @@ function CompanyStep({reimbursementAccount, route, reimbursementAccountDraft, ge label={translate('companyStep.incorporationState')} shouldSaveDraft paramName="incorporationState" - value={incorporationState} + {...(incorporationState ? {value: incorporationState} : {})} /> Date: Sat, 7 Oct 2023 15:29:42 +0530 Subject: [PATCH 003/500] Modify pages to accomodate for StateSelection --- src/pages/EnablePayments/AdditionalDetailsStep.js | 15 ++++++++++++++- src/pages/EnablePayments/EnablePaymentsPage.js | 4 ++-- src/pages/ReimbursementAccount/ACHContractStep.js | 13 +++++++++++++ src/pages/ReimbursementAccount/CompanyStep.js | 4 ++-- .../ReimbursementAccountPage.js | 2 ++ src/pages/ReimbursementAccount/RequestorStep.js | 15 ++++++++++++++- .../Profile/PersonalDetails/AddressPage.js | 3 +++ .../Profile/PersonalDetails/StateSelectionPage.js | 4 ++-- src/pages/settings/Wallet/AddDebitCardPage.js | 4 ++-- 9 files changed, 54 insertions(+), 10 deletions(-) diff --git a/src/pages/EnablePayments/AdditionalDetailsStep.js b/src/pages/EnablePayments/AdditionalDetailsStep.js index c304103f69c7..f521346b5fcf 100644 --- a/src/pages/EnablePayments/AdditionalDetailsStep.js +++ b/src/pages/EnablePayments/AdditionalDetailsStep.js @@ -24,6 +24,7 @@ import Form from '../../components/Form'; import withCurrentUserPersonalDetails, {withCurrentUserPersonalDetailsPropTypes, withCurrentUserPersonalDetailsDefaultProps} from '../../components/withCurrentUserPersonalDetails'; import * as PersonalDetails from '../../libs/actions/PersonalDetails'; import OfflineIndicator from '../../components/OfflineIndicator'; +import getStateFromRoute from '../../libs/getStateFromRoute'; const propTypes = { ...withLocalizePropTypes, @@ -55,6 +56,15 @@ const propTypes = { /** Error code to determine additional behavior */ errorCode: PropTypes.string, }), + + /** Route from navigation */ + route: PropTypes.shape({ + /** Params from the route */ + params: PropTypes.shape({ + /** Currently selected state */ + state: PropTypes.string, + }), + }).isRequired, }; const defaultProps = { @@ -79,7 +89,7 @@ const fieldNameTranslationKeys = { ssnFull9: 'common.ssnFull9', }; -function AdditionalDetailsStep({walletAdditionalDetails, translate, currentUserPersonalDetails}) { +function AdditionalDetailsStep({walletAdditionalDetails, translate, route, currentUserPersonalDetails}) { const minDate = moment().subtract(CONST.DATE_BIRTH.MAX_AGE, 'Y').toDate(); const maxDate = moment().subtract(CONST.DATE_BIRTH.MIN_AGE_FOR_PAYMENT, 'Y').toDate(); const shouldAskForFullSSN = walletAdditionalDetails.errorCode === CONST.WALLET.ERROR.SSN; @@ -163,6 +173,8 @@ function AdditionalDetailsStep({walletAdditionalDetails, translate, currentUserP ); } + const state = getStateFromRoute(route); + return ( <> @@ -209,6 +221,7 @@ function AdditionalDetailsStep({walletAdditionalDetails, translate, currentUserP state: 'addressState', zipCode: 'addressZipCode', }} + values={state ? {state} : {}} translate={translate} streetTranslationKey={fieldNameTranslationKeys.addressStreet} shouldSaveDraft diff --git a/src/pages/EnablePayments/EnablePaymentsPage.js b/src/pages/EnablePayments/EnablePaymentsPage.js index f7ef2a174208..8feb8342ac8e 100644 --- a/src/pages/EnablePayments/EnablePaymentsPage.js +++ b/src/pages/EnablePayments/EnablePaymentsPage.js @@ -29,7 +29,7 @@ const defaultProps = { userWallet: {}, }; -function EnablePaymentsPage({userWallet}) { +function EnablePaymentsPage({userWallet, route}) { const {translate} = useLocalize(); const {isOffline} = useNetwork(); @@ -72,7 +72,7 @@ function EnablePaymentsPage({userWallet}) { switch (currentStep) { case CONST.WALLET.STEP.ADDITIONAL_DETAILS: case CONST.WALLET.STEP.ADDITIONAL_DETAILS_KBA: - return ; + return ; case CONST.WALLET.STEP.ONFIDO: return ; case CONST.WALLET.STEP.TERMS: diff --git a/src/pages/ReimbursementAccount/ACHContractStep.js b/src/pages/ReimbursementAccount/ACHContractStep.js index 761be71d864a..7209d6250a94 100644 --- a/src/pages/ReimbursementAccount/ACHContractStep.js +++ b/src/pages/ReimbursementAccount/ACHContractStep.js @@ -19,12 +19,22 @@ import Form from '../../components/Form'; import * as FormActions from '../../libs/actions/FormActions'; import ScreenWrapper from '../../components/ScreenWrapper'; import StepPropTypes from './StepPropTypes'; +import getStateFromRoute from '../../libs/getStateFromRoute'; const propTypes = { ...StepPropTypes, /** Name of the company */ companyName: PropTypes.string.isRequired, + + /** Route from navigation */ + route: PropTypes.shape({ + /** Params from the route */ + params: PropTypes.shape({ + /** Currently selected state */ + state: PropTypes.string, + }), + }).isRequired, }; function ACHContractStep(props) { @@ -144,6 +154,8 @@ function ACHContractStep(props) { }); }; + const state = getStateFromRoute(props.route); + return ( { return errors; }; -function RequestorStep({reimbursementAccount, shouldShowOnfido, reimbursementAccountDraft, onBackButtonPress, getDefaultStateForField}) { +function RequestorStep({reimbursementAccount, route, shouldShowOnfido, reimbursementAccountDraft, onBackButtonPress, getDefaultStateForField}) { const {translate} = useLocalize(); const defaultValues = useMemo( @@ -118,6 +128,8 @@ function RequestorStep({reimbursementAccount, shouldShowOnfido, reimbursementAcc ); } + const state = getStateFromRoute(route); + return ( Date: Sun, 8 Oct 2023 08:24:10 +0530 Subject: [PATCH 004/500] Refactor to use useGeographicalStateFromRoute hook --- src/hooks/useGeographicalStateFromRoute.ts | 7 +++++++ src/libs/getStateFromRoute.ts | 3 ++- .../EnablePayments/AdditionalDetailsStep.js | 17 ++++------------- src/pages/EnablePayments/EnablePaymentsPage.js | 4 ++-- .../ReimbursementAccount/ACHContractStep.js | 13 ++----------- src/pages/ReimbursementAccount/CompanyStep.js | 18 +++++------------- .../ReimbursementAccountPage.js | 3 --- .../ReimbursementAccount/RequestorStep.js | 17 ++++------------- .../Profile/PersonalDetails/AddressPage.js | 7 ++----- src/pages/settings/Wallet/AddDebitCardPage.js | 15 +++------------ 10 files changed, 31 insertions(+), 73 deletions(-) create mode 100644 src/hooks/useGeographicalStateFromRoute.ts diff --git a/src/hooks/useGeographicalStateFromRoute.ts b/src/hooks/useGeographicalStateFromRoute.ts new file mode 100644 index 000000000000..33866e8b0cf1 --- /dev/null +++ b/src/hooks/useGeographicalStateFromRoute.ts @@ -0,0 +1,7 @@ +import {useRoute} from '@react-navigation/native'; +import getStateFromRoute from '../libs/getStateFromRoute'; + +export default function useGeographicalStateFromRoute(stateParamName = 'state') { + const route = useRoute(); + return getStateFromRoute(route, stateParamName); +} diff --git a/src/libs/getStateFromRoute.ts b/src/libs/getStateFromRoute.ts index ff01068eae3e..19d6de46014f 100644 --- a/src/libs/getStateFromRoute.ts +++ b/src/libs/getStateFromRoute.ts @@ -3,7 +3,8 @@ import lodashGet from 'lodash/get'; import {CONST as COMMON_CONST} from 'expensify-common/lib/CONST'; type RouteProps = { - params?: Record; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + params?: Record; }; export default function getStateFromRoute(route: RouteProps, stateParamName = 'state'): string { diff --git a/src/pages/EnablePayments/AdditionalDetailsStep.js b/src/pages/EnablePayments/AdditionalDetailsStep.js index 1d8691333069..5ab59899b139 100644 --- a/src/pages/EnablePayments/AdditionalDetailsStep.js +++ b/src/pages/EnablePayments/AdditionalDetailsStep.js @@ -24,7 +24,7 @@ import Form from '../../components/Form'; import withCurrentUserPersonalDetails, {withCurrentUserPersonalDetailsPropTypes, withCurrentUserPersonalDetailsDefaultProps} from '../../components/withCurrentUserPersonalDetails'; import * as PersonalDetails from '../../libs/actions/PersonalDetails'; import OfflineIndicator from '../../components/OfflineIndicator'; -import getStateFromRoute from '../../libs/getStateFromRoute'; +import useGeographicalStateFromRoute from '../../hooks/useGeographicalStateFromRoute'; const propTypes = { ...withLocalizePropTypes, @@ -56,15 +56,6 @@ const propTypes = { /** Error code to determine additional behavior */ errorCode: PropTypes.string, }), - - /** Route from navigation */ - route: PropTypes.shape({ - /** Params from the route */ - params: PropTypes.shape({ - /** Currently selected state */ - state: PropTypes.string, - }), - }).isRequired, }; const defaultProps = { @@ -89,7 +80,7 @@ const fieldNameTranslationKeys = { ssnFull9: 'common.ssnFull9', }; -function AdditionalDetailsStep({walletAdditionalDetails, route, translate, currentUserPersonalDetails}) { +function AdditionalDetailsStep({walletAdditionalDetails, translate, currentUserPersonalDetails}) { const currentDate = new Date(); const minDate = subYears(currentDate, CONST.DATE_BIRTH.MAX_AGE); const maxDate = subYears(currentDate, CONST.DATE_BIRTH.MIN_AGE_FOR_PAYMENT); @@ -155,6 +146,8 @@ function AdditionalDetailsStep({walletAdditionalDetails, route, translate, curre Wallet.updatePersonalDetails(personalDetails); }; + const state = useGeographicalStateFromRoute(); + if (!_.isEmpty(walletAdditionalDetails.questions)) { return ( diff --git a/src/pages/EnablePayments/EnablePaymentsPage.js b/src/pages/EnablePayments/EnablePaymentsPage.js index 8feb8342ac8e..f7ef2a174208 100644 --- a/src/pages/EnablePayments/EnablePaymentsPage.js +++ b/src/pages/EnablePayments/EnablePaymentsPage.js @@ -29,7 +29,7 @@ const defaultProps = { userWallet: {}, }; -function EnablePaymentsPage({userWallet, route}) { +function EnablePaymentsPage({userWallet}) { const {translate} = useLocalize(); const {isOffline} = useNetwork(); @@ -72,7 +72,7 @@ function EnablePaymentsPage({userWallet, route}) { switch (currentStep) { case CONST.WALLET.STEP.ADDITIONAL_DETAILS: case CONST.WALLET.STEP.ADDITIONAL_DETAILS_KBA: - return ; + return ; case CONST.WALLET.STEP.ONFIDO: return ; case CONST.WALLET.STEP.TERMS: diff --git a/src/pages/ReimbursementAccount/ACHContractStep.js b/src/pages/ReimbursementAccount/ACHContractStep.js index 7209d6250a94..b619abd68a33 100644 --- a/src/pages/ReimbursementAccount/ACHContractStep.js +++ b/src/pages/ReimbursementAccount/ACHContractStep.js @@ -19,22 +19,13 @@ import Form from '../../components/Form'; import * as FormActions from '../../libs/actions/FormActions'; import ScreenWrapper from '../../components/ScreenWrapper'; import StepPropTypes from './StepPropTypes'; -import getStateFromRoute from '../../libs/getStateFromRoute'; +import useGeographicalStateFromRoute from '../../hooks/useGeographicalStateFromRoute'; const propTypes = { ...StepPropTypes, /** Name of the company */ companyName: PropTypes.string.isRequired, - - /** Route from navigation */ - route: PropTypes.shape({ - /** Params from the route */ - params: PropTypes.shape({ - /** Currently selected state */ - state: PropTypes.string, - }), - }).isRequired, }; function ACHContractStep(props) { @@ -154,7 +145,7 @@ function ACHContractStep(props) { }); }; - const state = getStateFromRoute(props.route); + const state = useGeographicalStateFromRoute(); return ( diff --git a/src/pages/ReimbursementAccount/ReimbursementAccountPage.js b/src/pages/ReimbursementAccount/ReimbursementAccountPage.js index 2dc71f7b80fa..c6f53c2474f4 100644 --- a/src/pages/ReimbursementAccount/ReimbursementAccountPage.js +++ b/src/pages/ReimbursementAccount/ReimbursementAccountPage.js @@ -429,7 +429,6 @@ class ReimbursementAccountPage extends React.Component { if (currentStep === CONST.BANK_ACCOUNT.STEP.COMPANY) { return ( { return errors; }; -function RequestorStep({reimbursementAccount, route, shouldShowOnfido, reimbursementAccountDraft, onBackButtonPress, getDefaultStateForField}) { +function RequestorStep({reimbursementAccount, shouldShowOnfido, reimbursementAccountDraft, onBackButtonPress, getDefaultStateForField}) { const {translate} = useLocalize(); const defaultValues = useMemo( @@ -117,6 +108,8 @@ function RequestorStep({reimbursementAccount, route, shouldShowOnfido, reimburse ); + const state = useGeographicalStateFromRoute(); + if (shouldShowOnfido) { return ( ; } - const stateFromUrl = getStateFromRoute(props.route); - return ( nameOnCardRef.current && nameOnCardRef.current.focus()} From 0f83cff67014fc53ffa0d005247a0cc29f1a8a3f Mon Sep 17 00:00:00 2001 From: Yogesh Bhatt Date: Sat, 28 Oct 2023 02:35:21 +0530 Subject: [PATCH 005/500] move getStateFromRoute to useGeographicalStateFromRoute --- src/hooks/useGeographicalStateFromRoute.ts | 8 ++++++-- src/libs/getStateFromRoute.ts | 14 -------------- 2 files changed, 6 insertions(+), 16 deletions(-) delete mode 100644 src/libs/getStateFromRoute.ts diff --git a/src/hooks/useGeographicalStateFromRoute.ts b/src/hooks/useGeographicalStateFromRoute.ts index 33866e8b0cf1..3726499edf8f 100644 --- a/src/hooks/useGeographicalStateFromRoute.ts +++ b/src/hooks/useGeographicalStateFromRoute.ts @@ -1,7 +1,11 @@ import {useRoute} from '@react-navigation/native'; -import getStateFromRoute from '../libs/getStateFromRoute'; +// eslint-disable-next-line you-dont-need-lodash-underscore/get +import lodashGet from 'lodash/get'; +import {CONST as COMMON_CONST} from 'expensify-common/lib/CONST'; export default function useGeographicalStateFromRoute(stateParamName = 'state') { const route = useRoute(); - return getStateFromRoute(route, stateParamName); + const stateFromUrlTemp = lodashGet(route, `params.${stateParamName}`) as unknown as string; + // check if state is valid + return lodashGet(COMMON_CONST.STATES, stateFromUrlTemp) ? stateFromUrlTemp : ''; } diff --git a/src/libs/getStateFromRoute.ts b/src/libs/getStateFromRoute.ts deleted file mode 100644 index 19d6de46014f..000000000000 --- a/src/libs/getStateFromRoute.ts +++ /dev/null @@ -1,14 +0,0 @@ -// eslint-disable-next-line you-dont-need-lodash-underscore/get -import lodashGet from 'lodash/get'; -import {CONST as COMMON_CONST} from 'expensify-common/lib/CONST'; - -type RouteProps = { - // eslint-disable-next-line @typescript-eslint/no-explicit-any - params?: Record; -}; - -export default function getStateFromRoute(route: RouteProps, stateParamName = 'state'): string { - const stateFromUrlTemp = lodashGet(route, `params.${stateParamName}`) as unknown as string; - // check if state is valid - return lodashGet(COMMON_CONST.STATES, stateFromUrlTemp) ? stateFromUrlTemp : ''; -} From 0951d5d75b56a38009fe7bbd91d576bbd976cb25 Mon Sep 17 00:00:00 2001 From: Yogesh Bhatt Date: Sat, 28 Oct 2023 02:46:35 +0530 Subject: [PATCH 006/500] Add displayname to StateSelectorWithRef --- src/components/StateSelector.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/components/StateSelector.js b/src/components/StateSelector.js index c004fb8bf375..ba7705802982 100644 --- a/src/components/StateSelector.js +++ b/src/components/StateSelector.js @@ -78,10 +78,14 @@ StateSelector.propTypes = propTypes; StateSelector.defaultProps = defaultProps; StateSelector.displayName = 'StateSelector'; -export default React.forwardRef((props, ref) => ( +const StateSelectorWithRef = React.forwardRef((props, ref) => ( )); + +StateSelectorWithRef.displayName = 'StateSelectorWithRef'; + +export default StateSelectorWithRef; From 598c7c6c3378fe402e3b811a83f70b4cf16c38de Mon Sep 17 00:00:00 2001 From: Yogesh Bhatt Date: Sat, 28 Oct 2023 03:57:15 +0530 Subject: [PATCH 007/500] Fix AddressPage: State not changing on autocomplete --- src/pages/settings/Profile/PersonalDetails/AddressPage.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/pages/settings/Profile/PersonalDetails/AddressPage.js b/src/pages/settings/Profile/PersonalDetails/AddressPage.js index 1eec60d7d57f..112372abebcd 100644 --- a/src/pages/settings/Profile/PersonalDetails/AddressPage.js +++ b/src/pages/settings/Profile/PersonalDetails/AddressPage.js @@ -167,11 +167,11 @@ function AddressPage({privatePersonalDetails, route}) { }, [countryFromUrl, handleAddressChange]); useEffect(() => { - if (!stateFromUrl || stateFromUrl === state) { + if (!stateFromUrl) { return; } handleAddressChange(stateFromUrl, 'state'); - }, [state, handleAddressChange, stateFromUrl]); + }, [handleAddressChange, stateFromUrl]); return ( Date: Sat, 28 Oct 2023 04:56:23 +0530 Subject: [PATCH 008/500] Refactor StateSelector logic --- src/components/StateSelector.js | 30 +++++++++++++++---- .../EnablePayments/AdditionalDetailsStep.js | 4 --- .../ReimbursementAccount/ACHContractStep.js | 4 --- src/pages/ReimbursementAccount/CompanyStep.js | 3 +- .../ReimbursementAccount/RequestorStep.js | 4 --- src/pages/settings/Wallet/AddDebitCardPage.js | 8 +---- 6 files changed, 27 insertions(+), 26 deletions(-) diff --git a/src/components/StateSelector.js b/src/components/StateSelector.js index ba7705802982..52df1f61d7e4 100644 --- a/src/components/StateSelector.js +++ b/src/components/StateSelector.js @@ -1,4 +1,4 @@ -import React, {useEffect} from 'react'; +import React, {useEffect, useState} from 'react'; import PropTypes from 'prop-types'; import {View} from 'react-native'; import _ from 'underscore'; @@ -9,6 +9,7 @@ import ROUTES from '../ROUTES'; import useLocalize from '../hooks/useLocalize'; import MenuItemWithTopDescription from './MenuItemWithTopDescription'; import FormHelpMessage from './FormHelpMessage'; +import useGeographicalStateFromRoute from '../hooks/useGeographicalStateFromRoute'; const propTypes = { /** Form error text. e.g when no country is selected */ @@ -32,6 +33,9 @@ const propTypes = { /** Label to display on field */ label: PropTypes.string, + + /** whether to use state from url */ + useStateFromUrl: PropTypes.bool, }; const defaultProps = { @@ -40,20 +44,36 @@ const defaultProps = { forwardedRef: () => {}, label: undefined, paramName: 'state', + useStateFromUrl: true, }; -function StateSelector({errorText, value: stateCode, label, paramName, onInputChange, forwardedRef}) { +function StateSelector({errorText, useStateFromUrl, value: stateCode, label, paramName, onInputChange, forwardedRef}) { const {translate} = useLocalize(); - const title = stateCode && _.keys(COMMON_CONST.STATES).includes(stateCode) ? translate(`allStates.${stateCode}.stateName`) : ''; - const descStyle = title.length === 0 ? styles.textNormal : null; + const stateFromUrl = useGeographicalStateFromRoute(paramName); + + const [stateToDisplay, setStateToDisplay] = useState(''); + + useEffect(() => { + if (useStateFromUrl) { + // This will cause the form to revalidate and remove any error related to country name + stateFromUrl && onInputChange(stateFromUrl); + stateFromUrl && setStateToDisplay(stateFromUrl); + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [stateFromUrl, useStateFromUrl]); useEffect(() => { // This will cause the form to revalidate and remove any error related to country name - onInputChange(stateCode); + stateCode && onInputChange(stateCode); + stateCode && setStateToDisplay(stateCode); + // eslint-disable-next-line react-hooks/exhaustive-deps }, [stateCode]); + const title = stateToDisplay && _.keys(COMMON_CONST.STATES).includes(stateToDisplay) ? translate(`allStates.${stateToDisplay}.stateName`) : ''; + const descStyle = title.length === 0 ? styles.textNormal : null; + return ( diff --git a/src/pages/ReimbursementAccount/RequestorStep.js b/src/pages/ReimbursementAccount/RequestorStep.js index 875c884420ae..86f1d5077eca 100644 --- a/src/pages/ReimbursementAccount/RequestorStep.js +++ b/src/pages/ReimbursementAccount/RequestorStep.js @@ -17,7 +17,6 @@ import Form from '../../components/Form'; import ScreenWrapper from '../../components/ScreenWrapper'; import useLocalize from '../../hooks/useLocalize'; import {reimbursementAccountPropTypes} from './reimbursementAccountPropTypes'; -import useGeographicalStateFromRoute from '../../hooks/useGeographicalStateFromRoute'; const propTypes = { onBackButtonPress: PropTypes.func.isRequired, @@ -106,8 +105,6 @@ function RequestorStep({reimbursementAccount, shouldShowOnfido, onBackButtonPres ); - const state = useGeographicalStateFromRoute(); - if (shouldShowOnfido) { return ( ; } @@ -185,10 +182,7 @@ function DebitCardPage(props) { containerStyles={[styles.mt4]} /> - + Date: Sat, 28 Oct 2023 05:02:05 +0530 Subject: [PATCH 009/500] Refactor AddressPage --- src/pages/settings/Profile/PersonalDetails/AddressPage.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/pages/settings/Profile/PersonalDetails/AddressPage.js b/src/pages/settings/Profile/PersonalDetails/AddressPage.js index 112372abebcd..1e21a210dd40 100644 --- a/src/pages/settings/Profile/PersonalDetails/AddressPage.js +++ b/src/pages/settings/Profile/PersonalDetails/AddressPage.js @@ -232,6 +232,7 @@ function AddressPage({privatePersonalDetails, route}) { {isUSAForm ? ( From 8a293b5ceeaec50be8bcd7d262a097b46b0b783f Mon Sep 17 00:00:00 2001 From: Yogesh Bhatt Date: Sat, 28 Oct 2023 05:34:30 +0530 Subject: [PATCH 010/500] linting fixes StateSelector --- src/components/StateSelector.js | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/src/components/StateSelector.js b/src/components/StateSelector.js index 52df1f61d7e4..517142527552 100644 --- a/src/components/StateSelector.js +++ b/src/components/StateSelector.js @@ -55,18 +55,21 @@ function StateSelector({errorText, useStateFromUrl, value: stateCode, label, par const [stateToDisplay, setStateToDisplay] = useState(''); useEffect(() => { - if (useStateFromUrl) { - // This will cause the form to revalidate and remove any error related to country name - stateFromUrl && onInputChange(stateFromUrl); - stateFromUrl && setStateToDisplay(stateFromUrl); - } + if (!useStateFromUrl || !stateFromUrl) return; + + // This will cause the form to revalidate and remove any error related to country name + onInputChange(stateFromUrl); + setStateToDisplay(stateFromUrl); + // eslint-disable-next-line react-hooks/exhaustive-deps }, [stateFromUrl, useStateFromUrl]); useEffect(() => { + if (!stateCode) return; + // This will cause the form to revalidate and remove any error related to country name - stateCode && onInputChange(stateCode); - stateCode && setStateToDisplay(stateCode); + onInputChange(stateCode); + setStateToDisplay(stateCode); // eslint-disable-next-line react-hooks/exhaustive-deps }, [stateCode]); From c946cbb839e04468d5c2acedaac81905bb9233a9 Mon Sep 17 00:00:00 2001 From: Yogesh Bhatt Date: Sat, 28 Oct 2023 09:07:58 +0530 Subject: [PATCH 011/500] linting --- src/components/StateSelector.js | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/components/StateSelector.js b/src/components/StateSelector.js index 517142527552..aaa905d8940f 100644 --- a/src/components/StateSelector.js +++ b/src/components/StateSelector.js @@ -55,7 +55,9 @@ function StateSelector({errorText, useStateFromUrl, value: stateCode, label, par const [stateToDisplay, setStateToDisplay] = useState(''); useEffect(() => { - if (!useStateFromUrl || !stateFromUrl) return; + if (!useStateFromUrl || !stateFromUrl) { + return; + } // This will cause the form to revalidate and remove any error related to country name onInputChange(stateFromUrl); @@ -65,7 +67,9 @@ function StateSelector({errorText, useStateFromUrl, value: stateCode, label, par }, [stateFromUrl, useStateFromUrl]); useEffect(() => { - if (!stateCode) return; + if (!stateCode) { + return; + } // This will cause the form to revalidate and remove any error related to country name onInputChange(stateCode); From 144eea9e55e6bc9190ab71da057a0d20e0b04e39 Mon Sep 17 00:00:00 2001 From: Yogesh Bhatt Date: Sun, 29 Oct 2023 10:28:58 +0530 Subject: [PATCH 012/500] StateSelector and related files refactoring --- src/components/StateSelector.js | 2 +- src/pages/ReimbursementAccount/CompanyStep.js | 7 +------ 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/src/components/StateSelector.js b/src/components/StateSelector.js index aaa905d8940f..c1ebdaefc493 100644 --- a/src/components/StateSelector.js +++ b/src/components/StateSelector.js @@ -34,7 +34,7 @@ const propTypes = { /** Label to display on field */ label: PropTypes.string, - /** whether to use state from url */ + /** whether to use state from url, for cases when url value is passed from parent */ useStateFromUrl: PropTypes.bool, }; diff --git a/src/pages/ReimbursementAccount/CompanyStep.js b/src/pages/ReimbursementAccount/CompanyStep.js index 7e86ebf139b2..03a222abe3d7 100644 --- a/src/pages/ReimbursementAccount/CompanyStep.js +++ b/src/pages/ReimbursementAccount/CompanyStep.js @@ -25,7 +25,6 @@ import Form from '../../components/Form'; import ScreenWrapper from '../../components/ScreenWrapper'; import StepPropTypes from './StepPropTypes'; import StateSelector from '../../components/StateSelector'; -import useGeographicalStateFromRoute from '../../hooks/useGeographicalStateFromRoute'; const propTypes = { ...StepPropTypes, @@ -140,8 +139,6 @@ function CompanyStep({reimbursementAccount, reimbursementAccountDraft, getDefaul const shouldDisableCompanyName = Boolean(bankAccountID && getDefaultStateForField('companyName')); const shouldDisableCompanyTaxID = Boolean(bankAccountID && getDefaultStateForField('companyTaxID')); - const incorporationState = useGeographicalStateFromRoute('incorporationState') || getDefaultStateForField('incorporationState'); - return ( Date: Sun, 29 Oct 2023 11:18:33 +0530 Subject: [PATCH 013/500] Fix incorrect merge --- .../Profile/PersonalDetails/AddressPage.js | 7 +------ .../PersonalDetails/StateSelectionPage.js | 16 ++++++++-------- 2 files changed, 9 insertions(+), 14 deletions(-) diff --git a/src/pages/settings/Profile/PersonalDetails/AddressPage.js b/src/pages/settings/Profile/PersonalDetails/AddressPage.js index d3b9bdfba141..9c102e566b06 100644 --- a/src/pages/settings/Profile/PersonalDetails/AddressPage.js +++ b/src/pages/settings/Profile/PersonalDetails/AddressPage.js @@ -4,12 +4,6 @@ import PropTypes from 'prop-types'; import React, {useCallback, useEffect, useMemo, useState} from 'react'; import {View} from 'react-native'; import {withOnyx} from 'react-native-onyx'; -import {CONST as COMMON_CONST} from 'expensify-common/lib/CONST'; -import lodashGet from 'lodash/get'; -import PropTypes from 'prop-types'; -import React, {useCallback, useEffect, useMemo, useState} from 'react'; -import {View} from 'react-native'; -import {withOnyx} from 'react-native-onyx'; import _ from 'underscore'; import AddressSearch from '@components/AddressSearch'; import CountrySelector from '@components/CountrySelector'; @@ -28,6 +22,7 @@ import * as PersonalDetails from '@userActions/PersonalDetails'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; +import useGeographicalStateFromRoute from '@hooks/useGeographicalStateFromRoute'; const propTypes = { /* Onyx Props */ diff --git a/src/pages/settings/Profile/PersonalDetails/StateSelectionPage.js b/src/pages/settings/Profile/PersonalDetails/StateSelectionPage.js index ea443edc581d..37acb6e38397 100644 --- a/src/pages/settings/Profile/PersonalDetails/StateSelectionPage.js +++ b/src/pages/settings/Profile/PersonalDetails/StateSelectionPage.js @@ -3,14 +3,14 @@ import PropTypes from 'prop-types'; import _ from 'underscore'; import {CONST as COMMON_CONST} from 'expensify-common/lib/CONST'; import lodashGet from 'lodash/get'; -import Navigation from '../../../../libs/Navigation/Navigation'; -import ScreenWrapper from '../../../../components/ScreenWrapper'; -import HeaderWithBackButton from '../../../../components/HeaderWithBackButton'; -import SelectionList from '../../../../components/SelectionList'; -import searchCountryOptions from '../../../../libs/searchCountryOptions'; -import StringUtils from '../../../../libs/StringUtils'; -import useLocalize from '../../../../hooks/useLocalize'; -import styles from '../../../../styles/styles'; +import Navigation from '@libs/Navigation/Navigation'; +import ScreenWrapper from '@components/ScreenWrapper'; +import HeaderWithBackButton from '@components/HeaderWithBackButton'; +import SelectionList from '@components/SelectionList'; +import searchCountryOptions from '@libs/searchCountryOptions'; +import StringUtils from '@libs/StringUtils'; +import useLocalize from '@hooks/useLocalize'; +import styles from '@styles/styles'; const propTypes = { /** Route from navigation */ From 9fb4f72463d13aca2f897cbc0e17a4aa399c4b6a Mon Sep 17 00:00:00 2001 From: Yogesh Bhatt Date: Sun, 29 Oct 2023 11:22:35 +0530 Subject: [PATCH 014/500] Reverted scripts/shellCheck.sh to state at 33f15e8d85b3366b1a09bca914f61d5caa4a2fb5 --- scripts/shellCheck.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/shellCheck.sh b/scripts/shellCheck.sh index 18206a419509..d148958900d4 100755 --- a/scripts/shellCheck.sh +++ b/scripts/shellCheck.sh @@ -45,4 +45,4 @@ if [ $EXIT_CODE == 0 ]; then success "ShellCheck passed for all files!" fi -exit $EXIT_CODE \ No newline at end of file +exit $EXIT_CODE From d6a643c7b7f4db06243c420606afae458fb9ade8 Mon Sep 17 00:00:00 2001 From: Yogesh Bhatt Date: Sun, 29 Oct 2023 11:27:55 +0530 Subject: [PATCH 015/500] Linting --- src/components/StateSelector.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/StateSelector.js b/src/components/StateSelector.js index b5b1c3903c8c..c27c0e57e4c9 100644 --- a/src/components/StateSelector.js +++ b/src/components/StateSelector.js @@ -7,9 +7,9 @@ import styles from '@styles/styles'; import Navigation from '@libs/Navigation/Navigation'; import ROUTES from '@src/ROUTES'; import useLocalize from '@hooks/useLocalize'; +import useGeographicalStateFromRoute from '@hooks/useGeographicalStateFromRoute'; import MenuItemWithTopDescription from './MenuItemWithTopDescription'; import FormHelpMessage from './FormHelpMessage'; -import useGeographicalStateFromRoute from '@hooks/useGeographicalStateFromRoute'; const propTypes = { /** Form error text. e.g when no country is selected */ From 9bb88ee2129ebebdc9274598a72ab69ba84ce3c4 Mon Sep 17 00:00:00 2001 From: Yogesh Bhatt <65986357+ygshbht@users.noreply.github.com> Date: Tue, 31 Oct 2023 21:29:36 +0530 Subject: [PATCH 016/500] Comment formatting src/hooks/useGeographicalStateFromRoute.ts Co-authored-by: Rajat Parashar --- src/hooks/useGeographicalStateFromRoute.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/hooks/useGeographicalStateFromRoute.ts b/src/hooks/useGeographicalStateFromRoute.ts index 3726499edf8f..d86b9df6ce24 100644 --- a/src/hooks/useGeographicalStateFromRoute.ts +++ b/src/hooks/useGeographicalStateFromRoute.ts @@ -6,6 +6,6 @@ import {CONST as COMMON_CONST} from 'expensify-common/lib/CONST'; export default function useGeographicalStateFromRoute(stateParamName = 'state') { const route = useRoute(); const stateFromUrlTemp = lodashGet(route, `params.${stateParamName}`) as unknown as string; - // check if state is valid + // Check if state is valid return lodashGet(COMMON_CONST.STATES, stateFromUrlTemp) ? stateFromUrlTemp : ''; } From 5e3844785f3ef11d3bdb411078d2482490aa35bc Mon Sep 17 00:00:00 2001 From: Yogesh Bhatt Date: Tue, 31 Oct 2023 22:00:09 +0530 Subject: [PATCH 017/500] Rename useStateFomrUrl to shouldUseStateFromUrl --- src/components/StateSelector.js | 10 +++++----- .../settings/Profile/PersonalDetails/AddressPage.js | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/components/StateSelector.js b/src/components/StateSelector.js index c27c0e57e4c9..fb4046ad848b 100644 --- a/src/components/StateSelector.js +++ b/src/components/StateSelector.js @@ -35,7 +35,7 @@ const propTypes = { label: PropTypes.string, /** whether to use state from url, for cases when url value is passed from parent */ - useStateFromUrl: PropTypes.bool, + shouldUseStateFromUrl: PropTypes.bool, }; const defaultProps = { @@ -44,10 +44,10 @@ const defaultProps = { forwardedRef: () => {}, label: undefined, paramName: 'state', - useStateFromUrl: true, + shouldUseStateFromUrl: true, }; -function StateSelector({errorText, useStateFromUrl, value: stateCode, label, paramName, onInputChange, forwardedRef}) { +function StateSelector({errorText, shouldUseStateFromUrl, value: stateCode, label, paramName, onInputChange, forwardedRef}) { const {translate} = useLocalize(); const stateFromUrl = useGeographicalStateFromRoute(paramName); @@ -55,7 +55,7 @@ function StateSelector({errorText, useStateFromUrl, value: stateCode, label, par const [stateToDisplay, setStateToDisplay] = useState(''); useEffect(() => { - if (!useStateFromUrl || !stateFromUrl) { + if (!shouldUseStateFromUrl || !stateFromUrl) { return; } @@ -64,7 +64,7 @@ function StateSelector({errorText, useStateFromUrl, value: stateCode, label, par setStateToDisplay(stateFromUrl); // eslint-disable-next-line react-hooks/exhaustive-deps - }, [stateFromUrl, useStateFromUrl]); + }, [stateFromUrl, shouldUseStateFromUrl]); useEffect(() => { if (!stateCode) { diff --git a/src/pages/settings/Profile/PersonalDetails/AddressPage.js b/src/pages/settings/Profile/PersonalDetails/AddressPage.js index 9c102e566b06..a9b64b2420ed 100644 --- a/src/pages/settings/Profile/PersonalDetails/AddressPage.js +++ b/src/pages/settings/Profile/PersonalDetails/AddressPage.js @@ -232,7 +232,7 @@ function AddressPage({privatePersonalDetails, route}) { {isUSAForm ? ( From 470ec03d5e71e0580643a852a14fe2ee61e4f241 Mon Sep 17 00:00:00 2001 From: Yogesh Bhatt Date: Tue, 31 Oct 2023 22:07:00 +0530 Subject: [PATCH 018/500] forwardedRef type correction StateSelector --- src/components/StateSelector.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/components/StateSelector.js b/src/components/StateSelector.js index fb4046ad848b..59f974251143 100644 --- a/src/components/StateSelector.js +++ b/src/components/StateSelector.js @@ -10,6 +10,7 @@ import useLocalize from '@hooks/useLocalize'; import useGeographicalStateFromRoute from '@hooks/useGeographicalStateFromRoute'; import MenuItemWithTopDescription from './MenuItemWithTopDescription'; import FormHelpMessage from './FormHelpMessage'; +import refPropTypes from './refPropTypes'; const propTypes = { /** Form error text. e.g when no country is selected */ @@ -26,7 +27,7 @@ const propTypes = { inputID: PropTypes.string.isRequired, /** React ref being forwarded to the MenuItemWithTopDescription */ - forwardedRef: PropTypes.func, + forwardedRef: refPropTypes, /** Label of state in the url */ paramName: PropTypes.string, From 8f28580d6dca027d9698a8510a0ebaf28a6ba38d Mon Sep 17 00:00:00 2001 From: Yogesh Bhatt Date: Tue, 31 Oct 2023 22:08:46 +0530 Subject: [PATCH 019/500] StateSelector: rename paramName to queryParam --- src/components/StateSelector.js | 10 +++++----- src/pages/ReimbursementAccount/CompanyStep.js | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/components/StateSelector.js b/src/components/StateSelector.js index 59f974251143..13202f9d79e1 100644 --- a/src/components/StateSelector.js +++ b/src/components/StateSelector.js @@ -30,7 +30,7 @@ const propTypes = { forwardedRef: refPropTypes, /** Label of state in the url */ - paramName: PropTypes.string, + queryParam: PropTypes.string, /** Label to display on field */ label: PropTypes.string, @@ -44,14 +44,14 @@ const defaultProps = { value: undefined, forwardedRef: () => {}, label: undefined, - paramName: 'state', + queryParam: 'state', shouldUseStateFromUrl: true, }; -function StateSelector({errorText, shouldUseStateFromUrl, value: stateCode, label, paramName, onInputChange, forwardedRef}) { +function StateSelector({errorText, shouldUseStateFromUrl, value: stateCode, label, queryParam, onInputChange, forwardedRef}) { const {translate} = useLocalize(); - const stateFromUrl = useGeographicalStateFromRoute(paramName); + const stateFromUrl = useGeographicalStateFromRoute(queryParam); const [stateToDisplay, setStateToDisplay] = useState(''); @@ -92,7 +92,7 @@ function StateSelector({errorText, shouldUseStateFromUrl, value: stateCode, labe description={label || translate('common.state')} onPress={() => { const activeRoute = Navigation.getActiveRoute(); - Navigation.navigate(ROUTES.SETTINGS_PERSONAL_DETAILS_ADDRESS_STATE.getRoute(stateCode, activeRoute, label, paramName)); + Navigation.navigate(ROUTES.SETTINGS_PERSONAL_DETAILS_ADDRESS_STATE.getRoute(stateCode, activeRoute, label, queryParam)); }} /> diff --git a/src/pages/ReimbursementAccount/CompanyStep.js b/src/pages/ReimbursementAccount/CompanyStep.js index fa806521e5d0..04010f1db0c1 100644 --- a/src/pages/ReimbursementAccount/CompanyStep.js +++ b/src/pages/ReimbursementAccount/CompanyStep.js @@ -248,7 +248,7 @@ function CompanyStep({reimbursementAccount, reimbursementAccountDraft, getDefaul label={translate('companyStep.incorporationState')} defaultValue={getDefaultStateForField('incorporationState')} shouldSaveDraft - paramName="incorporationState" + queryParam="incorporationState" /> Date: Wed, 1 Nov 2023 08:24:52 +0530 Subject: [PATCH 020/500] Remove lodashGet from useGeographicalStateFromRoute --- src/hooks/useGeographicalStateFromRoute.ts | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/src/hooks/useGeographicalStateFromRoute.ts b/src/hooks/useGeographicalStateFromRoute.ts index d86b9df6ce24..112e0e8db69c 100644 --- a/src/hooks/useGeographicalStateFromRoute.ts +++ b/src/hooks/useGeographicalStateFromRoute.ts @@ -1,11 +1,14 @@ -import {useRoute} from '@react-navigation/native'; -// eslint-disable-next-line you-dont-need-lodash-underscore/get -import lodashGet from 'lodash/get'; +import {ParamListBase, RouteProp, useRoute} from '@react-navigation/native'; import {CONST as COMMON_CONST} from 'expensify-common/lib/CONST'; +type CustomParamList = ParamListBase & Record>; + export default function useGeographicalStateFromRoute(stateParamName = 'state') { - const route = useRoute(); - const stateFromUrlTemp = lodashGet(route, `params.${stateParamName}`) as unknown as string; - // Check if state is valid - return lodashGet(COMMON_CONST.STATES, stateFromUrlTemp) ? stateFromUrlTemp : ''; + const route = useRoute>(); + const stateFromUrlTemp = route.params?.[stateParamName] as string | undefined; + + if (!stateFromUrlTemp) { + return ''; + } + return COMMON_CONST.STATES[stateFromUrlTemp as keyof typeof COMMON_CONST.STATES] ? stateFromUrlTemp : ''; } From 428e48c177adcf9a77f0067114781307be11ea62 Mon Sep 17 00:00:00 2001 From: Yogesh Bhatt Date: Wed, 1 Nov 2023 08:37:52 +0530 Subject: [PATCH 021/500] StateSelectionPage appendParam restructuring --- src/libs/Url.ts | 16 +++++++++- .../PersonalDetails/StateSelectionPage.js | 32 ++++--------------- 2 files changed, 22 insertions(+), 26 deletions(-) diff --git a/src/libs/Url.ts b/src/libs/Url.ts index a21f007e8468..7e028aded60c 100644 --- a/src/libs/Url.ts +++ b/src/libs/Url.ts @@ -41,4 +41,18 @@ function hasSameExpensifyOrigin(url1: string, url2: string): boolean { } } -export {addTrailingForwardSlash, hasSameExpensifyOrigin, getPathFromURL}; +/** + * Appends or updates a query parameter in a given URL. + */ +function appendParam(url: string, paramName: string, paramValue: string) { + if (url.includes(`${paramName}=`)) { + // If parameter exists, replace it + const regex = new RegExp(`${paramName}=([^&]*)`); + return url.replace(regex, `${paramName}=${paramValue}`); + } + // If parameter doesn't exist, append it + const separator = url.includes('?') ? '&' : '?'; + return `${url}${separator}${paramName}=${paramValue}`; +} + +export {addTrailingForwardSlash, hasSameExpensifyOrigin, getPathFromURL, appendParam}; diff --git a/src/pages/settings/Profile/PersonalDetails/StateSelectionPage.js b/src/pages/settings/Profile/PersonalDetails/StateSelectionPage.js index 37acb6e38397..a506d7e61495 100644 --- a/src/pages/settings/Profile/PersonalDetails/StateSelectionPage.js +++ b/src/pages/settings/Profile/PersonalDetails/StateSelectionPage.js @@ -1,15 +1,16 @@ -import React, {useState, useMemo, useCallback} from 'react'; -import PropTypes from 'prop-types'; -import _ from 'underscore'; import {CONST as COMMON_CONST} from 'expensify-common/lib/CONST'; import lodashGet from 'lodash/get'; -import Navigation from '@libs/Navigation/Navigation'; -import ScreenWrapper from '@components/ScreenWrapper'; +import PropTypes from 'prop-types'; +import React, {useCallback, useMemo, useState} from 'react'; +import _ from 'underscore'; import HeaderWithBackButton from '@components/HeaderWithBackButton'; +import ScreenWrapper from '@components/ScreenWrapper'; import SelectionList from '@components/SelectionList'; +import useLocalize from '@hooks/useLocalize'; +import Navigation from '@libs/Navigation/Navigation'; import searchCountryOptions from '@libs/searchCountryOptions'; import StringUtils from '@libs/StringUtils'; -import useLocalize from '@hooks/useLocalize'; +import {appendParam} from '@libs/Url'; import styles from '@styles/styles'; const propTypes = { @@ -32,25 +33,6 @@ const propTypes = { }).isRequired, }; -/** - * Appends or updates a query parameter in a given URL. - * - * @param {string} url - The original URL. - * @param {string} paramName - The name of the query parameter to append or update. - * @param {string} paramValue - The value of the query parameter to append or update. - * @returns {string} The updated URL with the appended or updated query parameter. - */ -function appendParam(url, paramName, paramValue) { - if (url.includes(`${paramName}=`)) { - // If parameter exists, replace it - const regex = new RegExp(`${paramName}=([^&]*)`); - return url.replace(regex, `${paramName}=${paramValue}`); - } - // If parameter doesn't exist, append it - const separator = url.includes('?') ? '&' : '?'; - return `${url}${separator}${paramName}=${paramValue}`; -} - function StateSelectionPage({route, navigation}) { const [searchValue, setSearchValue] = useState(''); const {translate} = useLocalize(); From 7cd8b6fb2c30a45999f521355e97fdadfe6f4b2f Mon Sep 17 00:00:00 2001 From: Yogesh Bhatt Date: Wed, 1 Nov 2023 08:43:07 +0530 Subject: [PATCH 022/500] Prettier formatting --- src/components/StateSelector.js | 12 ++++++------ .../settings/Profile/PersonalDetails/AddressPage.js | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/components/StateSelector.js b/src/components/StateSelector.js index 13202f9d79e1..121037d91370 100644 --- a/src/components/StateSelector.js +++ b/src/components/StateSelector.js @@ -1,15 +1,15 @@ -import React, {useEffect, useState} from 'react'; +import {CONST as COMMON_CONST} from 'expensify-common/lib/CONST'; import PropTypes from 'prop-types'; +import React, {useEffect, useState} from 'react'; import {View} from 'react-native'; import _ from 'underscore'; -import {CONST as COMMON_CONST} from 'expensify-common/lib/CONST'; -import styles from '@styles/styles'; +import useGeographicalStateFromRoute from '@hooks/useGeographicalStateFromRoute'; +import useLocalize from '@hooks/useLocalize'; import Navigation from '@libs/Navigation/Navigation'; +import styles from '@styles/styles'; import ROUTES from '@src/ROUTES'; -import useLocalize from '@hooks/useLocalize'; -import useGeographicalStateFromRoute from '@hooks/useGeographicalStateFromRoute'; -import MenuItemWithTopDescription from './MenuItemWithTopDescription'; import FormHelpMessage from './FormHelpMessage'; +import MenuItemWithTopDescription from './MenuItemWithTopDescription'; import refPropTypes from './refPropTypes'; const propTypes = { diff --git a/src/pages/settings/Profile/PersonalDetails/AddressPage.js b/src/pages/settings/Profile/PersonalDetails/AddressPage.js index a9b64b2420ed..752c3d2984a2 100644 --- a/src/pages/settings/Profile/PersonalDetails/AddressPage.js +++ b/src/pages/settings/Profile/PersonalDetails/AddressPage.js @@ -13,6 +13,7 @@ import HeaderWithBackButton from '@components/HeaderWithBackButton'; import ScreenWrapper from '@components/ScreenWrapper'; import StateSelector from '@components/StateSelector'; import TextInput from '@components/TextInput'; +import useGeographicalStateFromRoute from '@hooks/useGeographicalStateFromRoute'; import useLocalize from '@hooks/useLocalize'; import usePrivatePersonalDetails from '@hooks/usePrivatePersonalDetails'; import Navigation from '@libs/Navigation/Navigation'; @@ -22,7 +23,6 @@ import * as PersonalDetails from '@userActions/PersonalDetails'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; -import useGeographicalStateFromRoute from '@hooks/useGeographicalStateFromRoute'; const propTypes = { /* Onyx Props */ From d60ca6ccd76ca612d708d368b206364edb540400 Mon Sep 17 00:00:00 2001 From: Etotaziba Olei Date: Thu, 4 Jan 2024 01:06:47 +0100 Subject: [PATCH 023/500] remove unused split transaction draft --- src/components/MoneyRequestConfirmationList.js | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/components/MoneyRequestConfirmationList.js b/src/components/MoneyRequestConfirmationList.js index 13dce9337673..5807eb66f143 100755 --- a/src/components/MoneyRequestConfirmationList.js +++ b/src/components/MoneyRequestConfirmationList.js @@ -825,9 +825,6 @@ export default compose( key: ({policyID}) => `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, selector: DistanceRequestUtils.getDefaultMileageRate, }, - splitTransactionDraft: { - key: ({transactionID}) => `${ONYXKEYS.COLLECTION.SPLIT_TRANSACTION_DRAFT}${transactionID}`, - }, policy: { key: ({policyID}) => `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, }, From d849ea347c5fa1c488212e54e9f68411893d76c1 Mon Sep 17 00:00:00 2001 From: Etotaziba Olei Date: Thu, 4 Jan 2024 01:08:55 +0100 Subject: [PATCH 024/500] use StepScreenWrapper and backTo for back navigation --- .../step/IOURequestStepTaxAmountPage.js | 54 +++++++------------ 1 file changed, 18 insertions(+), 36 deletions(-) diff --git a/src/pages/iou/request/step/IOURequestStepTaxAmountPage.js b/src/pages/iou/request/step/IOURequestStepTaxAmountPage.js index 8ee3abb56d00..89f592b89e38 100644 --- a/src/pages/iou/request/step/IOURequestStepTaxAmountPage.js +++ b/src/pages/iou/request/step/IOURequestStepTaxAmountPage.js @@ -1,17 +1,11 @@ import {useFocusEffect} from '@react-navigation/native'; import React, {useCallback, useRef} from 'react'; -import {View} from 'react-native'; import {withOnyx} from 'react-native-onyx'; -import FullPageNotFoundView from '@components/BlockingViews/FullPageNotFoundView'; -import HeaderWithBackButton from '@components/HeaderWithBackButton'; -import ScreenWrapper from '@components/ScreenWrapper'; import taxPropTypes from '@components/taxPropTypes'; import transactionPropTypes from '@components/transactionPropTypes'; import useLocalize from '@hooks/useLocalize'; -import useThemeStyles from '@hooks/useThemeStyles'; import compose from '@libs/compose'; import * as CurrencyUtils from '@libs/CurrencyUtils'; -import * as IOUUtils from '@libs/IOUUtils'; import Navigation from '@libs/Navigation/Navigation'; import * as TransactionUtils from '@libs/TransactionUtils'; import MoneyRequestAmountForm from '@pages/iou/steps/MoneyRequestAmountForm'; @@ -21,6 +15,7 @@ import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; import IOURequestStepRoutePropTypes from './IOURequestStepRoutePropTypes'; +import StepScreenWrapper from './StepScreenWrapper'; import withFullTransactionOrNotFound from './withFullTransactionOrNotFound'; import withWritableReportOrNotFound from './withWritableReportOrNotFound'; @@ -61,7 +56,6 @@ function IOURequestStepTaxAmountPage({ policyTaxRates, }) { const {translate} = useLocalize(); - const styles = useThemeStyles(); const textInput = useRef(null); const isEditing = Navigation.getActiveRoute().includes('taxAmount'); @@ -82,7 +76,7 @@ function IOURequestStepTaxAmountPage({ ); const navigateBack = () => { - Navigation.goBack(isEditing ? ROUTES.MONEY_REQUEST_CONFIRMATION.getRoute(iouType, reportID) : ROUTES.HOME); + Navigation.goBack(backTo || ROUTES.HOME); }; const navigateToCurrencySelectionPage = () => { @@ -117,37 +111,25 @@ function IOURequestStepTaxAmountPage({ Navigation.navigate(ROUTES.MONEY_REQUEST_STEP_PARTICIPANTS.getRoute(iouType, transactionID, reportID)); }; - const content = ( - (textInput.current = e)} - onCurrencyButtonPress={navigateToCurrencySelectionPage} - onSubmitButtonPress={updateTaxAmount} - /> - ); - return ( - - {({safeAreaPaddingBottomStyle}) => ( - - - - {content} - - - )} - + (textInput.current = e)} + onCurrencyButtonPress={navigateToCurrencySelectionPage} + onSubmitButtonPress={updateTaxAmount} + /> + ); } From f558497bacc30d13ae591bbc1667e6809b5b5419 Mon Sep 17 00:00:00 2001 From: Etotaziba Olei Date: Thu, 4 Jan 2024 01:09:10 +0100 Subject: [PATCH 025/500] use StepScreenWrapper and backTo for back navigation --- .../request/step/IOURequestStepTaxRatePage.js | 54 +++++++------------ 1 file changed, 18 insertions(+), 36 deletions(-) diff --git a/src/pages/iou/request/step/IOURequestStepTaxRatePage.js b/src/pages/iou/request/step/IOURequestStepTaxRatePage.js index bae08cd8cb62..ba3c9c7a321c 100644 --- a/src/pages/iou/request/step/IOURequestStepTaxRatePage.js +++ b/src/pages/iou/request/step/IOURequestStepTaxRatePage.js @@ -1,9 +1,6 @@ -import PropTypes from 'prop-types'; import React from 'react'; import {withOnyx} from 'react-native-onyx'; import _ from 'underscore'; -import HeaderWithBackButton from '@components/HeaderWithBackButton'; -import ScreenWrapper from '@components/ScreenWrapper'; import TaxPicker from '@components/TaxPicker'; import taxPropTypes from '@components/taxPropTypes'; import transactionPropTypes from '@components/transactionPropTypes'; @@ -16,21 +13,14 @@ import * as TransactionUtils from '@libs/TransactionUtils'; import * as IOU from '@userActions/IOU'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; +import IOURequestStepRoutePropTypes from './IOURequestStepRoutePropTypes'; +import StepScreenWrapper from './StepScreenWrapper'; import withFullTransactionOrNotFound from './withFullTransactionOrNotFound'; import withWritableReportOrNotFound from './withWritableReportOrNotFound'; const propTypes = { - /** Route from navigation */ - route: PropTypes.shape({ - /** Params from the route */ - params: PropTypes.shape({ - /** The type of IOU report, i.e. bill, request, send */ - iouType: PropTypes.string, - - /** The report ID of the IOU */ - reportID: PropTypes.string, - }), - }).isRequired, + /** Navigation route context info provided by react navigation */ + route: IOURequestStepRoutePropTypes.isRequired, /* Onyx Props */ /** Collection of tax rates attached to a policy */ @@ -52,16 +42,16 @@ const getTaxAmount = (taxRates, selectedTaxRate, amount) => { function IOURequestStepTaxRatePage({ route: { - params: {iouType, reportID}, + params: {iouType, reportID, backTo}, }, policyTaxRates, transaction, }) { const {translate} = useLocalize(); - function navigateBack() { - Navigation.goBack(ROUTES.MONEY_REQUEST_CONFIRMATION.getRoute(iouType, reportID)); - } + const navigateBack = () => { + Navigation.goBack(backTo || ROUTES.HOME); + }; const defaultTaxKey = policyTaxRates.defaultExternalID; const defaultTaxName = (defaultTaxKey && `${policyTaxRates.taxes[defaultTaxKey].name} (${policyTaxRates.taxes[defaultTaxKey].value}) • ${translate('common.default')}`) || ''; @@ -77,26 +67,18 @@ function IOURequestStepTaxRatePage({ }; return ( - - {({insets}) => ( - <> - navigateBack()} - /> - - - )} - + + ); } From de255d7a896b951659503bd7a483ee874d187dc8 Mon Sep 17 00:00:00 2001 From: Etotaziba Olei Date: Thu, 4 Jan 2024 09:16:34 +0100 Subject: [PATCH 026/500] remove unused transaction prop --- src/pages/iou/request/step/IOURequestStepTaxAmountPage.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/pages/iou/request/step/IOURequestStepTaxAmountPage.js b/src/pages/iou/request/step/IOURequestStepTaxAmountPage.js index 89f592b89e38..106119cff994 100644 --- a/src/pages/iou/request/step/IOURequestStepTaxAmountPage.js +++ b/src/pages/iou/request/step/IOURequestStepTaxAmountPage.js @@ -124,7 +124,6 @@ function IOURequestStepTaxAmountPage({ currency={currency} amount={transaction.taxAmount} taxAmount={getTaxAmount(transaction, policyTaxRates.defaultValue)} - transaction={transaction} ref={(e) => (textInput.current = e)} onCurrencyButtonPress={navigateToCurrencySelectionPage} onSubmitButtonPress={updateTaxAmount} From b1df50f1f0b0a9272d462484dd9827df7d9dc494 Mon Sep 17 00:00:00 2001 From: Etotaziba Olei Date: Thu, 4 Jan 2024 09:17:55 +0100 Subject: [PATCH 027/500] use policyTaxRates directly in taxPicker --- src/components/TaxPicker/index.js | 8 +++++++- src/pages/iou/request/step/IOURequestStepTaxRatePage.js | 8 +++++++- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/src/components/TaxPicker/index.js b/src/components/TaxPicker/index.js index f25a1b84bf64..287805692bcf 100644 --- a/src/components/TaxPicker/index.js +++ b/src/components/TaxPicker/index.js @@ -1,5 +1,6 @@ import lodashGet from 'lodash/get'; import React, {useMemo, useState} from 'react'; +import {withOnyx} from 'react-native-onyx'; import _ from 'underscore'; import OptionsSelector from '@components/OptionsSelector'; import useLocalize from '@hooks/useLocalize'; @@ -8,6 +9,7 @@ import useThemeStyles from '@hooks/useThemeStyles'; import * as OptionsListUtils from '@libs/OptionsListUtils'; import * as TransactionUtils from '@libs/TransactionUtils'; import CONST from '@src/CONST'; +import ONYXKEYS from '@src/ONYXKEYS'; import {defaultProps, propTypes} from './taxPickerPropTypes'; function TaxPicker({selectedTaxRate, policyTaxRates, insets, onSubmit}) { @@ -87,4 +89,8 @@ TaxPicker.displayName = 'TaxPicker'; TaxPicker.propTypes = propTypes; TaxPicker.defaultProps = defaultProps; -export default TaxPicker; +export default withOnyx({ + policyTaxRates: { + key: ({policyID}) => `${ONYXKEYS.COLLECTION.POLICY_TAX_RATE}${policyID}`, + }, +})(TaxPicker); diff --git a/src/pages/iou/request/step/IOURequestStepTaxRatePage.js b/src/pages/iou/request/step/IOURequestStepTaxRatePage.js index ba3c9c7a321c..22f826d0457b 100644 --- a/src/pages/iou/request/step/IOURequestStepTaxRatePage.js +++ b/src/pages/iou/request/step/IOURequestStepTaxRatePage.js @@ -10,6 +10,7 @@ import * as CurrencyUtils from '@libs/CurrencyUtils'; import Navigation from '@libs/Navigation/Navigation'; import * as OptionsListUtils from '@libs/OptionsListUtils'; import * as TransactionUtils from '@libs/TransactionUtils'; +import reportPropTypes from '@pages/reportPropTypes'; import * as IOU from '@userActions/IOU'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; @@ -28,9 +29,13 @@ const propTypes = { /** The transaction object being modified in Onyx */ transaction: transactionPropTypes, + + /** The report attached to the transaction */ + report: reportPropTypes, }; const defaultProps = { + report: {}, policyTaxRates: {}, transaction: {}, }; @@ -46,6 +51,7 @@ function IOURequestStepTaxRatePage({ }, policyTaxRates, transaction, + report, }) { const {translate} = useLocalize(); @@ -75,7 +81,7 @@ function IOURequestStepTaxRatePage({ > From 22c290eaddc9b2239995b034b84052810def0777 Mon Sep 17 00:00:00 2001 From: Etotaziba Olei Date: Thu, 4 Jan 2024 09:19:34 +0100 Subject: [PATCH 028/500] add EditRequestTaxAmount Page --- src/pages/EditRequestTaxAmountPage.js | 73 +++++++++++++++++++++++++++ 1 file changed, 73 insertions(+) create mode 100644 src/pages/EditRequestTaxAmountPage.js diff --git a/src/pages/EditRequestTaxAmountPage.js b/src/pages/EditRequestTaxAmountPage.js new file mode 100644 index 000000000000..2d10ccfe6f2b --- /dev/null +++ b/src/pages/EditRequestTaxAmountPage.js @@ -0,0 +1,73 @@ +import {useFocusEffect} from '@react-navigation/native'; +import PropTypes from 'prop-types'; +import React, {useCallback, useRef} from 'react'; +import HeaderWithBackButton from '@components/HeaderWithBackButton'; +import ScreenWrapper from '@components/ScreenWrapper'; +import useLocalize from '@hooks/useLocalize'; +import * as DeviceCapabilities from '@libs/DeviceCapabilities'; +import Navigation from '@libs/Navigation/Navigation'; +import CONST from '@src/CONST'; +import MoneyRequestAmountForm from './iou/steps/MoneyRequestAmountForm'; + +const propTypes = { + /** Transaction default amount value */ + defaultAmount: PropTypes.number.isRequired, + + /** Transaction default tax amount value */ + defaultTaxAmount: PropTypes.number.isRequired, + + /** Transaction default currency value */ + defaultCurrency: PropTypes.string.isRequired, + + /** Callback to fire when the Save button is pressed */ + onSubmit: PropTypes.func.isRequired, + + /** Callback to fire when we press on the currency */ + onNavigateToCurrency: PropTypes.func.isRequired, +}; + +function EditRequestTaxAmountPage({defaultAmount, defaultTaxAmount, defaultCurrency, onNavigateToCurrency, onSubmit}) { + const {translate} = useLocalize(); + const textInput = useRef(null); + const isEditing = Navigation.getActiveRoute().includes('taxAmount'); + + const focusTimeoutRef = useRef(null); + + useFocusEffect( + useCallback(() => { + focusTimeoutRef.current = setTimeout(() => textInput.current && textInput.current.focus(), CONST.ANIMATED_TRANSITION); + return () => { + if (!focusTimeoutRef.current) { + return; + } + clearTimeout(focusTimeoutRef.current); + }; + }, []), + ); + + return ( + + + (textInput.current = e)} + onCurrencyButtonPress={onNavigateToCurrency} + onSubmitButtonPress={onSubmit} + isEditing + /> + + ); +} + +EditRequestTaxAmountPage.propTypes = propTypes; +EditRequestTaxAmountPage.displayName = 'EditRequestTaxAmountPage'; + +export default EditRequestTaxAmountPage; From 42f79baa818863272e58e57ff6b87e7865c9b10b Mon Sep 17 00:00:00 2001 From: Etotaziba Olei Date: Thu, 4 Jan 2024 09:19:59 +0100 Subject: [PATCH 029/500] add EditRequestTaxRate Page --- src/pages/EditRequestTaxRatePage.js | 47 +++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) create mode 100644 src/pages/EditRequestTaxRatePage.js diff --git a/src/pages/EditRequestTaxRatePage.js b/src/pages/EditRequestTaxRatePage.js new file mode 100644 index 000000000000..965dfc1a65d7 --- /dev/null +++ b/src/pages/EditRequestTaxRatePage.js @@ -0,0 +1,47 @@ +import PropTypes from 'prop-types'; +import React from 'react'; +import HeaderWithBackButton from '@components/HeaderWithBackButton'; +import ScreenWrapper from '@components/ScreenWrapper'; +import TaxPicker from '@components/TaxPicker'; +import useLocalize from '@hooks/useLocalize'; +import Navigation from '@libs/Navigation/Navigation'; + +const propTypes = { + /** Transaction default tax Rate value */ + defaultTaxRate: PropTypes.string.isRequired, + + /** The policyID we are getting categories for */ + policyID: PropTypes.string.isRequired, + + /** Callback to fire when the Save button is pressed */ + onSubmit: PropTypes.func.isRequired, +}; + +function EditRequestTaxRatePage({defaultTaxRate, policyID, onSubmit}) { + const {translate} = useLocalize(); + + return ( + + {({insets}) => ( + <> + + + + )} + + ); +} + +EditRequestTaxRatePage.propTypes = propTypes; +EditRequestTaxRatePage.displayName = 'EditRequestTaxRatePage'; + +export default EditRequestTaxRatePage; From 3226d9cf8ba70e0218032197dd925d75e252faa3 Mon Sep 17 00:00:00 2001 From: Etotaziba Olei Date: Thu, 4 Jan 2024 09:23:47 +0100 Subject: [PATCH 030/500] add edit Tax amount and rate page to edit request page --- src/CONST.ts | 2 ++ src/pages/EditRequestPage.js | 39 +++++++++++++++++++++++++++++++++++- 2 files changed, 40 insertions(+), 1 deletion(-) diff --git a/src/CONST.ts b/src/CONST.ts index cec7cbc0b8a5..44bee149609f 100755 --- a/src/CONST.ts +++ b/src/CONST.ts @@ -1541,6 +1541,8 @@ const CONST = { RECEIPT: 'receipt', DISTANCE: 'distance', TAG: 'tag', + TAX_RATE: 'taxRate', + TAX_AMOUNT: 'taxAmount', }, FOOTER: { EXPENSE_MANAGEMENT_URL: `${USE_EXPENSIFY_URL}/expense-management`, diff --git a/src/pages/EditRequestPage.js b/src/pages/EditRequestPage.js index e41f30779f22..a43ddc44c492 100644 --- a/src/pages/EditRequestPage.js +++ b/src/pages/EditRequestPage.js @@ -27,6 +27,8 @@ import EditRequestDistancePage from './EditRequestDistancePage'; import EditRequestMerchantPage from './EditRequestMerchantPage'; import EditRequestReceiptPage from './EditRequestReceiptPage'; import EditRequestTagPage from './EditRequestTagPage'; +import EditRequestTaxAmountPage from './EditRequestTaxAmountPage'; +import EditRequestTaxRatePage from './EditRequestTaxRatePage'; import reportActionPropTypes from './home/report/reportActionPropTypes'; import reportPropTypes from './reportPropTypes'; @@ -61,6 +63,12 @@ const propTypes = { /** Transaction that stores the request data */ transaction: transactionPropTypes, + + /** The policy of the report */ + policy: PropTypes.shape({ + /** Is Tax tracking Enabled */ + isTaxTrackingEnabled: PropTypes.bool, + }), }; const defaultProps = { @@ -70,9 +78,10 @@ const defaultProps = { policyTags: {}, parentReportActions: {}, transaction: {}, + policy: {}, }; -function EditRequestPage({report, route, parentReport, policyCategories, policyTags, parentReportActions, transaction}) { +function EditRequestPage({report, policy, route, parentReport, policyCategories, policyTags, parentReportActions, transaction}) { const parentReportActionID = lodashGet(report, 'parentReportActionID', '0'); const parentReportAction = lodashGet(parentReportActions, parentReportActionID, {}); const { @@ -101,6 +110,9 @@ function EditRequestPage({report, route, parentReport, policyCategories, policyT // A flag for showing the tags page const shouldShowTags = isPolicyExpenseChat && (transactionTag || OptionsListUtils.hasEnabledOptions(lodashValues(policyTagList))); + // A flag for showing tax rate + const shouldShowTax = isPolicyExpenseChat && policy.isTaxTrackingEnabled; + // Decides whether to allow or disallow editing a money request useEffect(() => { // Do not dismiss the modal, when a current user can edit this property of the money request. @@ -251,6 +263,28 @@ function EditRequestPage({report, route, parentReport, policyCategories, policyT ); } + if (fieldToEdit === CONST.EDIT_REQUEST_FIELD.TAX_AMOUNT && shouldShowTax) { + return ( + {}} + onSubmit={() => {}} + /> + ); + } + + if (fieldToEdit === CONST.EDIT_REQUEST_FIELD.TAX_RATE && shouldShowTax) { + return ( + {}} + /> + ); + } + if (fieldToEdit === CONST.EDIT_REQUEST_FIELD.RECEIPT) { return ( `${ONYXKEYS.COLLECTION.POLICY_CATEGORIES}${report ? report.policyID : '0'}`, }, + policy: { + key: ({report}) => `${ONYXKEYS.COLLECTION.POLICY}${report.policyID}`, + }, policyTags: { key: ({report}) => `${ONYXKEYS.COLLECTION.POLICY_TAGS}${report ? report.policyID : '0'}`, }, From 9130f73d940ef8a8bb62fd3c8cf7914b15959dc2 Mon Sep 17 00:00:00 2001 From: Etotaziba Olei Date: Thu, 4 Jan 2024 09:24:32 +0100 Subject: [PATCH 031/500] add edit Tax amount and rate fields and route to pages --- .../ReportActionItem/MoneyRequestView.js | 39 ++++++++++++++++++- 1 file changed, 38 insertions(+), 1 deletion(-) diff --git a/src/components/ReportActionItem/MoneyRequestView.js b/src/components/ReportActionItem/MoneyRequestView.js index 514dc71ffe2c..4c8cc0c3d131 100644 --- a/src/components/ReportActionItem/MoneyRequestView.js +++ b/src/components/ReportActionItem/MoneyRequestView.js @@ -12,6 +12,7 @@ import ReceiptEmptyState from '@components/ReceiptEmptyState'; import SpacerView from '@components/SpacerView'; import Switch from '@components/Switch'; import tagPropTypes from '@components/tagPropTypes'; +import taxPropTypes from '@components/taxPropTypes'; import Text from '@components/Text'; import transactionPropTypes from '@components/transactionPropTypes'; import withCurrentUserPersonalDetails, {withCurrentUserPersonalDetailsPropTypes} from '@components/withCurrentUserPersonalDetails'; @@ -64,6 +65,10 @@ const propTypes = { /** Collection of tags attached to a policy */ policyTags: tagPropTypes, + /* Onyx Props */ + /** Collection of tax rates attached to a policy */ + policyTaxRates: taxPropTypes, + ...withCurrentUserPersonalDetailsPropTypes, }; @@ -77,9 +82,10 @@ const defaultProps = { comment: {comment: ''}, }, policyTags: {}, + policyTaxRates: {}, }; -function MoneyRequestView({report, parentReport, parentReportActions, policyCategories, shouldShowHorizontalRule, transaction, policyTags, policy}) { +function MoneyRequestView({report, parentReport, parentReportActions, policyCategories, policyTaxRates, shouldShowHorizontalRule, transaction, policyTags, policy}) { const theme = useTheme(); const styles = useThemeStyles(); const StyleUtils = useStyleUtils(); @@ -131,6 +137,9 @@ function MoneyRequestView({report, parentReport, parentReportActions, policyCate const shouldShowTag = isPolicyExpenseChat && (transactionTag || OptionsListUtils.hasEnabledOptions(lodashValues(policyTagsList))); const shouldShowBillable = isPolicyExpenseChat && (transactionBillable || !lodashGet(policy, 'disabledFields.defaultBillable', true)); + // A flag for showing tax rate + const shouldShowTax = isPolicyExpenseChat && policy.isTaxTrackingEnabled; + let amountDescription = `${translate('iou.amount')}`; if (isCardTransaction) { @@ -295,6 +304,31 @@ function MoneyRequestView({report, parentReport, parentReportActions, policyCate /> )} + {shouldShowTax && ( + + Navigation.navigate(ROUTES.EDIT_REQUEST.getRoute(report.reportID, CONST.EDIT_REQUEST_FIELD.TAX_RATE))} + /> + + )} + + {shouldShowTax && ( + + Navigation.navigate(ROUTES.EDIT_REQUEST.getRoute(report.reportID, CONST.EDIT_REQUEST_FIELD.TAX_AMOUNT))} + /> + + )} {shouldShowBillable && ( {translate('common.billable')} @@ -333,6 +367,9 @@ export default compose( policyTags: { key: ({report}) => `${ONYXKEYS.COLLECTION.POLICY_TAGS}${report.policyID}`, }, + policyTaxRates: { + key: ({report}) => `${ONYXKEYS.COLLECTION.POLICY_TAX_RATE}${report.policyID}`, + }, parentReport: { key: ({report}) => `${ONYXKEYS.COLLECTION.REPORT}${report.parentReportID}`, }, From 4b8a102c3cef9510991b97f4d96082e20eb159e0 Mon Sep 17 00:00:00 2001 From: Etotaziba Olei Date: Thu, 4 Jan 2024 09:37:44 +0100 Subject: [PATCH 032/500] fix lint --- src/pages/EditRequestTaxAmountPage.js | 1 - src/pages/EditRequestTaxRatePage.js | 1 - 2 files changed, 2 deletions(-) diff --git a/src/pages/EditRequestTaxAmountPage.js b/src/pages/EditRequestTaxAmountPage.js index 2d10ccfe6f2b..dabfdfcb519f 100644 --- a/src/pages/EditRequestTaxAmountPage.js +++ b/src/pages/EditRequestTaxAmountPage.js @@ -54,7 +54,6 @@ function EditRequestTaxAmountPage({defaultAmount, defaultTaxAmount, defaultCurre > Date: Thu, 4 Jan 2024 09:42:40 +0100 Subject: [PATCH 033/500] remove is editing check --- src/pages/EditRequestTaxAmountPage.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/pages/EditRequestTaxAmountPage.js b/src/pages/EditRequestTaxAmountPage.js index dabfdfcb519f..b5c20aa82d7c 100644 --- a/src/pages/EditRequestTaxAmountPage.js +++ b/src/pages/EditRequestTaxAmountPage.js @@ -29,7 +29,6 @@ const propTypes = { function EditRequestTaxAmountPage({defaultAmount, defaultTaxAmount, defaultCurrency, onNavigateToCurrency, onSubmit}) { const {translate} = useLocalize(); const textInput = useRef(null); - const isEditing = Navigation.getActiveRoute().includes('taxAmount'); const focusTimeoutRef = useRef(null); From 74f63f2eea05b7cb922d81867ecf95e82a7b45e4 Mon Sep 17 00:00:00 2001 From: Etotaziba Olei Date: Thu, 4 Jan 2024 13:28:12 +0100 Subject: [PATCH 034/500] complete types for policyTaxRates and Transaction --- src/types/onyx/PolicyTaxRates.ts | 25 +++++++++++++++++++++---- src/types/onyx/Transaction.ts | 3 +++ 2 files changed, 24 insertions(+), 4 deletions(-) diff --git a/src/types/onyx/PolicyTaxRates.ts b/src/types/onyx/PolicyTaxRates.ts index d549b620f51f..bc0905a0ff49 100644 --- a/src/types/onyx/PolicyTaxRates.ts +++ b/src/types/onyx/PolicyTaxRates.ts @@ -1,4 +1,4 @@ -type PolicyTaxRate = { +type TaxRate = { /** Name of a tax */ name: string; @@ -9,6 +9,23 @@ type PolicyTaxRate = { isDisabled?: boolean; }; -type PolicyTaxRates = Record; -export default PolicyTaxRate; -export type {PolicyTaxRates}; +type TaxRates = Record; +type PolicyTaxRates = { + /** Name of the tax */ + name: string; + + /** Default policy tax ID */ + defaultExternalID: string; + + /** Default value of taxes */ + defaultValue: string; + + /** Default foreign policy tax ID */ + foreignTaxDefault: string; + + /** List of tax names and values */ + taxes: TaxRates; +}; + +export default TaxRate; +export type {TaxRates, PolicyTaxRates}; diff --git a/src/types/onyx/Transaction.ts b/src/types/onyx/Transaction.ts index 53bfc36a4e47..d67aec17a845 100644 --- a/src/types/onyx/Transaction.ts +++ b/src/types/onyx/Transaction.ts @@ -94,6 +94,9 @@ type Transaction = { /** If the transaction was made in a foreign currency, we send the original amount and currency */ originalAmount?: number; originalCurrency?: string; + taxRate?: { + text: string; + }; }; export default Transaction; From f2503f4e90b1456eb143f100b7b47159bde9be79 Mon Sep 17 00:00:00 2001 From: Etotaziba Olei Date: Thu, 4 Jan 2024 13:31:22 +0100 Subject: [PATCH 035/500] add getDefaultTaxName func. in TransactionUtils --- src/libs/TransactionUtils.ts | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/src/libs/TransactionUtils.ts b/src/libs/TransactionUtils.ts index 615bea7ff18d..51a9f6ee188e 100644 --- a/src/libs/TransactionUtils.ts +++ b/src/libs/TransactionUtils.ts @@ -4,11 +4,12 @@ import {ValueOf} from 'type-fest'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import {RecentWaypoint, Report, ReportAction, Transaction} from '@src/types/onyx'; -import PolicyTaxRate, {PolicyTaxRates} from '@src/types/onyx/PolicyTaxRates'; +import TaxRate, {PolicyTaxRates, TaxRates} from '@src/types/onyx/PolicyTaxRates'; import {Comment, Receipt, Waypoint, WaypointCollection} from '@src/types/onyx/Transaction'; import {EmptyObject} from '@src/types/utils/EmptyObject'; import {isCorporateCard, isExpensifyCard} from './CardUtils'; import DateUtils from './DateUtils'; +import * as Localize from './Localize'; import * as NumberUtils from './NumberUtils'; type AdditionalTransactionChanges = {comment?: string; waypoints?: WaypointCollection}; @@ -529,13 +530,24 @@ function calculateTaxAmount(percentage: string, amount: number) { /** * Calculates count of all tax enabled options */ -function getEnabledTaxRateCount(options: PolicyTaxRates) { - return Object.values(options).filter((option: PolicyTaxRate) => !option.isDisabled).length; +function getEnabledTaxRateCount(options: TaxRates) { + return Object.values(options).filter((option: TaxRate) => !option.isDisabled).length; +} + +/** + * Calculates get's the default tax name + */ +function getDefaultTaxName(policyTaxRates: PolicyTaxRates, transaction: Transaction) { + const defaultTaxKey = policyTaxRates.defaultExternalID; + const defaultTaxName = + (defaultTaxKey && `${policyTaxRates.taxes[defaultTaxKey].name} (${policyTaxRates.taxes[defaultTaxKey].value}) • ${Localize.translateLocal('common.default')}`) || ''; + return transaction?.taxRate?.text ?? defaultTaxName; } export { buildOptimisticTransaction, calculateTaxAmount, + getDefaultTaxName, getEnabledTaxRateCount, getUpdatedTransaction, getTransaction, From f10e413a895ac554ad7e1fe6f82a0ec290634c36 Mon Sep 17 00:00:00 2001 From: Etotaziba Olei Date: Thu, 4 Jan 2024 13:32:09 +0100 Subject: [PATCH 036/500] use getDefaultTaxName --- src/components/MoneyRequestConfirmationList.js | 5 +---- .../MoneyTemporaryForRefactorRequestConfirmationList.js | 4 +--- src/pages/iou/request/step/IOURequestStepTaxRatePage.js | 4 +--- 3 files changed, 3 insertions(+), 10 deletions(-) diff --git a/src/components/MoneyRequestConfirmationList.js b/src/components/MoneyRequestConfirmationList.js index 5807eb66f143..260c27205e94 100755 --- a/src/components/MoneyRequestConfirmationList.js +++ b/src/components/MoneyRequestConfirmationList.js @@ -262,10 +262,7 @@ function MoneyRequestConfirmationList(props) { props.isDistanceRequest ? currency : props.iouCurrencyCode, ); const formattedTaxAmount = CurrencyUtils.convertToDisplayString(props.transaction.taxAmount, props.iouCurrencyCode); - - const defaultTaxKey = props.policyTaxRates.defaultExternalID; - const defaultTaxName = (defaultTaxKey && `${props.policyTaxRates.taxes[defaultTaxKey].name} (${props.policyTaxRates.taxes[defaultTaxKey].value}) • ${translate('common.default')}`) || ''; - const taxRateTitle = (props.transaction.taxRate && props.transaction.taxRate.text) || defaultTaxName; + const taxRateTitle = TransactionUtils.getDefaultTaxName(props.policyTaxRates, transaction); const isFocused = useIsFocused(); const [formError, setFormError] = useState(''); diff --git a/src/components/MoneyTemporaryForRefactorRequestConfirmationList.js b/src/components/MoneyTemporaryForRefactorRequestConfirmationList.js index 7ec95aec951f..55890c022179 100755 --- a/src/components/MoneyTemporaryForRefactorRequestConfirmationList.js +++ b/src/components/MoneyTemporaryForRefactorRequestConfirmationList.js @@ -292,9 +292,7 @@ function MoneyTemporaryForRefactorRequestConfirmationList({ ); const formattedTaxAmount = CurrencyUtils.convertToDisplayString(transaction.taxAmount, iouCurrencyCode); - const defaultTaxKey = policyTaxRates.defaultExternalID; - const defaultTaxName = (defaultTaxKey && `${policyTaxRates.taxes[defaultTaxKey].name} (${policyTaxRates.taxes[defaultTaxKey].value}) • ${translate('common.default')}`) || ''; - const taxRateTitle = (transaction.taxRate && transaction.taxRate.text) || defaultTaxName; + const taxRateTitle = TransactionUtils.getDefaultTaxName(policyTaxRates, transaction); const isFocused = useIsFocused(); const [formError, setFormError] = useState(''); diff --git a/src/pages/iou/request/step/IOURequestStepTaxRatePage.js b/src/pages/iou/request/step/IOURequestStepTaxRatePage.js index 22f826d0457b..ac25f2320684 100644 --- a/src/pages/iou/request/step/IOURequestStepTaxRatePage.js +++ b/src/pages/iou/request/step/IOURequestStepTaxRatePage.js @@ -59,9 +59,7 @@ function IOURequestStepTaxRatePage({ Navigation.goBack(backTo || ROUTES.HOME); }; - const defaultTaxKey = policyTaxRates.defaultExternalID; - const defaultTaxName = (defaultTaxKey && `${policyTaxRates.taxes[defaultTaxKey].name} (${policyTaxRates.taxes[defaultTaxKey].value}) • ${translate('common.default')}`) || ''; - const selectedTaxRate = (transaction.taxRate && transaction.taxRate.text) || defaultTaxName; + const selectedTaxRate = TransactionUtils.getDefaultTaxName(policyTaxRates, transaction); const updateTaxRates = (taxes) => { const taxAmount = getTaxAmount(policyTaxRates, taxes.text, transaction.amount); From 13dccd87ddf5d6a5aa952aed3c5c8a549e9b47a9 Mon Sep 17 00:00:00 2001 From: Etotaziba Olei Date: Thu, 4 Jan 2024 13:32:33 +0100 Subject: [PATCH 037/500] remove unused Navigation import --- src/pages/EditRequestTaxAmountPage.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/pages/EditRequestTaxAmountPage.js b/src/pages/EditRequestTaxAmountPage.js index b5c20aa82d7c..6a413bf12655 100644 --- a/src/pages/EditRequestTaxAmountPage.js +++ b/src/pages/EditRequestTaxAmountPage.js @@ -5,7 +5,6 @@ import HeaderWithBackButton from '@components/HeaderWithBackButton'; import ScreenWrapper from '@components/ScreenWrapper'; import useLocalize from '@hooks/useLocalize'; import * as DeviceCapabilities from '@libs/DeviceCapabilities'; -import Navigation from '@libs/Navigation/Navigation'; import CONST from '@src/CONST'; import MoneyRequestAmountForm from './iou/steps/MoneyRequestAmountForm'; From 7c154f116a91734baab62bbbfc6a480c7e9e9bf1 Mon Sep 17 00:00:00 2001 From: Etotaziba Olei Date: Thu, 4 Jan 2024 17:44:14 +0100 Subject: [PATCH 038/500] remove tax amount calculation implementation from requestStepAmount --- .../iou/request/step/IOURequestStepAmount.js | 47 +------------------ 1 file changed, 1 insertion(+), 46 deletions(-) diff --git a/src/pages/iou/request/step/IOURequestStepAmount.js b/src/pages/iou/request/step/IOURequestStepAmount.js index 84e0ac8533c5..531d4d4d403b 100644 --- a/src/pages/iou/request/step/IOURequestStepAmount.js +++ b/src/pages/iou/request/step/IOURequestStepAmount.js @@ -1,21 +1,15 @@ import {useFocusEffect} from '@react-navigation/native'; -import PropTypes from 'prop-types'; import React, {useCallback, useRef} from 'react'; -import {withOnyx} from 'react-native-onyx'; -import taxPropTypes from '@components/taxPropTypes'; import transactionPropTypes from '@components/transactionPropTypes'; import useLocalize from '@hooks/useLocalize'; import compose from '@libs/compose'; import * as CurrencyUtils from '@libs/CurrencyUtils'; import Navigation from '@libs/Navigation/Navigation'; -import * as ReportUtils from '@libs/ReportUtils'; import {getRequestType} from '@libs/TransactionUtils'; -import * as TransactionUtils from '@libs/TransactionUtils'; import MoneyRequestAmountForm from '@pages/iou/steps/MoneyRequestAmountForm'; import reportPropTypes from '@pages/reportPropTypes'; import * as IOU from '@userActions/IOU'; import CONST from '@src/CONST'; -import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; import IOURequestStepRoutePropTypes from './IOURequestStepRoutePropTypes'; import StepScreenWrapper from './StepScreenWrapper'; @@ -32,28 +26,11 @@ const propTypes = { /** The transaction object being modified in Onyx */ transaction: transactionPropTypes, - - /* Onyx Props */ - /** Collection of tax rates attached to a policy */ - policyTaxRates: taxPropTypes, - - /** The policy of the report */ - policy: PropTypes.shape({ - /** Is Tax tracking Enabled */ - isTaxTrackingEnabled: PropTypes.bool, - }), }; const defaultProps = { report: {}, transaction: {}, - policyTaxRates: {}, - policy: {}, -}; - -const getTaxAmount = (transaction, defaultTaxValue, amount) => { - const percentage = (transaction.taxRate ? transaction.taxRate.data.value : defaultTaxValue) || ''; - return TransactionUtils.calculateTaxAmount(percentage, amount); }; function IOURequestStepAmount({ @@ -63,8 +40,6 @@ function IOURequestStepAmount({ }, transaction, transaction: {currency: originalCurrency}, - policyTaxRates, - policy, }) { const {translate} = useLocalize(); const textInput = useRef(null); @@ -72,9 +47,6 @@ function IOURequestStepAmount({ const iouRequestType = getRequestType(transaction); const currency = selectedCurrency || originalCurrency; - const isPolicyExpenseChat = ReportUtils.isPolicyExpenseChat(ReportUtils.getRootParentReport(report)); - const isTaxTrackingEnabled = isPolicyExpenseChat && policy.isTaxTrackingEnabled; - useFocusEffect( useCallback(() => { focusTimeoutRef.current = setTimeout(() => textInput.current && textInput.current.focus(), CONST.ANIMATED_TRANSITION); @@ -101,12 +73,6 @@ function IOURequestStepAmount({ const navigateToNextPage = ({amount}) => { const amountInSmallestCurrencyUnits = CurrencyUtils.convertToBackendAmount(Number.parseFloat(amount)); - if ((iouRequestType === CONST.IOU.REQUEST_TYPE.MANUAL || backTo) && isTaxTrackingEnabled) { - const taxAmount = getTaxAmount(transaction, policyTaxRates.defaultValue, amountInSmallestCurrencyUnits); - const taxAmountInSmallestCurrencyUnits = CurrencyUtils.convertToBackendAmount(Number.parseFloat(taxAmount)); - IOU.setMoneyRequestTaxAmount(transaction.transactionID, taxAmountInSmallestCurrencyUnits); - } - IOU.setMoneyRequestAmount_temporaryForRefactor(transactionID, amountInSmallestCurrencyUnits, currency || CONST.CURRENCY.USD); if (backTo) { @@ -153,15 +119,4 @@ IOURequestStepAmount.propTypes = propTypes; IOURequestStepAmount.defaultProps = defaultProps; IOURequestStepAmount.displayName = 'IOURequestStepAmount'; -export default compose( - withWritableReportOrNotFound, - withFullTransactionOrNotFound, - withOnyx({ - policyTaxRates: { - key: ({report}) => `${ONYXKEYS.COLLECTION.POLICY_TAX_RATE}${report ? report.policyID : '0'}`, - }, - policy: { - key: ({report}) => `${ONYXKEYS.COLLECTION.POLICY}${report ? report.policyID : '0'}`, - }, - }), -)(IOURequestStepAmount); +export default compose(withWritableReportOrNotFound, withFullTransactionOrNotFound)(IOURequestStepAmount); From d501ad75a419ea632796612cc5beb347706df0ac Mon Sep 17 00:00:00 2001 From: Etotaziba Olei Date: Thu, 4 Jan 2024 17:44:50 +0100 Subject: [PATCH 039/500] set tax Amount --- ...poraryForRefactorRequestConfirmationList.js | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/components/MoneyTemporaryForRefactorRequestConfirmationList.js b/src/components/MoneyTemporaryForRefactorRequestConfirmationList.js index 55890c022179..79dd9e679263 100755 --- a/src/components/MoneyTemporaryForRefactorRequestConfirmationList.js +++ b/src/components/MoneyTemporaryForRefactorRequestConfirmationList.js @@ -8,6 +8,7 @@ import {withOnyx} from 'react-native-onyx'; import _ from 'underscore'; import useLocalize from '@hooks/useLocalize'; import usePermissions from '@hooks/usePermissions'; +import usePrevious from '@hooks/usePrevious'; import useTheme from '@hooks/useTheme'; import useThemeStyles from '@hooks/useThemeStyles'; import compose from '@libs/compose'; @@ -202,6 +203,11 @@ const defaultProps = { policyTaxRates: {}, }; +const getTaxAmount = (transaction, defaultTaxValue) => { + const percentage = (transaction.taxRate ? transaction.taxRate.data.value : defaultTaxValue) || ''; + return TransactionUtils.calculateTaxAmount(percentage, transaction.amount); +}; + function MoneyTemporaryForRefactorRequestConfirmationList({ bankAccountRoute, canModifyParticipants, @@ -294,6 +300,8 @@ function MoneyTemporaryForRefactorRequestConfirmationList({ const taxRateTitle = TransactionUtils.getDefaultTaxName(policyTaxRates, transaction); + const previousTransactionTaxAmount = usePrevious(transaction.taxAmount); + const isFocused = useIsFocused(); const [formError, setFormError] = useState(''); @@ -351,6 +359,16 @@ function MoneyTemporaryForRefactorRequestConfirmationList({ IOU.setMoneyRequestAmount_temporaryForRefactor(transaction.transactionID, amount, currency); }, [shouldCalculateDistanceAmount, distance, rate, unit, transaction, currency]); + // calculate and set tax amount in transaction draft + useEffect(() => { + const taxAmount = getTaxAmount(transaction, policyTaxRates.defaultValue); + const amountInSmallestCurrencyUnits = CurrencyUtils.convertToBackendAmount(Number.parseFloat(taxAmount)); + if (previousTransactionTaxAmount !== transaction.taxAmount && amountInSmallestCurrencyUnits !== transaction.taxAmount) { + return; + } + IOU.setMoneyRequestTaxAmount(transaction.transactionID, amountInSmallestCurrencyUnits); + }, [policyTaxRates.defaultValue, transaction, previousTransactionTaxAmount]); + /** * Returns the participants with amount * @param {Array} participants From 13deb831497581a1a314f42e975127cb6bde2883 Mon Sep 17 00:00:00 2001 From: Etotaziba Olei Date: Thu, 4 Jan 2024 22:19:40 +0100 Subject: [PATCH 040/500] add Tax amount and Code API for updates --- src/libs/actions/IOU.js | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/src/libs/actions/IOU.js b/src/libs/actions/IOU.js index b999435cc3e7..dc087ba42eac 100644 --- a/src/libs/actions/IOU.js +++ b/src/libs/actions/IOU.js @@ -1051,6 +1051,36 @@ function updateMoneyRequestDate(transactionID, transactionThreadReportID, val) { API.write('UpdateMoneyRequestDate', params, onyxData); } +/** + * Updates the created tax amount of a money request + * + * @param {String} transactionID + * @param {String} optimisticReportActionID + * @param {Number} val + */ +function updateMoneyRequestTaxAmount(transactionID, optimisticReportActionID, val) { + const transactionChanges = { + taxAmount: val, + }; + const {params, onyxData} = getUpdateMoneyRequestParams(transactionID, optimisticReportActionID, transactionChanges, true); + API.write('UpdateMoneyRequestTaxAmount', params, onyxData); +} + +/** + * Updates the created tax rate of a money request + * + * @param {String} transactionID + * @param {String} optimisticReportActionID + * @param {String} val + */ +function updateMoneyRequestTaxRate(transactionID, optimisticReportActionID, val) { + const transactionChanges = { + taxCode: val, + }; + const {params, onyxData} = getUpdateMoneyRequestParams(transactionID, optimisticReportActionID, transactionChanges, true); + API.write('UpdateMoneyRequestTaxRate', params, onyxData); +} + /** * Edits an existing distance request * @@ -3490,6 +3520,8 @@ export { setUpDistanceTransaction, navigateToNextPage, updateMoneyRequestDate, + updateMoneyRequestTaxAmount, + updateMoneyRequestTaxRate, updateMoneyRequestAmountAndCurrency, replaceReceipt, detachReceipt, From fc3e59356aa037f68c525100a5b83358120fe0d9 Mon Sep 17 00:00:00 2001 From: Etotaziba Olei Date: Mon, 8 Jan 2024 16:46:55 +0100 Subject: [PATCH 041/500] add function to check if tax rate has enabled options --- src/libs/OptionsListUtils.js | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/libs/OptionsListUtils.js b/src/libs/OptionsListUtils.js index 0dc12c720f31..3aa9ffc690bb 100644 --- a/src/libs/OptionsListUtils.js +++ b/src/libs/OptionsListUtils.js @@ -1058,6 +1058,17 @@ function getTagListSections(rawTags, recentlyUsedTags, selectedOptions, searchIn return tagSections; } +/** + * Verifies that there is at least one enabled option + * + * @param {Object[]} options - an initial strings array + * @property {boolean} [isDisabled] - Indicates if the tax rate is disabled. + * @returns {Boolean} + */ +function hasEnabledOptionsForTaxRate(options) { + return _.some(options, (option) => !option.isDisabled); +} + /** * Represents the data for a single tax rate. * @@ -1953,6 +1964,7 @@ export { getLastMessageTextForReport, getEnabledCategoriesCount, hasEnabledOptions, + hasEnabledOptionsForTaxRate, sortCategories, getCategoryOptionTree, formatMemberForList, From a6daa02f50f5ffb618e366dc6f10bbb3f7eedd4c Mon Sep 17 00:00:00 2001 From: Etotaziba Olei Date: Mon, 8 Jan 2024 16:48:15 +0100 Subject: [PATCH 042/500] add defaults for tax rate and amount and amount title --- .../ReportActionItem/MoneyRequestView.js | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/src/components/ReportActionItem/MoneyRequestView.js b/src/components/ReportActionItem/MoneyRequestView.js index 0b662f9d3578..a38b711e7192 100644 --- a/src/components/ReportActionItem/MoneyRequestView.js +++ b/src/components/ReportActionItem/MoneyRequestView.js @@ -155,6 +155,12 @@ function MoneyRequestView({report, parentReport, parentReportActions, policyCate const isCardTransaction = TransactionUtils.isCardTransaction(transaction); const cardProgramName = isCardTransaction ? CardUtils.getCardDescription(transactionCardID) : ''; + const transactionTaxAmount = (transaction.taxAmount && transaction.taxAmount) || 0; + const formattedTaxAmount = CurrencyUtils.convertToDisplayString(transactionTaxAmount, transactionCurrency); + + const transactionTaxCode = transaction.taxCode && transaction.taxCode; + const taxRateTitle = (transactionTaxCode && policyTaxRates.taxes[transactionTaxCode].name) || ''; + // Flags for allowing or disallowing editing a money request const isSettled = ReportUtils.isSettled(moneyRequestReport.reportID); const isCancelled = moneyRequestReport && moneyRequestReport.isCancelledIOU; @@ -181,7 +187,11 @@ function MoneyRequestView({report, parentReport, parentReportActions, policyCate const shouldShowBillable = isPolicyExpenseChat && (transactionBillable || !lodashGet(policy, 'disabledFields.defaultBillable', true)); // A flag for showing tax rate - const shouldShowTax = isPolicyExpenseChat && policy.isTaxTrackingEnabled; + const shouldShowTax = + isPolicyExpenseChat && + policy && + policy.isTaxTrackingEnabled && + ((transactionTaxCode && transactionTaxAmount) || OptionsListUtils.hasEnabledOptionsForTaxRate(lodashValues(policyTaxRates.taxes))); const {getViolationsForField} = useViolations(transactionViolations); const hasViolations = useCallback((field) => canUseViolations && getViolationsForField(field).length > 0, [canUseViolations, getViolationsForField]); @@ -359,7 +369,7 @@ function MoneyRequestView({report, parentReport, parentReportActions, policyCate {shouldShowTax && ( Date: Mon, 8 Jan 2024 16:50:03 +0100 Subject: [PATCH 043/500] add defaults for tax rate and amount and amount title --- src/pages/EditRequestPage.js | 29 +++++++++++++++++++++++------ 1 file changed, 23 insertions(+), 6 deletions(-) diff --git a/src/pages/EditRequestPage.js b/src/pages/EditRequestPage.js index 7e91b6787e5f..f47950eb71ff 100644 --- a/src/pages/EditRequestPage.js +++ b/src/pages/EditRequestPage.js @@ -7,6 +7,7 @@ import FullPageNotFoundView from '@components/BlockingViews/FullPageNotFoundView import categoryPropTypes from '@components/categoryPropTypes'; import ScreenWrapper from '@components/ScreenWrapper'; import tagPropTypes from '@components/tagPropTypes'; +import taxPropTypes from '@components/taxPropTypes'; import transactionPropTypes from '@components/transactionPropTypes'; import compose from '@libs/compose'; import * as CurrencyUtils from '@libs/CurrencyUtils'; @@ -66,6 +67,10 @@ const propTypes = { /** Is Tax tracking Enabled */ isTaxTrackingEnabled: PropTypes.bool, }), + + /* Onyx Props */ + /** Collection of tax rates attached to a policy */ + policyTaxRates: taxPropTypes, }; const defaultProps = { @@ -75,9 +80,10 @@ const defaultProps = { parentReportActions: {}, transaction: {}, policy: {}, + policyTaxRates: {}, }; -function EditRequestPage({report, policy, route, policyCategories, policyTags, parentReportActions, transaction}) { +function EditRequestPage({report, policy, policyTaxRates, route, policyCategories, policyTags, parentReportActions, transaction}) { const parentReportActionID = lodashGet(report, 'parentReportActionID', '0'); const parentReportAction = lodashGet(parentReportActions, parentReportActionID, {}); const { @@ -92,6 +98,11 @@ function EditRequestPage({report, policy, route, policyCategories, policyTags, p const defaultCurrency = lodashGet(route, 'params.currency', '') || transactionCurrency; const fieldToEdit = lodashGet(route, ['params', 'field'], ''); + const transactionTaxAmount = (transaction.taxAmount && transaction.taxAmount) || 0; + + const transactionTaxCode = transaction.taxCode && transaction.taxCode; + const taxRateTitle = (transactionTaxCode && policyTaxRates.taxes[transactionTaxCode].name) || ''; + // For now, it always defaults to the first tag of the policy const policyTag = PolicyUtils.getTag(policyTags); const policyTagList = lodashGet(policyTag, 'tags', {}); @@ -267,10 +278,13 @@ function EditRequestPage({report, policy, route, policyCategories, policyTags, p if (fieldToEdit === CONST.EDIT_REQUEST_FIELD.TAX_AMOUNT && shouldShowTax) { return ( {}} + defaultAmount={transactionAmount} + defaultTaxAmount={transactionTaxAmount} + defaultCurrency={defaultCurrency} + onNavigateToCurrency={() => { + const activeRoute = encodeURIComponent(Navigation.getActiveRouteWithoutParams()); + Navigation.navigate(ROUTES.EDIT_CURRENCY_REQUEST.getRoute(report.reportID, defaultCurrency, activeRoute)); + }} onSubmit={() => {}} /> ); @@ -279,7 +293,7 @@ function EditRequestPage({report, policy, route, policyCategories, policyTags, p if (fieldToEdit === CONST.EDIT_REQUEST_FIELD.TAX_RATE && shouldShowTax) { return ( {}} /> @@ -336,6 +350,9 @@ export default compose( policyTags: { key: ({report}) => `${ONYXKEYS.COLLECTION.POLICY_TAGS}${report ? report.policyID : '0'}`, }, + policyTaxRates: { + key: ({report}) => `${ONYXKEYS.COLLECTION.POLICY_TAX_RATE}${report.policyID}`, + }, parentReportActions: { key: ({report}) => `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${report ? report.parentReportID : '0'}`, canEvict: false, From 24e7c072e69f5853ac6499d2d1345f7cf807bf7d Mon Sep 17 00:00:00 2001 From: Etotaziba Olei Date: Mon, 8 Jan 2024 17:17:19 +0100 Subject: [PATCH 044/500] add update functions to taxAmount and taxRate --- src/pages/EditRequestPage.js | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/src/pages/EditRequestPage.js b/src/pages/EditRequestPage.js index f47950eb71ff..df13c727e430 100644 --- a/src/pages/EditRequestPage.js +++ b/src/pages/EditRequestPage.js @@ -139,6 +139,26 @@ function EditRequestPage({report, policy, policyTaxRates, route, policyCategorie Navigation.dismissModal(report.reportID); } + const updateTaxAmount = (transactionChanges) => { + if (transactionChanges.amount === transactionTaxAmount) { + return; + } + + const newTaxAmount = CurrencyUtils.convertToBackendAmount(Number.parseFloat(transactionChanges.amount)); + IOU.updateMoneyRequestTaxAmount(transaction.transactionID, report.reportID, newTaxAmount); + Navigation.dismissModal(report.reportID); + }; + + const updateTaxRate = (transactionChanges) => { + const newTaxCode = transactionChanges.data.code; + if (newTaxCode === transactionTaxCode) { + return; + } + + IOU.updateMoneyRequestTaxRate(transaction.transactionID, report.reportID, newTaxCode); + Navigation.dismissModal(report.reportID); + }; + const saveAmountAndCurrency = useCallback( ({amount, currency: newCurrency}) => { const newAmount = CurrencyUtils.convertToBackendAmount(Number.parseFloat(amount)); @@ -285,7 +305,7 @@ function EditRequestPage({report, policy, policyTaxRates, route, policyCategorie const activeRoute = encodeURIComponent(Navigation.getActiveRouteWithoutParams()); Navigation.navigate(ROUTES.EDIT_CURRENCY_REQUEST.getRoute(report.reportID, defaultCurrency, activeRoute)); }} - onSubmit={() => {}} + onSubmit={updateTaxAmount} /> ); } @@ -295,7 +315,7 @@ function EditRequestPage({report, policy, policyTaxRates, route, policyCategorie {}} + onSubmit={updateTaxRate} /> ); } From 101aa5b69eceb36e96d8ba09d1208be8f369fbee Mon Sep 17 00:00:00 2001 From: Etotaziba Olei Date: Mon, 8 Jan 2024 19:59:18 +0100 Subject: [PATCH 045/500] update default amount and taxAmount --- src/pages/EditRequestPage.js | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/pages/EditRequestPage.js b/src/pages/EditRequestPage.js index df13c727e430..3f346d649bca 100644 --- a/src/pages/EditRequestPage.js +++ b/src/pages/EditRequestPage.js @@ -82,7 +82,10 @@ const defaultProps = { policy: {}, policyTaxRates: {}, }; - +const getTaxAmount = (transaction, transactionTaxCode, policyTaxRates) => { + const percentage = (transactionTaxCode ? policyTaxRates.taxes[transactionTaxCode].value : policyTaxRates.defaultValue) || ''; + return CurrencyUtils.convertToBackendAmount(Number.parseFloat(TransactionUtils.calculateTaxAmount(percentage, transaction.amount))); +}; function EditRequestPage({report, policy, policyTaxRates, route, policyCategories, policyTags, parentReportActions, transaction}) { const parentReportActionID = lodashGet(report, 'parentReportActionID', '0'); const parentReportAction = lodashGet(parentReportActions, parentReportActionID, {}); @@ -298,8 +301,8 @@ function EditRequestPage({report, policy, policyTaxRates, route, policyCategorie if (fieldToEdit === CONST.EDIT_REQUEST_FIELD.TAX_AMOUNT && shouldShowTax) { return ( { const activeRoute = encodeURIComponent(Navigation.getActiveRouteWithoutParams()); From 61bf4767d02c4f00ed8eecd6f343567dad43a5dc Mon Sep 17 00:00:00 2001 From: Etotaziba Olei Date: Tue, 9 Jan 2024 07:25:51 +0100 Subject: [PATCH 046/500] use function for tax policy --- src/components/MoneyRequestConfirmationList.js | 5 +++-- .../MoneyTemporaryForRefactorRequestConfirmationList.js | 3 ++- src/components/ReportActionItem/MoneyRequestView.js | 6 ++---- src/libs/PolicyUtils.ts | 5 +++++ src/pages/EditRequestPage.js | 5 +++-- src/types/onyx/Policy.ts | 3 +++ 6 files changed, 18 insertions(+), 9 deletions(-) diff --git a/src/components/MoneyRequestConfirmationList.js b/src/components/MoneyRequestConfirmationList.js index 260c27205e94..385d411427ba 100755 --- a/src/components/MoneyRequestConfirmationList.js +++ b/src/components/MoneyRequestConfirmationList.js @@ -20,6 +20,7 @@ import * as MoneyRequestUtils from '@libs/MoneyRequestUtils'; import Navigation from '@libs/Navigation/Navigation'; import * as OptionsListUtils from '@libs/OptionsListUtils'; import * as PolicyUtils from '@libs/PolicyUtils'; +import {isTaxPolicyEnabled} from '@libs/PolicyUtils'; import * as ReceiptUtils from '@libs/ReceiptUtils'; import * as ReportUtils from '@libs/ReportUtils'; import * as TransactionUtils from '@libs/TransactionUtils'; @@ -247,8 +248,8 @@ function MoneyRequestConfirmationList(props) { // A flag for showing the tags field const shouldShowTags = props.isPolicyExpenseChat && (props.iouTag || OptionsListUtils.hasEnabledOptions(_.values(policyTagList))); - // A flag for showing tax fields - tax rate and tax amount - const shouldShowTax = props.isPolicyExpenseChat && props.policy.isTaxTrackingEnabled; + // A flag for showing tax rate + const shouldShowTax = isTaxPolicyEnabled(props.isPolicyExpenseChat, props.policy); // A flag for showing the billable field const shouldShowBillable = !lodashGet(props.policy, 'disabledFields.defaultBillable', true); diff --git a/src/components/MoneyTemporaryForRefactorRequestConfirmationList.js b/src/components/MoneyTemporaryForRefactorRequestConfirmationList.js index 335a9e2f6801..5bd42ff11cf7 100755 --- a/src/components/MoneyTemporaryForRefactorRequestConfirmationList.js +++ b/src/components/MoneyTemporaryForRefactorRequestConfirmationList.js @@ -20,6 +20,7 @@ import * as MoneyRequestUtils from '@libs/MoneyRequestUtils'; import Navigation from '@libs/Navigation/Navigation'; import * as OptionsListUtils from '@libs/OptionsListUtils'; import * as PolicyUtils from '@libs/PolicyUtils'; +import {isTaxPolicyEnabled} from '@libs/PolicyUtils'; import * as ReceiptUtils from '@libs/ReceiptUtils'; import * as ReportUtils from '@libs/ReportUtils'; import * as TransactionUtils from '@libs/TransactionUtils'; @@ -283,7 +284,7 @@ function MoneyTemporaryForRefactorRequestConfirmationList({ const shouldShowTags = isPolicyExpenseChat && OptionsListUtils.hasEnabledOptions(_.values(policyTagList)); // A flag for showing tax rate - const shouldShowTax = isPolicyExpenseChat && policy && policy.isTaxTrackingEnabled; + const shouldShowTax = isTaxPolicyEnabled(isPolicyExpenseChat, policy); // A flag for showing the billable field const shouldShowBillable = !lodashGet(policy, 'disabledFields.defaultBillable', true); diff --git a/src/components/ReportActionItem/MoneyRequestView.js b/src/components/ReportActionItem/MoneyRequestView.js index a38b711e7192..bd22c3f42415 100644 --- a/src/components/ReportActionItem/MoneyRequestView.js +++ b/src/components/ReportActionItem/MoneyRequestView.js @@ -30,6 +30,7 @@ import * as CurrencyUtils from '@libs/CurrencyUtils'; import Navigation from '@libs/Navigation/Navigation'; import * as OptionsListUtils from '@libs/OptionsListUtils'; import * as PolicyUtils from '@libs/PolicyUtils'; +import {isTaxPolicyEnabled} from '@libs/PolicyUtils'; import * as ReceiptUtils from '@libs/ReceiptUtils'; import * as ReportActionsUtils from '@libs/ReportActionsUtils'; import * as ReportUtils from '@libs/ReportUtils'; @@ -188,10 +189,7 @@ function MoneyRequestView({report, parentReport, parentReportActions, policyCate // A flag for showing tax rate const shouldShowTax = - isPolicyExpenseChat && - policy && - policy.isTaxTrackingEnabled && - ((transactionTaxCode && transactionTaxAmount) || OptionsListUtils.hasEnabledOptionsForTaxRate(lodashValues(policyTaxRates.taxes))); + isTaxPolicyEnabled(isPolicyExpenseChat, policy) && ((transactionTaxCode && transactionTaxAmount) || OptionsListUtils.hasEnabledOptionsForTaxRate(lodashValues(policyTaxRates.taxes))); const {getViolationsForField} = useViolations(transactionViolations); const hasViolations = useCallback((field) => canUseViolations && getViolationsForField(field).length > 0, [canUseViolations, getViolationsForField]); diff --git a/src/libs/PolicyUtils.ts b/src/libs/PolicyUtils.ts index 0cab97299324..831dc625cb95 100644 --- a/src/libs/PolicyUtils.ts +++ b/src/libs/PolicyUtils.ts @@ -202,6 +202,10 @@ function isPaidGroupPolicy(policy: OnyxEntry): boolean { return policy?.type === CONST.POLICY.TYPE.TEAM || policy?.type === CONST.POLICY.TYPE.CORPORATE; } +function isTaxPolicyEnabled(isPolicyExpenseChat: boolean, policy: OnyxEntry): boolean { + return (isPolicyExpenseChat && policy?.isTaxTrackingEnabled) ?? false; +} + export { getActivePolicies, hasPolicyMemberError, @@ -215,6 +219,7 @@ export { isExpensifyTeam, isExpensifyGuideTeam, isPolicyAdmin, + isTaxPolicyEnabled, getMemberAccountIDsForWorkspace, getIneligibleInvitees, getTag, diff --git a/src/pages/EditRequestPage.js b/src/pages/EditRequestPage.js index 7f9839f39111..8f1830e43a5f 100644 --- a/src/pages/EditRequestPage.js +++ b/src/pages/EditRequestPage.js @@ -14,6 +14,7 @@ import * as CurrencyUtils from '@libs/CurrencyUtils'; import Navigation from '@libs/Navigation/Navigation'; import * as OptionsListUtils from '@libs/OptionsListUtils'; import * as PolicyUtils from '@libs/PolicyUtils'; +import {isTaxPolicyEnabled} from '@libs/PolicyUtils'; import * as ReportUtils from '@libs/ReportUtils'; import * as TransactionUtils from '@libs/TransactionUtils'; import * as IOU from '@userActions/IOU'; @@ -83,7 +84,7 @@ const defaultProps = { policyTaxRates: {}, }; const getTaxAmount = (transaction, transactionTaxCode, policyTaxRates) => { - const percentage = (transactionTaxCode ? policyTaxRates.taxes[transactionTaxCode].value : policyTaxRates.defaultValue) || ''; + const percentage = (transactionTaxCode ? policyTaxRates.taxes[transactionTaxCode].value : policyTaxRates.defaultValue) || ''; return CurrencyUtils.convertToBackendAmount(Number.parseFloat(TransactionUtils.calculateTaxAmount(percentage, transaction.amount))); }; function EditRequestPage({report, policy, policyTaxRates, route, policyCategories, policyTags, parentReportActions, transaction}) { @@ -121,7 +122,7 @@ function EditRequestPage({report, policy, policyTaxRates, route, policyCategorie const shouldShowTags = isPolicyExpenseChat && (transactionTag || OptionsListUtils.hasEnabledOptions(lodashValues(policyTagList))); // A flag for showing tax rate - const shouldShowTax = isPolicyExpenseChat && policy && policy.isTaxTrackingEnabled; + const shouldShowTax = isTaxPolicyEnabled(isPolicyExpenseChat, policy); // Decides whether to allow or disallow editing a money request useEffect(() => { diff --git a/src/types/onyx/Policy.ts b/src/types/onyx/Policy.ts index ff3a5e1dd23c..fc2eb02171da 100644 --- a/src/types/onyx/Policy.ts +++ b/src/types/onyx/Policy.ts @@ -65,6 +65,9 @@ type Policy = { /** Whether chat rooms can be created and used on this policy. Enabled manually by CQ/JS snippet. Always true for free policies. */ areChatRoomsEnabled: boolean; + /** Is Tax tracking Enabled */ + isTaxTrackingEnabled: boolean; + /** Whether policy expense chats can be created and used on this policy. Enabled manually by CQ/JS snippet. Always true for free policies. */ isPolicyExpenseChatEnabled: boolean; From e85912965de70ef3c933b9b2ed7c9d22a80f8671 Mon Sep 17 00:00:00 2001 From: Etotaziba Olei Date: Tue, 9 Jan 2024 07:35:50 +0100 Subject: [PATCH 047/500] make isTaxTrackingEnabled optional --- src/types/onyx/Policy.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/types/onyx/Policy.ts b/src/types/onyx/Policy.ts index fc2eb02171da..fc7f273de76d 100644 --- a/src/types/onyx/Policy.ts +++ b/src/types/onyx/Policy.ts @@ -66,7 +66,7 @@ type Policy = { areChatRoomsEnabled: boolean; /** Is Tax tracking Enabled */ - isTaxTrackingEnabled: boolean; + isTaxTrackingEnabled?: boolean; /** Whether policy expense chats can be created and used on this policy. Enabled manually by CQ/JS snippet. Always true for free policies. */ isPolicyExpenseChatEnabled: boolean; From fab239109cc035481c0429affee138a4ac7d01b8 Mon Sep 17 00:00:00 2001 From: Etotaziba Olei Date: Wed, 10 Jan 2024 05:08:53 +0100 Subject: [PATCH 048/500] add tax amount and code type to Transaction types --- src/types/onyx/Transaction.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/types/onyx/Transaction.ts b/src/types/onyx/Transaction.ts index 399f1414db70..4aa61b8923c5 100644 --- a/src/types/onyx/Transaction.ts +++ b/src/types/onyx/Transaction.ts @@ -51,6 +51,8 @@ type Routes = Record; type Transaction = { amount: number; + taxAmount?: number; + taxCode?: string; billable: boolean; category: string; comment: Comment; From 9fd85f95026327b8f919252ba26f8320eb045bb2 Mon Sep 17 00:00:00 2001 From: Etotaziba Olei Date: Wed, 10 Jan 2024 05:11:34 +0100 Subject: [PATCH 049/500] get tax amount abs value and tax code --- src/libs/TransactionUtils.ts | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/src/libs/TransactionUtils.ts b/src/libs/TransactionUtils.ts index 5f8443b126c5..1b78d6f8ab6d 100644 --- a/src/libs/TransactionUtils.ts +++ b/src/libs/TransactionUtils.ts @@ -282,6 +282,27 @@ function getAmount(transaction: OnyxEntry, isFromExpenseReport: boo return amount ? -amount : 0; } +/** + * Return the tax amount field from the transaction. + */ +function getTaxAmount(transaction: OnyxEntry, isFromExpenseReport: boolean): number { + // IOU requests cannot have negative values but they can be stored as negative values, let's return absolute value + if (!isFromExpenseReport) { + return Math.abs(transaction?.taxAmount ?? 0); + } + + // To avoid -0 being shown, lets only change the sign if the value is other than 0. + const amount = transaction?.taxAmount ?? 0; + return amount ? -amount : 0; +} + +/** + * Return the tax code from the transaction. + */ +function getTaxCode(transaction: OnyxEntry): string { + return transaction?.taxCode ?? ''; +} + /** * Return the currency field from the transaction, return the modifiedCurrency if present. */ @@ -559,6 +580,8 @@ export { isManualRequest, isScanRequest, getAmount, + getTaxAmount, + getTaxCode, getCurrency, getDistance, getCardID, From 33cf5c438ec307cedb1fc1ef6b17691b046050b6 Mon Sep 17 00:00:00 2001 From: Etotaziba Olei Date: Wed, 10 Jan 2024 05:13:32 +0100 Subject: [PATCH 050/500] add new fields in getTransactionDetails --- src/libs/ReportUtils.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index 0d7658adf180..e4d7cb728e82 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -281,6 +281,8 @@ type TransactionDetails = | { created: string; amount: number; + taxAmount?: number; + taxCode?: string; currency: string; merchant: string; waypoints?: WaypointCollection; @@ -1839,6 +1841,8 @@ function getTransactionDetails(transaction: OnyxEntry, createdDateF return { created: TransactionUtils.getCreated(transaction, createdDateFormat), amount: TransactionUtils.getAmount(transaction, isNotEmptyObject(report) && isExpenseReport(report)), + taxAmount: TransactionUtils.getTaxAmount(transaction, isNotEmptyObject(report) && isExpenseReport(report)), + taxCode: TransactionUtils.getTaxCode(transaction), currency: TransactionUtils.getCurrency(transaction), comment: TransactionUtils.getDescription(transaction), merchant: TransactionUtils.getMerchant(transaction), From 9d6e5b0372519213cf0b9dd92cd7d2a3370e6e9d Mon Sep 17 00:00:00 2001 From: Etotaziba Olei Date: Wed, 10 Jan 2024 05:16:06 +0100 Subject: [PATCH 051/500] get and use new tax amount and code fields from getTransactionDetails --- .../ReportActionItem/MoneyRequestView.js | 15 ++++++------ src/pages/EditRequestPage.js | 23 +++++++------------ 2 files changed, 16 insertions(+), 22 deletions(-) diff --git a/src/components/ReportActionItem/MoneyRequestView.js b/src/components/ReportActionItem/MoneyRequestView.js index 73bbda7bd3b7..c656ff45add7 100644 --- a/src/components/ReportActionItem/MoneyRequestView.js +++ b/src/components/ReportActionItem/MoneyRequestView.js @@ -135,6 +135,8 @@ function MoneyRequestView({report, parentReport, parentReportActions, policyCate const { created: transactionDate, amount: transactionAmount, + taxAmount: transactionTaxAmount, + taxCode: transactionTaxCode, currency: transactionCurrency, comment: transactionDescription, merchant: transactionMerchant, @@ -156,11 +158,11 @@ function MoneyRequestView({report, parentReport, parentReportActions, policyCate const isCardTransaction = TransactionUtils.isCardTransaction(transaction); const cardProgramName = isCardTransaction ? CardUtils.getCardDescription(transactionCardID) : ''; - const transactionTaxAmount = (transaction.taxAmount && transaction.taxAmount) || 0; - const formattedTaxAmount = CurrencyUtils.convertToDisplayString(transactionTaxAmount, transactionCurrency); + const formattedTaxAmount = transactionTaxAmount ? CurrencyUtils.convertToDisplayString(transactionTaxAmount, transactionCurrency) : ''; - const transactionTaxCode = transaction.taxCode && transaction.taxCode; - const taxRateTitle = (transactionTaxCode && policyTaxRates.taxes[transactionTaxCode].name) || ''; + const taxName = `${policyTaxRates.taxes[transactionTaxCode].name}`; + const taxValue = `${policyTaxRates.taxes[transactionTaxCode].value}`; + const taxRateTitle = transactionTaxCode ? `${taxName} (${taxValue})` : ''; // Flags for allowing or disallowing editing a money request const isSettled = ReportUtils.isSettled(moneyRequestReport.reportID); @@ -188,8 +190,7 @@ function MoneyRequestView({report, parentReport, parentReportActions, policyCate const shouldShowBillable = isPolicyExpenseChat && (transactionBillable || !lodashGet(policy, 'disabledFields.defaultBillable', true)); // A flag for showing tax rate - const shouldShowTax = - isTaxPolicyEnabled(isPolicyExpenseChat, policy) && ((transactionTaxCode && transactionTaxAmount) || OptionsListUtils.hasEnabledOptionsForTaxRate(lodashValues(policyTaxRates.taxes))); + const shouldShowTax = isTaxPolicyEnabled(isPolicyExpenseChat, policy) && transactionTaxCode && transactionTaxAmount; const {getViolationsForField} = useViolations(transactionViolations); const hasViolations = useCallback((field) => canUseViolations && getViolationsForField(field).length > 0, [canUseViolations, getViolationsForField]); @@ -393,7 +394,7 @@ function MoneyRequestView({report, parentReport, parentReportActions, policyCate {shouldShowTax && ( { +const getTaxAmount = (transactionAmount, transactionTaxCode, policyTaxRates) => { const percentage = (transactionTaxCode ? policyTaxRates.taxes[transactionTaxCode].value : policyTaxRates.defaultValue) || ''; - return CurrencyUtils.convertToBackendAmount(Number.parseFloat(TransactionUtils.calculateTaxAmount(percentage, transaction.amount))); + return CurrencyUtils.convertToBackendAmount(Number.parseFloat(TransactionUtils.calculateTaxAmount(percentage, transactionAmount))); }; function EditRequestPage({report, policy, policyTaxRates, route, policyCategories, policyTags, parentReportActions, transaction}) { const parentReportActionID = lodashGet(report, 'parentReportActionID', '0'); const parentReportAction = lodashGet(parentReportActions, parentReportActionID, {}); const { amount: transactionAmount, + taxAmount: transactionTaxAmount, + taxCode: transactionTaxCode, currency: transactionCurrency, comment: transactionDescription, merchant: transactionMerchant, @@ -102,10 +104,9 @@ function EditRequestPage({report, policy, policyTaxRates, route, policyCategorie const defaultCurrency = lodashGet(route, 'params.currency', '') || transactionCurrency; const fieldToEdit = lodashGet(route, ['params', 'field'], ''); - const transactionTaxAmount = (transaction.taxAmount && transaction.taxAmount) || 0; - - const transactionTaxCode = transaction.taxCode && transaction.taxCode; - const taxRateTitle = (transactionTaxCode && policyTaxRates.taxes[transactionTaxCode].name) || ''; + const taxName = `${policyTaxRates.taxes[transactionTaxCode].name}`; + const taxValue = `${policyTaxRates.taxes[transactionTaxCode].value}`; + const taxRateTitle = transactionTaxCode ? `${taxName} (${taxValue})` : ''; // For now, it always defaults to the first tag of the policy const policyTag = PolicyUtils.getTag(policyTags); @@ -144,10 +145,6 @@ function EditRequestPage({report, policy, policyTaxRates, route, policyCategorie } const updateTaxAmount = (transactionChanges) => { - if (transactionChanges.amount === transactionTaxAmount) { - return; - } - const newTaxAmount = CurrencyUtils.convertToBackendAmount(Number.parseFloat(transactionChanges.amount)); IOU.updateMoneyRequestTaxAmount(transaction.transactionID, report.reportID, newTaxAmount); Navigation.dismissModal(report.reportID); @@ -155,10 +152,6 @@ function EditRequestPage({report, policy, policyTaxRates, route, policyCategorie const updateTaxRate = (transactionChanges) => { const newTaxCode = transactionChanges.data.code; - if (newTaxCode === transactionTaxCode) { - return; - } - IOU.updateMoneyRequestTaxRate(transaction.transactionID, report.reportID, newTaxCode); Navigation.dismissModal(report.reportID); }; @@ -310,7 +303,7 @@ function EditRequestPage({report, policy, policyTaxRates, route, policyCategorie return ( { const activeRoute = encodeURIComponent(Navigation.getActiveRouteWithoutParams()); From 25cb89edceac9386323076eb7eda8b53a5e9a17c Mon Sep 17 00:00:00 2001 From: Etotaziba Olei Date: Wed, 10 Jan 2024 05:29:28 +0100 Subject: [PATCH 052/500] add getTaxName to TransactionUtils --- src/libs/TransactionUtils.ts | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/libs/TransactionUtils.ts b/src/libs/TransactionUtils.ts index 1b78d6f8ab6d..8aa9f05f813b 100644 --- a/src/libs/TransactionUtils.ts +++ b/src/libs/TransactionUtils.ts @@ -558,7 +558,7 @@ function getEnabledTaxRateCount(options: TaxRates) { } /** - * Calculates get's the default tax name + * Get's the default tax name */ function getDefaultTaxName(policyTaxRates: PolicyTaxRates, transaction: Transaction) { const defaultTaxKey = policyTaxRates.defaultExternalID; @@ -567,9 +567,19 @@ function getDefaultTaxName(policyTaxRates: PolicyTaxRates, transaction: Transact return transaction?.taxRate?.text ?? defaultTaxName; } +/** + * Get's the tax name + */ +function getTaxName(taxes: TaxRates, transactionTaxCode: string) { + const taxName = `${taxes[transactionTaxCode].name}`; + const taxValue = `${taxes[transactionTaxCode].value}`; + return transactionTaxCode ? `${taxName} (${taxValue})` : ''; +} + export { buildOptimisticTransaction, calculateTaxAmount, + getTaxName, getDefaultTaxName, getEnabledTaxRateCount, getUpdatedTransaction, From 9a9b1820bd7d3d8b157ad0d5cdc846a36a1bbbad Mon Sep 17 00:00:00 2001 From: Etotaziba Olei Date: Wed, 10 Jan 2024 05:30:35 +0100 Subject: [PATCH 053/500] use getTaxName --- src/components/ReportActionItem/MoneyRequestView.js | 4 +--- src/pages/EditRequestPage.js | 4 +--- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/src/components/ReportActionItem/MoneyRequestView.js b/src/components/ReportActionItem/MoneyRequestView.js index c656ff45add7..60ed51d10506 100644 --- a/src/components/ReportActionItem/MoneyRequestView.js +++ b/src/components/ReportActionItem/MoneyRequestView.js @@ -160,9 +160,7 @@ function MoneyRequestView({report, parentReport, parentReportActions, policyCate const formattedTaxAmount = transactionTaxAmount ? CurrencyUtils.convertToDisplayString(transactionTaxAmount, transactionCurrency) : ''; - const taxName = `${policyTaxRates.taxes[transactionTaxCode].name}`; - const taxValue = `${policyTaxRates.taxes[transactionTaxCode].value}`; - const taxRateTitle = transactionTaxCode ? `${taxName} (${taxValue})` : ''; + const taxRateTitle = TransactionUtils.getTaxName(policyTaxRates.taxes, transactionTaxCode); // Flags for allowing or disallowing editing a money request const isSettled = ReportUtils.isSettled(moneyRequestReport.reportID); diff --git a/src/pages/EditRequestPage.js b/src/pages/EditRequestPage.js index a3375efbbdd5..7508e0796845 100644 --- a/src/pages/EditRequestPage.js +++ b/src/pages/EditRequestPage.js @@ -104,9 +104,7 @@ function EditRequestPage({report, policy, policyTaxRates, route, policyCategorie const defaultCurrency = lodashGet(route, 'params.currency', '') || transactionCurrency; const fieldToEdit = lodashGet(route, ['params', 'field'], ''); - const taxName = `${policyTaxRates.taxes[transactionTaxCode].name}`; - const taxValue = `${policyTaxRates.taxes[transactionTaxCode].value}`; - const taxRateTitle = transactionTaxCode ? `${taxName} (${taxValue})` : ''; + const taxRateTitle = TransactionUtils.getTaxName(policyTaxRates.taxes, transactionTaxCode); // For now, it always defaults to the first tag of the policy const policyTag = PolicyUtils.getTag(policyTags); From f723d0f68bf58f750df537c856f73845d95f044a Mon Sep 17 00:00:00 2001 From: Etotaziba Olei Date: Wed, 10 Jan 2024 13:45:33 +0100 Subject: [PATCH 054/500] Update src/components/MoneyTemporaryForRefactorRequestConfirmationList.js Co-authored-by: Michael (Mykhailo) Kravchenko --- .../MoneyTemporaryForRefactorRequestConfirmationList.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/MoneyTemporaryForRefactorRequestConfirmationList.js b/src/components/MoneyTemporaryForRefactorRequestConfirmationList.js index 5bd42ff11cf7..85948dbaa530 100755 --- a/src/components/MoneyTemporaryForRefactorRequestConfirmationList.js +++ b/src/components/MoneyTemporaryForRefactorRequestConfirmationList.js @@ -360,7 +360,7 @@ function MoneyTemporaryForRefactorRequestConfirmationList({ IOU.setMoneyRequestAmount_temporaryForRefactor(transaction.transactionID, amount, currency); }, [shouldCalculateDistanceAmount, distance, rate, unit, transaction, currency]); - // calculate and set tax amount in transaction draft + // Calculate and set tax amount in transaction draft useEffect(() => { const taxAmount = getTaxAmount(transaction, policyTaxRates.defaultValue); const amountInSmallestCurrencyUnits = CurrencyUtils.convertToBackendAmount(Number.parseFloat(taxAmount)); From cc2831e8358a09c23db14511811df9141fcdde3c Mon Sep 17 00:00:00 2001 From: Etotaziba Olei Date: Wed, 10 Jan 2024 13:45:48 +0100 Subject: [PATCH 055/500] Update src/libs/TransactionUtils.ts Co-authored-by: Michael (Mykhailo) Kravchenko --- src/libs/TransactionUtils.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/TransactionUtils.ts b/src/libs/TransactionUtils.ts index 8aa9f05f813b..87c23974ab8a 100644 --- a/src/libs/TransactionUtils.ts +++ b/src/libs/TransactionUtils.ts @@ -568,7 +568,7 @@ function getDefaultTaxName(policyTaxRates: PolicyTaxRates, transaction: Transact } /** - * Get's the tax name + * Gets the tax name */ function getTaxName(taxes: TaxRates, transactionTaxCode: string) { const taxName = `${taxes[transactionTaxCode].name}`; From 1270f49460c6e83d4e429c868723f583da8fe4ce Mon Sep 17 00:00:00 2001 From: Etotaziba Olei Date: Wed, 10 Jan 2024 13:45:56 +0100 Subject: [PATCH 056/500] Update src/libs/TransactionUtils.ts Co-authored-by: Michael (Mykhailo) Kravchenko --- src/libs/TransactionUtils.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/TransactionUtils.ts b/src/libs/TransactionUtils.ts index 87c23974ab8a..64e3ab79ab2b 100644 --- a/src/libs/TransactionUtils.ts +++ b/src/libs/TransactionUtils.ts @@ -558,7 +558,7 @@ function getEnabledTaxRateCount(options: TaxRates) { } /** - * Get's the default tax name + * Gets the default tax name */ function getDefaultTaxName(policyTaxRates: PolicyTaxRates, transaction: Transaction) { const defaultTaxKey = policyTaxRates.defaultExternalID; From 37d651384db96233599b2410a582a35cca5173d0 Mon Sep 17 00:00:00 2001 From: Etotaziba Olei Date: Wed, 10 Jan 2024 13:46:42 +0100 Subject: [PATCH 057/500] Update src/types/onyx/Policy.ts Co-authored-by: Michael (Mykhailo) Kravchenko --- src/types/onyx/Policy.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/types/onyx/Policy.ts b/src/types/onyx/Policy.ts index 5d315508f35a..3a7aacd5808a 100644 --- a/src/types/onyx/Policy.ts +++ b/src/types/onyx/Policy.ts @@ -62,7 +62,7 @@ type Policy = { /** The custom units data for this policy */ customUnits?: Record; - /** Is Tax tracking Enabled */ + /** Is tax tracking enabled */ isTaxTrackingEnabled?: boolean; /** Whether policy expense chats can be created and used on this policy. Enabled manually by CQ/JS snippet. Always true for free policies. */ From 584df427d6b35fc5c2b1e70e03ef88a9429320b2 Mon Sep 17 00:00:00 2001 From: Etotaziba Olei Date: Wed, 10 Jan 2024 13:47:06 +0100 Subject: [PATCH 058/500] Update src/pages/EditRequestPage.js Co-authored-by: Michael (Mykhailo) Kravchenko --- src/pages/EditRequestPage.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/pages/EditRequestPage.js b/src/pages/EditRequestPage.js index 7508e0796845..46fb719d26b5 100644 --- a/src/pages/EditRequestPage.js +++ b/src/pages/EditRequestPage.js @@ -62,14 +62,13 @@ const propTypes = { /** Transaction that stores the request data */ transaction: transactionPropTypes, - + /* Onyx Props */ /** The policy of the report */ policy: PropTypes.shape({ /** Is Tax tracking Enabled */ isTaxTrackingEnabled: PropTypes.bool, }), - /* Onyx Props */ /** Collection of tax rates attached to a policy */ policyTaxRates: taxPropTypes, }; From 1e8ffbc1da439bc23602f9bd67985dbbc0c51ed2 Mon Sep 17 00:00:00 2001 From: Etotaziba Olei Date: Tue, 16 Jan 2024 10:59:42 +0100 Subject: [PATCH 059/500] fix prettier --- src/libs/actions/IOU.js | 44 ++++++++++++++++++++--------------------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/src/libs/actions/IOU.js b/src/libs/actions/IOU.js index 47d959db9f16..816763037c3d 100644 --- a/src/libs/actions/IOU.js +++ b/src/libs/actions/IOU.js @@ -1184,33 +1184,33 @@ function updateMoneyRequestTag(transactionID, transactionThreadReportID, tag) { } /** -* Updates the created tax amount of a money request -* -* @param {String} transactionID -* @param {String} optimisticReportActionID -* @param {Number} val -*/ + * Updates the created tax amount of a money request + * + * @param {String} transactionID + * @param {String} optimisticReportActionID + * @param {Number} val + */ function updateMoneyRequestTaxAmount(transactionID, optimisticReportActionID, val) { - const transactionChanges = { - taxAmount: val, - }; - const {params, onyxData} = getUpdateMoneyRequestParams(transactionID, optimisticReportActionID, transactionChanges, true); - API.write('UpdateMoneyRequestTaxAmount', params, onyxData); + const transactionChanges = { + taxAmount: val, + }; + const {params, onyxData} = getUpdateMoneyRequestParams(transactionID, optimisticReportActionID, transactionChanges, true); + API.write('UpdateMoneyRequestTaxAmount', params, onyxData); } /** -* Updates the created tax rate of a money request -* -* @param {String} transactionID -* @param {String} optimisticReportActionID -* @param {String} val -*/ + * Updates the created tax rate of a money request + * + * @param {String} transactionID + * @param {String} optimisticReportActionID + * @param {String} val + */ function updateMoneyRequestTaxRate(transactionID, optimisticReportActionID, val) { - const transactionChanges = { - taxCode: val, - }; - const {params, onyxData} = getUpdateMoneyRequestParams(transactionID, optimisticReportActionID, transactionChanges, true); - API.write('UpdateMoneyRequestTaxRate', params, onyxData); + const transactionChanges = { + taxCode: val, + }; + const {params, onyxData} = getUpdateMoneyRequestParams(transactionID, optimisticReportActionID, transactionChanges, true); + API.write('UpdateMoneyRequestTaxRate', params, onyxData); } /** From 404732f89f3c9c3032db907ef6b4acb47b5c9059 Mon Sep 17 00:00:00 2001 From: Aldo Canepa Date: Mon, 22 Jan 2024 13:34:48 -0300 Subject: [PATCH 060/500] Kill ReconnectToReport --- src/libs/actions/Report.ts | 59 ---------------------- src/pages/home/report/ReportActionsView.js | 29 +---------- 2 files changed, 2 insertions(+), 86 deletions(-) diff --git a/src/libs/actions/Report.ts b/src/libs/actions/Report.ts index 2ac85dfafa27..72b977d01e7d 100644 --- a/src/libs/actions/Report.ts +++ b/src/libs/actions/Report.ts @@ -767,58 +767,6 @@ function navigateToAndOpenChildReport(childReportID = '0', parentReportAction: P } } -/** Get the latest report history without marking the report as read. */ -function reconnect(reportID: string) { - const optimisticData: OnyxUpdate[] = [ - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT}${reportID}`, - value: { - reportName: allReports?.[reportID]?.reportName ?? CONST.REPORT.DEFAULT_REPORT_NAME, - }, - }, - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT_METADATA}${reportID}`, - value: { - isLoadingInitialReportActions: true, - isLoadingNewerReportActions: false, - isLoadingOlderReportActions: false, - }, - }, - ]; - - const successData: OnyxUpdate[] = [ - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT_METADATA}${reportID}`, - value: { - isLoadingInitialReportActions: false, - }, - }, - ]; - - const failureData: OnyxUpdate[] = [ - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT_METADATA}${reportID}`, - value: { - isLoadingInitialReportActions: false, - }, - }, - ]; - - type ReconnectToReportParameters = { - reportID: string; - }; - - const parameters: ReconnectToReportParameters = { - reportID, - }; - - API.write('ReconnectToReport', parameters, {optimisticData, successData, failureData}); -} - /** * Gets the older actions that have not been read yet. * Normally happens when you scroll up on a chat, and the actions have not been read yet. @@ -1095,12 +1043,6 @@ function handleReportChanged(report: OnyxEntry) { conciergeChatReportID = report.reportID; } } - - // A report can be missing a name if a comment is received via pusher event and the report does not yet exist in Onyx (eg. a new DM created with the logged in person) - // In this case, we call reconnect so that we can fetch the report data without marking it as read - if (report.reportID && report.reportName === undefined) { - reconnect(report.reportID); - } } Onyx.connect({ @@ -2654,7 +2596,6 @@ export { searchInServer, addComment, addAttachment, - reconnect, updateWelcomeMessage, updateWriteCapabilityAndNavigate, updateNotificationPreference, diff --git a/src/pages/home/report/ReportActionsView.js b/src/pages/home/report/ReportActionsView.js index 2758437a3962..d2ec0f124a62 100755 --- a/src/pages/home/report/ReportActionsView.js +++ b/src/pages/home/report/ReportActionsView.js @@ -88,10 +88,7 @@ function ReportActionsView(props) { const isFirstRender = useRef(true); const hasCachedActions = useInitialValue(() => _.size(props.reportActions) > 0); const mostRecentIOUReportActionID = useInitialValue(() => ReportActionsUtils.getMostRecentIOURequestActionID(props.reportActions)); - - const prevNetworkRef = useRef(props.network); const prevAuthTokenType = usePrevious(props.session.authTokenType); - const prevIsSmallScreenWidthRef = useRef(props.isSmallScreenWidth); const isFocused = useIsFocused(); @@ -118,36 +115,14 @@ function ReportActionsView(props) { // eslint-disable-next-line react-hooks/exhaustive-deps }, []); - useEffect(() => { - const prevNetwork = prevNetworkRef.current; - // When returning from offline to online state we want to trigger a request to OpenReport which - // will fetch the reportActions data and mark the report as read. If the report is not fully visible - // then we call ReconnectToReport which only loads the reportActions data without marking the report as read. - const wasNetworkChangeDetected = lodashGet(prevNetwork, 'isOffline') && !lodashGet(props.network, 'isOffline'); - if (wasNetworkChangeDetected) { - if (isReportFullyVisible) { - openReportIfNecessary(); - } else { - Report.reconnect(reportID); - } - } - // update ref with current network state - prevNetworkRef.current = props.network; - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [props.network, props.report, isReportFullyVisible]); useEffect(() => { const wasLoginChangedDetected = prevAuthTokenType === 'anonymousAccount' && !props.session.authTokenType; if (wasLoginChangedDetected && didUserLogInDuringSession() && isUserCreatedPolicyRoom(props.report)) { - if (isReportFullyVisible) { - openReportIfNecessary(); - } else { - Report.reconnect(reportID); - } + openReportIfNecessary(); } // eslint-disable-next-line react-hooks/exhaustive-deps - }, [props.session, props.report, isReportFullyVisible]); - + }, [props.session, props.report]); useEffect(() => { const prevIsSmallScreenWidth = prevIsSmallScreenWidthRef.current; // If the view is expanded from mobile to desktop layout From 0b4b6669a894f02f91af23fcac12e66e309e9494 Mon Sep 17 00:00:00 2001 From: Aldo Canepa Date: Mon, 22 Jan 2024 16:51:10 -0300 Subject: [PATCH 061/500] Lint --- src/pages/home/report/ReportActionsView.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/pages/home/report/ReportActionsView.js b/src/pages/home/report/ReportActionsView.js index d2ec0f124a62..adb771ed4f81 100755 --- a/src/pages/home/report/ReportActionsView.js +++ b/src/pages/home/report/ReportActionsView.js @@ -115,7 +115,6 @@ function ReportActionsView(props) { // eslint-disable-next-line react-hooks/exhaustive-deps }, []); - useEffect(() => { const wasLoginChangedDetected = prevAuthTokenType === 'anonymousAccount' && !props.session.authTokenType; if (wasLoginChangedDetected && didUserLogInDuringSession() && isUserCreatedPolicyRoom(props.report)) { From fb08033cd73698250232de21554945f06a8f569e Mon Sep 17 00:00:00 2001 From: Etotaziba Olei Date: Wed, 31 Jan 2024 06:18:28 +0100 Subject: [PATCH 062/500] remove duplicate identifier --- src/types/onyx/Policy.ts | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/types/onyx/Policy.ts b/src/types/onyx/Policy.ts index 23d7494f08cf..eca7e9d1ee06 100644 --- a/src/types/onyx/Policy.ts +++ b/src/types/onyx/Policy.ts @@ -76,9 +76,6 @@ type Policy = { /** The custom units data for this policy */ customUnits?: Record; - /** Is tax tracking enabled */ - isTaxTrackingEnabled?: boolean; - /** Whether chat rooms can be created and used on this policy. Enabled manually by CQ/JS snippet. Always true for free policies. */ areChatRoomsEnabled: boolean; From 70c02fd72cdb2cb34520bd5787c8bd40eef7796d Mon Sep 17 00:00:00 2001 From: Etotaziba Olei Date: Wed, 31 Jan 2024 06:37:34 +0100 Subject: [PATCH 063/500] fix lint and tsc --- src/libs/OptionsListUtils.ts | 10 ++-------- src/libs/TransactionUtils.ts | 2 +- 2 files changed, 3 insertions(+), 9 deletions(-) diff --git a/src/libs/OptionsListUtils.ts b/src/libs/OptionsListUtils.ts index d550ae4305b0..51d1bc9699ad 100644 --- a/src/libs/OptionsListUtils.ts +++ b/src/libs/OptionsListUtils.ts @@ -4,7 +4,6 @@ import Str from 'expensify-common/lib/str'; import lodashGet from 'lodash/get'; import lodashOrderBy from 'lodash/orderBy'; import lodashSet from 'lodash/set'; -import lodashSome from 'lodash/some'; import lodashSortBy from 'lodash/sortBy'; import Onyx from 'react-native-onyx'; import type {OnyxCollection, OnyxEntry} from 'react-native-onyx'; @@ -1148,13 +1147,9 @@ function getTagListSections(tags: Tag[], recentlyUsedTags: string[], selectedOpt /** * Verifies that there is at least one enabled option * - * @param {Object[]} options - an initial strings array - * @property {boolean} [isDisabled] - Indicates if the tax rate is disabled. - * @returns {Boolean} + * @param options - an initial strings array + * @returns boolean */ -function hasEnabledOptionsForTaxRate(options: TaxRates) { - return lodashSome(options, (option) => !option.isDisabled); -} /** * Transforms tax rates to a new object format - to add codes and new name with concatenated name and value. @@ -2001,7 +1996,6 @@ export { getLastMessageTextForReport, getEnabledCategoriesCount, hasEnabledOptions, - hasEnabledOptionsForTaxRate, sortCategories, getCategoryOptionTree, formatMemberForList, diff --git a/src/libs/TransactionUtils.ts b/src/libs/TransactionUtils.ts index 711ff3fbbcd2..13a77183d1c5 100644 --- a/src/libs/TransactionUtils.ts +++ b/src/libs/TransactionUtils.ts @@ -5,7 +5,7 @@ import type {ValueOf} from 'type-fest'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import type {RecentWaypoint, Report, ReportAction, Transaction, TransactionViolation} from '@src/types/onyx'; -import type {TaxRate, TaxRates, PolicyTaxRates} from '@src/types/onyx/PolicyTaxRates'; +import type {PolicyTaxRates, TaxRate, TaxRates} from '@src/types/onyx/PolicyTaxRates'; import type {Comment, Receipt, Waypoint, WaypointCollection} from '@src/types/onyx/Transaction'; import type {EmptyObject} from '@src/types/utils/EmptyObject'; import {isCorporateCard, isExpensifyCard} from './CardUtils'; From 841e4bff4cdc3192dcfd77fbf7319dd75b18ca13 Mon Sep 17 00:00:00 2001 From: Dylan Date: Wed, 31 Jan 2024 16:09:06 +0700 Subject: [PATCH 064/500] remove MoneyRequestConfirmPage --- src/ROUTES.ts | 4 - src/SCREENS.ts | 1 - src/components/DistanceRequest/index.js | 2 +- .../AppNavigator/ModalStackNavigators.tsx | 1 - src/libs/Navigation/linkingConfig.ts | 1 - src/libs/Navigation/types.ts | 3 +- src/libs/actions/IOU.js | 4 +- src/pages/iou/MoneyRequestCategoryPage.js | 4 +- src/pages/iou/MoneyRequestDatePage.js | 2 +- src/pages/iou/MoneyRequestDescriptionPage.js | 2 +- src/pages/iou/MoneyRequestMerchantPage.js | 2 +- src/pages/iou/MoneyRequestTagPage.js | 2 +- src/pages/iou/NewDistanceRequestPage.js | 2 +- .../step/IOURequestStepConfirmation.js | 14 +- .../iou/steps/MoneyRequestConfirmPage.js | 473 ------------------ .../MoneyRequestParticipantsPage.js | 2 +- src/pages/iou/steps/NewRequestAmountPage.js | 4 +- 17 files changed, 17 insertions(+), 506 deletions(-) delete mode 100644 src/pages/iou/steps/MoneyRequestConfirmPage.js diff --git a/src/ROUTES.ts b/src/ROUTES.ts index 9c4375b84ab6..deabdc0ac853 100644 --- a/src/ROUTES.ts +++ b/src/ROUTES.ts @@ -267,10 +267,6 @@ const ROUTES = { route: ':iouType/new/participants/:reportID?', getRoute: (iouType: string, reportID = '') => `${iouType}/new/participants/${reportID}` as const, }, - MONEY_REQUEST_CONFIRMATION: { - route: ':iouType/new/confirmation/:reportID?', - getRoute: (iouType: string, reportID = '') => `${iouType}/new/confirmation/${reportID}` as const, - }, MONEY_REQUEST_DATE: { route: ':iouType/new/date/:reportID?', getRoute: (iouType: string, reportID = '') => `${iouType}/new/date/${reportID}` as const, diff --git a/src/SCREENS.ts b/src/SCREENS.ts index 2bf40caede57..5a8922ee01c3 100644 --- a/src/SCREENS.ts +++ b/src/SCREENS.ts @@ -141,7 +141,6 @@ const SCREENS = { ROOT: 'Money_Request', AMOUNT: 'Money_Request_Amount', PARTICIPANTS: 'Money_Request_Participants', - CONFIRMATION: 'Money_Request_Confirmation', CURRENCY: 'Money_Request_Currency', DATE: 'Money_Request_Date', DESCRIPTION: 'Money_Request_Description', diff --git a/src/components/DistanceRequest/index.js b/src/components/DistanceRequest/index.js index b63ce337a1d9..34295dda74e7 100644 --- a/src/components/DistanceRequest/index.js +++ b/src/components/DistanceRequest/index.js @@ -163,7 +163,7 @@ function DistanceRequest({transactionID, report, transaction, route, isEditingRe }, [waypoints, previousWaypoints]); const navigateBack = () => { - Navigation.goBack(isEditingNewRequest ? ROUTES.MONEY_REQUEST_CONFIRMATION.getRoute(iouType, reportID) : ROUTES.HOME); + Navigation.goBack(isEditingNewRequest ? ROUTES.MONEY_REQUEST_STEP_CONFIRMATION.getRoute(iouType, transactionID, reportID) : ROUTES.HOME); }; /** diff --git a/src/libs/Navigation/AppNavigator/ModalStackNavigators.tsx b/src/libs/Navigation/AppNavigator/ModalStackNavigators.tsx index c9325206e5b2..3a843e400409 100644 --- a/src/libs/Navigation/AppNavigator/ModalStackNavigators.tsx +++ b/src/libs/Navigation/AppNavigator/ModalStackNavigators.tsx @@ -93,7 +93,6 @@ const MoneyRequestModalStackNavigator = createModalStackNavigator require('../../../pages/iou/MoneyRequestSelectorPage').default as React.ComponentType, [SCREENS.MONEY_REQUEST.AMOUNT]: () => require('../../../pages/iou/steps/NewRequestAmountPage').default as React.ComponentType, [SCREENS.MONEY_REQUEST.PARTICIPANTS]: () => require('../../../pages/iou/steps/MoneyRequstParticipantsPage/MoneyRequestParticipantsPage').default as React.ComponentType, - [SCREENS.MONEY_REQUEST.CONFIRMATION]: () => require('../../../pages/iou/steps/MoneyRequestConfirmPage').default as React.ComponentType, [SCREENS.MONEY_REQUEST.CURRENCY]: () => require('../../../pages/iou/IOUCurrencySelection').default as React.ComponentType, [SCREENS.MONEY_REQUEST.DATE]: () => require('../../../pages/iou/MoneyRequestDatePage').default as React.ComponentType, [SCREENS.MONEY_REQUEST.DESCRIPTION]: () => require('../../../pages/iou/MoneyRequestDescriptionPage').default as React.ComponentType, diff --git a/src/libs/Navigation/linkingConfig.ts b/src/libs/Navigation/linkingConfig.ts index 5df2bcf0e57b..d4e04d5402e2 100644 --- a/src/libs/Navigation/linkingConfig.ts +++ b/src/libs/Navigation/linkingConfig.ts @@ -428,7 +428,6 @@ const linkingConfig: LinkingOptions = { [SCREENS.MONEY_REQUEST.STEP_TAX_AMOUNT]: ROUTES.MONEY_REQUEST_STEP_TAX_AMOUNT.route, [SCREENS.MONEY_REQUEST.STEP_TAX_RATE]: ROUTES.MONEY_REQUEST_STEP_TAX_RATE.route, [SCREENS.MONEY_REQUEST.PARTICIPANTS]: ROUTES.MONEY_REQUEST_PARTICIPANTS.route, - [SCREENS.MONEY_REQUEST.CONFIRMATION]: ROUTES.MONEY_REQUEST_CONFIRMATION.route, [SCREENS.MONEY_REQUEST.DATE]: ROUTES.MONEY_REQUEST_DATE.route, [SCREENS.MONEY_REQUEST.CURRENCY]: ROUTES.MONEY_REQUEST_CURRENCY.route, [SCREENS.MONEY_REQUEST.DESCRIPTION]: ROUTES.MONEY_REQUEST_DESCRIPTION.route, diff --git a/src/libs/Navigation/types.ts b/src/libs/Navigation/types.ts index 2371c764f42a..29f7cd4aac30 100644 --- a/src/libs/Navigation/types.ts +++ b/src/libs/Navigation/types.ts @@ -185,8 +185,9 @@ type MoneyRequestNavigatorParamList = { iouType: string; reportID: string; }; - [SCREENS.MONEY_REQUEST.CONFIRMATION]: { + [SCREENS.MONEY_REQUEST.STEP_CONFIRMATION]: { iouType: string; + transactionID: string; reportID: string; }; [SCREENS.MONEY_REQUEST.CURRENCY]: { diff --git a/src/libs/actions/IOU.js b/src/libs/actions/IOU.js index a9e1b09ed984..34fb20091493 100644 --- a/src/libs/actions/IOU.js +++ b/src/libs/actions/IOU.js @@ -3684,7 +3684,7 @@ function navigateToNextPage(iou, iouType, report, path = '') { // If we're adding a receipt, that means the user came from the confirmation page and we need to navigate back to it. if (path.slice(1) === ROUTES.MONEY_REQUEST_RECEIPT.getRoute(iouType, report.reportID)) { - Navigation.navigate(ROUTES.MONEY_REQUEST_CONFIRMATION.getRoute(iouType, report.reportID)); + Navigation.navigate(ROUTES.MONEY_REQUEST_STEP_CONFIRMATION.getRoute(iouType, '1', report.reportID)); return; } @@ -3705,7 +3705,7 @@ function navigateToNextPage(iou, iouType, report, path = '') { resetMoneyRequestCategory(); resetMoneyRequestTag(); } - Navigation.navigate(ROUTES.MONEY_REQUEST_CONFIRMATION.getRoute(iouType, report.reportID)); + Navigation.navigate(ROUTES.MONEY_REQUEST_STEP_CONFIRMATION.getRoute(iouType, '1', report.reportID)); return; } Navigation.navigate(ROUTES.MONEY_REQUEST_PARTICIPANTS.getRoute(iouType)); diff --git a/src/pages/iou/MoneyRequestCategoryPage.js b/src/pages/iou/MoneyRequestCategoryPage.js index ceb2152d2b49..b3ca0a5d7ae0 100644 --- a/src/pages/iou/MoneyRequestCategoryPage.js +++ b/src/pages/iou/MoneyRequestCategoryPage.js @@ -50,7 +50,7 @@ function MoneyRequestCategoryPage({route, report, iou}) { const iouType = lodashGet(route, 'params.iouType', ''); const navigateBack = () => { - Navigation.goBack(ROUTES.MONEY_REQUEST_CONFIRMATION.getRoute(iouType, reportID)); + Navigation.goBack(ROUTES.MONEY_REQUEST_STEP_CONFIRMATION.getRoute(iouType, '1', reportID)); }; const updateCategory = (category) => { @@ -60,7 +60,7 @@ function MoneyRequestCategoryPage({route, report, iou}) { IOU.setMoneyRequestCategory(category.searchText); } - Navigation.goBack(ROUTES.MONEY_REQUEST_CONFIRMATION.getRoute(iouType, reportID)); + Navigation.goBack(ROUTES.MONEY_REQUEST_STEP_CONFIRMATION.getRoute(iouType, '1', reportID)); }; return ( diff --git a/src/pages/iou/MoneyRequestDatePage.js b/src/pages/iou/MoneyRequestDatePage.js index f6159abd73f6..c99c07a80624 100644 --- a/src/pages/iou/MoneyRequestDatePage.js +++ b/src/pages/iou/MoneyRequestDatePage.js @@ -69,7 +69,7 @@ function MoneyRequestDatePage({iou, route, selectedTab}) { }, [iou.id, iou.participants, iou.amount, iou.receiptPath, iouType, reportID, isDistanceRequest]); function navigateBack() { - Navigation.goBack(ROUTES.MONEY_REQUEST_CONFIRMATION.getRoute(iouType, reportID)); + Navigation.goBack(ROUTES.MONEY_REQUEST_STEP_CONFIRMATION.getRoute(iouType, '1', reportID)); } /** diff --git a/src/pages/iou/MoneyRequestDescriptionPage.js b/src/pages/iou/MoneyRequestDescriptionPage.js index fe3100b8c3bd..a2209818ee1a 100644 --- a/src/pages/iou/MoneyRequestDescriptionPage.js +++ b/src/pages/iou/MoneyRequestDescriptionPage.js @@ -92,7 +92,7 @@ function MoneyRequestDescriptionPage({iou, route, selectedTab}) { }, [iou.id, iou.participants, iou.amount, iou.receiptPath, iouType, reportID, isDistanceRequest]); function navigateBack() { - Navigation.goBack(ROUTES.MONEY_REQUEST_CONFIRMATION.getRoute(iouType, reportID)); + Navigation.goBack(ROUTES.MONEY_REQUEST_STEP_CONFIRMATION.getRoute(iouType, '1', reportID)); } /** diff --git a/src/pages/iou/MoneyRequestMerchantPage.js b/src/pages/iou/MoneyRequestMerchantPage.js index ce96a09446b9..894bd317464c 100644 --- a/src/pages/iou/MoneyRequestMerchantPage.js +++ b/src/pages/iou/MoneyRequestMerchantPage.js @@ -68,7 +68,7 @@ function MoneyRequestMerchantPage({iou, route}) { }, [iou.id, iou.participants, iou.amount, iou.receiptPath, iouType, reportID]); function navigateBack() { - Navigation.goBack(ROUTES.MONEY_REQUEST_CONFIRMATION.getRoute(iouType, reportID)); + Navigation.goBack(ROUTES.MONEY_REQUEST__STEP_CONFIRMATION.getRoute(iouType, '1', reportID)); } const validate = useCallback((value) => { diff --git a/src/pages/iou/MoneyRequestTagPage.js b/src/pages/iou/MoneyRequestTagPage.js index 60e40d665580..7fd08f20d652 100644 --- a/src/pages/iou/MoneyRequestTagPage.js +++ b/src/pages/iou/MoneyRequestTagPage.js @@ -60,7 +60,7 @@ function MoneyRequestTagPage({route, report, policyTags, iou}) { const policyTagListName = PolicyUtils.getTagListName(policyTags) || translate('common.tag'); const navigateBack = () => { - Navigation.goBack(ROUTES.MONEY_REQUEST_CONFIRMATION.getRoute(iouType, report.reportID)); + Navigation.goBack(ROUTES.MONEY_REQUEST_STEP_CONFIRMATION.getRoute(iouType, '1', report.reportID)); }; const updateTag = (selectedTag) => { diff --git a/src/pages/iou/NewDistanceRequestPage.js b/src/pages/iou/NewDistanceRequestPage.js index 750ac5d0141e..87f92b8c1907 100644 --- a/src/pages/iou/NewDistanceRequestPage.js +++ b/src/pages/iou/NewDistanceRequestPage.js @@ -57,7 +57,7 @@ function NewDistanceRequestPage({iou, report, route}) { const onSubmit = useCallback(() => { if (isEditingNewRequest) { - Navigation.goBack(ROUTES.MONEY_REQUEST_CONFIRMATION.getRoute(iouType, report.reportID)); + Navigation.goBack(ROUTES.MONEY_REQUEST_STEP_CONFIRMATION.getRoute(iouType, lodashGet(iou, 'transactionID', '1'), report.reportID)); return; } IOU.navigateToNextPage(iou, iouType, report); diff --git a/src/pages/iou/request/step/IOURequestStepConfirmation.js b/src/pages/iou/request/step/IOURequestStepConfirmation.js index 6028a735d132..cb9edfb5868b 100644 --- a/src/pages/iou/request/step/IOURequestStepConfirmation.js +++ b/src/pages/iou/request/step/IOURequestStepConfirmation.js @@ -23,7 +23,7 @@ import Navigation from '@libs/Navigation/Navigation'; import * as OptionsListUtils from '@libs/OptionsListUtils'; import * as ReportUtils from '@libs/ReportUtils'; import * as TransactionUtils from '@libs/TransactionUtils'; -import personalDetailsPropType from '@pages/personalDetailsPropType'; +import {usePersonalDetails} from '@components/OnyxProvider'; import reportPropTypes from '@pages/reportPropTypes'; import {policyPropTypes} from '@pages/workspace/withPolicy'; import * as IOU from '@userActions/IOU'; @@ -43,9 +43,6 @@ const propTypes = { /** The personal details of the current user */ ...withCurrentUserPersonalDetailsPropTypes, - /** Personal details of all users */ - personalDetails: personalDetailsPropType, - /** The policy of the report */ ...policyPropTypes, @@ -62,7 +59,6 @@ const propTypes = { transaction: transactionPropTypes, }; const defaultProps = { - personalDetails: {}, policy: {}, policyCategories: {}, policyTags: {}, @@ -72,7 +68,6 @@ const defaultProps = { }; function IOURequestStepConfirmation({ currentUserPersonalDetails, - personalDetails, policy, policyTags, policyCategories, @@ -86,6 +81,7 @@ function IOURequestStepConfirmation({ const {translate} = useLocalize(); const {windowWidth} = useWindowDimensions(); const {isOffline} = useNetwork(); + const personalDetails = usePersonalDetails() || CONST.EMPTY_OBJECT; const [receiptFile, setReceiptFile] = useState(); const receiptFilename = lodashGet(transaction, 'filename'); const receiptPath = lodashGet(transaction, 'receipt.source'); @@ -385,12 +381,6 @@ export default compose( withCurrentUserPersonalDetails, withWritableReportOrNotFound, withFullTransactionOrNotFound, - withOnyx({ - personalDetails: { - key: ONYXKEYS.PERSONAL_DETAILS_LIST, - }, - }), - // eslint-disable-next-line rulesdir/no-multiple-onyx-in-file withOnyx({ policy: { key: ({report}) => `${ONYXKEYS.COLLECTION.POLICY}${report ? report.policyID : '0'}`, diff --git a/src/pages/iou/steps/MoneyRequestConfirmPage.js b/src/pages/iou/steps/MoneyRequestConfirmPage.js deleted file mode 100644 index 1738ac78df47..000000000000 --- a/src/pages/iou/steps/MoneyRequestConfirmPage.js +++ /dev/null @@ -1,473 +0,0 @@ -import lodashGet from 'lodash/get'; -import PropTypes from 'prop-types'; -import React, {useCallback, useEffect, useMemo, useRef, useState} from 'react'; -import {View} from 'react-native'; -import {withOnyx} from 'react-native-onyx'; -import _ from 'underscore'; -import categoryPropTypes from '@components/categoryPropTypes'; -import HeaderWithBackButton from '@components/HeaderWithBackButton'; -import * as Expensicons from '@components/Icon/Expensicons'; -import MoneyRequestConfirmationList from '@components/MoneyRequestConfirmationList'; -import {usePersonalDetails} from '@components/OnyxProvider'; -import ScreenWrapper from '@components/ScreenWrapper'; -import tagPropTypes from '@components/tagPropTypes'; -import withCurrentUserPersonalDetails, {withCurrentUserPersonalDetailsDefaultProps, withCurrentUserPersonalDetailsPropTypes} from '@components/withCurrentUserPersonalDetails'; -import withLocalize from '@components/withLocalize'; -import useInitialValue from '@hooks/useInitialValue'; -import useNetwork from '@hooks/useNetwork'; -import useThemeStyles from '@hooks/useThemeStyles'; -import useWindowDimensions from '@hooks/useWindowDimensions'; -import compose from '@libs/compose'; -import * as FileUtils from '@libs/fileDownload/FileUtils'; -import * as MoneyRequestUtils from '@libs/MoneyRequestUtils'; -import Navigation from '@libs/Navigation/Navigation'; -import * as OptionsListUtils from '@libs/OptionsListUtils'; -import * as ReportUtils from '@libs/ReportUtils'; -import {iouDefaultProps, iouPropTypes} from '@pages/iou/propTypes'; -import reportPropTypes from '@pages/reportPropTypes'; -import {policyDefaultProps, policyPropTypes} from '@pages/workspace/withPolicy'; -import * as IOU from '@userActions/IOU'; -import * as Policy from '@userActions/Policy'; -import CONST from '@src/CONST'; -import ONYXKEYS from '@src/ONYXKEYS'; -import ROUTES from '@src/ROUTES'; - -const propTypes = { - /** React Navigation route */ - route: PropTypes.shape({ - /** Params from the route */ - params: PropTypes.shape({ - /** The type of IOU report, i.e. bill, request, send */ - iouType: PropTypes.string, - - /** The report ID of the IOU */ - reportID: PropTypes.string, - }), - }).isRequired, - - report: reportPropTypes, - - /** Holds data related to Money Request view state, rather than the underlying Money Request data. */ - iou: iouPropTypes, - - /** The policy of the current request */ - policy: policyPropTypes, - - policyTags: tagPropTypes, - - policyCategories: PropTypes.objectOf(categoryPropTypes), - - ...withCurrentUserPersonalDetailsPropTypes, -}; - -const defaultProps = { - report: {}, - policyCategories: {}, - policyTags: {}, - iou: iouDefaultProps, - policy: policyDefaultProps, - ...withCurrentUserPersonalDetailsDefaultProps, -}; - -function MoneyRequestConfirmPage(props) { - const styles = useThemeStyles(); - const {isOffline} = useNetwork(); - const {windowWidth} = useWindowDimensions(); - const prevMoneyRequestId = useRef(props.iou.id); - const iouType = useInitialValue(() => lodashGet(props.route, 'params.iouType', '')); - const reportID = useInitialValue(() => lodashGet(props.route, 'params.reportID', '')); - const isDistanceRequest = MoneyRequestUtils.isDistanceRequest(iouType, props.selectedTab); - const isScanRequest = MoneyRequestUtils.isScanRequest(props.selectedTab); - const [receiptFile, setReceiptFile] = useState(); - const personalDetails = usePersonalDetails() || CONST.EMPTY_OBJECT; - - const participants = useMemo( - () => - _.map(props.iou.participants, (participant) => { - const isPolicyExpenseChat = lodashGet(participant, 'isPolicyExpenseChat', false); - return isPolicyExpenseChat ? OptionsListUtils.getPolicyExpenseReportOption(participant) : OptionsListUtils.getParticipantsOption(participant, personalDetails); - }), - [props.iou.participants, personalDetails], - ); - const isPolicyExpenseChat = useMemo(() => ReportUtils.isPolicyExpenseChat(ReportUtils.getRootParentReport(props.report)), [props.report]); - const isManualRequestDM = props.selectedTab === CONST.TAB_REQUEST.MANUAL && iouType === CONST.IOU.TYPE.REQUEST; - - useEffect(() => { - const policyExpenseChat = _.find(participants, (participant) => participant.isPolicyExpenseChat); - if (policyExpenseChat) { - Policy.openDraftWorkspaceRequest(policyExpenseChat.policyID); - } - }, [isOffline, participants, props.iou.billable, props.policy]); - - const defaultBillable = lodashGet(props.policy, 'defaultBillable', false); - useEffect(() => { - IOU.setMoneyRequestBillable(defaultBillable); - }, [defaultBillable, isOffline]); - - useEffect(() => { - if (!props.iou.receiptPath || !props.iou.receiptFilename) { - return; - } - const onSuccess = (file) => { - const receipt = file; - receipt.state = file && isManualRequestDM ? CONST.IOU.RECEIPT_STATE.OPEN : CONST.IOU.RECEIPT_STATE.SCANREADY; - setReceiptFile(receipt); - }; - const onFailure = () => { - Navigation.goBack(ROUTES.MONEY_REQUEST.getRoute(iouType, reportID)); - }; - FileUtils.readFileAsync(props.iou.receiptPath, props.iou.receiptFilename, onSuccess, onFailure); - }, [props.iou.receiptPath, props.iou.receiptFilename, isManualRequestDM, iouType, reportID]); - - useEffect(() => { - // ID in Onyx could change by initiating a new request in a separate browser tab or completing a request - if (!isDistanceRequest && prevMoneyRequestId.current !== props.iou.id) { - // The ID is cleared on completing a request. In that case, we will do nothing. - if (props.iou.id) { - Navigation.goBack(ROUTES.MONEY_REQUEST.getRoute(iouType, reportID), true); - } - return; - } - - // Reset the money request Onyx if the ID in Onyx does not match the ID from params - const moneyRequestId = `${iouType}${reportID}`; - const shouldReset = !isDistanceRequest && props.iou.id !== moneyRequestId && !_.isEmpty(reportID); - if (shouldReset) { - IOU.resetMoneyRequestInfo(moneyRequestId); - } - - if (_.isEmpty(props.iou.participants) || (props.iou.amount === 0 && !props.iou.receiptPath && !isDistanceRequest) || shouldReset || ReportUtils.isArchivedRoom(props.report)) { - Navigation.goBack(ROUTES.MONEY_REQUEST.getRoute(iouType, reportID), true); - } - - return () => { - prevMoneyRequestId.current = props.iou.id; - }; - }, [props.iou.participants, props.iou.amount, props.iou.id, props.iou.receiptPath, isDistanceRequest, props.report, iouType, reportID]); - - const navigateBack = () => { - let fallback; - if (reportID) { - fallback = ROUTES.MONEY_REQUEST.getRoute(iouType, reportID); - } else { - fallback = ROUTES.MONEY_REQUEST_PARTICIPANTS.getRoute(iouType); - } - Navigation.goBack(fallback); - }; - - /** - * @param {Array} selectedParticipants - * @param {String} trimmedComment - * @param {File} [receipt] - */ - const requestMoney = useCallback( - (selectedParticipants, trimmedComment, receipt) => { - IOU.requestMoney( - props.report, - props.iou.amount, - props.iou.currency, - props.iou.created, - props.iou.merchant, - props.currentUserPersonalDetails.login, - props.currentUserPersonalDetails.accountID, - selectedParticipants[0], - trimmedComment, - receipt, - props.iou.category, - props.iou.tag, - props.iou.billable, - props.policy, - props.policyTags, - props.policyCategories, - ); - }, - [ - props.report, - props.iou.amount, - props.iou.currency, - props.iou.created, - props.iou.merchant, - props.currentUserPersonalDetails.login, - props.currentUserPersonalDetails.accountID, - props.iou.category, - props.iou.tag, - props.iou.billable, - props.policy, - props.policyTags, - props.policyCategories, - ], - ); - - /** - * @param {Array} selectedParticipants - * @param {String} trimmedComment - */ - const createDistanceRequest = useCallback( - (selectedParticipants, trimmedComment) => { - IOU.createDistanceRequest( - props.report, - selectedParticipants[0], - trimmedComment, - props.iou.created, - props.iou.transactionID, - props.iou.category, - props.iou.tag, - props.iou.amount, - props.iou.currency, - props.iou.merchant, - props.iou.billable, - props.policy, - props.policyTags, - props.policyCategories, - ); - }, - [ - props.report, - props.iou.created, - props.iou.transactionID, - props.iou.category, - props.iou.tag, - props.iou.amount, - props.iou.currency, - props.iou.merchant, - props.iou.billable, - props.policy, - props.policyTags, - props.policyCategories, - ], - ); - - const createTransaction = useCallback( - (selectedParticipants) => { - const trimmedComment = props.iou.comment.trim(); - - // If we have a receipt let's start the split bill by creating only the action, the transaction, and the group DM if needed - if (iouType === CONST.IOU.TYPE.SPLIT && props.iou.receiptPath) { - const existingSplitChatReportID = CONST.REGEX.NUMBER.test(reportID) ? reportID : ''; - const onSuccess = (receipt) => { - IOU.startSplitBill( - selectedParticipants, - props.currentUserPersonalDetails.login, - props.currentUserPersonalDetails.accountID, - trimmedComment, - receipt, - existingSplitChatReportID, - ); - }; - FileUtils.readFileAsync(props.iou.receiptPath, props.iou.receiptFilename, onSuccess); - return; - } - - // IOUs created from a group report will have a reportID param in the route. - // Since the user is already viewing the report, we don't need to navigate them to the report - if (iouType === CONST.IOU.TYPE.SPLIT && CONST.REGEX.NUMBER.test(reportID)) { - IOU.splitBill( - selectedParticipants, - props.currentUserPersonalDetails.login, - props.currentUserPersonalDetails.accountID, - props.iou.amount, - trimmedComment, - props.iou.currency, - props.iou.category, - props.iou.tag, - reportID, - props.iou.merchant, - ); - return; - } - - // If the request is created from the global create menu, we also navigate the user to the group report - if (iouType === CONST.IOU.TYPE.SPLIT) { - IOU.splitBillAndOpenReport( - selectedParticipants, - props.currentUserPersonalDetails.login, - props.currentUserPersonalDetails.accountID, - props.iou.amount, - trimmedComment, - props.iou.currency, - props.iou.category, - props.iou.tag, - props.iou.merchant, - ); - return; - } - - if (receiptFile) { - requestMoney(selectedParticipants, trimmedComment, receiptFile); - return; - } - - if (isDistanceRequest) { - createDistanceRequest(selectedParticipants, trimmedComment); - return; - } - - requestMoney(selectedParticipants, trimmedComment); - }, - [ - props.iou.amount, - props.iou.comment, - props.currentUserPersonalDetails.login, - props.currentUserPersonalDetails.accountID, - props.iou.currency, - props.iou.category, - props.iou.tag, - props.iou.receiptPath, - props.iou.receiptFilename, - isDistanceRequest, - requestMoney, - createDistanceRequest, - receiptFile, - iouType, - reportID, - props.iou.merchant, - ], - ); - - /** - * Checks if user has a GOLD wallet then creates a paid IOU report on the fly - * - * @param {String} paymentMethodType - */ - const sendMoney = useCallback( - (paymentMethodType) => { - const currency = props.iou.currency; - const trimmedComment = props.iou.comment.trim(); - const participant = participants[0]; - - if (paymentMethodType === CONST.IOU.PAYMENT_TYPE.ELSEWHERE) { - IOU.sendMoneyElsewhere(props.report, props.iou.amount, currency, trimmedComment, props.currentUserPersonalDetails.accountID, participant); - return; - } - - if (paymentMethodType === CONST.IOU.PAYMENT_TYPE.EXPENSIFY) { - IOU.sendMoneyWithWallet(props.report, props.iou.amount, currency, trimmedComment, props.currentUserPersonalDetails.accountID, participant); - } - }, - [props.iou.amount, props.iou.comment, participants, props.iou.currency, props.currentUserPersonalDetails.accountID, props.report], - ); - - const headerTitle = () => { - if (isDistanceRequest) { - return props.translate('common.distance'); - } - - if (iouType === CONST.IOU.TYPE.SPLIT) { - return props.translate('iou.split'); - } - - if (iouType === CONST.IOU.TYPE.SEND) { - return props.translate('common.send'); - } - - if (isScanRequest) { - return props.translate('tabSelector.scan'); - } - - return props.translate('tabSelector.manual'); - }; - - return ( - - {({safeAreaPaddingBottomStyle}) => ( - - Navigation.navigate(ROUTES.MONEY_REQUEST_RECEIPT.getRoute(iouType, reportID)), - }, - ]} - /> - { - const newParticipants = _.map(props.iou.participants, (participant) => { - if (participant.accountID === option.accountID) { - return {...participant, selected: !participant.selected}; - } - return participant; - }); - IOU.setMoneyRequestParticipants(newParticipants); - }} - receiptPath={props.iou.receiptPath} - receiptFilename={props.iou.receiptFilename} - iouType={iouType} - reportID={reportID} - isPolicyExpenseChat={isPolicyExpenseChat} - // The participants can only be modified when the action is initiated from directly within a group chat and not the floating-action-button. - // This is because when there is a group of people, say they are on a trip, and you have some shared expenses with some of the people, - // but not all of them (maybe someone skipped out on dinner). Then it's nice to be able to select/deselect people from the group chat bill - // split rather than forcing the user to create a new group, just for that expense. The reportID is empty, when the action was initiated from - // the floating-action-button (since it is something that exists outside the context of a report). - canModifyParticipants={!_.isEmpty(reportID)} - policyID={props.report.policyID} - bankAccountRoute={ReportUtils.getBankAccountRoute(props.report)} - iouMerchant={props.iou.merchant} - iouCreated={props.iou.created} - isScanRequest={isScanRequest} - isDistanceRequest={isDistanceRequest} - shouldShowSmartScanFields={_.isEmpty(props.iou.receiptPath)} - /> - - )} - - ); -} - -MoneyRequestConfirmPage.displayName = 'MoneyRequestConfirmPage'; -MoneyRequestConfirmPage.propTypes = propTypes; -MoneyRequestConfirmPage.defaultProps = defaultProps; - -export default compose( - withCurrentUserPersonalDetails, - withLocalize, - withOnyx({ - iou: { - key: ONYXKEYS.IOU, - }, - }), - // eslint-disable-next-line rulesdir/no-multiple-onyx-in-file - withOnyx({ - report: { - key: ({route, iou}) => { - const reportID = IOU.getIOUReportID(iou, route); - - return `${ONYXKEYS.COLLECTION.REPORT}${reportID}`; - }, - }, - selectedTab: { - key: `${ONYXKEYS.COLLECTION.SELECTED_TAB}${CONST.TAB.RECEIPT_TAB_ID}`, - }, - }), - withOnyx({ - policy: { - key: ({report}) => `${ONYXKEYS.COLLECTION.POLICY}${report ? report.policyID : '0'}`, - }, - policyCategories: { - key: ({report}) => `${ONYXKEYS.COLLECTION.POLICY_CATEGORIES}${report ? report.policyID : '0'}`, - }, - policyTags: { - key: ({report}) => `${ONYXKEYS.COLLECTION.POLICY_TAGS}${report ? report.policyID : '0'}`, - }, - }), -)(MoneyRequestConfirmPage); diff --git a/src/pages/iou/steps/MoneyRequstParticipantsPage/MoneyRequestParticipantsPage.js b/src/pages/iou/steps/MoneyRequstParticipantsPage/MoneyRequestParticipantsPage.js index 216154be9cd4..1feb7a2c32fd 100644 --- a/src/pages/iou/steps/MoneyRequstParticipantsPage/MoneyRequestParticipantsPage.js +++ b/src/pages/iou/steps/MoneyRequstParticipantsPage/MoneyRequestParticipantsPage.js @@ -89,7 +89,7 @@ function MoneyRequestParticipantsPage({iou, selectedTab, route, transaction}) { IOU.setMoneyRequestId(moneyRequestType); IOU.resetMoneyRequestCategory(); IOU.resetMoneyRequestTag(); - Navigation.navigate(ROUTES.MONEY_REQUEST_CONFIRMATION.getRoute(moneyRequestType, reportID)); + Navigation.navigate(ROUTES.MONEY_REQUEST_STEP_CONFIRMATION.getRoute(moneyRequestType, lodashGet(transaction, 'transactionID', 1), reportID)); }; const navigateBack = useCallback((forceFallback = false) => { diff --git a/src/pages/iou/steps/NewRequestAmountPage.js b/src/pages/iou/steps/NewRequestAmountPage.js index 1df74569e4c3..007a63bdb713 100644 --- a/src/pages/iou/steps/NewRequestAmountPage.js +++ b/src/pages/iou/steps/NewRequestAmountPage.js @@ -113,7 +113,7 @@ function NewRequestAmountPage({route, iou, report, selectedTab}) { }, [iou.participants, iou.amount, iou.id, isEditing, iouType, reportID, isDistanceRequestTab]); const navigateBack = () => { - Navigation.goBack(isEditing ? ROUTES.MONEY_REQUEST_CONFIRMATION.getRoute(iouType, reportID) : ROUTES.HOME); + Navigation.goBack(isEditing ? ROUTES.MONEY_REQUEST_STEP_CONFIRMATION.getRoute(iouType, '1', reportID) : ROUTES.HOME); }; const navigateToCurrencySelectionPage = () => { @@ -134,7 +134,7 @@ function NewRequestAmountPage({route, iou, report, selectedTab}) { IOU.setMoneyRequestCurrency(currency); if (isEditing) { - Navigation.goBack(ROUTES.MONEY_REQUEST_CONFIRMATION.getRoute(iouType, reportID)); + Navigation.goBack(ROUTES.MONEY_REQUEST_STEP_CONFIRMATION.getRoute(iouType, '1', reportID)); return; } From 8e4e4d803f40cd0c35b05ee1a51da2c4899154a3 Mon Sep 17 00:00:00 2001 From: Dylan Date: Wed, 31 Jan 2024 16:54:59 +0700 Subject: [PATCH 065/500] fix lint --- src/pages/iou/request/step/IOURequestStepConfirmation.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/iou/request/step/IOURequestStepConfirmation.js b/src/pages/iou/request/step/IOURequestStepConfirmation.js index cb9edfb5868b..f867e57f9a13 100644 --- a/src/pages/iou/request/step/IOURequestStepConfirmation.js +++ b/src/pages/iou/request/step/IOURequestStepConfirmation.js @@ -8,6 +8,7 @@ import categoryPropTypes from '@components/categoryPropTypes'; import HeaderWithBackButton from '@components/HeaderWithBackButton'; import * as Expensicons from '@components/Icon/Expensicons'; import MoneyRequestConfirmationList from '@components/MoneyTemporaryForRefactorRequestConfirmationList'; +import {usePersonalDetails} from '@components/OnyxProvider'; import ScreenWrapper from '@components/ScreenWrapper'; import tagPropTypes from '@components/tagPropTypes'; import transactionPropTypes from '@components/transactionPropTypes'; @@ -23,7 +24,6 @@ import Navigation from '@libs/Navigation/Navigation'; import * as OptionsListUtils from '@libs/OptionsListUtils'; import * as ReportUtils from '@libs/ReportUtils'; import * as TransactionUtils from '@libs/TransactionUtils'; -import {usePersonalDetails} from '@components/OnyxProvider'; import reportPropTypes from '@pages/reportPropTypes'; import {policyPropTypes} from '@pages/workspace/withPolicy'; import * as IOU from '@userActions/IOU'; From ee1efe95e93aea8be888b50338534cf3f6f28bff Mon Sep 17 00:00:00 2001 From: Dylan Date: Wed, 31 Jan 2024 17:09:35 +0700 Subject: [PATCH 066/500] update send money flow --- .../SidebarScreen/FloatingActionButtonAndPopover.js | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/pages/home/sidebar/SidebarScreen/FloatingActionButtonAndPopover.js b/src/pages/home/sidebar/SidebarScreen/FloatingActionButtonAndPopover.js index bbcdc5cebef4..7281868f47d9 100644 --- a/src/pages/home/sidebar/SidebarScreen/FloatingActionButtonAndPopover.js +++ b/src/pages/home/sidebar/SidebarScreen/FloatingActionButtonAndPopover.js @@ -182,7 +182,14 @@ function FloatingActionButtonAndPopover(props) { { icon: Expensicons.Send, text: props.translate('iou.sendMoney'), - onSelected: () => interceptAnonymousUser(() => IOU.startMoneyRequest(CONST.IOU.TYPE.SEND)), + onSelected: () => + interceptAnonymousUser(() => + Navigation.navigate( + // When starting to create a money request from the global FAB, there is not an existing report yet. A random optimistic reportID is generated and used + // for all of the routes in the creation flow. + ROUTES.MONEY_REQUEST_CREATE.getRoute(CONST.IOU.TYPE.SEND, CONST.IOU.OPTIMISTIC_TRANSACTION_ID, ReportUtils.generateReportID()), + ), + ), }, ...[ { From 8bb0b3406cdb3a294766e2c875935d757c48dc86 Mon Sep 17 00:00:00 2001 From: Etotaziba Olei Date: Wed, 31 Jan 2024 15:27:39 +0100 Subject: [PATCH 067/500] migrate EditRequestTaxAmountPage to tsc --- ...ntPage.js => EditRequestTaxAmountPage.tsx} | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) rename src/pages/{EditRequestTaxAmountPage.js => EditRequestTaxAmountPage.tsx} (79%) diff --git a/src/pages/EditRequestTaxAmountPage.js b/src/pages/EditRequestTaxAmountPage.tsx similarity index 79% rename from src/pages/EditRequestTaxAmountPage.js rename to src/pages/EditRequestTaxAmountPage.tsx index 6a413bf12655..7c99465fdca0 100644 --- a/src/pages/EditRequestTaxAmountPage.js +++ b/src/pages/EditRequestTaxAmountPage.tsx @@ -1,6 +1,6 @@ import {useFocusEffect} from '@react-navigation/native'; -import PropTypes from 'prop-types'; import React, {useCallback, useRef} from 'react'; +import {TextInput} from 'react-native'; import HeaderWithBackButton from '@components/HeaderWithBackButton'; import ScreenWrapper from '@components/ScreenWrapper'; import useLocalize from '@hooks/useLocalize'; @@ -8,28 +8,28 @@ import * as DeviceCapabilities from '@libs/DeviceCapabilities'; import CONST from '@src/CONST'; import MoneyRequestAmountForm from './iou/steps/MoneyRequestAmountForm'; -const propTypes = { +type EditRequestTaxAmountPageProps = { /** Transaction default amount value */ - defaultAmount: PropTypes.number.isRequired, + defaultAmount: number; /** Transaction default tax amount value */ - defaultTaxAmount: PropTypes.number.isRequired, + defaultTaxAmount: number; /** Transaction default currency value */ - defaultCurrency: PropTypes.string.isRequired, + defaultCurrency: string; /** Callback to fire when the Save button is pressed */ - onSubmit: PropTypes.func.isRequired, + onSubmit: () => void; /** Callback to fire when we press on the currency */ - onNavigateToCurrency: PropTypes.func.isRequired, + onNavigateToCurrency: () => void; }; -function EditRequestTaxAmountPage({defaultAmount, defaultTaxAmount, defaultCurrency, onNavigateToCurrency, onSubmit}) { +function EditRequestTaxAmountPage({defaultAmount, defaultTaxAmount, defaultCurrency, onNavigateToCurrency, onSubmit}: EditRequestTaxAmountPageProps) { const {translate} = useLocalize(); - const textInput = useRef(null); + const textInput = useRef(null); - const focusTimeoutRef = useRef(null); + const focusTimeoutRef = useRef(null); useFocusEffect( useCallback(() => { @@ -52,10 +52,11 @@ function EditRequestTaxAmountPage({defaultAmount, defaultTaxAmount, defaultCurre > (textInput.current = e)} + ref={textInput} onCurrencyButtonPress={onNavigateToCurrency} onSubmitButtonPress={onSubmit} isEditing @@ -64,7 +65,6 @@ function EditRequestTaxAmountPage({defaultAmount, defaultTaxAmount, defaultCurre ); } -EditRequestTaxAmountPage.propTypes = propTypes; EditRequestTaxAmountPage.displayName = 'EditRequestTaxAmountPage'; export default EditRequestTaxAmountPage; From 1f1df34f216dfce421e436f4e1282a5771982bab Mon Sep 17 00:00:00 2001 From: Etotaziba Olei Date: Wed, 31 Jan 2024 15:28:00 +0100 Subject: [PATCH 068/500] migrate EditRequestTaxRatePage to tsc --- ...uestTaxRatePage.js => EditRequestTaxRatePage.tsx} | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) rename src/pages/{EditRequestTaxRatePage.js => EditRequestTaxRatePage.tsx} (86%) diff --git a/src/pages/EditRequestTaxRatePage.js b/src/pages/EditRequestTaxRatePage.tsx similarity index 86% rename from src/pages/EditRequestTaxRatePage.js rename to src/pages/EditRequestTaxRatePage.tsx index 2a22ab087435..4991349d4225 100644 --- a/src/pages/EditRequestTaxRatePage.js +++ b/src/pages/EditRequestTaxRatePage.tsx @@ -5,18 +5,18 @@ import ScreenWrapper from '@components/ScreenWrapper'; import TaxPicker from '@components/TaxPicker'; import useLocalize from '@hooks/useLocalize'; -const propTypes = { +type EditRequestTaxRatePageProps = { /** Transaction default tax Rate value */ - defaultTaxRate: PropTypes.string.isRequired, + defaultTaxRate: string, /** The policyID we are getting categories for */ - policyID: PropTypes.string.isRequired, + policyID: string, /** Callback to fire when the Save button is pressed */ - onSubmit: PropTypes.func.isRequired, + onSubmit: () => void, }; -function EditRequestTaxRatePage({defaultTaxRate, policyID, onSubmit}) { +function EditRequestTaxRatePage({defaultTaxRate, policyID, onSubmit}: EditRequestTaxRatePageProps) { const {translate} = useLocalize(); return ( @@ -29,6 +29,7 @@ function EditRequestTaxRatePage({defaultTaxRate, policyID, onSubmit}) { <> Date: Wed, 31 Jan 2024 15:34:36 +0100 Subject: [PATCH 069/500] fix tsc errors --- src/pages/EditRequestTaxAmountPage.tsx | 4 ++-- src/pages/EditRequestTaxRatePage.tsx | 9 ++++----- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/src/pages/EditRequestTaxAmountPage.tsx b/src/pages/EditRequestTaxAmountPage.tsx index 7c99465fdca0..437e84df652c 100644 --- a/src/pages/EditRequestTaxAmountPage.tsx +++ b/src/pages/EditRequestTaxAmountPage.tsx @@ -1,6 +1,6 @@ import {useFocusEffect} from '@react-navigation/native'; import React, {useCallback, useRef} from 'react'; -import {TextInput} from 'react-native'; +import type {TextInput} from 'react-native'; import HeaderWithBackButton from '@components/HeaderWithBackButton'; import ScreenWrapper from '@components/ScreenWrapper'; import useLocalize from '@hooks/useLocalize'; @@ -52,7 +52,7 @@ function EditRequestTaxAmountPage({defaultAmount, defaultTaxAmount, defaultCurre > void, + onSubmit: () => void; }; function EditRequestTaxRatePage({defaultTaxRate, policyID, onSubmit}: EditRequestTaxRatePageProps) { @@ -29,7 +28,7 @@ function EditRequestTaxRatePage({defaultTaxRate, policyID, onSubmit}: EditReques <> Date: Wed, 31 Jan 2024 15:35:25 +0100 Subject: [PATCH 070/500] fix lint --- src/pages/EditRequestTaxRatePage.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/EditRequestTaxRatePage.tsx b/src/pages/EditRequestTaxRatePage.tsx index 778f25ae0d98..1c1dc8219ae2 100644 --- a/src/pages/EditRequestTaxRatePage.tsx +++ b/src/pages/EditRequestTaxRatePage.tsx @@ -28,7 +28,7 @@ function EditRequestTaxRatePage({defaultTaxRate, policyID, onSubmit}: EditReques <> Date: Wed, 31 Jan 2024 12:58:51 -0800 Subject: [PATCH 071/500] Delete unused params for deleted command ReconnectToReport --- src/libs/API/parameters/ReconnectToReportParams.ts | 5 ----- 1 file changed, 5 deletions(-) delete mode 100644 src/libs/API/parameters/ReconnectToReportParams.ts diff --git a/src/libs/API/parameters/ReconnectToReportParams.ts b/src/libs/API/parameters/ReconnectToReportParams.ts deleted file mode 100644 index e7701cd36ca9..000000000000 --- a/src/libs/API/parameters/ReconnectToReportParams.ts +++ /dev/null @@ -1,5 +0,0 @@ -type ReconnectToReportParams = { - reportID: string; -}; - -export default ReconnectToReportParams; From 10360a32be5e48825a62ce4fd058f494a5020cc1 Mon Sep 17 00:00:00 2001 From: Aldo Canepa Date: Wed, 31 Jan 2024 15:21:02 -0800 Subject: [PATCH 072/500] Remove ReconnectToReportParams --- src/libs/API/parameters/index.ts | 1 - src/libs/API/types.ts | 1 - 2 files changed, 2 deletions(-) diff --git a/src/libs/API/parameters/index.ts b/src/libs/API/parameters/index.ts index 039398c0fbf6..0fd627ec7f9f 100644 --- a/src/libs/API/parameters/index.ts +++ b/src/libs/API/parameters/index.ts @@ -76,7 +76,6 @@ export type {default as VerifyIdentityForBankAccountParams} from './VerifyIdenti export type {default as AnswerQuestionsForWalletParams} from './AnswerQuestionsForWalletParams'; export type {default as AddCommentOrAttachementParams} from './AddCommentOrAttachementParams'; export type {default as OptInOutToPushNotificationsParams} from './OptInOutToPushNotificationsParams'; -export type {default as ReconnectToReportParams} from './ReconnectToReportParams'; export type {default as ReadNewestActionParams} from './ReadNewestActionParams'; export type {default as MarkAsUnreadParams} from './MarkAsUnreadParams'; export type {default as TogglePinnedChatParams} from './TogglePinnedChatParams'; diff --git a/src/libs/API/types.ts b/src/libs/API/types.ts index f58ebc30b4a2..f17645a3e3ac 100644 --- a/src/libs/API/types.ts +++ b/src/libs/API/types.ts @@ -185,7 +185,6 @@ type WriteCommandParameters = { [WRITE_COMMANDS.ADD_PERSONAL_BANK_ACCOUNT]: Parameters.AddPersonalBankAccountParams; [WRITE_COMMANDS.OPT_IN_TO_PUSH_NOTIFICATIONS]: Parameters.OptInOutToPushNotificationsParams; [WRITE_COMMANDS.OPT_OUT_OF_PUSH_NOTIFICATIONS]: Parameters.OptInOutToPushNotificationsParams; - [WRITE_COMMANDS.RECONNECT_TO_REPORT]: Parameters.ReconnectToReportParams; [WRITE_COMMANDS.READ_NEWEST_ACTION]: Parameters.ReadNewestActionParams; [WRITE_COMMANDS.MARK_AS_UNREAD]: Parameters.MarkAsUnreadParams; [WRITE_COMMANDS.TOGGLE_PINNED_CHAT]: Parameters.TogglePinnedChatParams; From a73e36e5bf801b65ade15afcbf284a9b7f1f5239 Mon Sep 17 00:00:00 2001 From: Etotaziba Olei Date: Sun, 4 Feb 2024 22:20:22 +0100 Subject: [PATCH 073/500] export tax rates types in onyx types --- src/types/onyx/index.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/types/onyx/index.ts b/src/types/onyx/index.ts index d0ac2ce395fa..138bc0ba2270 100644 --- a/src/types/onyx/index.ts +++ b/src/types/onyx/index.ts @@ -44,6 +44,7 @@ import type {PolicyMembers} from './PolicyMember'; import type PolicyMember from './PolicyMember'; import type {PolicyReportField, PolicyReportFields} from './PolicyReportField'; import type {PolicyTag, PolicyTags} from './PolicyTag'; +import type {PolicyTaxRates, TaxRate, TaxRates} from './PolicyTaxRates'; import type PrivatePersonalDetails from './PrivatePersonalDetails'; import type RecentlyUsedCategories from './RecentlyUsedCategories'; import type RecentlyUsedReportFields from './RecentlyUsedReportFields'; @@ -121,6 +122,9 @@ export type { PolicyMembers, PolicyTag, PolicyTags, + TaxRate, + TaxRates, + PolicyTaxRates, PrivatePersonalDetails, RecentWaypoint, RecentlyUsedCategories, From 3e19f56714ba0927c1d0b2cbbb4de72587b926a5 Mon Sep 17 00:00:00 2001 From: Etotaziba Olei Date: Mon, 5 Feb 2024 09:52:14 +0100 Subject: [PATCH 074/500] add PolicyTaxRates to ONYX KEYS --- src/ONYXKEYS.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/ONYXKEYS.ts b/src/ONYXKEYS.ts index 7328fb2543ad..bf1cdacea8d9 100755 --- a/src/ONYXKEYS.ts +++ b/src/ONYXKEYS.ts @@ -460,6 +460,7 @@ type OnyxValues = { [ONYXKEYS.COLLECTION.POLICY_DRAFTS]: OnyxTypes.Policy; [ONYXKEYS.COLLECTION.POLICY_CATEGORIES]: OnyxTypes.PolicyCategories; [ONYXKEYS.COLLECTION.POLICY_TAGS]: OnyxTypes.PolicyTags; + [ONYXKEYS.COLLECTION.POLICY_TAX_RATE]: OnyxTypes.PolicyTaxRates; [ONYXKEYS.COLLECTION.POLICY_MEMBERS]: OnyxTypes.PolicyMembers; [ONYXKEYS.COLLECTION.POLICY_MEMBERS_DRAFTS]: OnyxTypes.PolicyMember; [ONYXKEYS.COLLECTION.POLICY_RECENTLY_USED_CATEGORIES]: OnyxTypes.RecentlyUsedCategories; From ec6cf4f93decac331add63ba0f631f76c384d533 Mon Sep 17 00:00:00 2001 From: Etotaziba Olei Date: Mon, 5 Feb 2024 09:52:35 +0100 Subject: [PATCH 075/500] fix typescript --- .../ReportActionItem/MoneyRequestView.tsx | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/components/ReportActionItem/MoneyRequestView.tsx b/src/components/ReportActionItem/MoneyRequestView.tsx index e92821f8e94c..9c1db3d07a7a 100644 --- a/src/components/ReportActionItem/MoneyRequestView.tsx +++ b/src/components/ReportActionItem/MoneyRequestView.tsx @@ -56,7 +56,7 @@ type MoneyRequestViewOnyxPropsWithoutTransaction = { /** Collection of tags attached to a policy */ policyTags: OnyxEntry; - /** Collection of tax rates attached to a policy */ + /** Collection of tax rates attached to a policy */ policyTaxRates: OnyxEntry; /** The expense report or iou report (only will have a value if this is a transaction thread) */ @@ -124,7 +124,8 @@ function MoneyRequestView({ const formattedTaxAmount = transactionTaxAmount ? CurrencyUtils.convertToDisplayString(transactionTaxAmount, transactionCurrency) : ''; - const taxRateTitle = TransactionUtils.getTaxName(policyTaxRates.taxes, transactionTaxCode); + const policyTaxRatesDescription = (policyTaxRates && policyTaxRates.name) ?? ''; + const taxRateTitle = (transactionTaxCode && policyTaxRates && TransactionUtils.getTaxName(policyTaxRates.taxes, transactionTaxCode)) ?? ''; // Flags for allowing or disallowing editing a money request const isSettled = ReportUtils.isSettled(moneyRequestReport?.reportID); @@ -155,8 +156,8 @@ function MoneyRequestView({ const shouldShowTag = isPolicyExpenseChat && (transactionTag || OptionsListUtils.hasEnabledOptions(Object.values(policyTagsList))); const shouldShowBillable = isPolicyExpenseChat && (!!transactionBillable || !(policy?.disabledFields?.defaultBillable ?? true)); - // A flag for showing tax rate - const shouldShowTax = isTaxPolicyEnabled(isPolicyExpenseChat, policy) && transactionTaxCode && transactionTaxAmount; + // A flag for showing tax rate + const shouldShowTax = isTaxPolicyEnabled(isPolicyExpenseChat, policy) && transactionTaxCode && transactionTaxAmount; const {getViolationsForField} = useViolations(transactionViolations ?? []); const hasViolations = useCallback((field: ViolationField): boolean => !!canUseViolations && getViolationsForField(field).length > 0, [canUseViolations, getViolationsForField]); @@ -370,7 +371,7 @@ function MoneyRequestView({ Date: Mon, 5 Feb 2024 10:07:04 +0100 Subject: [PATCH 076/500] use optional chaining --- src/components/ReportActionItem/MoneyRequestView.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/ReportActionItem/MoneyRequestView.tsx b/src/components/ReportActionItem/MoneyRequestView.tsx index 9fdcd7b5125a..2163332b507e 100644 --- a/src/components/ReportActionItem/MoneyRequestView.tsx +++ b/src/components/ReportActionItem/MoneyRequestView.tsx @@ -124,7 +124,7 @@ function MoneyRequestView({ const formattedTaxAmount = transactionTaxAmount ? CurrencyUtils.convertToDisplayString(transactionTaxAmount, transactionCurrency) : ''; - const policyTaxRatesDescription = (policyTaxRates && policyTaxRates.name) ?? ''; + const policyTaxRatesDescription = policyTaxRates?.name; const taxRateTitle = (transactionTaxCode && policyTaxRates && TransactionUtils.getTaxName(policyTaxRates.taxes, transactionTaxCode)) ?? ''; // Flags for allowing or disallowing editing a money request From 6364ee97a9b52a3edd970134a2d9a786e5477459 Mon Sep 17 00:00:00 2001 From: dukenv0307 Date: Mon, 5 Feb 2024 16:58:36 +0700 Subject: [PATCH 077/500] remove NewRequestAmountPage --- src/ROUTES.ts | 20 ++++++------ src/SCREENS.ts | 1 - .../MoneyRequestConfirmationList.js | 10 +++++- ...oraryForRefactorRequestConfirmationList.js | 7 ++--- .../ReportActionItem/MoneyRequestView.tsx | 4 ++- src/libs/IOUUtils.ts | 2 +- .../AppNavigator/ModalStackNavigators.tsx | 1 - src/libs/Navigation/linkingConfig/config.ts | 1 - src/libs/Navigation/types.ts | 1 - .../AttachmentPickerWithMenuItems.js | 6 ++-- .../FloatingActionButtonAndPopover.js | 7 ++++- .../request/IOURequestRedirectToStartPage.js | 2 +- .../iou/request/step/IOURequestStepAmount.js | 31 ++++++++++++++++--- .../step/withFullTransactionOrNotFound.js | 11 ++++++- 14 files changed, 71 insertions(+), 33 deletions(-) diff --git a/src/ROUTES.ts b/src/ROUTES.ts index a84dc9c8f9ae..7156ecdfd2fb 100644 --- a/src/ROUTES.ts +++ b/src/ROUTES.ts @@ -265,10 +265,6 @@ const ROUTES = { route: ':iouType/new/:reportID?', getRoute: (iouType: string, reportID = '') => `${iouType}/new/${reportID}` as const, }, - MONEY_REQUEST_AMOUNT: { - route: ':iouType/new/amount/:reportID?', - getRoute: (iouType: string, reportID = '') => `${iouType}/new/amount/${reportID}` as const, - }, MONEY_REQUEST_PARTICIPANTS: { route: ':iouType/new/participants/:reportID?', getRoute: (iouType: string, reportID = '') => `${iouType}/new/participants/${reportID}` as const, @@ -317,17 +313,18 @@ const ROUTES = { MONEY_REQUEST_SCAN_TAB: ':iouType/new/:reportID?/scan', MONEY_REQUEST_CREATE: { - route: 'create/:iouType/start/:transactionID/:reportID', - getRoute: (iouType: ValueOf, transactionID: string, reportID: string) => `create/${iouType}/start/${transactionID}/${reportID}` as const, + route: ':action/:iouType/start/:transactionID/:reportID', + getRoute: (action: ValueOf, iouType: ValueOf, transactionID: string, reportID: string) => + `${action}/${iouType}/start/${transactionID}/${reportID}` as const, }, MONEY_REQUEST_STEP_CONFIRMATION: { route: 'create/:iouType/confirmation/:transactionID/:reportID', getRoute: (iouType: ValueOf, transactionID: string, reportID: string) => `create/${iouType}/confirmation/${transactionID}/${reportID}` as const, }, MONEY_REQUEST_STEP_AMOUNT: { - route: 'create/:iouType/amount/:transactionID/:reportID', - getRoute: (iouType: ValueOf, transactionID: string, reportID: string, backTo = '') => - getUrlWithBackToParam(`create/${iouType}/amount/${transactionID}/${reportID}`, backTo), + route: ':action/:iouType/amount/:transactionID/:reportID', + getRoute: (action: ValueOf, iouType: ValueOf, transactionID: string, reportID: string, backTo = '') => + getUrlWithBackToParam(`${action}/${iouType}/amount/${transactionID}/${reportID}`, backTo), }, MONEY_REQUEST_STEP_TAX_RATE: { route: 'create/:iouType/taxRate/:transactionID/:reportID?', @@ -400,8 +397,9 @@ const ROUTES = { getRoute: (iouType: ValueOf, transactionID: string, reportID: string) => `create/${iouType}/start/${transactionID}/${reportID}/distance` as const, }, MONEY_REQUEST_CREATE_TAB_MANUAL: { - route: 'create/:iouType/start/:transactionID/:reportID/manual', - getRoute: (iouType: ValueOf, transactionID: string, reportID: string) => `create/${iouType}/start/${transactionID}/${reportID}/manual` as const, + route: ':action/:iouType/start/:transactionID/:reportID/manual', + getRoute: (action: ValueOf, iouType: ValueOf, transactionID: string, reportID: string) => + `${action}/${iouType}/start/${transactionID}/${reportID}/manual` as const, }, MONEY_REQUEST_CREATE_TAB_SCAN: { route: 'create/:iouType/start/:transactionID/:reportID/scan', diff --git a/src/SCREENS.ts b/src/SCREENS.ts index 96b284dbea2f..8fc25571c9d9 100644 --- a/src/SCREENS.ts +++ b/src/SCREENS.ts @@ -145,7 +145,6 @@ const SCREENS = { STEP_TAX_AMOUNT: 'Money_Request_Step_Tax_Amount', STEP_TAX_RATE: 'Money_Request_Step_Tax_Rate', ROOT: 'Money_Request', - AMOUNT: 'Money_Request_Amount', PARTICIPANTS: 'Money_Request_Participants', CONFIRMATION: 'Money_Request_Confirmation', CURRENCY: 'Money_Request_Currency', diff --git a/src/components/MoneyRequestConfirmationList.js b/src/components/MoneyRequestConfirmationList.js index afabb40fd9f4..4f4861bdc2e5 100755 --- a/src/components/MoneyRequestConfirmationList.js +++ b/src/components/MoneyRequestConfirmationList.js @@ -645,7 +645,15 @@ function MoneyRequestConfirmationList(props) { return; } if (props.isEditingSplitBill) { - Navigation.navigate(ROUTES.EDIT_SPLIT_BILL.getRoute(props.reportID, props.reportActionID, CONST.EDIT_REQUEST_FIELD.AMOUNT)); + Navigation.navigate( + ROUTES.MONEY_REQUEST_STEP_AMOUNT.getRoute( + CONST.IOU.ACTION.EDIT, + CONST.IOU.TYPE.SPLIT, + props.transaction.transactionID, + props.reportID, + Navigation.getActiveRouteWithoutParams(), + ), + ); return; } Navigation.navigate(ROUTES.MONEY_REQUEST_AMOUNT.getRoute(props.iouType, props.reportID)); diff --git a/src/components/MoneyTemporaryForRefactorRequestConfirmationList.js b/src/components/MoneyTemporaryForRefactorRequestConfirmationList.js index 2aff0444a59e..34212facfdc4 100755 --- a/src/components/MoneyTemporaryForRefactorRequestConfirmationList.js +++ b/src/components/MoneyTemporaryForRefactorRequestConfirmationList.js @@ -672,11 +672,8 @@ function MoneyTemporaryForRefactorRequestConfirmationList({ if (isDistanceRequest) { return; } - if (isEditingSplitBill) { - Navigation.navigate(ROUTES.EDIT_SPLIT_BILL.getRoute(reportID, reportActionID, CONST.EDIT_REQUEST_FIELD.AMOUNT)); - return; - } - Navigation.navigate(ROUTES.MONEY_REQUEST_STEP_AMOUNT.getRoute(iouType, transaction.transactionID, reportID, Navigation.getActiveRouteWithoutParams())); + const action = isEditingSplitBill ? CONST.IOU.ACTION.EDIT : CONST.IOU.ACTION.CREATE; + Navigation.navigate(ROUTES.MONEY_REQUEST_STEP_AMOUNT.getRoute(action, iouType, transaction.transactionID, reportID, Navigation.getActiveRouteWithoutParams())); }} style={[styles.moneyRequestMenuItem, styles.mt2]} titleStyle={styles.moneyRequestConfirmationAmount} diff --git a/src/components/ReportActionItem/MoneyRequestView.tsx b/src/components/ReportActionItem/MoneyRequestView.tsx index 3533506797bb..1d9267f09a91 100644 --- a/src/components/ReportActionItem/MoneyRequestView.tsx +++ b/src/components/ReportActionItem/MoneyRequestView.tsx @@ -254,7 +254,9 @@ function MoneyRequestView({ titleStyle={styles.newKansasLarge} interactive={canEditAmount} shouldShowRightIcon={canEditAmount} - onPress={() => Navigation.navigate(ROUTES.EDIT_REQUEST.getRoute(report.reportID, CONST.EDIT_REQUEST_FIELD.AMOUNT))} + onPress={() => + Navigation.navigate(ROUTES.MONEY_REQUEST_STEP_AMOUNT.getRoute(CONST.IOU.ACTION.EDIT, CONST.IOU.TYPE.REQUEST, transaction?.transactionID ?? '', report.reportID)) + } brickRoadIndicator={hasViolations('amount') || (hasErrors && transactionAmount === 0) ? CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR : undefined} error={hasErrors && transactionAmount === 0 ? translate('common.error.enterAmount') : ''} /> diff --git a/src/libs/IOUUtils.ts b/src/libs/IOUUtils.ts index c0fb4c6195b1..fcaafd961950 100644 --- a/src/libs/IOUUtils.ts +++ b/src/libs/IOUUtils.ts @@ -17,7 +17,7 @@ function navigateToStartMoneyRequestStep(requestType: ValueOf require('../../../pages/iou/request/step/IOURequestStepTag').default as React.ComponentType, [SCREENS.MONEY_REQUEST.STEP_WAYPOINT]: () => require('../../../pages/iou/request/step/IOURequestStepWaypoint').default as React.ComponentType, [SCREENS.MONEY_REQUEST.ROOT]: () => require('../../../pages/iou/MoneyRequestSelectorPage').default as React.ComponentType, - [SCREENS.MONEY_REQUEST.AMOUNT]: () => require('../../../pages/iou/steps/NewRequestAmountPage').default as React.ComponentType, [SCREENS.MONEY_REQUEST.PARTICIPANTS]: () => require('../../../pages/iou/steps/MoneyRequstParticipantsPage/MoneyRequestParticipantsPage').default as React.ComponentType, [SCREENS.MONEY_REQUEST.CONFIRMATION]: () => require('../../../pages/iou/steps/MoneyRequestConfirmPage').default as React.ComponentType, [SCREENS.MONEY_REQUEST.CURRENCY]: () => require('../../../pages/iou/IOUCurrencySelection').default as React.ComponentType, diff --git a/src/libs/Navigation/linkingConfig/config.ts b/src/libs/Navigation/linkingConfig/config.ts index f1c9c316fe93..7cdf9c3679fa 100644 --- a/src/libs/Navigation/linkingConfig/config.ts +++ b/src/libs/Navigation/linkingConfig/config.ts @@ -397,7 +397,6 @@ const config: LinkingOptions['config'] = { }, }, }, - [SCREENS.MONEY_REQUEST.AMOUNT]: ROUTES.MONEY_REQUEST_AMOUNT.route, [SCREENS.MONEY_REQUEST.STEP_TAX_AMOUNT]: ROUTES.MONEY_REQUEST_STEP_TAX_AMOUNT.route, [SCREENS.MONEY_REQUEST.STEP_TAX_RATE]: ROUTES.MONEY_REQUEST_STEP_TAX_RATE.route, [SCREENS.MONEY_REQUEST.PARTICIPANTS]: ROUTES.MONEY_REQUEST_PARTICIPANTS.route, diff --git a/src/libs/Navigation/types.ts b/src/libs/Navigation/types.ts index 3c4cf17853f1..077ebd4caaac 100644 --- a/src/libs/Navigation/types.ts +++ b/src/libs/Navigation/types.ts @@ -194,7 +194,6 @@ type RoomInviteNavigatorParamList = { type MoneyRequestNavigatorParamList = { [SCREENS.MONEY_REQUEST.ROOT]: undefined; - [SCREENS.MONEY_REQUEST.AMOUNT]: undefined; [SCREENS.MONEY_REQUEST.PARTICIPANTS]: { iouType: string; reportID: string; diff --git a/src/pages/home/report/ReportActionCompose/AttachmentPickerWithMenuItems.js b/src/pages/home/report/ReportActionCompose/AttachmentPickerWithMenuItems.js index df5645ae61ad..7f10e7fa96bd 100644 --- a/src/pages/home/report/ReportActionCompose/AttachmentPickerWithMenuItems.js +++ b/src/pages/home/report/ReportActionCompose/AttachmentPickerWithMenuItems.js @@ -145,12 +145,14 @@ function AttachmentPickerWithMenuItems({ [CONST.IOU.TYPE.SPLIT]: { icon: Expensicons.Receipt, text: translate('iou.splitBill'), - onSelected: () => Navigation.navigate(ROUTES.MONEY_REQUEST_CREATE.getRoute(CONST.IOU.TYPE.SPLIT, CONST.IOU.OPTIMISTIC_TRANSACTION_ID, report.reportID)), + onSelected: () => + Navigation.navigate(ROUTES.MONEY_REQUEST_CREATE.getRoute(CONST.IOU.ACTION.CREATE, CONST.IOU.TYPE.SPLIT, CONST.IOU.OPTIMISTIC_TRANSACTION_ID, report.reportID)), }, [CONST.IOU.TYPE.REQUEST]: { icon: Expensicons.MoneyCircle, text: translate('iou.requestMoney'), - onSelected: () => Navigation.navigate(ROUTES.MONEY_REQUEST_CREATE.getRoute(CONST.IOU.TYPE.REQUEST, CONST.IOU.OPTIMISTIC_TRANSACTION_ID, report.reportID)), + onSelected: () => + Navigation.navigate(ROUTES.MONEY_REQUEST_CREATE.getRoute(CONST.IOU.ACTION.CREATE, CONST.IOU.TYPE.REQUEST, CONST.IOU.OPTIMISTIC_TRANSACTION_ID, report.reportID)), }, [CONST.IOU.TYPE.SEND]: { icon: Expensicons.Send, diff --git a/src/pages/home/sidebar/SidebarScreen/FloatingActionButtonAndPopover.js b/src/pages/home/sidebar/SidebarScreen/FloatingActionButtonAndPopover.js index 0df490fa4466..0dc290f53f2a 100644 --- a/src/pages/home/sidebar/SidebarScreen/FloatingActionButtonAndPopover.js +++ b/src/pages/home/sidebar/SidebarScreen/FloatingActionButtonAndPopover.js @@ -169,7 +169,12 @@ function FloatingActionButtonAndPopover(props) { Navigation.navigate( // When starting to create a money request from the global FAB, there is not an existing report yet. A random optimistic reportID is generated and used // for all of the routes in the creation flow. - ROUTES.MONEY_REQUEST_CREATE.getRoute(CONST.IOU.TYPE.REQUEST, CONST.IOU.OPTIMISTIC_TRANSACTION_ID, ReportUtils.generateReportID()), + ROUTES.MONEY_REQUEST_CREATE.getRoute( + CONST.IOU.ACTION.CREATE, + CONST.IOU.TYPE.REQUEST, + CONST.IOU.OPTIMISTIC_TRANSACTION_ID, + ReportUtils.generateReportID(), + ), ), ), }, diff --git a/src/pages/iou/request/IOURequestRedirectToStartPage.js b/src/pages/iou/request/IOURequestRedirectToStartPage.js index ee98c8006cdb..e4091c48f1b8 100644 --- a/src/pages/iou/request/IOURequestRedirectToStartPage.js +++ b/src/pages/iou/request/IOURequestRedirectToStartPage.js @@ -43,7 +43,7 @@ function IOURequestRedirectToStartPage({ if (iouRequestType === CONST.IOU.REQUEST_TYPE.DISTANCE) { Navigation.navigate(ROUTES.MONEY_REQUEST_CREATE_TAB_DISTANCE.getRoute(iouType, CONST.IOU.OPTIMISTIC_TRANSACTION_ID, optimisticReportID)); } else if (iouRequestType === CONST.IOU.REQUEST_TYPE.MANUAL) { - Navigation.navigate(ROUTES.MONEY_REQUEST_CREATE_TAB_MANUAL.getRoute(iouType, CONST.IOU.OPTIMISTIC_TRANSACTION_ID, optimisticReportID)); + Navigation.navigate(ROUTES.MONEY_REQUEST_CREATE_TAB_MANUAL.getRoute(CONST.IOU.ACTION.CREATE, iouType, CONST.IOU.OPTIMISTIC_TRANSACTION_ID, optimisticReportID)); } else if (iouRequestType === CONST.IOU.REQUEST_TYPE.SCAN) { Navigation.navigate(ROUTES.MONEY_REQUEST_CREATE_TAB_SCAN.getRoute(iouType, CONST.IOU.OPTIMISTIC_TRANSACTION_ID, optimisticReportID)); } diff --git a/src/pages/iou/request/step/IOURequestStepAmount.js b/src/pages/iou/request/step/IOURequestStepAmount.js index 84e0ac8533c5..4c2b24c87be2 100644 --- a/src/pages/iou/request/step/IOURequestStepAmount.js +++ b/src/pages/iou/request/step/IOURequestStepAmount.js @@ -59,7 +59,7 @@ const getTaxAmount = (transaction, defaultTaxValue, amount) => { function IOURequestStepAmount({ report, route: { - params: {iouType, reportID, transactionID, backTo, currency: selectedCurrency}, + params: {iouType, reportID, transactionID, backTo, currency: selectedCurrency, action}, }, transaction, transaction: {currency: originalCurrency}, @@ -71,6 +71,8 @@ function IOURequestStepAmount({ const focusTimeoutRef = useRef(null); const iouRequestType = getRequestType(transaction); const currency = selectedCurrency || originalCurrency; + const isEditing = action === CONST.IOU.ACTION.EDIT; + const isSplitBill = iouType === CONST.IOU.TYPE.SPLIT; const isPolicyExpenseChat = ReportUtils.isPolicyExpenseChat(ReportUtils.getRootParentReport(report)); const isTaxTrackingEnabled = isPolicyExpenseChat && policy.isTaxTrackingEnabled; @@ -128,21 +130,40 @@ function IOURequestStepAmount({ Navigation.navigate(ROUTES.MONEY_REQUEST_STEP_PARTICIPANTS.getRoute(iouType, transactionID, reportID)); }; + const saveAmountAndCurrency = ({amount}) => { + const newAmount = CurrencyUtils.convertToBackendAmount(Number.parseFloat(amount)); + + // If the value hasn't changed, don't request to save changes on the server and just close the modal + if (newAmount === TransactionUtils.getAmount(transaction) && currency === TransactionUtils.getCurrency(transaction)) { + Navigation.dismissModal(); + return; + } + + if (isSplitBill) { + IOU.setDraftSplitTransaction(transactionID, {amount: newAmount, currency}); + Navigation.goBack(backTo); + return; + } + + IOU.updateMoneyRequestAmountAndCurrency(transactionID, reportID, currency, newAmount); + Navigation.dismissModal(); + }; + return ( (textInput.current = e)} onCurrencyButtonPress={navigateToCurrencySelectionPage} - onSubmitButtonPress={navigateToNextPage} + onSubmitButtonPress={isEditing ? saveAmountAndCurrency : navigateToNextPage} selectedTab={iouRequestType} /> diff --git a/src/pages/iou/request/step/withFullTransactionOrNotFound.js b/src/pages/iou/request/step/withFullTransactionOrNotFound.js index 7cdbb3484999..801ad403af2a 100644 --- a/src/pages/iou/request/step/withFullTransactionOrNotFound.js +++ b/src/pages/iou/request/step/withFullTransactionOrNotFound.js @@ -71,7 +71,16 @@ export default function (WrappedComponent) { key: ({route}) => { const transactionID = lodashGet(route, 'params.transactionID', 0); const userAction = lodashGet(route, 'params.action', CONST.IOU.ACTION.CREATE); - return `${userAction === CONST.IOU.ACTION.CREATE ? ONYXKEYS.COLLECTION.TRANSACTION_DRAFT : ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`; + const isSplitBill = lodashGet(route, 'params.iouType', CONST.IOU.TYPE.REQUEST) === CONST.IOU.TYPE.SPLIT; + let transactionCollectionKey = ONYXKEYS.COLLECTION.TRANSACTION_DRAFT; + if (userAction === CONST.IOU.ACTION.EDIT) { + if (isSplitBill) { + transactionCollectionKey = ONYXKEYS.COLLECTION.SPLIT_TRANSACTION_DRAFT; + } else { + transactionCollectionKey = ONYXKEYS.COLLECTION.TRANSACTION; + } + } + return `${transactionCollectionKey}${transactionID}`; }, }, })(WithFullTransactionOrNotFoundWithRef); From 02a8a1897982b3d356a8541246c2545be55a5c6d Mon Sep 17 00:00:00 2001 From: dukenv0307 Date: Mon, 5 Feb 2024 17:20:20 +0700 Subject: [PATCH 078/500] fix negative amount case --- src/libs/TransactionUtils.ts | 4 ++-- src/pages/iou/request/step/IOURequestStepAmount.js | 5 ++++- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/libs/TransactionUtils.ts b/src/libs/TransactionUtils.ts index 8e48eddea0ac..6f55eb961b14 100644 --- a/src/libs/TransactionUtils.ts +++ b/src/libs/TransactionUtils.ts @@ -281,12 +281,12 @@ function getAmount(transaction: OnyxEntry, isFromExpenseReport?: bo // we need to return an opposite sign than is saved in the transaction object let amount = transaction?.modifiedAmount ?? 0; if (amount) { - return -amount; + return Math.abs(amount); } // To avoid -0 being shown, lets only change the sign if the value is other than 0. amount = transaction?.amount ?? 0; - return amount ? -amount : 0; + return amount ? Math.abs(amount) : 0; } /** diff --git a/src/pages/iou/request/step/IOURequestStepAmount.js b/src/pages/iou/request/step/IOURequestStepAmount.js index 4c2b24c87be2..fdc07f38eed6 100644 --- a/src/pages/iou/request/step/IOURequestStepAmount.js +++ b/src/pages/iou/request/step/IOURequestStepAmount.js @@ -71,9 +71,12 @@ function IOURequestStepAmount({ const focusTimeoutRef = useRef(null); const iouRequestType = getRequestType(transaction); const currency = selectedCurrency || originalCurrency; + const {amount: transactionAmount} = ReportUtils.getTransactionDetails(transaction); const isEditing = action === CONST.IOU.ACTION.EDIT; const isSplitBill = iouType === CONST.IOU.TYPE.SPLIT; + console.log(transaction); + const isPolicyExpenseChat = ReportUtils.isPolicyExpenseChat(ReportUtils.getRootParentReport(report)); const isTaxTrackingEnabled = isPolicyExpenseChat && policy.isTaxTrackingEnabled; @@ -160,7 +163,7 @@ function IOURequestStepAmount({ (textInput.current = e)} onCurrencyButtonPress={navigateToCurrencySelectionPage} onSubmitButtonPress={isEditing ? saveAmountAndCurrency : navigateToNextPage} From a719511f5f925628405cb595599cf2fcb2f444d2 Mon Sep 17 00:00:00 2001 From: dukenv0307 Date: Mon, 5 Feb 2024 22:57:09 +0700 Subject: [PATCH 079/500] fix the case edit split --- .../iou/request/step/IOURequestStepAmount.js | 10 ++++--- .../step/withFullTransactionOrNotFound.js | 26 +++++++++++-------- 2 files changed, 22 insertions(+), 14 deletions(-) diff --git a/src/pages/iou/request/step/IOURequestStepAmount.js b/src/pages/iou/request/step/IOURequestStepAmount.js index fdc07f38eed6..63cf19299a80 100644 --- a/src/pages/iou/request/step/IOURequestStepAmount.js +++ b/src/pages/iou/request/step/IOURequestStepAmount.js @@ -2,6 +2,7 @@ import {useFocusEffect} from '@react-navigation/native'; import PropTypes from 'prop-types'; import React, {useCallback, useRef} from 'react'; import {withOnyx} from 'react-native-onyx'; +import _ from 'underscore'; import taxPropTypes from '@components/taxPropTypes'; import transactionPropTypes from '@components/transactionPropTypes'; import useLocalize from '@hooks/useLocalize'; @@ -33,6 +34,9 @@ const propTypes = { /** The transaction object being modified in Onyx */ transaction: transactionPropTypes, + /** The draft transaction of scan split bill */ + splitTransactionDraft: transactionPropTypes, + /* Onyx Props */ /** Collection of tax rates attached to a policy */ policyTaxRates: taxPropTypes, @@ -49,6 +53,7 @@ const defaultProps = { transaction: {}, policyTaxRates: {}, policy: {}, + splitTransactionDraft: {}, }; const getTaxAmount = (transaction, defaultTaxValue, amount) => { @@ -62,6 +67,7 @@ function IOURequestStepAmount({ params: {iouType, reportID, transactionID, backTo, currency: selectedCurrency, action}, }, transaction, + splitTransactionDraft, transaction: {currency: originalCurrency}, policyTaxRates, policy, @@ -71,12 +77,10 @@ function IOURequestStepAmount({ const focusTimeoutRef = useRef(null); const iouRequestType = getRequestType(transaction); const currency = selectedCurrency || originalCurrency; - const {amount: transactionAmount} = ReportUtils.getTransactionDetails(transaction); + const {amount: transactionAmount} = ReportUtils.getTransactionDetails(_.isEmpty(splitTransactionDraft) ? transaction : splitTransactionDraft); const isEditing = action === CONST.IOU.ACTION.EDIT; const isSplitBill = iouType === CONST.IOU.TYPE.SPLIT; - console.log(transaction); - const isPolicyExpenseChat = ReportUtils.isPolicyExpenseChat(ReportUtils.getRootParentReport(report)); const isTaxTrackingEnabled = isPolicyExpenseChat && policy.isTaxTrackingEnabled; diff --git a/src/pages/iou/request/step/withFullTransactionOrNotFound.js b/src/pages/iou/request/step/withFullTransactionOrNotFound.js index 801ad403af2a..1999b3a90574 100644 --- a/src/pages/iou/request/step/withFullTransactionOrNotFound.js +++ b/src/pages/iou/request/step/withFullTransactionOrNotFound.js @@ -18,12 +18,16 @@ const propTypes = { /** The report corresponding to the reportID in the route params */ transaction: transactionPropTypes, + /** The draft transaction of scan split bill */ + splitTransactionDraft: transactionPropTypes, + route: IOURequestStepRoutePropTypes.isRequired, }; const defaultProps = { forwardedRef: () => {}, transaction: {}, + splitTransactionDraft: {}, }; export default function (WrappedComponent) { @@ -31,6 +35,7 @@ export default function (WrappedComponent) { function WithFullTransactionOrNotFound({forwardedRef, ...props}) { const { transaction: {transactionID}, + splitTransactionDraft: {transactionID: splitTransactionDraftID}, } = props; const isFocused = useIsFocused(); @@ -38,7 +43,7 @@ export default function (WrappedComponent) { // If the transaction does not have a transactionID, then the transaction no longer exists in Onyx as a full transaction and the not-found page should be shown. // In addition, the not-found page should be shown only if the component screen's route is active (i.e. is focused). // This is to prevent it from showing when the modal is being dismissed while navigating to a different route (e.g. on requesting money). - if (!transactionID) { + if (!transactionID && !splitTransactionDraftID) { return ; } @@ -71,16 +76,15 @@ export default function (WrappedComponent) { key: ({route}) => { const transactionID = lodashGet(route, 'params.transactionID', 0); const userAction = lodashGet(route, 'params.action', CONST.IOU.ACTION.CREATE); - const isSplitBill = lodashGet(route, 'params.iouType', CONST.IOU.TYPE.REQUEST) === CONST.IOU.TYPE.SPLIT; - let transactionCollectionKey = ONYXKEYS.COLLECTION.TRANSACTION_DRAFT; - if (userAction === CONST.IOU.ACTION.EDIT) { - if (isSplitBill) { - transactionCollectionKey = ONYXKEYS.COLLECTION.SPLIT_TRANSACTION_DRAFT; - } else { - transactionCollectionKey = ONYXKEYS.COLLECTION.TRANSACTION; - } - } - return `${transactionCollectionKey}${transactionID}`; + return `${userAction === CONST.IOU.ACTION.CREATE ? ONYXKEYS.COLLECTION.TRANSACTION_DRAFT : ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`; + }, + }, + splitTransactionDraft: { + key: ({route}) => { + const transactionID = lodashGet(route, 'params.transactionID', 0); + const userAction = lodashGet(route, 'params.action', CONST.IOU.ACTION.CREATE); + const isEditingSplitBill = userAction === CONST.IOU.ACTION.EDIT && lodashGet(route, 'params.iouType', CONST.IOU.TYPE.REQUEST) === CONST.IOU.TYPE.SPLIT; + return `${ONYXKEYS.COLLECTION.SPLIT_TRANSACTION_DRAFT}${isEditingSplitBill ? transactionID : '0'}`; }, }, })(WithFullTransactionOrNotFoundWithRef); From ae15fd56fb9fbfb41f4ca201e0e1fb3ed1f232fa Mon Sep 17 00:00:00 2001 From: dukenv0307 Date: Mon, 5 Feb 2024 23:03:08 +0700 Subject: [PATCH 080/500] delete old page --- src/pages/iou/MoneyRequestSelectorPage.js | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/src/pages/iou/MoneyRequestSelectorPage.js b/src/pages/iou/MoneyRequestSelectorPage.js index 0a0efc38313a..b8b81a904499 100644 --- a/src/pages/iou/MoneyRequestSelectorPage.js +++ b/src/pages/iou/MoneyRequestSelectorPage.js @@ -25,7 +25,6 @@ import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import NewDistanceRequestPage from './NewDistanceRequestPage'; import IOURequestStepScan from './request/step/IOURequestStepScan'; -import NewRequestAmountPage from './steps/NewRequestAmountPage'; const propTypes = { /** React Navigation route */ @@ -127,11 +126,6 @@ function MoneyRequestSelectorPage(props) { /> )} > - {() => } {shouldDisplayDistanceRequest && ( ) : ( - + null )} From b3dbe2607ec04fb9b8519fc3f1ed828e0857fa96 Mon Sep 17 00:00:00 2001 From: Etotaziba Olei Date: Wed, 7 Feb 2024 09:28:57 +0100 Subject: [PATCH 081/500] add tax amount API types --- src/libs/API/types.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/libs/API/types.ts b/src/libs/API/types.ts index a4ab3db9a7cd..f6ec3b8732e3 100644 --- a/src/libs/API/types.ts +++ b/src/libs/API/types.ts @@ -121,6 +121,8 @@ const WRITE_COMMANDS = { UPDATE_MONEY_REQUEST_BILLABLE: 'UpdateMoneyRequestBillable', UPDATE_MONEY_REQUEST_MERCHANT: 'UpdateMoneyRequestMerchant', UPDATE_MONEY_REQUEST_TAG: 'UpdateMoneyRequestTag', + UPDATE_MONEY_REQUEST_TAX_AMOUNT: 'UpdateMoneyRequestTaxAmount', + UPDATE_MONEY_REQUEST_TAX_RATE: 'UpdateMoneyRequestTaxRate', UPDATE_MONEY_REQUEST_DISTANCE: 'UpdateMoneyRequestDistance', UPDATE_MONEY_REQUEST_CATEGORY: 'UpdateMoneyRequestCategory', UPDATE_MONEY_REQUEST_DESCRIPTION: 'UpdateMoneyRequestDescription', @@ -259,6 +261,8 @@ type WriteCommandParameters = { [WRITE_COMMANDS.UPDATE_MONEY_REQUEST_MERCHANT]: Parameters.UpdateMoneyRequestParams; [WRITE_COMMANDS.UPDATE_MONEY_REQUEST_BILLABLE]: Parameters.UpdateMoneyRequestParams; [WRITE_COMMANDS.UPDATE_MONEY_REQUEST_TAG]: Parameters.UpdateMoneyRequestParams; + [WRITE_COMMANDS.UPDATE_MONEY_REQUEST_TAX_AMOUNT]: Parameters.UpdateMoneyRequestParams; + [WRITE_COMMANDS.UPDATE_MONEY_REQUEST_TAX_RATE]: Parameters.UpdateMoneyRequestParams; [WRITE_COMMANDS.UPDATE_MONEY_REQUEST_DISTANCE]: Parameters.UpdateMoneyRequestParams; [WRITE_COMMANDS.UPDATE_MONEY_REQUEST_CATEGORY]: Parameters.UpdateMoneyRequestParams; [WRITE_COMMANDS.UPDATE_MONEY_REQUEST_DESCRIPTION]: Parameters.UpdateMoneyRequestParams; From 0bdef874da54f3a6e8a0630f640e95d08d00928b Mon Sep 17 00:00:00 2001 From: Etotaziba Olei Date: Wed, 7 Feb 2024 09:29:48 +0100 Subject: [PATCH 082/500] update tax request types --- src/libs/actions/IOU.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/libs/actions/IOU.ts b/src/libs/actions/IOU.ts index bc1a8b9dd4cf..319889f54496 100644 --- a/src/libs/actions/IOU.ts +++ b/src/libs/actions/IOU.ts @@ -1217,18 +1217,18 @@ function updateMoneyRequestTag(transactionID: string, transactionThreadReportID: } /** Updates the created tax amount of a money request */ -function updateMoneyRequestTaxAmount(transactionID: string, optimisticReportActionID: string, val: number) { +function updateMoneyRequestTaxAmount(transactionID: string, optimisticReportActionID: string, taxAmount: number) { const transactionChanges = { - taxAmount: val, + taxAmount, }; const {params, onyxData} = getUpdateMoneyRequestParams(transactionID, optimisticReportActionID, transactionChanges, true); API.write('UpdateMoneyRequestTaxAmount', params, onyxData); } /** Updates the created tax rate of a money request */ -function updateMoneyRequestTaxRate(transactionID: string, optimisticReportActionID: string, val: string) { +function updateMoneyRequestTaxRate(transactionID: string, optimisticReportActionID: string, taxCode: string) { const transactionChanges = { - taxCode: val, + taxCode, }; const {params, onyxData} = getUpdateMoneyRequestParams(transactionID, optimisticReportActionID, transactionChanges, true); API.write('UpdateMoneyRequestTaxRate', params, onyxData); From 3293311d92215d039c17f6654d8718ee94e5d46f Mon Sep 17 00:00:00 2001 From: Etotaziba Olei Date: Wed, 7 Feb 2024 11:37:57 +0100 Subject: [PATCH 083/500] add taxCode and taxAmount to getUpdatedTransaction --- src/libs/TransactionUtils.ts | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/libs/TransactionUtils.ts b/src/libs/TransactionUtils.ts index bce784819769..7964e8c25b78 100644 --- a/src/libs/TransactionUtils.ts +++ b/src/libs/TransactionUtils.ts @@ -204,6 +204,16 @@ function getUpdatedTransaction(transaction: Transaction, transactionChanges: Tra shouldStopSmartscan = true; } + if (Object.hasOwn(transactionChanges, 'taxAmount') && typeof transactionChanges.taxAmount === 'number') { + updatedTransaction.taxAmount = isFromExpenseReport ? -transactionChanges.taxAmount : transactionChanges.taxAmount; + shouldStopSmartscan = true; + } + + if (Object.hasOwn(transactionChanges, 'taxCode') && typeof transactionChanges.taxCode === 'string') { + updatedTransaction.taxCode = transactionChanges.taxCode; + shouldStopSmartscan = true; + } + if (Object.hasOwn(transactionChanges, 'billable') && typeof transactionChanges.billable === 'boolean') { updatedTransaction.billable = transactionChanges.billable; } @@ -237,6 +247,8 @@ function getUpdatedTransaction(transaction: Transaction, transactionChanges: Tra ...(Object.hasOwn(transactionChanges, 'billable') && {billable: CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE}), ...(Object.hasOwn(transactionChanges, 'category') && {category: CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE}), ...(Object.hasOwn(transactionChanges, 'tag') && {tag: CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE}), + ...(Object.hasOwn(transactionChanges, 'taxAmount') && {taxAmount: CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE}), + ...(Object.hasOwn(transactionChanges, 'taxCode') && {taxCode: CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE}), }; return updatedTransaction; From 9abeb11ad5ee99e91a05a44862d006e2093777ab Mon Sep 17 00:00:00 2001 From: Etotaziba Olei Date: Wed, 7 Feb 2024 17:31:32 +0100 Subject: [PATCH 084/500] update Tax Amount Menu Item Description in ConfirmationList --- src/components/MoneyRequestConfirmationList.js | 2 +- .../MoneyTemporaryForRefactorRequestConfirmationList.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/MoneyRequestConfirmationList.js b/src/components/MoneyRequestConfirmationList.js index 596811086ee4..824652d481cf 100755 --- a/src/components/MoneyRequestConfirmationList.js +++ b/src/components/MoneyRequestConfirmationList.js @@ -810,7 +810,7 @@ function MoneyRequestConfirmationList(props) { diff --git a/src/components/MoneyTemporaryForRefactorRequestConfirmationList.js b/src/components/MoneyTemporaryForRefactorRequestConfirmationList.js index cd1874197063..902139610489 100755 --- a/src/components/MoneyTemporaryForRefactorRequestConfirmationList.js +++ b/src/components/MoneyTemporaryForRefactorRequestConfirmationList.js @@ -865,7 +865,7 @@ function MoneyTemporaryForRefactorRequestConfirmationList({ From 59768825ac3e1a1f07253269c180b5417ed9ef1b Mon Sep 17 00:00:00 2001 From: Etotaziba Olei Date: Wed, 7 Feb 2024 17:32:02 +0100 Subject: [PATCH 085/500] update Tax Amount Menu Item Description in MoneyRequestView --- src/components/ReportActionItem/MoneyRequestView.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/ReportActionItem/MoneyRequestView.tsx b/src/components/ReportActionItem/MoneyRequestView.tsx index a545bd99c378..7e04b3224119 100644 --- a/src/components/ReportActionItem/MoneyRequestView.tsx +++ b/src/components/ReportActionItem/MoneyRequestView.tsx @@ -390,7 +390,7 @@ function MoneyRequestView({ Date: Wed, 7 Feb 2024 17:34:15 +0100 Subject: [PATCH 086/500] add pending action for taxCode --- src/components/ReportActionItem/MoneyRequestView.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/ReportActionItem/MoneyRequestView.tsx b/src/components/ReportActionItem/MoneyRequestView.tsx index 7e04b3224119..927dff901158 100644 --- a/src/components/ReportActionItem/MoneyRequestView.tsx +++ b/src/components/ReportActionItem/MoneyRequestView.tsx @@ -374,7 +374,7 @@ function MoneyRequestView({ )} {shouldShowTax && ( - + Date: Wed, 7 Feb 2024 17:35:02 +0100 Subject: [PATCH 087/500] add pending action for taxAmount --- src/components/ReportActionItem/MoneyRequestView.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/ReportActionItem/MoneyRequestView.tsx b/src/components/ReportActionItem/MoneyRequestView.tsx index 927dff901158..e6a20977ae15 100644 --- a/src/components/ReportActionItem/MoneyRequestView.tsx +++ b/src/components/ReportActionItem/MoneyRequestView.tsx @@ -387,7 +387,7 @@ function MoneyRequestView({ )} {shouldShowTax && ( - + Date: Mon, 19 Feb 2024 15:13:58 +0700 Subject: [PATCH 088/500] fix ts check --- src/libs/Navigation/Navigation.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/Navigation/Navigation.ts b/src/libs/Navigation/Navigation.ts index 8009c963ade7..4e56586bf1f5 100644 --- a/src/libs/Navigation/Navigation.ts +++ b/src/libs/Navigation/Navigation.ts @@ -94,7 +94,7 @@ function getActiveRouteIndex(stateOrRoute: StateOrRoute, index?: number): number function parseHybridAppUrl(url: HybridAppRoute | Route): Route { switch (url) { case HYBRID_APP_ROUTES.MONEY_REQUEST_CREATE: - return ROUTES.MONEY_REQUEST_CREATE.getRoute(CONST.IOU.TYPE.REQUEST, CONST.IOU.OPTIMISTIC_TRANSACTION_ID, ReportUtils.generateReportID()); + return ROUTES.MONEY_REQUEST_CREATE.getRoute(CONST.IOU.ACTION.CREATE, CONST.IOU.TYPE.REQUEST, CONST.IOU.OPTIMISTIC_TRANSACTION_ID, ReportUtils.generateReportID()); default: return url; } From 68ef4f53cc8e9420bada67903b07ca9fab37cd14 Mon Sep 17 00:00:00 2001 From: Etotaziba Olei Date: Mon, 19 Feb 2024 09:26:07 +0100 Subject: [PATCH 089/500] fix bad merge for IOURequestStepAmount --- src/pages/iou/request/step/IOURequestStepAmount.js | 8 -------- 1 file changed, 8 deletions(-) diff --git a/src/pages/iou/request/step/IOURequestStepAmount.js b/src/pages/iou/request/step/IOURequestStepAmount.js index 7861cbc041e5..c360f837a0c3 100644 --- a/src/pages/iou/request/step/IOURequestStepAmount.js +++ b/src/pages/iou/request/step/IOURequestStepAmount.js @@ -1,5 +1,4 @@ import {useFocusEffect} from '@react-navigation/native'; -import lodashGet from 'lodash/get'; import PropTypes from 'prop-types'; import React, {useCallback, useEffect, useRef} from 'react'; import transactionPropTypes from '@components/transactionPropTypes'; @@ -29,10 +28,6 @@ const propTypes = { /** The transaction object being modified in Onyx */ transaction: transactionPropTypes, - /* Onyx Props */ - /** Collection of tax rates attached to a policy */ - policyTaxRates: taxPropTypes, - /** The policy of the report */ policy: PropTypes.shape({ /** @@ -69,9 +64,6 @@ function IOURequestStepAmount({ const originalCurrency = useRef(null); const iouRequestType = getRequestType(transaction); - const isPolicyExpenseChat = ReportUtils.isPolicyExpenseChat(ReportUtils.getRootParentReport(report)); - const isTaxTrackingEnabled = isPolicyExpenseChat && lodashGet(policy, 'tax.trackingEnabled', policy.isTaxTrackingEnabled); - useFocusEffect( useCallback(() => { focusTimeoutRef.current = setTimeout(() => textInput.current && textInput.current.focus(), CONST.ANIMATED_TRANSITION); From a105896eee3873b173f38583a5ab64495dd9bdc1 Mon Sep 17 00:00:00 2001 From: Etotaziba Olei Date: Mon, 19 Feb 2024 09:36:32 +0100 Subject: [PATCH 090/500] fix bad merge for EditRequestPage --- src/pages/EditRequestPage.js | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/src/pages/EditRequestPage.js b/src/pages/EditRequestPage.js index ba07afae7388..77d4f8369403 100644 --- a/src/pages/EditRequestPage.js +++ b/src/pages/EditRequestPage.js @@ -89,10 +89,22 @@ const defaultProps = { policyTaxRates: {}, }; -function EditRequestPage({report, route, policy, policyCategories, policyTags, parentReportActions, transaction}) { +const getTaxAmount = (transactionAmount, transactionTaxCode, policyTaxRates) => { + const percentage = (transactionTaxCode ? policyTaxRates.taxes[transactionTaxCode].value : policyTaxRates.defaultValue) || ''; + return CurrencyUtils.convertToBackendAmount(Number.parseFloat(TransactionUtils.calculateTaxAmount(percentage, transactionAmount))); +}; + +function EditRequestPage({report, route, policy, policyTaxRates, policyCategories, policyTags, parentReportActions, transaction}) { const parentReportActionID = lodashGet(report, 'parentReportActionID', '0'); const parentReportAction = lodashGet(parentReportActions, parentReportActionID, {}); - const {amount: transactionAmount, currency: transactionCurrency, category: transactionCategory, tag: transactionTag} = ReportUtils.getTransactionDetails(transaction); + const { + amount: transactionAmount, + taxAmount: transactionTaxAmount, + taxCode: transactionTaxCode, + currency: transactionCurrency, + category: transactionCategory, + tag: transactionTag, + } = ReportUtils.getTransactionDetails(transaction); const defaultCurrency = lodashGet(route, 'params.currency', '') || transactionCurrency; const fieldToEdit = lodashGet(route, ['params', 'field'], ''); @@ -102,6 +114,8 @@ function EditRequestPage({report, route, policy, policyCategories, policyTags, p const policyTagListName = PolicyUtils.getTagListName(policyTags, tagIndex); const policyTagLists = useMemo(() => PolicyUtils.getTagLists(policyTags), [policyTags]); + const taxRateTitle = TransactionUtils.getTaxName(policyTaxRates.taxes, transactionTaxCode); + // A flag for verifying that the current report is a sub-report of a workspace chat const isPolicyExpenseChat = ReportUtils.isGroupPolicy(report); From 58af4ad224cde512a00715f21ae5732ba0aff4fd Mon Sep 17 00:00:00 2001 From: Etotaziba Olei Date: Mon, 19 Feb 2024 09:54:25 +0100 Subject: [PATCH 091/500] use trackingEnabled for chrcking if tax tracking is enabled --- src/libs/PolicyUtils.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/PolicyUtils.ts b/src/libs/PolicyUtils.ts index 6b1c63785ea4..12b3d6952183 100644 --- a/src/libs/PolicyUtils.ts +++ b/src/libs/PolicyUtils.ts @@ -215,7 +215,7 @@ function isPaidGroupPolicy(policy: OnyxEntry | EmptyObject): boolean { } function isTaxPolicyEnabled(isPolicyExpenseChat: boolean, policy: OnyxEntry): boolean { - return (isPolicyExpenseChat && policy?.isTaxTrackingEnabled) ?? false; + return (isPolicyExpenseChat && (policy?.tax?.trackingEnabled || policy?.isTaxTrackingEnabled)) ?? false; } /** From 81fb82ce80bbb46da9508ce546b827918730ccb7 Mon Sep 17 00:00:00 2001 From: Etotaziba Olei Date: Mon, 19 Feb 2024 10:08:18 +0100 Subject: [PATCH 092/500] fix prettier --- src/types/onyx/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/types/onyx/index.ts b/src/types/onyx/index.ts index 28b26ed77c3e..59cabbe020ee 100644 --- a/src/types/onyx/index.ts +++ b/src/types/onyx/index.ts @@ -34,8 +34,8 @@ import type {PolicyCategories, PolicyCategory} from './PolicyCategory'; import type {PolicyMembers} from './PolicyMember'; import type PolicyMember from './PolicyMember'; import type {PolicyReportField, PolicyReportFields} from './PolicyReportField'; -import type {PolicyTaxRates, TaxRate, TaxRates} from './PolicyTaxRates'; import type {PolicyTag, PolicyTagList, PolicyTags} from './PolicyTag'; +import type {PolicyTaxRates, TaxRate, TaxRates} from './PolicyTaxRates'; import type PrivatePersonalDetails from './PrivatePersonalDetails'; import type RecentlyUsedCategories from './RecentlyUsedCategories'; import type RecentlyUsedReportFields from './RecentlyUsedReportFields'; From 88c091434482a37637c015ebdbae13994b8a5452 Mon Sep 17 00:00:00 2001 From: Etotaziba Olei Date: Mon, 19 Feb 2024 10:18:01 +0100 Subject: [PATCH 093/500] fix lint --- src/libs/PolicyUtils.ts | 2 +- src/pages/EditRequestPage.js | 10 ---------- .../iou/request/step/IOURequestStepAmount.js | 16 ---------------- 3 files changed, 1 insertion(+), 27 deletions(-) diff --git a/src/libs/PolicyUtils.ts b/src/libs/PolicyUtils.ts index 12b3d6952183..0a6cc9e7a35a 100644 --- a/src/libs/PolicyUtils.ts +++ b/src/libs/PolicyUtils.ts @@ -215,7 +215,7 @@ function isPaidGroupPolicy(policy: OnyxEntry | EmptyObject): boolean { } function isTaxPolicyEnabled(isPolicyExpenseChat: boolean, policy: OnyxEntry): boolean { - return (isPolicyExpenseChat && (policy?.tax?.trackingEnabled || policy?.isTaxTrackingEnabled)) ?? false; + return (isPolicyExpenseChat && (policy?.tax?.trackingEnabled ?? policy?.isTaxTrackingEnabled)) ?? false; } /** diff --git a/src/pages/EditRequestPage.js b/src/pages/EditRequestPage.js index 77d4f8369403..3653e37a6555 100644 --- a/src/pages/EditRequestPage.js +++ b/src/pages/EditRequestPage.js @@ -67,12 +67,6 @@ const propTypes = { /** Transaction that stores the request data */ transaction: transactionPropTypes, - /* Onyx Props */ - /** The policy of the report */ - policy: PropTypes.shape({ - /** Is Tax tracking Enabled */ - isTaxTrackingEnabled: PropTypes.bool, - }), /** Collection of tax rates attached to a policy */ policyTaxRates: taxPropTypes, @@ -85,7 +79,6 @@ const defaultProps = { policyTags: {}, parentReportActions: {}, transaction: {}, - policy: {}, policyTaxRates: {}, }; @@ -308,9 +301,6 @@ export default compose( policyCategories: { key: ({report}) => `${ONYXKEYS.COLLECTION.POLICY_CATEGORIES}${report ? report.policyID : '0'}`, }, - policy: { - key: ({report}) => `${ONYXKEYS.COLLECTION.POLICY}${report.policyID}`, - }, policyTags: { key: ({report}) => `${ONYXKEYS.COLLECTION.POLICY_TAGS}${report ? report.policyID : '0'}`, }, diff --git a/src/pages/iou/request/step/IOURequestStepAmount.js b/src/pages/iou/request/step/IOURequestStepAmount.js index c360f837a0c3..0bc51a517466 100644 --- a/src/pages/iou/request/step/IOURequestStepAmount.js +++ b/src/pages/iou/request/step/IOURequestStepAmount.js @@ -1,5 +1,4 @@ import {useFocusEffect} from '@react-navigation/native'; -import PropTypes from 'prop-types'; import React, {useCallback, useEffect, useRef} from 'react'; import transactionPropTypes from '@components/transactionPropTypes'; import useLocalize from '@hooks/useLocalize'; @@ -27,21 +26,6 @@ const propTypes = { /** The transaction object being modified in Onyx */ transaction: transactionPropTypes, - - /** The policy of the report */ - policy: PropTypes.shape({ - /** - * Whether or not the policy has tax tracking enabled - * - * @deprecated - use tax.trackingEnabled instead - */ - isTaxTrackingEnabled: PropTypes.bool, - - /** Whether or not the policy has tax tracking enabled */ - tax: PropTypes.shape({ - trackingEnabled: PropTypes.bool, - }), - }), }; const defaultProps = { From 0381cb36ce2533bd8772e9cd3b955e327e43d438 Mon Sep 17 00:00:00 2001 From: Etotaziba Olei Date: Mon, 19 Feb 2024 12:17:58 +0100 Subject: [PATCH 094/500] fix typecheck --- src/libs/actions/IOU.ts | 22 ++++++++++++++++++---- src/pages/EditRequestPage.js | 4 ++-- 2 files changed, 20 insertions(+), 6 deletions(-) diff --git a/src/libs/actions/IOU.ts b/src/libs/actions/IOU.ts index 2decc65456f8..7fe33ec54881 100644 --- a/src/libs/actions/IOU.ts +++ b/src/libs/actions/IOU.ts @@ -1349,20 +1349,34 @@ function updateMoneyRequestTag( } /** Updates the created tax amount of a money request */ -function updateMoneyRequestTaxAmount(transactionID: string, optimisticReportActionID: string, taxAmount: number) { +function updateMoneyRequestTaxAmount( + transactionID: string, + optimisticReportActionID: string, + taxAmount: number, + policy: OnyxEntry, + policyTagList: OnyxEntry, + policyCategories: OnyxEntry, +) { const transactionChanges = { taxAmount, }; - const {params, onyxData} = getUpdateMoneyRequestParams(transactionID, optimisticReportActionID, transactionChanges, true); + const {params, onyxData} = getUpdateMoneyRequestParams(transactionID, optimisticReportActionID, transactionChanges, policy, policyTagList, policyCategories, true); API.write('UpdateMoneyRequestTaxAmount', params, onyxData); } /** Updates the created tax rate of a money request */ -function updateMoneyRequestTaxRate(transactionID: string, optimisticReportActionID: string, taxCode: string) { +function updateMoneyRequestTaxRate( + transactionID: string, + optimisticReportActionID: string, + taxCode: string, + policy: OnyxEntry, + policyTagList: OnyxEntry, + policyCategories: OnyxEntry, +) { const transactionChanges = { taxCode, }; - const {params, onyxData} = getUpdateMoneyRequestParams(transactionID, optimisticReportActionID, transactionChanges, true); + const {params, onyxData} = getUpdateMoneyRequestParams(transactionID, optimisticReportActionID, transactionChanges, policy, policyTagList, policyCategories, true); API.write('UpdateMoneyRequestTaxRate', params, onyxData); } diff --git a/src/pages/EditRequestPage.js b/src/pages/EditRequestPage.js index 3653e37a6555..c6e239d8c766 100644 --- a/src/pages/EditRequestPage.js +++ b/src/pages/EditRequestPage.js @@ -136,13 +136,13 @@ function EditRequestPage({report, route, policy, policyTaxRates, policyCategorie const updateTaxAmount = (transactionChanges) => { const newTaxAmount = CurrencyUtils.convertToBackendAmount(Number.parseFloat(transactionChanges.amount)); - IOU.updateMoneyRequestTaxAmount(transaction.transactionID, report.reportID, newTaxAmount); + IOU.updateMoneyRequestTaxAmount(transaction.transactionID, report.reportID, newTaxAmount, policy, policyTags, policyCategories); Navigation.dismissModal(report.reportID); }; const updateTaxRate = (transactionChanges) => { const newTaxCode = transactionChanges.data.code; - IOU.updateMoneyRequestTaxRate(transaction.transactionID, report.reportID, newTaxCode); + IOU.updateMoneyRequestTaxRate(transaction.transactionID, report.reportID, newTaxCode, policy, policyTags, policyCategories); Navigation.dismissModal(report.reportID); }; From ec0eb6f7ed0d3a0cd0884cd7999fb91472702797 Mon Sep 17 00:00:00 2001 From: Yogesh Bhatt <65986357+ygshbht@users.noreply.github.com> Date: Tue, 20 Feb 2024 03:20:43 +0530 Subject: [PATCH 095/500] Comment typo fix src/pages/settings/Profile/PersonalDetails/StateSelectionPage.tsx Co-authored-by: Vit Horacek <36083550+mountiny@users.noreply.github.com> --- .../settings/Profile/PersonalDetails/StateSelectionPage.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/settings/Profile/PersonalDetails/StateSelectionPage.tsx b/src/pages/settings/Profile/PersonalDetails/StateSelectionPage.tsx index 7c0320244620..d5221c98b046 100644 --- a/src/pages/settings/Profile/PersonalDetails/StateSelectionPage.tsx +++ b/src/pages/settings/Profile/PersonalDetails/StateSelectionPage.tsx @@ -68,7 +68,7 @@ function StateSelectionPage() { Navigation.goBack(appendParam(backTo, 'state', option.value)); } else { // Otherwise, navigate to the specific route defined in "backTo" with a country parameter - // @ts-expect-error Navigation.navigate does take a paraml + // @ts-expect-error Navigation.navigate does take a param Navigation.navigate(appendParam(backTo, 'state', option.value)); } }, From 07f6f2160d2670a8155708a88e38a291f6cb6ca7 Mon Sep 17 00:00:00 2001 From: Yogesh Bhatt <65986357+ygshbht@users.noreply.github.com> Date: Tue, 20 Feb 2024 03:21:40 +0530 Subject: [PATCH 096/500] Comment formatting src/pages/settings/Profile/PersonalDetails/AddressPage.js Co-authored-by: Vit Horacek <36083550+mountiny@users.noreply.github.com> --- src/pages/settings/Profile/PersonalDetails/AddressPage.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/pages/settings/Profile/PersonalDetails/AddressPage.js b/src/pages/settings/Profile/PersonalDetails/AddressPage.js index 95b006828c39..87960d1f6687 100644 --- a/src/pages/settings/Profile/PersonalDetails/AddressPage.js +++ b/src/pages/settings/Profile/PersonalDetails/AddressPage.js @@ -65,9 +65,9 @@ function AddressPage({privatePersonalDetails, route}) { usePrivatePersonalDetails(); const {translate} = useLocalize(); const address = useMemo(() => lodashGet(privatePersonalDetails, 'address') || {}, [privatePersonalDetails]); - const countryFromUrlTemp = lodashGet(route, 'params.country'); - // check if country is valid + + // Check if country is valid const countryFromUrl = lodashGet(CONST.ALL_COUNTRIES, countryFromUrlTemp) ? countryFromUrlTemp : ''; const stateFromUrl = useGeographicalStateFromRoute(); From 877740b9160d380890798ac2a6e42e52f2bc7bea Mon Sep 17 00:00:00 2001 From: Yogesh Bhatt Date: Tue, 20 Feb 2024 17:01:43 +0530 Subject: [PATCH 097/500] Fix TS errors --- src/ROUTES.ts | 17 +++++++---------- src/components/StateSelector.tsx | 1 - .../PersonalDetails/StateSelectionPage.tsx | 11 ++++++----- 3 files changed, 13 insertions(+), 16 deletions(-) diff --git a/src/ROUTES.ts b/src/ROUTES.ts index 3a4b92e9e529..1cf8eef0f4ab 100644 --- a/src/ROUTES.ts +++ b/src/ROUTES.ts @@ -133,16 +133,13 @@ const ROUTES = { }, SETTINGS_ADDRESS_STATE: { route: 'settings/profile/address/state', - getRoute: (state: string, backTo?: string, label?: string) => { - let route = `settings/profile/address/state?state=${state}`; - if (backTo) { - route += `&backTo=${encodeURIComponent(backTo)}`; - } - if (label) { - route += `&label=${encodeURIComponent(label)}`; - } - return route; - }, + + getRoute: (state?: string, backTo?: string, label?: string) => + `${getUrlWithBackToParam(`settings/profile/address/state${state ? `?state=${encodeURIComponent(state)}` : ''}`, backTo)}${ + // Nullish operator ?? doesnt seem to be a replacement for || here + // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing + label ? `${backTo || state ? '&' : '?'}label=${encodeURIComponent(label)}` : '' + }` as const, }, SETTINGS_CONTACT_METHODS: { route: 'settings/profile/contact-methods', diff --git a/src/components/StateSelector.tsx b/src/components/StateSelector.tsx index 79c086057f88..9ddf2573f857 100644 --- a/src/components/StateSelector.tsx +++ b/src/components/StateSelector.tsx @@ -83,7 +83,6 @@ function StateSelector({errorText, shouldUseStateFromUrl = true, value: stateCod description={label || translate('common.state')} onPress={() => { const activeRoute = Navigation.getActiveRoute(); - // @ts-expect-error Navigation.navigate does take a param Navigation.navigate(ROUTES.SETTINGS_ADDRESS_STATE.getRoute(stateCode, activeRoute, label)); }} wrapperStyle={wrapperStyle} diff --git a/src/pages/settings/Profile/PersonalDetails/StateSelectionPage.tsx b/src/pages/settings/Profile/PersonalDetails/StateSelectionPage.tsx index d5221c98b046..532f4d3f97e4 100644 --- a/src/pages/settings/Profile/PersonalDetails/StateSelectionPage.tsx +++ b/src/pages/settings/Profile/PersonalDetails/StateSelectionPage.tsx @@ -12,6 +12,7 @@ import searchCountryOptions from '@libs/searchCountryOptions'; import type {CountryData} from '@libs/searchCountryOptions'; import StringUtils from '@libs/StringUtils'; import {appendParam} from '@libs/Url'; +import type {Route} from '@src/ROUTES'; type State = keyof typeof COMMON_CONST.STATES; @@ -64,12 +65,12 @@ function StateSelectionPage() { Navigation.goBack(); } else if (!_.isEmpty(backTo) && navigation.getState().routes.length === 1) { // If "backTo" is not empty and there is only one route, go back to the specific route defined in "backTo" with a country parameter - // @ts-expect-error Navigation.goBack does take a param - Navigation.goBack(appendParam(backTo, 'state', option.value)); - } else { + Navigation.goBack(appendParam(backTo, 'state', option.value) as Route); + } else if (!_.isEmpty(backTo)) { // Otherwise, navigate to the specific route defined in "backTo" with a country parameter - // @ts-expect-error Navigation.navigate does take a param - Navigation.navigate(appendParam(backTo, 'state', option.value)); + Navigation.navigate(appendParam(backTo, 'state', option.value) as Route); + } else { + Navigation.goBack(); } }, [navigation, params?.backTo], From b479e69163b26ba6cb8b5f9e339e49d45f51352a Mon Sep 17 00:00:00 2001 From: Etotaziba Olei Date: Tue, 20 Feb 2024 14:41:36 +0100 Subject: [PATCH 098/500] use taxRates from policy key for MoneyTemporaryForRefactorRequestConfirmationList --- ...TemporaryForRefactorRequestConfirmationList.js | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/src/components/MoneyTemporaryForRefactorRequestConfirmationList.js b/src/components/MoneyTemporaryForRefactorRequestConfirmationList.js index 0be77a201f1f..5f4abfec7794 100755 --- a/src/components/MoneyTemporaryForRefactorRequestConfirmationList.js +++ b/src/components/MoneyTemporaryForRefactorRequestConfirmationList.js @@ -259,6 +259,7 @@ function MoneyTemporaryForRefactorRequestConfirmationList({ const {unit, rate, currency} = mileageRate; const distance = lodashGet(transaction, 'routes.route0.distance', 0); const shouldCalculateDistanceAmount = isDistanceRequest && iouAmount === 0; + const taxRates = lodashGet(policy, 'taxRates', {}); // A flag for showing the categories field const shouldShowCategories = isPolicyExpenseChat && (iouCategory || OptionsListUtils.hasEnabledOptions(_.values(policyCategories))); @@ -293,7 +294,7 @@ function MoneyTemporaryForRefactorRequestConfirmationList({ ); const formattedTaxAmount = CurrencyUtils.convertToDisplayString(transaction.taxAmount, iouCurrencyCode); - const taxRateTitle = TransactionUtils.getDefaultTaxName(policyTaxRates, transaction); + const taxRateTitle = TransactionUtils.getDefaultTaxName(taxRates, transaction); const previousTransactionTaxAmount = usePrevious(transaction.taxAmount); @@ -359,13 +360,13 @@ function MoneyTemporaryForRefactorRequestConfirmationList({ // Calculate and set tax amount in transaction draft useEffect(() => { - const taxAmount = getTaxAmount(transaction, policyTaxRates.defaultValue); + const taxAmount = getTaxAmount(transaction, taxRates.defaultValue); const amountInSmallestCurrencyUnits = CurrencyUtils.convertToBackendAmount(Number.parseFloat(taxAmount)); if (previousTransactionTaxAmount !== transaction.taxAmount && amountInSmallestCurrencyUnits !== transaction.taxAmount) { return; } IOU.setMoneyRequestTaxAmount(transaction.transactionID, amountInSmallestCurrencyUnits); - }, [policyTaxRates.defaultValue, transaction, previousTransactionTaxAmount]); + }, [taxRates.defaultValue, transaction, previousTransactionTaxAmount]); /** * Returns the participants with amount @@ -800,10 +801,10 @@ function MoneyTemporaryForRefactorRequestConfirmationList({ { item: ( Navigation.navigate(ROUTES.MONEY_REQUEST_STEP_TAX_RATE.getRoute(iouType, transaction.transactionID, reportID, Navigation.getActiveRouteWithoutParams()))} @@ -817,10 +818,10 @@ function MoneyTemporaryForRefactorRequestConfirmationList({ { item: ( Navigation.navigate(ROUTES.MONEY_REQUEST_STEP_TAX_AMOUNT.getRoute(iouType, transaction.transactionID, reportID, Navigation.getActiveRouteWithoutParams()))} From c9150473c7c9e77e0a3ec936d652eadbebf2257c Mon Sep 17 00:00:00 2001 From: Etotaziba Olei Date: Tue, 20 Feb 2024 14:45:13 +0100 Subject: [PATCH 099/500] use taxRates from policy key for MoneyTemporaryForRefactorRequestConfirmationList --- .../MoneyTemporaryForRefactorRequestConfirmationList.js | 9 --------- 1 file changed, 9 deletions(-) diff --git a/src/components/MoneyTemporaryForRefactorRequestConfirmationList.js b/src/components/MoneyTemporaryForRefactorRequestConfirmationList.js index 5f4abfec7794..ee1975220a24 100755 --- a/src/components/MoneyTemporaryForRefactorRequestConfirmationList.js +++ b/src/components/MoneyTemporaryForRefactorRequestConfirmationList.js @@ -162,10 +162,6 @@ const propTypes = { /** Collection of tags attached to a policy */ policyTags: tagPropTypes, - /* Onyx Props */ - /** Collection of tax rates attached to a policy */ - policyTaxRates: taxPropTypes, - /** Transaction that represents the money request */ transaction: transactionPropTypes, }; @@ -199,7 +195,6 @@ const defaultProps = { isDistanceRequest: false, shouldShowSmartScanFields: true, isPolicyExpenseChat: false, - policyTaxRates: {}, }; const getTaxAmount = (transaction, defaultTaxValue) => { @@ -245,7 +240,6 @@ function MoneyTemporaryForRefactorRequestConfirmationList({ session: {accountID}, shouldShowSmartScanFields, transaction, - policyTaxRates, }) { const theme = useTheme(); const styles = useThemeStyles(); @@ -950,8 +944,5 @@ export default compose( policy: { key: ({policyID}) => `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, }, - policyTaxRates: { - key: ({policyID}) => `${ONYXKEYS.COLLECTION.POLICY_TAX_RATE}${policyID}`, - }, }), )(MoneyTemporaryForRefactorRequestConfirmationList); From 4e57d4ae08c18fb59b55fc4501b4c7ad6cfb57eb Mon Sep 17 00:00:00 2001 From: Etotaziba Olei Date: Tue, 20 Feb 2024 14:49:49 +0100 Subject: [PATCH 100/500] add taxRates propTypes to policy for MoneyTemporaryForRefactorRequestConfirmationList --- .../MoneyTemporaryForRefactorRequestConfirmationList.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/components/MoneyTemporaryForRefactorRequestConfirmationList.js b/src/components/MoneyTemporaryForRefactorRequestConfirmationList.js index ee1975220a24..c9afa52be850 100755 --- a/src/components/MoneyTemporaryForRefactorRequestConfirmationList.js +++ b/src/components/MoneyTemporaryForRefactorRequestConfirmationList.js @@ -162,6 +162,10 @@ const propTypes = { /** Collection of tags attached to a policy */ policyTags: tagPropTypes, + /* Onyx Props */ + /** Collection of tax rates attached to a policy */ + policyTaxRates: taxPropTypes, + /** Transaction that represents the money request */ transaction: transactionPropTypes, }; From 17281f6829da70f9dc828d2b0def2c5f3105868a Mon Sep 17 00:00:00 2001 From: Etotaziba Olei Date: Tue, 20 Feb 2024 14:50:44 +0100 Subject: [PATCH 101/500] remove unused Fragment import for MoneyTemporaryForRefactorRequestConfirmation --- .../MoneyTemporaryForRefactorRequestConfirmationList.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/MoneyTemporaryForRefactorRequestConfirmationList.js b/src/components/MoneyTemporaryForRefactorRequestConfirmationList.js index c9afa52be850..1224c12255c6 100755 --- a/src/components/MoneyTemporaryForRefactorRequestConfirmationList.js +++ b/src/components/MoneyTemporaryForRefactorRequestConfirmationList.js @@ -2,7 +2,7 @@ import {useIsFocused} from '@react-navigation/native'; import {format} from 'date-fns'; import lodashGet from 'lodash/get'; import PropTypes from 'prop-types'; -import React, {Fragment, useCallback, useEffect, useMemo, useReducer, useRef, useState} from 'react'; +import React, {useCallback, useEffect, useMemo, useReducer, useRef, useState} from 'react'; import {View} from 'react-native'; import {withOnyx} from 'react-native-onyx'; import _ from 'underscore'; From 9b3ac1296a0ce0c01eae5b9e6b2dc65546e8fd8b Mon Sep 17 00:00:00 2001 From: Etotaziba Olei Date: Tue, 20 Feb 2024 14:52:22 +0100 Subject: [PATCH 102/500] update taxAmount Description for MoneyTemporaryForRefactorRequestConfirmation --- .../MoneyTemporaryForRefactorRequestConfirmationList.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/MoneyTemporaryForRefactorRequestConfirmationList.js b/src/components/MoneyTemporaryForRefactorRequestConfirmationList.js index 1224c12255c6..094e2d58066e 100755 --- a/src/components/MoneyTemporaryForRefactorRequestConfirmationList.js +++ b/src/components/MoneyTemporaryForRefactorRequestConfirmationList.js @@ -819,7 +819,7 @@ function MoneyTemporaryForRefactorRequestConfirmationList({ key={`${taxRates.name}${formattedTaxAmount}`} shouldShowRightIcon={!isReadOnly} title={formattedTaxAmount} - description={taxRates.name} + description={translate('iou.taxAmount')} style={[styles.moneyRequestMenuItem]} titleStyle={styles.flex1} onPress={() => Navigation.navigate(ROUTES.MONEY_REQUEST_STEP_TAX_AMOUNT.getRoute(iouType, transaction.transactionID, reportID, Navigation.getActiveRouteWithoutParams()))} From af4783aa91236784e84eb133c691f40b6bfef9e8 Mon Sep 17 00:00:00 2001 From: Etotaziba Olei Date: Tue, 20 Feb 2024 15:01:55 +0100 Subject: [PATCH 103/500] use taxRates from policy key for MoneyRequestRequestConfirmationList --- src/components/MoneyRequestConfirmationList.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/components/MoneyRequestConfirmationList.js b/src/components/MoneyRequestConfirmationList.js index c5fea084674d..13a118d416a4 100755 --- a/src/components/MoneyRequestConfirmationList.js +++ b/src/components/MoneyRequestConfirmationList.js @@ -229,6 +229,7 @@ function MoneyRequestConfirmationList(props) { const {unit, rate, currency} = props.mileageRate; const distance = lodashGet(transaction, 'routes.route0.distance', 0); const shouldCalculateDistanceAmount = props.isDistanceRequest && props.iouAmount === 0; + const taxRates = lodashGet(props.policy, 'taxRates', {}); // A flag for showing the categories field const shouldShowCategories = props.isPolicyExpenseChat && (props.iouCategory || OptionsListUtils.hasEnabledOptions(_.values(props.policyCategories))); @@ -262,7 +263,7 @@ function MoneyRequestConfirmationList(props) { props.isDistanceRequest ? currency : props.iouCurrencyCode, ); const formattedTaxAmount = CurrencyUtils.convertToDisplayString(props.transaction.taxAmount, props.iouCurrencyCode); - const taxRateTitle = TransactionUtils.getDefaultTaxName(props.policyTaxRates, transaction); + const taxRateTitle = TransactionUtils.getDefaultTaxName(taxRates, transaction); const isFocused = useIsFocused(); const [formError, setFormError] = useState(''); @@ -812,7 +813,7 @@ function MoneyRequestConfirmationList(props) { From dbd68de9d793cd328281efa0a357d9f6535ca315 Mon Sep 17 00:00:00 2001 From: Etotaziba Olei Date: Tue, 20 Feb 2024 15:03:46 +0100 Subject: [PATCH 104/500] remove policyTaxRates props and ONYX key for MoneyRequestConfirmationList --- src/components/MoneyRequestConfirmationList.js | 8 -------- 1 file changed, 8 deletions(-) diff --git a/src/components/MoneyRequestConfirmationList.js b/src/components/MoneyRequestConfirmationList.js index 13a118d416a4..a71c8bc96b2c 100755 --- a/src/components/MoneyRequestConfirmationList.js +++ b/src/components/MoneyRequestConfirmationList.js @@ -167,10 +167,6 @@ const propTypes = { /** Collection of tags attached to a policy */ policyTags: tagPropTypes, - /* Onyx Props */ - /** Collection of tax rates attached to a policy */ - policyTaxRates: taxPropTypes, - /** Holds data related to Money Request view state, rather than the underlying Money Request data. */ iou: iouPropTypes, }; @@ -207,7 +203,6 @@ const defaultProps = { shouldShowSmartScanFields: true, isPolicyExpenseChat: false, iou: iouDefaultProps, - policyTaxRates: {}, }; function MoneyRequestConfirmationList(props) { @@ -882,9 +877,6 @@ export default compose( policy: { key: ({policyID}) => `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, }, - policyTaxRates: { - key: ({policyID}) => `${ONYXKEYS.COLLECTION.POLICY_TAX_RATE}${policyID}`, - }, iou: { key: ONYXKEYS.IOU, }, From dfb486a9685f3d9ef279df6a51799062ef4204b4 Mon Sep 17 00:00:00 2001 From: Etotaziba Olei Date: Tue, 20 Feb 2024 15:24:43 +0100 Subject: [PATCH 105/500] update taxRates propTypes to policy for MoneyTemporaryForRefactorRequestConfirmationList --- .../MoneyTemporaryForRefactorRequestConfirmationList.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/components/MoneyTemporaryForRefactorRequestConfirmationList.js b/src/components/MoneyTemporaryForRefactorRequestConfirmationList.js index 094e2d58066e..3e4435d6c4fc 100755 --- a/src/components/MoneyTemporaryForRefactorRequestConfirmationList.js +++ b/src/components/MoneyTemporaryForRefactorRequestConfirmationList.js @@ -25,6 +25,7 @@ import * as ReceiptUtils from '@libs/ReceiptUtils'; import * as ReportUtils from '@libs/ReportUtils'; import playSound, {SOUNDS} from '@libs/Sound'; import * as TransactionUtils from '@libs/TransactionUtils'; +import {policyPropTypes} from '@pages/workspace/withPolicy'; import * as IOU from '@userActions/IOU'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; @@ -43,7 +44,6 @@ import ReceiptEmptyState from './ReceiptEmptyState'; import SettlementButton from './SettlementButton'; import Switch from './Switch'; import tagPropTypes from './tagPropTypes'; -import taxPropTypes from './taxPropTypes'; import Text from './Text'; import transactionPropTypes from './transactionPropTypes'; import withCurrentUserPersonalDetails, {withCurrentUserPersonalDetailsDefaultProps, withCurrentUserPersonalDetailsPropTypes} from './withCurrentUserPersonalDetails'; @@ -163,8 +163,8 @@ const propTypes = { policyTags: tagPropTypes, /* Onyx Props */ - /** Collection of tax rates attached to a policy */ - policyTaxRates: taxPropTypes, + /** The policy of the report */ + policy: policyPropTypes.policy, /** Transaction that represents the money request */ transaction: transactionPropTypes, @@ -193,6 +193,7 @@ const defaultProps = { listStyles: [], policyCategories: {}, policyTags: {}, + policy: {}, transactionID: '', transaction: {}, mileageRate: {unit: CONST.CUSTOM_UNITS.DISTANCE_UNIT_MILES, rate: 0, currency: 'USD'}, From e252453fad3d53e756cf3afba491dc57747b72ba Mon Sep 17 00:00:00 2001 From: Etotaziba Olei Date: Tue, 20 Feb 2024 15:26:37 +0100 Subject: [PATCH 106/500] update taxRates propTypes to policy for MoneyRequestConfirmationList --- src/components/MoneyRequestConfirmationList.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/components/MoneyRequestConfirmationList.js b/src/components/MoneyRequestConfirmationList.js index a71c8bc96b2c..1587a4ace6f0 100755 --- a/src/components/MoneyRequestConfirmationList.js +++ b/src/components/MoneyRequestConfirmationList.js @@ -25,6 +25,7 @@ import * as ReceiptUtils from '@libs/ReceiptUtils'; import * as ReportUtils from '@libs/ReportUtils'; import * as TransactionUtils from '@libs/TransactionUtils'; import {iouDefaultProps, iouPropTypes} from '@pages/iou/propTypes'; +import {policyPropTypes} from '@pages/workspace/withPolicy'; import * as IOU from '@userActions/IOU'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; @@ -42,7 +43,6 @@ import SettlementButton from './SettlementButton'; import ShowMoreButton from './ShowMoreButton'; import Switch from './Switch'; import tagPropTypes from './tagPropTypes'; -import taxPropTypes from './taxPropTypes'; import Text from './Text'; import transactionPropTypes from './transactionPropTypes'; import withCurrentUserPersonalDetails, {withCurrentUserPersonalDetailsDefaultProps, withCurrentUserPersonalDetailsPropTypes} from './withCurrentUserPersonalDetails'; @@ -167,6 +167,10 @@ const propTypes = { /** Collection of tags attached to a policy */ policyTags: tagPropTypes, + /* Onyx Props */ + /** The policy of the report */ + policy: policyPropTypes.policy, + /** Holds data related to Money Request view state, rather than the underlying Money Request data. */ iou: iouPropTypes, }; From a811a81bf818fcee01d3ba5255cddfd225597e88 Mon Sep 17 00:00:00 2001 From: Etotaziba Olei Date: Tue, 20 Feb 2024 15:46:36 +0100 Subject: [PATCH 107/500] add taxRates types to policy --- src/types/onyx/Policy.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/types/onyx/Policy.ts b/src/types/onyx/Policy.ts index 7d4c08374b81..37ce28f93abc 100644 --- a/src/types/onyx/Policy.ts +++ b/src/types/onyx/Policy.ts @@ -1,5 +1,6 @@ import type {ValueOf} from 'type-fest'; import type CONST from '@src/CONST'; +import type {PolicyTaxRates} from '@src/types/onyx/PolicyTaxRates'; import type * as OnyxCommon from './OnyxCommon'; type Unit = 'mi' | 'km'; @@ -171,6 +172,9 @@ type Policy = { trackingEnabled: boolean; }; + /** Collection of tax rates attached to a policy */ + taxRates?: PolicyTaxRates; + /** ReportID of the admins room for this workspace */ chatReportIDAdmins?: number; From b4e61f4cfc3bb004cd8a48080d50bc13bcabf70a Mon Sep 17 00:00:00 2001 From: Etotaziba Olei Date: Tue, 20 Feb 2024 15:46:48 +0100 Subject: [PATCH 108/500] use taxRates from policy key for MoneyRequestView --- src/components/ReportActionItem/MoneyRequestView.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/components/ReportActionItem/MoneyRequestView.tsx b/src/components/ReportActionItem/MoneyRequestView.tsx index 2125610a3ea8..147d8e6ea31a 100644 --- a/src/components/ReportActionItem/MoneyRequestView.tsx +++ b/src/components/ReportActionItem/MoneyRequestView.tsx @@ -120,11 +120,11 @@ function MoneyRequestView({ const isCardTransaction = TransactionUtils.isCardTransaction(transaction); const cardProgramName = isCardTransaction && transactionCardID !== undefined ? CardUtils.getCardDescription(transactionCardID) : ''; const isApproved = ReportUtils.isReportApproved(moneyRequestReport); - + const taxRates = policy?.taxRates; const formattedTaxAmount = transactionTaxAmount ? CurrencyUtils.convertToDisplayString(transactionTaxAmount, transactionCurrency) : ''; - const policyTaxRatesDescription = policyTaxRates?.name; - const taxRateTitle = (transactionTaxCode && policyTaxRates && TransactionUtils.getTaxName(policyTaxRates.taxes, transactionTaxCode)) ?? ''; + const policyTaxRatesDescription = taxRates?.name; + const taxRateTitle = (transactionTaxCode && taxRates && TransactionUtils.getTaxName(taxRates?.taxes, transactionTaxCode)) ?? ''; // Flags for allowing or disallowing editing a money request const isSettled = ReportUtils.isSettled(moneyRequestReport?.reportID); From 0cd0f457bc6cdaa641dd155b39a869ea5d79cf44 Mon Sep 17 00:00:00 2001 From: Etotaziba Olei Date: Tue, 20 Feb 2024 15:50:14 +0100 Subject: [PATCH 109/500] remove policyTaxRates props and ONYX key for MoneyRequestView --- src/components/ReportActionItem/MoneyRequestView.tsx | 7 ------- 1 file changed, 7 deletions(-) diff --git a/src/components/ReportActionItem/MoneyRequestView.tsx b/src/components/ReportActionItem/MoneyRequestView.tsx index 147d8e6ea31a..0242c83069fd 100644 --- a/src/components/ReportActionItem/MoneyRequestView.tsx +++ b/src/components/ReportActionItem/MoneyRequestView.tsx @@ -58,9 +58,6 @@ type MoneyRequestViewOnyxPropsWithoutTransaction = { /** Collection of tags attached to a policy */ policyTagList: OnyxEntry; - /** Collection of tax rates attached to a policy */ - policyTaxRates: OnyxEntry; - /** The expense report or iou report (only will have a value if this is a transaction thread) */ parentReport: OnyxEntry; @@ -87,7 +84,6 @@ function MoneyRequestView({ transaction, policyTagList, policy, - policyTaxRates, transactionViolations, }: MoneyRequestViewProps) { const theme = useTheme(); @@ -491,9 +487,6 @@ export default withOnyx `${ONYXKEYS.COLLECTION.POLICY_TAGS}${report.policyID}`, }, - policyTaxRates: { - key: ({report}) => `${ONYXKEYS.COLLECTION.POLICY_TAX_RATE}${report.policyID}`, - }, parentReport: { key: ({report}) => `${ONYXKEYS.COLLECTION.REPORT}${report.parentReportID}`, }, From a175dac5612296883cd32c49eae9ceca45a4ef25 Mon Sep 17 00:00:00 2001 From: Etotaziba Olei Date: Tue, 20 Feb 2024 16:04:40 +0100 Subject: [PATCH 110/500] fix lint warning for MoneyRequestView --- src/types/onyx/Policy.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/types/onyx/Policy.ts b/src/types/onyx/Policy.ts index 37ce28f93abc..1ba392c6ad3c 100644 --- a/src/types/onyx/Policy.ts +++ b/src/types/onyx/Policy.ts @@ -1,6 +1,6 @@ import type {ValueOf} from 'type-fest'; import type CONST from '@src/CONST'; -import type {PolicyTaxRates} from '@src/types/onyx/PolicyTaxRates'; +import type {PolicyTaxRates} from './PolicyTaxRates'; import type * as OnyxCommon from './OnyxCommon'; type Unit = 'mi' | 'km'; From 8480869271cb762b2f0d0a8bd1b613d2728df13e Mon Sep 17 00:00:00 2001 From: Etotaziba Olei Date: Tue, 20 Feb 2024 16:21:19 +0100 Subject: [PATCH 111/500] use taxRates from policy key for taxPicker --- src/components/TaxPicker/index.js | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/src/components/TaxPicker/index.js b/src/components/TaxPicker/index.js index 287805692bcf..b83d75f314aa 100644 --- a/src/components/TaxPicker/index.js +++ b/src/components/TaxPicker/index.js @@ -12,14 +12,15 @@ import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import {defaultProps, propTypes} from './taxPickerPropTypes'; -function TaxPicker({selectedTaxRate, policyTaxRates, insets, onSubmit}) { +function TaxPicker({selectedTaxRate, policy, insets, onSubmit}) { const styles = useThemeStyles(); const StyleUtils = useStyleUtils(); const {translate} = useLocalize(); const [searchValue, setSearchValue] = useState(''); - const policyTaxRatesCount = TransactionUtils.getEnabledTaxRateCount(policyTaxRates.taxes); - const isTaxRatesCountBelowThreshold = policyTaxRatesCount < CONST.TAX_RATES_LIST_THRESHOLD; + const taxRates = policy.taxRates; + const taxRatesCount = TransactionUtils.getEnabledTaxRateCount(taxRates.taxes); + const isTaxRatesCountBelowThreshold = taxRatesCount < CONST.TAX_RATES_LIST_THRESHOLD; const shouldShowTextInput = !isTaxRatesCountBelowThreshold; @@ -56,10 +57,10 @@ function TaxPicker({selectedTaxRate, policyTaxRates, insets, onSubmit}) { false, false, true, - policyTaxRates, + taxRates, ); return policyTaxRatesOptions; - }, [policyTaxRates, searchValue, selectedOptions]); + }, [taxRates, searchValue, selectedOptions]); const selectedOptionKey = lodashGet(_.filter(lodashGet(sections, '[0].data', []), (taxRate) => taxRate.searchText === selectedTaxRate)[0], 'keyForList'); @@ -90,7 +91,7 @@ TaxPicker.propTypes = propTypes; TaxPicker.defaultProps = defaultProps; export default withOnyx({ - policyTaxRates: { - key: ({policyID}) => `${ONYXKEYS.COLLECTION.POLICY_TAX_RATE}${policyID}`, + policy: { + key: ({policyID}) => `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, }, })(TaxPicker); From dc0ec64db55aa607208df89f1f71a13e01a9f4f1 Mon Sep 17 00:00:00 2001 From: Etotaziba Olei Date: Tue, 20 Feb 2024 16:45:47 +0100 Subject: [PATCH 112/500] update TaxPicker PropTypes --- src/components/TaxPicker/taxPickerPropTypes.js | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/src/components/TaxPicker/taxPickerPropTypes.js b/src/components/TaxPicker/taxPickerPropTypes.js index 289b4e19aaa4..35d1866ff5d1 100644 --- a/src/components/TaxPicker/taxPickerPropTypes.js +++ b/src/components/TaxPicker/taxPickerPropTypes.js @@ -1,5 +1,6 @@ import PropTypes from 'prop-types'; import taxPropTypes from '@components/taxPropTypes'; +import safeAreaInsetPropTypes from '@pages/safeAreaInsetPropTypes'; const propTypes = { /** The selected tax rate of an expense */ @@ -8,9 +9,16 @@ const propTypes = { /** Callback to fire when a tax is pressed */ onSubmit: PropTypes.func.isRequired, - /* Onyx Props */ - /** Collection of tax rates attached to a policy */ - policyTaxRates: taxPropTypes, + policy: PropTypes.shape({ + /** Collection of tax rates attached to a policy */ + taxRates: taxPropTypes, + }), + + /** + * Safe area insets required for reflecting the portion of the view, + * that is not covered by navigation bars, tab bars, toolbars, and other ancestor views. + */ + insets: safeAreaInsetPropTypes.isRequired, }; const defaultProps = { From 94a2e3fb6e0927a49a98bad626cacb3dfc83746e Mon Sep 17 00:00:00 2001 From: Etotaziba Olei Date: Tue, 20 Feb 2024 16:47:03 +0100 Subject: [PATCH 113/500] fix prettier --- src/types/onyx/Policy.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/types/onyx/Policy.ts b/src/types/onyx/Policy.ts index 1ba392c6ad3c..90b6c0da7e8d 100644 --- a/src/types/onyx/Policy.ts +++ b/src/types/onyx/Policy.ts @@ -1,7 +1,7 @@ import type {ValueOf} from 'type-fest'; import type CONST from '@src/CONST'; -import type {PolicyTaxRates} from './PolicyTaxRates'; import type * as OnyxCommon from './OnyxCommon'; +import type {PolicyTaxRates} from './PolicyTaxRates'; type Unit = 'mi' | 'km'; From 3d5af3b73c518c4fc92db662a5187a51366cfbcc Mon Sep 17 00:00:00 2001 From: Etotaziba Olei Date: Wed, 21 Feb 2024 11:05:46 +0100 Subject: [PATCH 114/500] use taxRates from policy key for EditRequestPage --- src/pages/EditRequestPage.js | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/src/pages/EditRequestPage.js b/src/pages/EditRequestPage.js index c6e239d8c766..b69dfcb32577 100644 --- a/src/pages/EditRequestPage.js +++ b/src/pages/EditRequestPage.js @@ -7,7 +7,6 @@ import FullPageNotFoundView from '@components/BlockingViews/FullPageNotFoundView import categoryPropTypes from '@components/categoryPropTypes'; import ScreenWrapper from '@components/ScreenWrapper'; import tagPropTypes from '@components/tagPropTypes'; -import taxPropTypes from '@components/taxPropTypes'; import transactionPropTypes from '@components/transactionPropTypes'; import compose from '@libs/compose'; import * as CurrencyUtils from '@libs/CurrencyUtils'; @@ -67,9 +66,6 @@ const propTypes = { /** Transaction that stores the request data */ transaction: transactionPropTypes, - - /** Collection of tax rates attached to a policy */ - policyTaxRates: taxPropTypes, }; const defaultProps = { @@ -79,7 +75,6 @@ const defaultProps = { policyTags: {}, parentReportActions: {}, transaction: {}, - policyTaxRates: {}, }; const getTaxAmount = (transactionAmount, transactionTaxCode, policyTaxRates) => { @@ -87,7 +82,7 @@ const getTaxAmount = (transactionAmount, transactionTaxCode, policyTaxRates) => return CurrencyUtils.convertToBackendAmount(Number.parseFloat(TransactionUtils.calculateTaxAmount(percentage, transactionAmount))); }; -function EditRequestPage({report, route, policy, policyTaxRates, policyCategories, policyTags, parentReportActions, transaction}) { +function EditRequestPage({report, route, policy, policyCategories, policyTags, parentReportActions, transaction}) { const parentReportActionID = lodashGet(report, 'parentReportActionID', '0'); const parentReportAction = lodashGet(parentReportActions, parentReportActionID, {}); const { @@ -107,6 +102,8 @@ function EditRequestPage({report, route, policy, policyTaxRates, policyCategorie const policyTagListName = PolicyUtils.getTagListName(policyTags, tagIndex); const policyTagLists = useMemo(() => PolicyUtils.getTagLists(policyTags), [policyTags]); + const policyTaxRates = lodashGet(props.policy, 'taxRates', {}); + const taxRateTitle = TransactionUtils.getTaxName(policyTaxRates.taxes, transactionTaxCode); // A flag for verifying that the current report is a sub-report of a workspace chat @@ -304,9 +301,6 @@ export default compose( policyTags: { key: ({report}) => `${ONYXKEYS.COLLECTION.POLICY_TAGS}${report ? report.policyID : '0'}`, }, - policyTaxRates: { - key: ({report}) => `${ONYXKEYS.COLLECTION.POLICY_TAX_RATE}${report.policyID}`, - }, parentReportActions: { key: ({report}) => `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${report ? report.parentReportID : '0'}`, canEvict: false, From 446c7c8427ad90306460ce799186386cd9118725 Mon Sep 17 00:00:00 2001 From: Etotaziba Olei Date: Wed, 21 Feb 2024 11:08:04 +0100 Subject: [PATCH 115/500] remove undefined props usage for EditRequestPage --- src/pages/EditRequestPage.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/EditRequestPage.js b/src/pages/EditRequestPage.js index b69dfcb32577..ce52e4db3052 100644 --- a/src/pages/EditRequestPage.js +++ b/src/pages/EditRequestPage.js @@ -102,7 +102,7 @@ function EditRequestPage({report, route, policy, policyCategories, policyTags, p const policyTagListName = PolicyUtils.getTagListName(policyTags, tagIndex); const policyTagLists = useMemo(() => PolicyUtils.getTagLists(policyTags), [policyTags]); - const policyTaxRates = lodashGet(props.policy, 'taxRates', {}); + const policyTaxRates = lodashGet(policy, 'taxRates', {}); const taxRateTitle = TransactionUtils.getTaxName(policyTaxRates.taxes, transactionTaxCode); From 3e4c6c61ed756484379442ca9eae3398a9a03f9e Mon Sep 17 00:00:00 2001 From: Etotaziba Olei Date: Wed, 21 Feb 2024 12:45:38 +0100 Subject: [PATCH 116/500] use taxRates from policy key for IOURequestStepTaxAmountPage --- .../iou/request/step/IOURequestStepTaxAmountPage.js | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/src/pages/iou/request/step/IOURequestStepTaxAmountPage.js b/src/pages/iou/request/step/IOURequestStepTaxAmountPage.js index 491d9801c2dc..6a8fff080de7 100644 --- a/src/pages/iou/request/step/IOURequestStepTaxAmountPage.js +++ b/src/pages/iou/request/step/IOURequestStepTaxAmountPage.js @@ -29,16 +29,12 @@ const propTypes = { /** The transaction object being modified in Onyx */ transaction: transactionPropTypes, - - /* Onyx Props */ - /** Collection of tax rates attached to a policy */ - policyTaxRates: taxPropTypes, }; const defaultProps = { report: {}, + policy: {}, transaction: {}, - policyTaxRates: {}, }; const getTaxAmount = (transaction, defaultTaxValue) => { @@ -53,7 +49,7 @@ function IOURequestStepTaxAmountPage({ transaction, transaction: {currency}, report, - policyTaxRates, + policy, }) { const {translate} = useLocalize(); const textInput = useRef(null); @@ -63,6 +59,7 @@ function IOURequestStepTaxAmountPage({ const isSaveButtonPressed = useRef(false); const originalCurrency = useRef(null); + const policyTaxRates = lodashGet(policy, 'taxRates', {}); useEffect(() => { if (transaction.originalCurrency) { @@ -158,8 +155,8 @@ export default compose( withWritableReportOrNotFound, withFullTransactionOrNotFound, withOnyx({ - policyTaxRates: { - key: ({report}) => `${ONYXKEYS.COLLECTION.POLICY_TAX_RATE}${report ? report.policyID : '0'}`, + policy: { + key: ({report}) => `${ONYXKEYS.COLLECTION.POLICY}${report ? report.policyID : '0'}`, }, }), )(IOURequestStepTaxAmountPage); From 7a1cb79c716dcd91a50576223f56780e31d35d59 Mon Sep 17 00:00:00 2001 From: Etotaziba Olei Date: Wed, 21 Feb 2024 12:47:31 +0100 Subject: [PATCH 117/500] add taxRates propTypes to policy for IOURequestStepTaxAmountPage --- src/pages/iou/request/step/IOURequestStepTaxAmountPage.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/pages/iou/request/step/IOURequestStepTaxAmountPage.js b/src/pages/iou/request/step/IOURequestStepTaxAmountPage.js index 6a8fff080de7..5135f5f319b4 100644 --- a/src/pages/iou/request/step/IOURequestStepTaxAmountPage.js +++ b/src/pages/iou/request/step/IOURequestStepTaxAmountPage.js @@ -29,6 +29,12 @@ const propTypes = { /** The transaction object being modified in Onyx */ transaction: transactionPropTypes, + + /** The policy of the report */ + policy: PropTypes.shape({ + /** Collection of tax rates attached to a policy */ + taxRates: taxPropTypes, + }), }; const defaultProps = { From 7376d2e006ecf260545c41a1f73ce0fefa4bcfee Mon Sep 17 00:00:00 2001 From: Etotaziba Olei Date: Wed, 21 Feb 2024 12:50:08 +0100 Subject: [PATCH 118/500] add missing imports for IOURequestStepTaxAmountPage --- src/pages/iou/request/step/IOURequestStepTaxAmountPage.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/pages/iou/request/step/IOURequestStepTaxAmountPage.js b/src/pages/iou/request/step/IOURequestStepTaxAmountPage.js index 5135f5f319b4..1d5ea686e975 100644 --- a/src/pages/iou/request/step/IOURequestStepTaxAmountPage.js +++ b/src/pages/iou/request/step/IOURequestStepTaxAmountPage.js @@ -1,4 +1,6 @@ import {useFocusEffect} from '@react-navigation/native'; +import lodashGet from 'lodash/get'; +import PropTypes from 'prop-types'; import React, {useCallback, useEffect, useRef} from 'react'; import {withOnyx} from 'react-native-onyx'; import taxPropTypes from '@components/taxPropTypes'; From cd2883c47a696e5bdd3fbba36b343055c72adb58 Mon Sep 17 00:00:00 2001 From: Etotaziba Olei Date: Wed, 21 Feb 2024 12:54:36 +0100 Subject: [PATCH 119/500] add policy onyx props for IOURequestStepTaxRatePage --- src/pages/iou/request/step/IOURequestStepTaxRatePage.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/pages/iou/request/step/IOURequestStepTaxRatePage.js b/src/pages/iou/request/step/IOURequestStepTaxRatePage.js index f930e33f129f..79429d57cfe1 100644 --- a/src/pages/iou/request/step/IOURequestStepTaxRatePage.js +++ b/src/pages/iou/request/step/IOURequestStepTaxRatePage.js @@ -96,5 +96,8 @@ export default compose( policyTaxRates: { key: ({report}) => `${ONYXKEYS.COLLECTION.POLICY_TAX_RATE}${report ? report.policyID : '0'}`, }, + policy: { + key: ({report}) => `${ONYXKEYS.COLLECTION.POLICY}${report ? report.policyID : '0'}`, + }, }), )(IOURequestStepTaxRatePage); From 7c88b5d6a958325e4db2722cd7e664cbd22f23de Mon Sep 17 00:00:00 2001 From: Etotaziba Olei Date: Wed, 21 Feb 2024 12:56:19 +0100 Subject: [PATCH 120/500] use taxRates from policy key for IOURequestStepTaxRatePage --- src/pages/iou/request/step/IOURequestStepTaxRatePage.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/pages/iou/request/step/IOURequestStepTaxRatePage.js b/src/pages/iou/request/step/IOURequestStepTaxRatePage.js index 79429d57cfe1..c9f4b0cab940 100644 --- a/src/pages/iou/request/step/IOURequestStepTaxRatePage.js +++ b/src/pages/iou/request/step/IOURequestStepTaxRatePage.js @@ -35,7 +35,7 @@ const propTypes = { const defaultProps = { report: {}, - policyTaxRates: {}, + policy: {}, transaction: {}, }; @@ -48,12 +48,14 @@ function IOURequestStepTaxRatePage({ route: { params: {backTo}, }, - policyTaxRates, + policy, transaction, report, }) { const {translate} = useLocalize(); + const policyTaxRates = lodashGet(policy, 'taxRates', {}); + const navigateBack = () => { Navigation.goBack(backTo); }; From 3d18ec40de77f13cb49b88c437755c453b1cafb2 Mon Sep 17 00:00:00 2001 From: Etotaziba Olei Date: Wed, 21 Feb 2024 12:57:30 +0100 Subject: [PATCH 121/500] remove taxRates props for IOURequestStepTaxRatePage --- src/pages/iou/request/step/IOURequestStepTaxRatePage.js | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/pages/iou/request/step/IOURequestStepTaxRatePage.js b/src/pages/iou/request/step/IOURequestStepTaxRatePage.js index c9f4b0cab940..502b9e223059 100644 --- a/src/pages/iou/request/step/IOURequestStepTaxRatePage.js +++ b/src/pages/iou/request/step/IOURequestStepTaxRatePage.js @@ -2,7 +2,6 @@ import React from 'react'; import {withOnyx} from 'react-native-onyx'; import _ from 'underscore'; import TaxPicker from '@components/TaxPicker'; -import taxPropTypes from '@components/taxPropTypes'; import transactionPropTypes from '@components/transactionPropTypes'; import useLocalize from '@hooks/useLocalize'; import compose from '@libs/compose'; @@ -22,10 +21,6 @@ const propTypes = { /** Navigation route context info provided by react navigation */ route: IOURequestStepRoutePropTypes.isRequired, - /* Onyx Props */ - /** Collection of tax rates attached to a policy */ - policyTaxRates: taxPropTypes, - /** The transaction object being modified in Onyx */ transaction: transactionPropTypes, From 3c8c714bb470721a4688a383f56bcdfdf0c74dc2 Mon Sep 17 00:00:00 2001 From: Etotaziba Olei Date: Wed, 21 Feb 2024 12:58:53 +0100 Subject: [PATCH 122/500] add policy props types for IOURequestStepTaxRatePage --- src/pages/iou/request/step/IOURequestStepTaxRatePage.js | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/pages/iou/request/step/IOURequestStepTaxRatePage.js b/src/pages/iou/request/step/IOURequestStepTaxRatePage.js index 502b9e223059..c56d0e860f33 100644 --- a/src/pages/iou/request/step/IOURequestStepTaxRatePage.js +++ b/src/pages/iou/request/step/IOURequestStepTaxRatePage.js @@ -1,3 +1,4 @@ +import lodashGet from 'lodash/get'; import React from 'react'; import {withOnyx} from 'react-native-onyx'; import _ from 'underscore'; @@ -26,6 +27,12 @@ const propTypes = { /** The report attached to the transaction */ report: reportPropTypes, + + /** The policy of the report */ + policy: PropTypes.shape({ + /** Collection of tax rates attached to a policy */ + taxRates: taxPropTypes, + }), }; const defaultProps = { From 1df6ec5ccfdee83b0b98a7b95beb9304d6d27a2c Mon Sep 17 00:00:00 2001 From: Etotaziba Olei Date: Wed, 21 Feb 2024 13:00:17 +0100 Subject: [PATCH 123/500] remove policyTaxRates onyx keys for IOURequestStepTaxRatePage --- src/pages/iou/request/step/IOURequestStepTaxRatePage.js | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/pages/iou/request/step/IOURequestStepTaxRatePage.js b/src/pages/iou/request/step/IOURequestStepTaxRatePage.js index c56d0e860f33..e8bf5915e648 100644 --- a/src/pages/iou/request/step/IOURequestStepTaxRatePage.js +++ b/src/pages/iou/request/step/IOURequestStepTaxRatePage.js @@ -97,9 +97,6 @@ export default compose( withWritableReportOrNotFound, withFullTransactionOrNotFound, withOnyx({ - policyTaxRates: { - key: ({report}) => `${ONYXKEYS.COLLECTION.POLICY_TAX_RATE}${report ? report.policyID : '0'}`, - }, policy: { key: ({report}) => `${ONYXKEYS.COLLECTION.POLICY}${report ? report.policyID : '0'}`, }, From 30ddf52f4df7212723c8a4bea5e03f6496688331 Mon Sep 17 00:00:00 2001 From: Etotaziba Olei Date: Wed, 21 Feb 2024 13:21:42 +0100 Subject: [PATCH 124/500] add missing proptypes for IOURequestStepTaxRatePage --- src/pages/iou/request/step/IOURequestStepTaxRatePage.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/pages/iou/request/step/IOURequestStepTaxRatePage.js b/src/pages/iou/request/step/IOURequestStepTaxRatePage.js index e8bf5915e648..694c76e87d99 100644 --- a/src/pages/iou/request/step/IOURequestStepTaxRatePage.js +++ b/src/pages/iou/request/step/IOURequestStepTaxRatePage.js @@ -1,8 +1,10 @@ import lodashGet from 'lodash/get'; +import PropTypes from 'prop-types'; import React from 'react'; import {withOnyx} from 'react-native-onyx'; import _ from 'underscore'; import TaxPicker from '@components/TaxPicker'; +import taxPropTypes from '@components/taxPropTypes'; import transactionPropTypes from '@components/transactionPropTypes'; import useLocalize from '@hooks/useLocalize'; import compose from '@libs/compose'; From aa0832653f4355e113fe6b28df2f95f6f1e0a0a3 Mon Sep 17 00:00:00 2001 From: Etotaziba Olei Date: Wed, 21 Feb 2024 14:04:30 +0100 Subject: [PATCH 125/500] remove tax rates types and keys --- src/ONYXKEYS.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/ONYXKEYS.ts b/src/ONYXKEYS.ts index 2e0ea7a6c9f9..d39d9a6020be 100755 --- a/src/ONYXKEYS.ts +++ b/src/ONYXKEYS.ts @@ -279,7 +279,6 @@ const ONYXKEYS = { POLICY_CATEGORIES: 'policyCategories_', POLICY_RECENTLY_USED_CATEGORIES: 'policyRecentlyUsedCategories_', POLICY_TAGS: 'policyTags_', - POLICY_TAX_RATE: 'policyTaxRates_', POLICY_RECENTLY_USED_TAGS: 'policyRecentlyUsedTags_', POLICY_REPORT_FIELDS: 'policyReportFields_', WORKSPACE_INVITE_MEMBERS_DRAFT: 'workspaceInviteMembersDraft_', @@ -469,7 +468,6 @@ type OnyxCollectionValuesMapping = { [ONYXKEYS.COLLECTION.SELECTED_TAB]: string; [ONYXKEYS.COLLECTION.PRIVATE_NOTES_DRAFT]: string; [ONYXKEYS.COLLECTION.NEXT_STEP]: OnyxTypes.ReportNextStep; - [ONYXKEYS.COLLECTION.POLICY_TAX_RATE]: OnyxTypes.PolicyTaxRates; }; type OnyxValuesMapping = { From be1c293edaac9be71e5066c6bf193971ee11f234 Mon Sep 17 00:00:00 2001 From: Etotaziba Olei Date: Wed, 21 Feb 2024 14:14:07 +0100 Subject: [PATCH 126/500] use BaseTextInputRef for text input ref --- src/pages/EditRequestTaxAmountPage.tsx | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/pages/EditRequestTaxAmountPage.tsx b/src/pages/EditRequestTaxAmountPage.tsx index 437e84df652c..22ce8a4de2df 100644 --- a/src/pages/EditRequestTaxAmountPage.tsx +++ b/src/pages/EditRequestTaxAmountPage.tsx @@ -1,10 +1,10 @@ import {useFocusEffect} from '@react-navigation/native'; import React, {useCallback, useRef} from 'react'; -import type {TextInput} from 'react-native'; import HeaderWithBackButton from '@components/HeaderWithBackButton'; import ScreenWrapper from '@components/ScreenWrapper'; import useLocalize from '@hooks/useLocalize'; import * as DeviceCapabilities from '@libs/DeviceCapabilities'; +import type {BaseTextInputRef} from '@src/components/TextInput/BaseTextInput/types'; import CONST from '@src/CONST'; import MoneyRequestAmountForm from './iou/steps/MoneyRequestAmountForm'; @@ -27,7 +27,7 @@ type EditRequestTaxAmountPageProps = { function EditRequestTaxAmountPage({defaultAmount, defaultTaxAmount, defaultCurrency, onNavigateToCurrency, onSubmit}: EditRequestTaxAmountPageProps) { const {translate} = useLocalize(); - const textInput = useRef(null); + const textInput = useRef(null); const focusTimeoutRef = useRef(null); @@ -52,7 +52,6 @@ function EditRequestTaxAmountPage({defaultAmount, defaultTaxAmount, defaultCurre > Date: Thu, 22 Feb 2024 12:43:38 +0100 Subject: [PATCH 127/500] update Tax picker prop Types --- src/components/TaxPicker/taxPickerPropTypes.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/TaxPicker/taxPickerPropTypes.js b/src/components/TaxPicker/taxPickerPropTypes.js index 35d1866ff5d1..476bd9f8eaf4 100644 --- a/src/components/TaxPicker/taxPickerPropTypes.js +++ b/src/components/TaxPicker/taxPickerPropTypes.js @@ -18,12 +18,12 @@ const propTypes = { * Safe area insets required for reflecting the portion of the view, * that is not covered by navigation bars, tab bars, toolbars, and other ancestor views. */ - insets: safeAreaInsetPropTypes.isRequired, + insets?: safeAreaInsetPropTypes.isRequired, }; const defaultProps = { selectedTaxRate: '', - policyTaxRates: {}, + policy: {}, }; export {propTypes, defaultProps}; From 7abbcb9eac173c1328a3dd36fc020baee38258f9 Mon Sep 17 00:00:00 2001 From: Etotaziba Olei Date: Thu, 22 Feb 2024 12:48:45 +0100 Subject: [PATCH 128/500] remove unexoected token --- src/components/TaxPicker/taxPickerPropTypes.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/TaxPicker/taxPickerPropTypes.js b/src/components/TaxPicker/taxPickerPropTypes.js index 476bd9f8eaf4..e0c15a058c0f 100644 --- a/src/components/TaxPicker/taxPickerPropTypes.js +++ b/src/components/TaxPicker/taxPickerPropTypes.js @@ -18,7 +18,7 @@ const propTypes = { * Safe area insets required for reflecting the portion of the view, * that is not covered by navigation bars, tab bars, toolbars, and other ancestor views. */ - insets?: safeAreaInsetPropTypes.isRequired, + insets: safeAreaInsetPropTypes, }; const defaultProps = { From 78f82327a02f35552706769b24761431ddd74fea Mon Sep 17 00:00:00 2001 From: Etotaziba Olei Date: Thu, 22 Feb 2024 12:54:29 +0100 Subject: [PATCH 129/500] update tax rates var --- src/pages/EditRequestPage.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/pages/EditRequestPage.js b/src/pages/EditRequestPage.js index ce52e4db3052..4ac9de3cb2f6 100644 --- a/src/pages/EditRequestPage.js +++ b/src/pages/EditRequestPage.js @@ -77,8 +77,8 @@ const defaultProps = { transaction: {}, }; -const getTaxAmount = (transactionAmount, transactionTaxCode, policyTaxRates) => { - const percentage = (transactionTaxCode ? policyTaxRates.taxes[transactionTaxCode].value : policyTaxRates.defaultValue) || ''; +const getTaxAmount = (transactionAmount, transactionTaxCode, taxRates) => { + const percentage = (transactionTaxCode ? taxRates.taxes[transactionTaxCode].value : taxRates.defaultValue) || ''; return CurrencyUtils.convertToBackendAmount(Number.parseFloat(TransactionUtils.calculateTaxAmount(percentage, transactionAmount))); }; @@ -102,9 +102,9 @@ function EditRequestPage({report, route, policy, policyCategories, policyTags, p const policyTagListName = PolicyUtils.getTagListName(policyTags, tagIndex); const policyTagLists = useMemo(() => PolicyUtils.getTagLists(policyTags), [policyTags]); - const policyTaxRates = lodashGet(policy, 'taxRates', {}); + const taxRates = lodashGet(policy, 'taxRates', {}); - const taxRateTitle = TransactionUtils.getTaxName(policyTaxRates.taxes, transactionTaxCode); + const taxRateTitle = TransactionUtils.getTaxName(taxRates.taxes, transactionTaxCode); // A flag for verifying that the current report is a sub-report of a workspace chat const isPolicyExpenseChat = ReportUtils.isGroupPolicy(report); @@ -230,7 +230,7 @@ function EditRequestPage({report, route, policy, policyCategories, policyTags, p return ( { const activeRoute = encodeURIComponent(Navigation.getActiveRouteWithoutParams()); From 60ab450b8716499fa3b18133ebdaec143e108b5e Mon Sep 17 00:00:00 2001 From: Etotaziba Olei Date: Thu, 22 Feb 2024 12:59:10 +0100 Subject: [PATCH 130/500] update tax rate name --- src/components/ReportActionItem/MoneyRequestView.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/ReportActionItem/MoneyRequestView.tsx b/src/components/ReportActionItem/MoneyRequestView.tsx index dfae8702abef..2cf016556429 100644 --- a/src/components/ReportActionItem/MoneyRequestView.tsx +++ b/src/components/ReportActionItem/MoneyRequestView.tsx @@ -122,7 +122,7 @@ function MoneyRequestView({ const taxRates = policy?.taxRates; const formattedTaxAmount = transactionTaxAmount ? CurrencyUtils.convertToDisplayString(transactionTaxAmount, transactionCurrency) : ''; - const policyTaxRatesDescription = taxRates?.name; + const taxRatesDescription = taxRates?.name; const taxRateTitle = (transactionTaxCode && taxRates && TransactionUtils.getTaxName(taxRates?.taxes, transactionTaxCode)) ?? ''; // Flags for allowing or disallowing editing a money request @@ -433,7 +433,7 @@ function MoneyRequestView({ Date: Thu, 22 Feb 2024 13:01:02 +0100 Subject: [PATCH 131/500] update tax rate name --- src/pages/iou/request/step/IOURequestStepTaxAmountPage.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/pages/iou/request/step/IOURequestStepTaxAmountPage.js b/src/pages/iou/request/step/IOURequestStepTaxAmountPage.js index 1d5ea686e975..907b79f6968f 100644 --- a/src/pages/iou/request/step/IOURequestStepTaxAmountPage.js +++ b/src/pages/iou/request/step/IOURequestStepTaxAmountPage.js @@ -67,7 +67,7 @@ function IOURequestStepTaxAmountPage({ const isSaveButtonPressed = useRef(false); const originalCurrency = useRef(null); - const policyTaxRates = lodashGet(policy, 'taxRates', {}); + const taxRates = lodashGet(policy, 'taxRates', {}); useEffect(() => { if (transaction.originalCurrency) { @@ -146,7 +146,7 @@ function IOURequestStepTaxAmountPage({ isEditing={isEditing} currency={currency} amount={transaction.taxAmount} - taxAmount={getTaxAmount(transaction, policyTaxRates.defaultValue)} + taxAmount={getTaxAmount(transaction, taxRates.defaultValue)} ref={(e) => (textInput.current = e)} onCurrencyButtonPress={navigateToCurrencySelectionPage} onSubmitButtonPress={updateTaxAmount} From 1127e7c58684ce9c6d0320610b673263ef4f7111 Mon Sep 17 00:00:00 2001 From: Etotaziba Olei Date: Thu, 22 Feb 2024 13:01:52 +0100 Subject: [PATCH 132/500] update tax rate name --- src/pages/iou/request/step/IOURequestStepTaxRatePage.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/pages/iou/request/step/IOURequestStepTaxRatePage.js b/src/pages/iou/request/step/IOURequestStepTaxRatePage.js index 694c76e87d99..241f5cccdc17 100644 --- a/src/pages/iou/request/step/IOURequestStepTaxRatePage.js +++ b/src/pages/iou/request/step/IOURequestStepTaxRatePage.js @@ -58,16 +58,16 @@ function IOURequestStepTaxRatePage({ }) { const {translate} = useLocalize(); - const policyTaxRates = lodashGet(policy, 'taxRates', {}); + const taxRates = lodashGet(policy, 'taxRates', {}); const navigateBack = () => { Navigation.goBack(backTo); }; - const selectedTaxRate = TransactionUtils.getDefaultTaxName(policyTaxRates, transaction); + const selectedTaxRate = TransactionUtils.getDefaultTaxName(taxRates, transaction); const updateTaxRates = (taxes) => { - const taxAmount = getTaxAmount(policyTaxRates, taxes.text, transaction.amount); + const taxAmount = getTaxAmount(taxRates, taxes.text, transaction.amount); const amountInSmallestCurrencyUnits = CurrencyUtils.convertToBackendAmount(Number.parseFloat(taxAmount)); IOU.setMoneyRequestTaxRate(transaction.transactionID, taxes); IOU.setMoneyRequestTaxAmount(transaction.transactionID, amountInSmallestCurrencyUnits); From e7be70e76caa5d6b0be91a2218bd6977540ba669 Mon Sep 17 00:00:00 2001 From: Etotaziba Olei Date: Thu, 22 Feb 2024 13:33:50 +0100 Subject: [PATCH 133/500] add taxRates types to Policy --- src/types/onyx/Policy.ts | 38 +++++++++++++++++++++++++++++++++++++- 1 file changed, 37 insertions(+), 1 deletion(-) diff --git a/src/types/onyx/Policy.ts b/src/types/onyx/Policy.ts index f85d4e0cba05..31b2d1ac4b53 100644 --- a/src/types/onyx/Policy.ts +++ b/src/types/onyx/Policy.ts @@ -35,6 +35,42 @@ type DisabledFields = { reimbursable?: boolean; }; +type TaxRate = { + /** Name of the a tax rate. */ + name: string; + + /** The value of the tax rate. */ + value: string; + + /** The code associated with the tax rate. */ + code: string; + + /** This contains the tax name and tax value as one name */ + modifiedName: string; + + /** Indicates if the tax rate is disabled. */ + isDisabled?: boolean; +}; + +type TaxRates = Record; + +type TaxRatesWithDefault = { + /** Name of the tax */ + name: string; + + /** Default policy tax ID */ + defaultExternalID: string; + + /** Default value of taxes */ + defaultValue: string; + + /** Default foreign policy tax ID */ + foreignTaxDefault: string; + + /** List of tax names and values */ + taxes: TaxRates; +}; + // These types are for the Integration connections for a policy (eg. Quickbooks, Xero, etc). // This data is not yet used in the codebase which is why it is given a very generic type, but the data is being put into Onyx for future use. // Once the data is being used, these types should be defined appropriately. @@ -190,4 +226,4 @@ type Policy = { export default Policy; -export type {Unit, CustomUnit, Attributes, Rate}; +export type {Unit, CustomUnit, Attributes, Rate, TaxRate, TaxRates, TaxRatesWithDefault}; From 76cd4d0787575cecfffbf0be39af9ddd41a52c7c Mon Sep 17 00:00:00 2001 From: Etotaziba Olei Date: Thu, 22 Feb 2024 13:35:01 +0100 Subject: [PATCH 134/500] export policy tax rates types from onyx types --- src/types/onyx/index.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/types/onyx/index.ts b/src/types/onyx/index.ts index 59cabbe020ee..9c59a89e4040 100644 --- a/src/types/onyx/index.ts +++ b/src/types/onyx/index.ts @@ -30,12 +30,12 @@ import type {PersonalDetailsList} from './PersonalDetails'; import type PersonalDetails from './PersonalDetails'; import type PlaidData from './PlaidData'; import type Policy from './Policy'; +import type {TaxRate, TaxRates, TaxRatesWithDefault} from './Policy'; import type {PolicyCategories, PolicyCategory} from './PolicyCategory'; import type {PolicyMembers} from './PolicyMember'; import type PolicyMember from './PolicyMember'; import type {PolicyReportField, PolicyReportFields} from './PolicyReportField'; import type {PolicyTag, PolicyTagList, PolicyTags} from './PolicyTag'; -import type {PolicyTaxRates, TaxRate, TaxRates} from './PolicyTaxRates'; import type PrivatePersonalDetails from './PrivatePersonalDetails'; import type RecentlyUsedCategories from './RecentlyUsedCategories'; import type RecentlyUsedReportFields from './RecentlyUsedReportFields'; @@ -108,9 +108,6 @@ export type { PolicyMembers, PolicyTag, PolicyTags, - TaxRate, - TaxRates, - PolicyTaxRates, PolicyTagList, PrivatePersonalDetails, RecentWaypoint, @@ -131,6 +128,9 @@ export type { SecurityGroup, Session, Task, + TaxRate, + TaxRates, + TaxRatesWithDefault, Transaction, TransactionViolation, TransactionViolations, From b8315690f541d4668247ca40e6c45b082d0e716b Mon Sep 17 00:00:00 2001 From: Etotaziba Olei Date: Thu, 22 Feb 2024 13:37:48 +0100 Subject: [PATCH 135/500] @update getFiltered options for taxRates --- src/libs/OptionsListUtils.ts | 44 ++++++++++++++++++------------------ 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/src/libs/OptionsListUtils.ts b/src/libs/OptionsListUtils.ts index cdda08f8497c..051c9a4cbd0f 100644 --- a/src/libs/OptionsListUtils.ts +++ b/src/libs/OptionsListUtils.ts @@ -111,8 +111,8 @@ type GetOptionsConfig = { recentlyUsedTags?: string[]; canInviteUser?: boolean; includeSelectedOptions?: boolean; - includePolicyTaxRates?: boolean; - policyTaxRates?: PolicyTaxRates; + includeTaxRates?: boolean; + taxRates?: PolicyTaxRates; transactionViolations?: OnyxCollection; }; @@ -141,7 +141,7 @@ type GetOptions = { currentUserOption: ReportUtils.OptionData | null | undefined; categoryOptions: CategorySection[]; tagOptions: CategorySection[]; - policyTaxRatesOptions: CategorySection[]; + taxRatesOptions: CategorySection[]; }; type PreviewConfig = {showChatPreviewLine?: boolean; forcePolicyNamePreview?: boolean}; @@ -1175,13 +1175,13 @@ function hasEnabledTags(policyTagList: Array /** * Transforms tax rates to a new object format - to add codes and new name with concatenated name and value. * - * @param policyTaxRates - The original tax rates object. + * @param taxRates - The original tax rates object. * @returns The transformed tax rates object.g */ -function transformedTaxRates(policyTaxRates: PolicyTaxRates | undefined): Record { - const defaultTaxKey = policyTaxRates?.defaultExternalID; +function transformedTaxRates(taxRates: PolicyTaxRates | undefined): Record { + const defaultTaxKey = taxRates?.defaultExternalID; const getModifiedName = (data: TaxRate, code: string) => `${data.name} (${data.value})${defaultTaxKey === code ? ` • ${Localize.translateLocal('common.default')}` : ''}`; - const taxes = Object.fromEntries(Object.entries(policyTaxRates?.taxes ?? {}).map(([code, data]) => [code, {...data, code, modifiedName: getModifiedName(data, code), name: data.name}])); + const taxes = Object.fromEntries(Object.entries(taxRates?.taxes ?? {}).map(([code, data]) => [code, {...data, code, modifiedName: getModifiedName(data, code), name: data.name}])); return taxes; } @@ -1210,10 +1210,10 @@ function getTaxRatesOptions(taxRates: Array>): Option[] { /** * Builds the section list for tax rates */ -function getTaxRatesSection(policyTaxRates: PolicyTaxRates | undefined, selectedOptions: Category[], searchInputValue: string): CategorySection[] { +function getTaxRatesSection(taxRates: PolicyTaxRates | undefined, selectedOptions: Category[], searchInputValue: string): CategorySection[] { const policyRatesSections = []; - const taxes = transformedTaxRates(policyTaxRates); + const taxes = transformedTaxRates(taxRates); const sortedTaxRates = sortTaxRates(taxes); const enabledTaxRates = sortedTaxRates.filter((taxRate) => !taxRate.isDisabled); @@ -1352,8 +1352,8 @@ function getOptions( canInviteUser = true, includeSelectedOptions = false, transactionViolations = {}, - includePolicyTaxRates, - policyTaxRates, + includeTaxRates, + taxRates, }: GetOptionsConfig, ): GetOptions { if (includeCategories) { @@ -1366,7 +1366,7 @@ function getOptions( currentUserOption: null, categoryOptions, tagOptions: [], - policyTaxRatesOptions: [], + taxRatesOptions: [], }; } @@ -1380,12 +1380,12 @@ function getOptions( currentUserOption: null, categoryOptions: [], tagOptions, - policyTaxRatesOptions: [], + taxRatesOptions: [], }; } - if (includePolicyTaxRates) { - const policyTaxRatesOptions = getTaxRatesSection(policyTaxRates, selectedOptions as Category[], searchInputValue); + if (includeTaxRates) { + const taxRatesOptions = getTaxRatesSection(taxRates, selectedOptions as Category[], searchInputValue); return { recentReports: [], @@ -1394,7 +1394,7 @@ function getOptions( currentUserOption: null, categoryOptions: [], tagOptions: [], - policyTaxRatesOptions, + taxRatesOptions, }; } @@ -1406,7 +1406,7 @@ function getOptions( currentUserOption: null, categoryOptions: [], tagOptions: [], - policyTaxRatesOptions: [], + taxRatesOptions: [], }; } @@ -1695,7 +1695,7 @@ function getOptions( currentUserOption, categoryOptions: [], tagOptions: [], - policyTaxRatesOptions: [], + taxRatesOptions: [], }; } @@ -1792,8 +1792,8 @@ function getFilteredOptions( recentlyUsedTags: string[] = [], canInviteUser = true, includeSelectedOptions = false, - includePolicyTaxRates = false, - policyTaxRates: PolicyTaxRates = {} as PolicyTaxRates, + includeTaxRates = false, + taxRates: PolicyTaxRates = {} as PolicyTaxRates, ) { return getOptions(reports, personalDetails, { betas, @@ -1813,8 +1813,8 @@ function getFilteredOptions( recentlyUsedTags, canInviteUser, includeSelectedOptions, - includePolicyTaxRates, - policyTaxRates, + includeTaxRates, + taxRates, }); } From 45ea252ef358198ebed4bdb5b16bb8f0b45a4e15 Mon Sep 17 00:00:00 2001 From: Etotaziba Olei Date: Thu, 22 Feb 2024 13:41:10 +0100 Subject: [PATCH 136/500] destructure taxRatesOptions from options utils --- src/components/TaxPicker/index.js | 23 ++--------------------- 1 file changed, 2 insertions(+), 21 deletions(-) diff --git a/src/components/TaxPicker/index.js b/src/components/TaxPicker/index.js index b83d75f314aa..1e01b0adca93 100644 --- a/src/components/TaxPicker/index.js +++ b/src/components/TaxPicker/index.js @@ -39,27 +39,8 @@ function TaxPicker({selectedTaxRate, policy, insets, onSubmit}) { }, [selectedTaxRate]); const sections = useMemo(() => { - const {policyTaxRatesOptions} = OptionsListUtils.getFilteredOptions( - {}, - {}, - [], - searchValue, - selectedOptions, - [], - false, - false, - false, - {}, - [], - false, - {}, - [], - false, - false, - true, - taxRates, - ); - return policyTaxRatesOptions; + const {taxRatesOptions} = OptionsListUtils.getFilteredOptions({}, {}, [], searchValue, selectedOptions, [], false, false, false, {}, [], false, {}, [], false, false, true, taxRates); + return taxRatesOptions; }, [taxRates, searchValue, selectedOptions]); const selectedOptionKey = lodashGet(_.filter(lodashGet(sections, '[0].data', []), (taxRate) => taxRate.searchText === selectedTaxRate)[0], 'keyForList'); From b3f55a88608c213f90c921591dd358634444dded Mon Sep 17 00:00:00 2001 From: Etotaziba Olei Date: Thu, 22 Feb 2024 13:50:16 +0100 Subject: [PATCH 137/500] update OptionsListUtils tax rate types --- src/libs/OptionsListUtils.ts | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/libs/OptionsListUtils.ts b/src/libs/OptionsListUtils.ts index 051c9a4cbd0f..22e1176293fd 100644 --- a/src/libs/OptionsListUtils.ts +++ b/src/libs/OptionsListUtils.ts @@ -22,12 +22,14 @@ import type { Report, ReportAction, ReportActions, + TaxRate, + TaxRates, + TaxRatesWithDefault, Transaction, TransactionViolation, } from '@src/types/onyx'; import type {Participant} from '@src/types/onyx/IOU'; import type * as OnyxCommon from '@src/types/onyx/OnyxCommon'; -import type {PolicyTaxRates, TaxRate, TaxRates} from '@src/types/onyx/PolicyTaxRates'; import type DeepValueOf from '@src/types/utils/DeepValueOf'; import {isEmptyObject} from '@src/types/utils/EmptyObject'; import times from '@src/utils/times'; @@ -112,7 +114,7 @@ type GetOptionsConfig = { canInviteUser?: boolean; includeSelectedOptions?: boolean; includeTaxRates?: boolean; - taxRates?: PolicyTaxRates; + taxRates?: TaxRatesWithDefault; transactionViolations?: OnyxCollection; }; @@ -1178,7 +1180,7 @@ function hasEnabledTags(policyTagList: Array * @param taxRates - The original tax rates object. * @returns The transformed tax rates object.g */ -function transformedTaxRates(taxRates: PolicyTaxRates | undefined): Record { +function transformedTaxRates(taxRates: TaxRatesWithDefault | undefined): Record { const defaultTaxKey = taxRates?.defaultExternalID; const getModifiedName = (data: TaxRate, code: string) => `${data.name} (${data.value})${defaultTaxKey === code ? ` • ${Localize.translateLocal('common.default')}` : ''}`; const taxes = Object.fromEntries(Object.entries(taxRates?.taxes ?? {}).map(([code, data]) => [code, {...data, code, modifiedName: getModifiedName(data, code), name: data.name}])); @@ -1210,7 +1212,7 @@ function getTaxRatesOptions(taxRates: Array>): Option[] { /** * Builds the section list for tax rates */ -function getTaxRatesSection(taxRates: PolicyTaxRates | undefined, selectedOptions: Category[], searchInputValue: string): CategorySection[] { +function getTaxRatesSection(taxRates: TaxRatesWithDefault | undefined, selectedOptions: Category[], searchInputValue: string): CategorySection[] { const policyRatesSections = []; const taxes = transformedTaxRates(taxRates); @@ -1793,7 +1795,7 @@ function getFilteredOptions( canInviteUser = true, includeSelectedOptions = false, includeTaxRates = false, - taxRates: PolicyTaxRates = {} as PolicyTaxRates, + taxRates: TaxRatesWithDefault = {} as TaxRatesWithDefault, ) { return getOptions(reports, personalDetails, { betas, From 09364ce2ad7c7cf7ad64063311b8458cc8e65218 Mon Sep 17 00:00:00 2001 From: Etotaziba Olei Date: Thu, 22 Feb 2024 13:52:44 +0100 Subject: [PATCH 138/500] update TransactionUtils tax rate types --- src/libs/TransactionUtils.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/libs/TransactionUtils.ts b/src/libs/TransactionUtils.ts index 0291d5e77a8d..484acf62cf0e 100644 --- a/src/libs/TransactionUtils.ts +++ b/src/libs/TransactionUtils.ts @@ -4,8 +4,7 @@ import Onyx from 'react-native-onyx'; import type {ValueOf} from 'type-fest'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; -import type {RecentWaypoint, Report, ReportAction, Transaction, TransactionViolation} from '@src/types/onyx'; -import type {PolicyTaxRates, TaxRate, TaxRates} from '@src/types/onyx/PolicyTaxRates'; +import type {RecentWaypoint, Report, ReportAction, TaxRate, TaxRates, TaxRatesWithDefault, Transaction, TransactionViolation} from '@src/types/onyx'; import type {Comment, Receipt, TransactionChanges, TransactionPendingFieldsKey, Waypoint, WaypointCollection} from '@src/types/onyx/Transaction'; import type {EmptyObject} from '@src/types/utils/EmptyObject'; import {isEmptyObject} from '@src/types/utils/EmptyObject'; @@ -622,7 +621,7 @@ function getEnabledTaxRateCount(options: TaxRates) { /** * Gets the default tax name */ -function getDefaultTaxName(policyTaxRates: PolicyTaxRates, transaction: Transaction) { +function getDefaultTaxName(policyTaxRates: TaxRatesWithDefault, transaction: Transaction) { const defaultTaxKey = policyTaxRates.defaultExternalID; const defaultTaxName = (defaultTaxKey && `${policyTaxRates.taxes[defaultTaxKey].name} (${policyTaxRates.taxes[defaultTaxKey].value}) • ${Localize.translateLocal('common.default')}`) || ''; From 5f123ff141789f82584ba2bae66577a404e8d762 Mon Sep 17 00:00:00 2001 From: Etotaziba Olei Date: Thu, 22 Feb 2024 13:59:54 +0100 Subject: [PATCH 139/500] update getDefaultTaxName parameter name --- src/libs/TransactionUtils.ts | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/libs/TransactionUtils.ts b/src/libs/TransactionUtils.ts index 484acf62cf0e..250c5650cd6d 100644 --- a/src/libs/TransactionUtils.ts +++ b/src/libs/TransactionUtils.ts @@ -621,10 +621,9 @@ function getEnabledTaxRateCount(options: TaxRates) { /** * Gets the default tax name */ -function getDefaultTaxName(policyTaxRates: TaxRatesWithDefault, transaction: Transaction) { - const defaultTaxKey = policyTaxRates.defaultExternalID; - const defaultTaxName = - (defaultTaxKey && `${policyTaxRates.taxes[defaultTaxKey].name} (${policyTaxRates.taxes[defaultTaxKey].value}) • ${Localize.translateLocal('common.default')}`) || ''; +function getDefaultTaxName(taxRates: TaxRatesWithDefault, transaction: Transaction) { + const defaultTaxKey = taxRates.defaultExternalID; + const defaultTaxName = (defaultTaxKey && `${taxRates.taxes[defaultTaxKey].name} (${taxRates.taxes[defaultTaxKey].value}) • ${Localize.translateLocal('common.default')}`) || ''; return transaction?.taxRate?.text ?? defaultTaxName; } From 071ff8b5145465c5fbc114bc58c68c96200bfdee Mon Sep 17 00:00:00 2001 From: Etotaziba Olei Date: Thu, 22 Feb 2024 14:01:43 +0100 Subject: [PATCH 140/500] update taxRates in policy types --- src/types/onyx/Policy.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/types/onyx/Policy.ts b/src/types/onyx/Policy.ts index 31b2d1ac4b53..7db309194452 100644 --- a/src/types/onyx/Policy.ts +++ b/src/types/onyx/Policy.ts @@ -1,7 +1,6 @@ import type {ValueOf} from 'type-fest'; import type CONST from '@src/CONST'; import type * as OnyxCommon from './OnyxCommon'; -import type {PolicyTaxRates} from './PolicyTaxRates'; type Unit = 'mi' | 'km'; @@ -212,7 +211,7 @@ type Policy = { }; /** Collection of tax rates attached to a policy */ - taxRates?: PolicyTaxRates; + taxRates?: TaxRatesWithDefault; /** ReportID of the admins room for this workspace */ chatReportIDAdmins?: number; From 84c51b06a74820a9e41deb8d8ed3d1acae0ba26a Mon Sep 17 00:00:00 2001 From: Etotaziba Olei Date: Thu, 22 Feb 2024 14:07:30 +0100 Subject: [PATCH 141/500] update taxRates name in OptionListUtilsTest --- tests/unit/OptionsListUtilsTest.js | 33 +++++++----------------------- 1 file changed, 7 insertions(+), 26 deletions(-) diff --git a/tests/unit/OptionsListUtilsTest.js b/tests/unit/OptionsListUtilsTest.js index 00f1307ab59f..7244b7830a29 100644 --- a/tests/unit/OptionsListUtilsTest.js +++ b/tests/unit/OptionsListUtilsTest.js @@ -2063,7 +2063,7 @@ describe('OptionsListUtils', () => { const emptySearch = ''; const wrongSearch = 'bla bla'; - const policyTaxRatesWithDefault = { + const taxRatesWithDefault = { name: 'Tax', defaultExternalID: 'CODE1', defaultValue: '0%', @@ -2170,34 +2170,15 @@ describe('OptionsListUtils', () => { }, ]; - const result = OptionsListUtils.getFilteredOptions({}, {}, [], emptySearch, [], [], false, false, false, {}, [], false, {}, [], false, false, true, policyTaxRatesWithDefault); + const result = OptionsListUtils.getFilteredOptions({}, {}, [], emptySearch, [], [], false, false, false, {}, [], false, {}, [], false, false, true, taxRatesWithDefault); - expect(result.policyTaxRatesOptions).toStrictEqual(resultList); + expect(result.taxRatesOptions).toStrictEqual(resultList); - const searchResult = OptionsListUtils.getFilteredOptions({}, {}, [], search, [], [], false, false, false, {}, [], false, {}, [], false, false, true, policyTaxRatesWithDefault); - expect(searchResult.policyTaxRatesOptions).toStrictEqual(searchResultList); + const searchResult = OptionsListUtils.getFilteredOptions({}, {}, [], search, [], [], false, false, false, {}, [], false, {}, [], false, false, true, taxRatesWithDefault); + expect(searchResult.taxRatesOptions).toStrictEqual(searchResultList); - const wrongSearchResult = OptionsListUtils.getFilteredOptions( - {}, - {}, - [], - wrongSearch, - [], - [], - false, - false, - false, - {}, - [], - false, - {}, - [], - false, - false, - true, - policyTaxRatesWithDefault, - ); - expect(wrongSearchResult.policyTaxRatesOptions).toStrictEqual(wrongSearchResultList); + const wrongSearchResult = OptionsListUtils.getFilteredOptions({}, {}, [], wrongSearch, [], [], false, false, false, {}, [], false, {}, [], false, false, true, taxRatesWithDefault); + expect(wrongSearchResult.taxRatesOptions).toStrictEqual(wrongSearchResultList); }); it('formatMemberForList()', () => { From f8b66da13c5fe911a46c53b17d932d83e9b4f106 Mon Sep 17 00:00:00 2001 From: Etotaziba Olei Date: Thu, 22 Feb 2024 15:19:14 +0100 Subject: [PATCH 142/500] add taxRate and oldTaxRate to ExpenseOriginalMessage --- src/libs/ReportUtils.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index a36fc095489b..db3568ef34f7 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -87,6 +87,8 @@ type ExpenseOriginalMessage = { oldTag?: string; billable?: string; oldBillable?: string; + taxRate?: string; + oldTaxRate?: string; }; type SpendBreakdown = { From 2527914bfadfc75030084c691b337b1e1363eea0 Mon Sep 17 00:00:00 2001 From: Etotaziba Olei Date: Thu, 22 Feb 2024 16:10:50 +0100 Subject: [PATCH 143/500] add taxRate system message --- src/libs/ModifiedExpenseMessage.ts | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/libs/ModifiedExpenseMessage.ts b/src/libs/ModifiedExpenseMessage.ts index 13a58834860b..e2d0741eca43 100644 --- a/src/libs/ModifiedExpenseMessage.ts +++ b/src/libs/ModifiedExpenseMessage.ts @@ -115,6 +115,7 @@ function getForReportAction(reportID: string | undefined, reportAction: OnyxEntr 'currency' in reportActionOriginalMessage; const hasModifiedMerchant = reportActionOriginalMessage && 'oldMerchant' in reportActionOriginalMessage && 'merchant' in reportActionOriginalMessage; + if (hasModifiedAmount) { const oldCurrency = reportActionOriginalMessage?.oldCurrency ?? ''; const oldAmountValue = reportActionOriginalMessage?.oldAmount ?? 0; @@ -214,6 +215,19 @@ function getForReportAction(reportID: string | undefined, reportAction: OnyxEntr }); } + const hasModifiedTaxRate = reportActionOriginalMessage && 'oldTaxRate' in reportActionOriginalMessage && 'taxRate' in reportActionOriginalMessage; + if (hasModifiedTaxRate) { + buildMessageFragmentForValue( + reportActionOriginalMessage?.taxRate ?? '', + reportActionOriginalMessage?.oldTaxRate ?? '', + Localize.translateLocal('iou.taxRate'), + true, + setFragments, + removalFragments, + changeFragments, + ); + } + const hasModifiedBillable = reportActionOriginalMessage && 'oldBillable' in reportActionOriginalMessage && 'billable' in reportActionOriginalMessage; if (hasModifiedBillable) { buildMessageFragmentForValue( From a6faef416ad62d6e0c58df96dce26ce1ca1ac7b7 Mon Sep 17 00:00:00 2001 From: Yogesh Bhatt Date: Mon, 26 Feb 2024 18:17:58 +0530 Subject: [PATCH 144/500] Refactor StateSelectionPage with more explanatory comments --- .../PersonalDetails/StateSelectionPage.tsx | 21 ++++++++++++------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/src/pages/settings/Profile/PersonalDetails/StateSelectionPage.tsx b/src/pages/settings/Profile/PersonalDetails/StateSelectionPage.tsx index 7fe667f5b9ea..804d3de6bf13 100644 --- a/src/pages/settings/Profile/PersonalDetails/StateSelectionPage.tsx +++ b/src/pages/settings/Profile/PersonalDetails/StateSelectionPage.tsx @@ -60,17 +60,22 @@ function StateSelectionPage() { (option: CountryData) => { const backTo = params?.backTo ?? ''; - // Check the navigation state and "backTo" parameter to decide navigation behavior - if (navigation.getState().routes.length === 1 && _.isEmpty(backTo)) { - // If there is only one route and "backTo" is empty, go back in navigation - Navigation.goBack(); - } else if (!_.isEmpty(backTo) && navigation.getState().routes.length === 1) { - // If "backTo" is not empty and there is only one route, go back to the specific route defined in "backTo" with a country parameter - Navigation.goBack(appendParam(backTo, 'state', option.value) as Route); + // Determine navigation action based on "backTo" presence and route stack length. + if (navigation.getState().routes.length === 1) { + // If this is the only page in the navigation stack (examples include direct navigation to this page via URL or page reload). + if (_.isEmpty(backTo)) { + // No "backTo": default back navigation. + Navigation.goBack(); + } else { + // "backTo" provided: navigate back to "backTo" with state parameter. + Navigation.goBack(appendParam(backTo, 'state', option.value) as Route); + } } else if (!_.isEmpty(backTo)) { - // Otherwise, navigate to the specific route defined in "backTo" with a country parameter + // Most common case: Navigation stack has multiple routes and "backTo" is defined: navigate to "backTo" with state parameter. Navigation.navigate(appendParam(backTo, 'state', option.value) as Route); } else { + // This is a fallback block and should never execute if StateSelector is correctly appending the "backTo" route. + // Navigation stack has multiple routes but no "backTo" defined: default back navigation. Navigation.goBack(); } }, From c0cc219b0a3c16b888822087f624dd50184f711d Mon Sep 17 00:00:00 2001 From: Etotaziba Olei Date: Mon, 26 Feb 2024 16:53:00 +0100 Subject: [PATCH 145/500] fix lint --- .../MoneyTemporaryForRefactorRequestConfirmationList.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/components/MoneyTemporaryForRefactorRequestConfirmationList.js b/src/components/MoneyTemporaryForRefactorRequestConfirmationList.js index 1ecb22c99908..fa8d0d51990c 100755 --- a/src/components/MoneyTemporaryForRefactorRequestConfirmationList.js +++ b/src/components/MoneyTemporaryForRefactorRequestConfirmationList.js @@ -194,7 +194,6 @@ const defaultProps = { policy: {}, policyCategories: {}, policyTags: {}, - policy: {}, transactionID: '', transaction: {}, mileageRate: {unit: CONST.CUSTOM_UNITS.DISTANCE_UNIT_MILES, rate: 0, currency: 'USD'}, From cc3916e18595f463f7d4647cf7cfcc3d99afc25a Mon Sep 17 00:00:00 2001 From: tienifr Date: Wed, 28 Feb 2024 05:28:16 +0700 Subject: [PATCH 146/500] feature: empty state ui for lhn --- src/components/BlockingViews/BlockingView.tsx | 63 +++++++++++++++---- .../LHNOptionsList/LHNOptionsList.tsx | 58 +++++++++++++++++ src/languages/en.ts | 6 ++ src/languages/es.ts | 6 ++ src/styles/index.ts | 9 +++ src/styles/variables.ts | 2 + 6 files changed, 133 insertions(+), 11 deletions(-) diff --git a/src/components/BlockingViews/BlockingView.tsx b/src/components/BlockingViews/BlockingView.tsx index 7b33c8054950..f83deaf1477c 100644 --- a/src/components/BlockingViews/BlockingView.tsx +++ b/src/components/BlockingViews/BlockingView.tsx @@ -1,9 +1,11 @@ import React from 'react'; -import type {ImageSourcePropType} from 'react-native'; +import type {ImageSourcePropType, StyleProp, ViewStyle} from 'react-native'; import {View} from 'react-native'; import type {SvgProps} from 'react-native-svg'; import AutoEmailLink from '@components/AutoEmailLink'; import Icon from '@components/Icon'; +import Lottie from '@components/Lottie'; +import type DotLottieAnimation from '@components/LottieAnimations/types'; import Text from '@components/Text'; import TextLink from '@components/TextLink'; import useLocalize from '@hooks/useLocalize'; @@ -12,10 +14,29 @@ import Navigation from '@libs/Navigation/Navigation'; import variables from '@styles/variables'; import type {TranslationPaths} from '@src/languages/types'; -type BlockingViewProps = { - /** Expensicon for the page */ - icon: React.FC | ImageSourcePropType; +type RequiredIllustrationProps = + | { + /** Expensicon for the page */ + icon: React.FC | ImageSourcePropType; + /** + * Animation for the page + * If icon is provided, animation is not required + */ + animation?: DotLottieAnimation; + } + | { + /** Animation for the page */ + animation: DotLottieAnimation; + + /** + * Expensicon for the page + * If animation is provided, icon is not required + */ + icon?: React.FC | ImageSourcePropType; + }; + +type BlockingViewProps = RequiredIllustrationProps & { /** Color for the icon (should be from theme) */ iconColor?: string; @@ -42,9 +63,16 @@ type BlockingViewProps = { /** Whether we should embed the link with subtitle */ shouldEmbedLinkWithSubtitle?: boolean; + + /** Style for the animation */ + animationStyles?: StyleProp; + + /** Render custom subtitle */ + renderSubtitle?: () => React.ReactElement; }; function BlockingView({ + animation, icon, iconColor, title, @@ -55,6 +83,8 @@ function BlockingView({ iconHeight = variables.iconSizeSuperLarge, onLinkPress = () => Navigation.dismissModal(), shouldEmbedLinkWithSubtitle = false, + animationStyles = [], + renderSubtitle, }: BlockingViewProps) { const styles = useThemeStyles(); const {translate} = useLocalize(); @@ -79,16 +109,27 @@ function BlockingView({ return ( - + {animation && ( + + )} + {icon && ( + + )} {title} - {shouldEmbedLinkWithSubtitle ? ( + {renderSubtitle ? ( + renderSubtitle() + ) : shouldEmbedLinkWithSubtitle ? ( {renderContent()} ) : ( {renderContent()} diff --git a/src/components/LHNOptionsList/LHNOptionsList.tsx b/src/components/LHNOptionsList/LHNOptionsList.tsx index 27f424ad1b70..18832440082b 100644 --- a/src/components/LHNOptionsList/LHNOptionsList.tsx +++ b/src/components/LHNOptionsList/LHNOptionsList.tsx @@ -3,9 +3,17 @@ import type {ReactElement} from 'react'; import React, {memo, useCallback} from 'react'; import {StyleSheet, View} from 'react-native'; import {withOnyx} from 'react-native-onyx'; +import BlockingView from '@components/BlockingViews/BlockingView'; +import Icon from '@components/Icon'; +import * as Expensicons from '@components/Icon/Expensicons'; +import LottieAnimations from '@components/LottieAnimations'; +import Text from '@components/Text'; import withCurrentReportID from '@components/withCurrentReportID'; +import useLocalize from '@hooks/useLocalize'; import usePermissions from '@hooks/usePermissions'; +import useTheme from '@hooks/useTheme'; import useThemeStyles from '@hooks/useThemeStyles'; +import useWindowDimensions from '@hooks/useWindowDimensions'; import * as ReportActionsUtils from '@libs/ReportActionsUtils'; import variables from '@styles/variables'; import CONST from '@src/CONST'; @@ -33,8 +41,11 @@ function LHNOptionsList({ transactionViolations = {}, onFirstItemRendered = () => {}, }: LHNOptionsListProps) { + const theme = useTheme(); const styles = useThemeStyles(); const {canUseViolations} = usePermissions(); + const {translate} = useLocalize(); + const {isSmallScreenWidth} = useWindowDimensions(); // When the first item renders we want to call the onFirstItemRendered callback. // At this point in time we know that the list is actually displaying items. @@ -48,6 +59,40 @@ function LHNOptionsList({ onFirstItemRendered(); }, [onFirstItemRendered]); + const renderEmptyStateSubtitle = useCallback( + () => ( + + + {translate('common.emptyLHN.subtitleText1')} + + {translate('common.emptyLHN.subtitleText2')} + + {translate('common.emptyLHN.subtitleText3')} + + + ), + [theme, styles.dFlex, styles.gap1, styles.alignItemsCenter, styles.justifyContentCenter, styles.textAlignCenter, translate], + ); + /** * Function which renders a row in the list */ @@ -125,6 +170,19 @@ function LHNOptionsList({ estimatedItemSize={optionMode === CONST.OPTION_MODE.COMPACT ? variables.optionRowHeightCompact : variables.optionRowHeight} extraData={[currentReportID]} showsVerticalScrollIndicator={false} + ListEmptyComponent={ + isSmallScreenWidth ? ( + + + + ) : null + } /> ); diff --git a/src/languages/en.ts b/src/languages/en.ts index 4d7041d4a791..1fbc9ac0e198 100755 --- a/src/languages/en.ts +++ b/src/languages/en.ts @@ -312,6 +312,12 @@ export default { update: 'Update', member: 'Member', role: 'Role', + emptyLHN: { + title: 'Woohoo! All caught up.', + subtitleText1: 'Find a chat using the', + subtitleText2: 'button above, or create something using the', + subtitleText3: 'button below.', + }, }, location: { useCurrent: 'Use current location', diff --git a/src/languages/es.ts b/src/languages/es.ts index c9ff087d0de7..d42d0aa1faf2 100644 --- a/src/languages/es.ts +++ b/src/languages/es.ts @@ -302,6 +302,12 @@ export default { update: 'Actualizar', member: 'Miembro', role: 'Role', + emptyLHN: { + title: 'Woohoo! Todo al día.', + subtitleText1: 'Encuentra un chat usando el botón', + subtitleText2: 'o crea algo usando el botón', + subtitleText3: '.', + }, }, location: { useCurrent: 'Usar ubicación actual', diff --git a/src/styles/index.ts b/src/styles/index.ts index 32392d62e5c4..3bce9d90c8ea 100644 --- a/src/styles/index.ts +++ b/src/styles/index.ts @@ -3166,6 +3166,15 @@ const styles = (theme: ThemeColors) => alignItems: 'center', }, + emptyLHNAnimation: { + width: 180, + height: 180, + }, + + emptyLHNBlockingView: { + marginTop: 120, + }, + locationErrorLinkText: { textAlignVertical: 'center', fontSize: variables.fontSizeLabel, diff --git a/src/styles/variables.ts b/src/styles/variables.ts index f7c9bd055041..2f8ecac14c32 100644 --- a/src/styles/variables.ts +++ b/src/styles/variables.ts @@ -121,6 +121,8 @@ export default { avatarChatSpacing: 12, chatInputSpacing: 52, // 40 + avatarChatSpacing borderTopWidth: 1, + emptyLHNIconWidth: 24, // iconSizeSmall + 4*2 horizontal margin + emptyLHNIconHeight: 16, emptyWorkspaceIconWidth: 84, emptyWorkspaceIconHeight: 84, modalTopIconWidth: 200, From 0fd03c00e0d9e3938490ac91ca9c133f55cb39ce Mon Sep 17 00:00:00 2001 From: Etotaziba Olei Date: Wed, 28 Feb 2024 01:24:49 +0100 Subject: [PATCH 147/500] add taxAmount and oldTaxAmount types to ExpenseOriginalMessage --- src/libs/ReportUtils.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index 7c4cfbbb0cb0..63d1dd019db2 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -88,6 +88,8 @@ type ExpenseOriginalMessage = { oldTag?: string; billable?: string; oldBillable?: string; + oldTaxAmount?: number; + taxAmount?: number; taxRate?: string; oldTaxRate?: string; }; From 8b212f594840864fda7ea67c8aae601291e5a50e Mon Sep 17 00:00:00 2001 From: Etotaziba Olei Date: Wed, 28 Feb 2024 01:25:47 +0100 Subject: [PATCH 148/500] add taxAmount edit request system message --- src/libs/ModifiedExpenseMessage.ts | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/libs/ModifiedExpenseMessage.ts b/src/libs/ModifiedExpenseMessage.ts index b132c66c84c6..fb5cb7756804 100644 --- a/src/libs/ModifiedExpenseMessage.ts +++ b/src/libs/ModifiedExpenseMessage.ts @@ -215,6 +215,14 @@ function getForReportAction(reportID: string | undefined, reportAction: OnyxEntr } }); } + + const hasModifiedTaxAmount = reportActionOriginalMessage && 'oldTaxAmount' in reportActionOriginalMessage && 'taxAmount' in reportActionOriginalMessage; + if (hasModifiedTaxAmount) { + const taxAmount = CurrencyUtils.convertToDisplayString(reportActionOriginalMessage?.amount ?? 0); + const oldTaxAmountValue = reportActionOriginalMessage?.oldAmount ?? 0; + const oldTaxAmount = oldTaxAmountValue > 0 ? CurrencyUtils.convertToDisplayString(reportActionOriginalMessage?.oldTaxAmount ?? 0) : ''; + buildMessageFragmentForValue(taxAmount, oldTaxAmount, Localize.translateLocal('iou.taxAmount'), true, setFragments, removalFragments, changeFragments); + } const hasModifiedTaxRate = reportActionOriginalMessage && 'oldTaxRate' in reportActionOriginalMessage && 'taxRate' in reportActionOriginalMessage; if (hasModifiedTaxRate) { From f05fdfeab61a771569c17668105c794199e72da6 Mon Sep 17 00:00:00 2001 From: Etotaziba Olei Date: Wed, 28 Feb 2024 02:43:20 +0100 Subject: [PATCH 149/500] use taxAmount --- src/libs/ModifiedExpenseMessage.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libs/ModifiedExpenseMessage.ts b/src/libs/ModifiedExpenseMessage.ts index fb5cb7756804..0a8df556405c 100644 --- a/src/libs/ModifiedExpenseMessage.ts +++ b/src/libs/ModifiedExpenseMessage.ts @@ -218,9 +218,9 @@ function getForReportAction(reportID: string | undefined, reportAction: OnyxEntr const hasModifiedTaxAmount = reportActionOriginalMessage && 'oldTaxAmount' in reportActionOriginalMessage && 'taxAmount' in reportActionOriginalMessage; if (hasModifiedTaxAmount) { - const taxAmount = CurrencyUtils.convertToDisplayString(reportActionOriginalMessage?.amount ?? 0); + const taxAmount = CurrencyUtils.convertToDisplayString(reportActionOriginalMessage?.taxAmount ?? 0); const oldTaxAmountValue = reportActionOriginalMessage?.oldAmount ?? 0; - const oldTaxAmount = oldTaxAmountValue > 0 ? CurrencyUtils.convertToDisplayString(reportActionOriginalMessage?.oldTaxAmount ?? 0) : ''; + const oldTaxAmount = oldTaxAmountValue > 0 ? CurrencyUtils.convertToDisplayString(oldTaxAmountValue) : ''; buildMessageFragmentForValue(taxAmount, oldTaxAmount, Localize.translateLocal('iou.taxAmount'), true, setFragments, removalFragments, changeFragments); } From 5f966270ce681e62adabf5964de09f03cb3d58a3 Mon Sep 17 00:00:00 2001 From: Etotaziba Olei Date: Wed, 28 Feb 2024 02:45:16 +0100 Subject: [PATCH 150/500] add allReports from Onyx --- src/libs/ModifiedExpenseMessage.ts | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/libs/ModifiedExpenseMessage.ts b/src/libs/ModifiedExpenseMessage.ts index 0a8df556405c..4ced8f0b6cf7 100644 --- a/src/libs/ModifiedExpenseMessage.ts +++ b/src/libs/ModifiedExpenseMessage.ts @@ -3,6 +3,7 @@ import type {OnyxCollection, OnyxEntry} from 'react-native-onyx'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import type {PolicyTagList, ReportAction} from '@src/types/onyx'; +import type * as OnyxTypes from '@src/types/onyx'; import * as CurrencyUtils from './CurrencyUtils'; import DateUtils from './DateUtils'; import * as Localize from './Localize'; @@ -24,6 +25,13 @@ Onyx.connect({ }, }); +let allReports: OnyxCollection = null; +Onyx.connect({ + key: ONYXKEYS.COLLECTION.REPORT, + waitForCollectionCallback: true, + callback: (value) => (allReports = value), +}); + /** * Builds the partial message fragment for a modified field on the expense. */ @@ -215,7 +223,7 @@ function getForReportAction(reportID: string | undefined, reportAction: OnyxEntr } }); } - + const hasModifiedTaxAmount = reportActionOriginalMessage && 'oldTaxAmount' in reportActionOriginalMessage && 'taxAmount' in reportActionOriginalMessage; if (hasModifiedTaxAmount) { const taxAmount = CurrencyUtils.convertToDisplayString(reportActionOriginalMessage?.taxAmount ?? 0); From d04d366ae7a5261b1e33e059f7b9fa49bdf74588 Mon Sep 17 00:00:00 2001 From: Etotaziba Olei Date: Wed, 28 Feb 2024 02:47:24 +0100 Subject: [PATCH 151/500] add getTaxAmount Func --- src/libs/ModifiedExpenseMessage.ts | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/libs/ModifiedExpenseMessage.ts b/src/libs/ModifiedExpenseMessage.ts index 4ced8f0b6cf7..9630c5220d99 100644 --- a/src/libs/ModifiedExpenseMessage.ts +++ b/src/libs/ModifiedExpenseMessage.ts @@ -99,6 +99,20 @@ function getForDistanceRequest(newDistance: string, oldDistance: string, newAmou }); } +/** + * Return the tax amount field from the transaction. + */ +function getTaxAmount(taxAmount: number, isFromExpenseReport: boolean): number { + // IOU requests cannot have negative values but they can be stored as negative values, let's return absolute value + if (!isFromExpenseReport) { + return Math.abs(taxAmount ?? 0); + } + + // To avoid -0 being shown, lets only change the sign if the value is other than 0. + const amount = taxAmount ?? 0; + return amount ? -amount : 0; +} + /** * Get the report action message when expense has been modified. * From 91aa0365088d16c637169711ef95aad6f545260e Mon Sep 17 00:00:00 2001 From: Etotaziba Olei Date: Wed, 28 Feb 2024 02:51:46 +0100 Subject: [PATCH 152/500] use getTaxAmount for taxAmount --- src/libs/ModifiedExpenseMessage.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/libs/ModifiedExpenseMessage.ts b/src/libs/ModifiedExpenseMessage.ts index 9630c5220d99..9c398f33852a 100644 --- a/src/libs/ModifiedExpenseMessage.ts +++ b/src/libs/ModifiedExpenseMessage.ts @@ -240,7 +240,11 @@ function getForReportAction(reportID: string | undefined, reportAction: OnyxEntr const hasModifiedTaxAmount = reportActionOriginalMessage && 'oldTaxAmount' in reportActionOriginalMessage && 'taxAmount' in reportActionOriginalMessage; if (hasModifiedTaxAmount) { - const taxAmount = CurrencyUtils.convertToDisplayString(reportActionOriginalMessage?.taxAmount ?? 0); + const transactionThread = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${reportID}`] ?? null; + const iouReport = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${transactionThread?.parentReportID}`] ?? null; + const isFromExpenseReport = ReportUtils.isExpenseReport(iouReport); + + const taxAmount = CurrencyUtils.convertToDisplayString(getTaxAmount(reportActionOriginalMessage?.taxAmount ?? 0, isFromExpenseReport)); const oldTaxAmountValue = reportActionOriginalMessage?.oldAmount ?? 0; const oldTaxAmount = oldTaxAmountValue > 0 ? CurrencyUtils.convertToDisplayString(oldTaxAmountValue) : ''; buildMessageFragmentForValue(taxAmount, oldTaxAmount, Localize.translateLocal('iou.taxAmount'), true, setFragments, removalFragments, changeFragments); From dc5fc19ced1cbeade62726c18d58678728a0f5c0 Mon Sep 17 00:00:00 2001 From: Etotaziba Olei Date: Wed, 28 Feb 2024 02:52:41 +0100 Subject: [PATCH 153/500] use getTaxAmount for oldTaxAmount --- src/libs/ModifiedExpenseMessage.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/ModifiedExpenseMessage.ts b/src/libs/ModifiedExpenseMessage.ts index 9c398f33852a..75bbf412012f 100644 --- a/src/libs/ModifiedExpenseMessage.ts +++ b/src/libs/ModifiedExpenseMessage.ts @@ -246,7 +246,7 @@ function getForReportAction(reportID: string | undefined, reportAction: OnyxEntr const taxAmount = CurrencyUtils.convertToDisplayString(getTaxAmount(reportActionOriginalMessage?.taxAmount ?? 0, isFromExpenseReport)); const oldTaxAmountValue = reportActionOriginalMessage?.oldAmount ?? 0; - const oldTaxAmount = oldTaxAmountValue > 0 ? CurrencyUtils.convertToDisplayString(oldTaxAmountValue) : ''; + const oldTaxAmount = oldTaxAmountValue > 0 ? CurrencyUtils.convertToDisplayString(getTaxAmount(oldTaxAmountValue, isFromExpenseReport)) : ''; buildMessageFragmentForValue(taxAmount, oldTaxAmount, Localize.translateLocal('iou.taxAmount'), true, setFragments, removalFragments, changeFragments); } From 70f9cde696ab478c08233c8e2a138951f594d714 Mon Sep 17 00:00:00 2001 From: Etotaziba Olei Date: Wed, 28 Feb 2024 02:54:14 +0100 Subject: [PATCH 154/500] format string with currency for taxAmount --- src/libs/ModifiedExpenseMessage.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/libs/ModifiedExpenseMessage.ts b/src/libs/ModifiedExpenseMessage.ts index 75bbf412012f..369090070c88 100644 --- a/src/libs/ModifiedExpenseMessage.ts +++ b/src/libs/ModifiedExpenseMessage.ts @@ -243,8 +243,9 @@ function getForReportAction(reportID: string | undefined, reportAction: OnyxEntr const transactionThread = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${reportID}`] ?? null; const iouReport = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${transactionThread?.parentReportID}`] ?? null; const isFromExpenseReport = ReportUtils.isExpenseReport(iouReport); + const currency = iouReport?.currency ?? ''; - const taxAmount = CurrencyUtils.convertToDisplayString(getTaxAmount(reportActionOriginalMessage?.taxAmount ?? 0, isFromExpenseReport)); + const taxAmount = CurrencyUtils.convertToDisplayString(getTaxAmount(reportActionOriginalMessage?.taxAmount ?? 0, isFromExpenseReport), currency); const oldTaxAmountValue = reportActionOriginalMessage?.oldAmount ?? 0; const oldTaxAmount = oldTaxAmountValue > 0 ? CurrencyUtils.convertToDisplayString(getTaxAmount(oldTaxAmountValue, isFromExpenseReport)) : ''; buildMessageFragmentForValue(taxAmount, oldTaxAmount, Localize.translateLocal('iou.taxAmount'), true, setFragments, removalFragments, changeFragments); From 184a4e19c3ea041a3fab8f40840fd9dc802d537e Mon Sep 17 00:00:00 2001 From: Etotaziba Olei Date: Wed, 28 Feb 2024 02:54:52 +0100 Subject: [PATCH 155/500] format string with currency for oldTaxAmount --- src/libs/ModifiedExpenseMessage.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/ModifiedExpenseMessage.ts b/src/libs/ModifiedExpenseMessage.ts index 369090070c88..c3ca42729bfb 100644 --- a/src/libs/ModifiedExpenseMessage.ts +++ b/src/libs/ModifiedExpenseMessage.ts @@ -247,7 +247,7 @@ function getForReportAction(reportID: string | undefined, reportAction: OnyxEntr const taxAmount = CurrencyUtils.convertToDisplayString(getTaxAmount(reportActionOriginalMessage?.taxAmount ?? 0, isFromExpenseReport), currency); const oldTaxAmountValue = reportActionOriginalMessage?.oldAmount ?? 0; - const oldTaxAmount = oldTaxAmountValue > 0 ? CurrencyUtils.convertToDisplayString(getTaxAmount(oldTaxAmountValue, isFromExpenseReport)) : ''; + const oldTaxAmount = oldTaxAmountValue > 0 ? CurrencyUtils.convertToDisplayString(getTaxAmount(oldTaxAmountValue, isFromExpenseReport), currency) : ''; buildMessageFragmentForValue(taxAmount, oldTaxAmount, Localize.translateLocal('iou.taxAmount'), true, setFragments, removalFragments, changeFragments); } From 55886f318345654356ba89d081ebe31d6173fc73 Mon Sep 17 00:00:00 2001 From: Etotaziba Olei Date: Wed, 28 Feb 2024 03:01:39 +0100 Subject: [PATCH 156/500] set valueInQuotes to false --- src/libs/ModifiedExpenseMessage.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libs/ModifiedExpenseMessage.ts b/src/libs/ModifiedExpenseMessage.ts index c3ca42729bfb..1d24a403c864 100644 --- a/src/libs/ModifiedExpenseMessage.ts +++ b/src/libs/ModifiedExpenseMessage.ts @@ -248,7 +248,7 @@ function getForReportAction(reportID: string | undefined, reportAction: OnyxEntr const taxAmount = CurrencyUtils.convertToDisplayString(getTaxAmount(reportActionOriginalMessage?.taxAmount ?? 0, isFromExpenseReport), currency); const oldTaxAmountValue = reportActionOriginalMessage?.oldAmount ?? 0; const oldTaxAmount = oldTaxAmountValue > 0 ? CurrencyUtils.convertToDisplayString(getTaxAmount(oldTaxAmountValue, isFromExpenseReport), currency) : ''; - buildMessageFragmentForValue(taxAmount, oldTaxAmount, Localize.translateLocal('iou.taxAmount'), true, setFragments, removalFragments, changeFragments); + buildMessageFragmentForValue(taxAmount, oldTaxAmount, Localize.translateLocal('iou.taxAmount'), false, setFragments, removalFragments, changeFragments); } const hasModifiedTaxRate = reportActionOriginalMessage && 'oldTaxRate' in reportActionOriginalMessage && 'taxRate' in reportActionOriginalMessage; @@ -257,7 +257,7 @@ function getForReportAction(reportID: string | undefined, reportAction: OnyxEntr reportActionOriginalMessage?.taxRate ?? '', reportActionOriginalMessage?.oldTaxRate ?? '', Localize.translateLocal('iou.taxRate'), - true, + false, setFragments, removalFragments, changeFragments, From 9d9703aca82b3880f2d5b568fa6ed6440688b5e7 Mon Sep 17 00:00:00 2001 From: Etotaziba Olei Date: Wed, 28 Feb 2024 03:03:37 +0100 Subject: [PATCH 157/500] fix duplicate import --- src/libs/ModifiedExpenseMessage.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/libs/ModifiedExpenseMessage.ts b/src/libs/ModifiedExpenseMessage.ts index 1d24a403c864..05803702f310 100644 --- a/src/libs/ModifiedExpenseMessage.ts +++ b/src/libs/ModifiedExpenseMessage.ts @@ -2,8 +2,7 @@ import Onyx from 'react-native-onyx'; import type {OnyxCollection, OnyxEntry} from 'react-native-onyx'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; -import type {PolicyTagList, ReportAction} from '@src/types/onyx'; -import type * as OnyxTypes from '@src/types/onyx'; +import type {PolicyTagList, Report, ReportAction} from '@src/types/onyx'; import * as CurrencyUtils from './CurrencyUtils'; import DateUtils from './DateUtils'; import * as Localize from './Localize'; @@ -25,7 +24,7 @@ Onyx.connect({ }, }); -let allReports: OnyxCollection = null; +let allReports: OnyxCollection = null; Onyx.connect({ key: ONYXKEYS.COLLECTION.REPORT, waitForCollectionCallback: true, From e8e279b7fad8a11b8702bf34186683327b445892 Mon Sep 17 00:00:00 2001 From: Etotaziba Olei Date: Wed, 28 Feb 2024 03:12:16 +0100 Subject: [PATCH 158/500] handle oldTaxAmount --- src/libs/ModifiedExpenseMessage.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/libs/ModifiedExpenseMessage.ts b/src/libs/ModifiedExpenseMessage.ts index 05803702f310..a6491d3025fc 100644 --- a/src/libs/ModifiedExpenseMessage.ts +++ b/src/libs/ModifiedExpenseMessage.ts @@ -245,8 +245,9 @@ function getForReportAction(reportID: string | undefined, reportAction: OnyxEntr const currency = iouReport?.currency ?? ''; const taxAmount = CurrencyUtils.convertToDisplayString(getTaxAmount(reportActionOriginalMessage?.taxAmount ?? 0, isFromExpenseReport), currency); - const oldTaxAmountValue = reportActionOriginalMessage?.oldAmount ?? 0; - const oldTaxAmount = oldTaxAmountValue > 0 ? CurrencyUtils.convertToDisplayString(getTaxAmount(oldTaxAmountValue, isFromExpenseReport), currency) : ''; + const oldTaxAmountValue = getTaxAmount(reportActionOriginalMessage?.oldTaxAmount ?? 0, isFromExpenseReport); + const oldTaxAmount = oldTaxAmountValue > 0 ? CurrencyUtils.convertToDisplayString(oldTaxAmountValue, currency) : ''; + buildMessageFragmentForValue(taxAmount, oldTaxAmount, Localize.translateLocal('iou.taxAmount'), false, setFragments, removalFragments, changeFragments); } From ec0cc2775357d90870636edc2bee226ba3c86eeb Mon Sep 17 00:00:00 2001 From: tienifr Date: Wed, 28 Feb 2024 14:17:01 +0700 Subject: [PATCH 159/500] middle align UI --- src/components/BlockingViews/BlockingView.tsx | 6 ++++ .../LHNOptionsList/LHNOptionsList.tsx | 30 +++++++++---------- src/styles/index.ts | 7 +++-- 3 files changed, 25 insertions(+), 18 deletions(-) diff --git a/src/components/BlockingViews/BlockingView.tsx b/src/components/BlockingViews/BlockingView.tsx index f83deaf1477c..afdc2d547d07 100644 --- a/src/components/BlockingViews/BlockingView.tsx +++ b/src/components/BlockingViews/BlockingView.tsx @@ -1,4 +1,5 @@ import React from 'react'; +import type {CSSProperties} from 'react'; import type {ImageSourcePropType, StyleProp, ViewStyle} from 'react-native'; import {View} from 'react-native'; import type {SvgProps} from 'react-native-svg'; @@ -67,6 +68,8 @@ type BlockingViewProps = RequiredIllustrationProps & { /** Style for the animation */ animationStyles?: StyleProp; + animationWebStyle?: CSSProperties; + /** Render custom subtitle */ renderSubtitle?: () => React.ReactElement; }; @@ -84,6 +87,7 @@ function BlockingView({ onLinkPress = () => Navigation.dismissModal(), shouldEmbedLinkWithSubtitle = false, animationStyles = [], + animationWebStyle = {}, renderSubtitle, }: BlockingViewProps) { const styles = useThemeStyles(); @@ -112,8 +116,10 @@ function BlockingView({ {animation && ( )} {icon && ( diff --git a/src/components/LHNOptionsList/LHNOptionsList.tsx b/src/components/LHNOptionsList/LHNOptionsList.tsx index 18832440082b..b032d5f0ccd5 100644 --- a/src/components/LHNOptionsList/LHNOptionsList.tsx +++ b/src/components/LHNOptionsList/LHNOptionsList.tsx @@ -45,7 +45,7 @@ function LHNOptionsList({ const styles = useThemeStyles(); const {canUseViolations} = usePermissions(); const {translate} = useLocalize(); - const {isSmallScreenWidth} = useWindowDimensions(); + const {isExtraSmallScreenHeight, isSmallScreenWidth} = useWindowDimensions(); // When the first item renders we want to call the onFirstItemRendered callback. // At this point in time we know that the list is actually displaying items. @@ -90,7 +90,7 @@ function LHNOptionsList({ ), - [theme, styles.dFlex, styles.gap1, styles.alignItemsCenter, styles.justifyContentCenter, styles.textAlignCenter, translate], + [theme, styles.alignItemsCenter, styles.textAlignCenter, translate], ); /** @@ -157,6 +157,19 @@ function LHNOptionsList({ ], ); + if (isSmallScreenWidth && data.length === 0) { + return ( + + ); + } + return ( - - - ) : null - } /> ); diff --git a/src/styles/index.ts b/src/styles/index.ts index 3bce9d90c8ea..a0de88de3cf3 100644 --- a/src/styles/index.ts +++ b/src/styles/index.ts @@ -3171,9 +3171,10 @@ const styles = (theme: ThemeColors) => height: 180, }, - emptyLHNBlockingView: { - marginTop: 120, - }, + emptyLHNAnimationWeb: (isExtraSmallScreenHeight: boolean) => ({ + width: isExtraSmallScreenHeight ? 160 : 180, + height: isExtraSmallScreenHeight ? 160 : 180, + }), locationErrorLinkText: { textAlignVertical: 'center', From 0dbec9ef30fdf08d2af0fa10b48a1f1130be58da Mon Sep 17 00:00:00 2001 From: tienifr Date: Wed, 28 Feb 2024 14:57:01 +0700 Subject: [PATCH 160/500] fix nested teneray operation --- src/components/BlockingViews/BlockingView.tsx | 28 +++++++++++-------- .../LHNOptionsList/LHNOptionsList.tsx | 2 +- 2 files changed, 17 insertions(+), 13 deletions(-) diff --git a/src/components/BlockingViews/BlockingView.tsx b/src/components/BlockingViews/BlockingView.tsx index afdc2d547d07..05ffbd6a0d58 100644 --- a/src/components/BlockingViews/BlockingView.tsx +++ b/src/components/BlockingViews/BlockingView.tsx @@ -1,6 +1,5 @@ import React from 'react'; -import type {CSSProperties} from 'react'; -import type {ImageSourcePropType, StyleProp, ViewStyle} from 'react-native'; +import type {ImageSourcePropType, StyleProp, ViewStyle, WebStyle} from 'react-native'; import {View} from 'react-native'; import type {SvgProps} from 'react-native-svg'; import AutoEmailLink from '@components/AutoEmailLink'; @@ -68,10 +67,10 @@ type BlockingViewProps = RequiredIllustrationProps & { /** Style for the animation */ animationStyles?: StyleProp; - animationWebStyle?: CSSProperties; + animationWebStyle?: WebStyle; /** Render custom subtitle */ - renderSubtitle?: () => React.ReactElement; + renderCustomSubtitle?: () => React.ReactElement; }; function BlockingView({ @@ -88,7 +87,7 @@ function BlockingView({ shouldEmbedLinkWithSubtitle = false, animationStyles = [], animationWebStyle = {}, - renderSubtitle, + renderCustomSubtitle, }: BlockingViewProps) { const styles = useThemeStyles(); const {translate} = useLocalize(); @@ -111,6 +110,17 @@ function BlockingView({ ); } + function renderSubtitle() { + if (renderCustomSubtitle) { + return renderCustomSubtitle(); + } + return shouldEmbedLinkWithSubtitle ? ( + {renderContent()} + ) : ( + {renderContent()} + ); + } + return ( {animation && ( @@ -133,13 +143,7 @@ function BlockingView({ {title} - {renderSubtitle ? ( - renderSubtitle() - ) : shouldEmbedLinkWithSubtitle ? ( - {renderContent()} - ) : ( - {renderContent()} - )} + {renderSubtitle()} ); diff --git a/src/components/LHNOptionsList/LHNOptionsList.tsx b/src/components/LHNOptionsList/LHNOptionsList.tsx index b032d5f0ccd5..40c79c45946c 100644 --- a/src/components/LHNOptionsList/LHNOptionsList.tsx +++ b/src/components/LHNOptionsList/LHNOptionsList.tsx @@ -165,7 +165,7 @@ function LHNOptionsList({ animationWebStyle={styles.emptyLHNAnimationWeb(isExtraSmallScreenHeight)} title={translate('common.emptyLHN.title')} shouldShowLink={false} - renderSubtitle={renderEmptyStateSubtitle} + renderCustomSubtitle={renderEmptyStateSubtitle} /> ); } From d783a3c690a3d513ae768b91992413ff5d2443a5 Mon Sep 17 00:00:00 2001 From: Etotaziba Olei Date: Wed, 28 Feb 2024 09:56:50 +0100 Subject: [PATCH 161/500] add isCurrencyPressable prop --- src/pages/iou/steps/MoneyRequestAmountForm.tsx | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/pages/iou/steps/MoneyRequestAmountForm.tsx b/src/pages/iou/steps/MoneyRequestAmountForm.tsx index cb1f73ae2207..5a5541239f42 100644 --- a/src/pages/iou/steps/MoneyRequestAmountForm.tsx +++ b/src/pages/iou/steps/MoneyRequestAmountForm.tsx @@ -33,6 +33,9 @@ type MoneyRequestAmountFormProps = { /** Whether the amount is being edited or not */ isEditing?: boolean; + /** Whether the currency symbol is pressable */ + isCurrencyPressable?: boolean; + /** Fired when back button pressed, navigates to currency selection page */ onCurrencyButtonPress: () => void; @@ -69,6 +72,7 @@ function MoneyRequestAmountForm( amount = 0, taxAmount = 0, currency = CONST.CURRENCY.USD, + isCurrencyPressable = true, isEditing = false, onCurrencyButtonPress, onSubmitButtonPress, @@ -300,7 +304,7 @@ function MoneyRequestAmountForm( setSelection({start, end}); }} onKeyPress={textInputKeyPress} - isCurrencyPressable + isCurrencyPressable={isCurrencyPressable} /> {!!formError && ( Date: Wed, 28 Feb 2024 09:57:27 +0100 Subject: [PATCH 162/500] use isCurrencyPressable prop to hide currency dropdown --- src/pages/EditRequestTaxAmountPage.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/pages/EditRequestTaxAmountPage.tsx b/src/pages/EditRequestTaxAmountPage.tsx index 22ce8a4de2df..ed7f14b9091e 100644 --- a/src/pages/EditRequestTaxAmountPage.tsx +++ b/src/pages/EditRequestTaxAmountPage.tsx @@ -56,6 +56,7 @@ function EditRequestTaxAmountPage({defaultAmount, defaultTaxAmount, defaultCurre amount={defaultAmount} taxAmount={defaultTaxAmount} ref={textInput} + isCurrencyPressable={false} onCurrencyButtonPress={onNavigateToCurrency} onSubmitButtonPress={onSubmit} isEditing From bddbd10fe7e4b15e5e7bd5455f90eda25c1720a9 Mon Sep 17 00:00:00 2001 From: Etotaziba Olei Date: Wed, 28 Feb 2024 09:58:56 +0100 Subject: [PATCH 163/500] make onCurrencyButtonPress optional --- src/pages/iou/steps/MoneyRequestAmountForm.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/iou/steps/MoneyRequestAmountForm.tsx b/src/pages/iou/steps/MoneyRequestAmountForm.tsx index 5a5541239f42..e073fdefae20 100644 --- a/src/pages/iou/steps/MoneyRequestAmountForm.tsx +++ b/src/pages/iou/steps/MoneyRequestAmountForm.tsx @@ -37,7 +37,7 @@ type MoneyRequestAmountFormProps = { isCurrencyPressable?: boolean; /** Fired when back button pressed, navigates to currency selection page */ - onCurrencyButtonPress: () => void; + onCurrencyButtonPress?: () => void; /** Fired when submit button pressed, saves the given amount and navigates to the next page */ onSubmitButtonPress: ({amount, currency}: {amount: string; currency: string}) => void; From c3c4a075bf0f9ccbc6dcfbe70cab1dfb3fd427bc Mon Sep 17 00:00:00 2001 From: Etotaziba Olei Date: Wed, 28 Feb 2024 10:01:14 +0100 Subject: [PATCH 164/500] remove onCurrencyButtonPress and props values --- src/pages/EditRequestTaxAmountPage.tsx | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/pages/EditRequestTaxAmountPage.tsx b/src/pages/EditRequestTaxAmountPage.tsx index ed7f14b9091e..a34ed8a5252d 100644 --- a/src/pages/EditRequestTaxAmountPage.tsx +++ b/src/pages/EditRequestTaxAmountPage.tsx @@ -20,12 +20,9 @@ type EditRequestTaxAmountPageProps = { /** Callback to fire when the Save button is pressed */ onSubmit: () => void; - - /** Callback to fire when we press on the currency */ - onNavigateToCurrency: () => void; }; -function EditRequestTaxAmountPage({defaultAmount, defaultTaxAmount, defaultCurrency, onNavigateToCurrency, onSubmit}: EditRequestTaxAmountPageProps) { +function EditRequestTaxAmountPage({defaultAmount, defaultTaxAmount, defaultCurrency, onSubmit}: EditRequestTaxAmountPageProps) { const {translate} = useLocalize(); const textInput = useRef(null); @@ -57,7 +54,6 @@ function EditRequestTaxAmountPage({defaultAmount, defaultTaxAmount, defaultCurre taxAmount={defaultTaxAmount} ref={textInput} isCurrencyPressable={false} - onCurrencyButtonPress={onNavigateToCurrency} onSubmitButtonPress={onSubmit} isEditing /> From 687d846c1d9815bd695895bde143a2c98c73be6e Mon Sep 17 00:00:00 2001 From: Etotaziba Olei Date: Wed, 28 Feb 2024 10:02:48 +0100 Subject: [PATCH 165/500] remove onNavigateToCurrency to Props --- src/pages/EditRequestPage.js | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/pages/EditRequestPage.js b/src/pages/EditRequestPage.js index 64fb4028516c..638b3711dc2e 100644 --- a/src/pages/EditRequestPage.js +++ b/src/pages/EditRequestPage.js @@ -206,10 +206,6 @@ function EditRequestPage({report, route, policy, policyCategories, policyTags, p defaultAmount={transactionTaxAmount} defaultTaxAmount={getTaxAmount(transactionAmount, transactionTaxCode, taxRates)} defaultCurrency={defaultCurrency} - onNavigateToCurrency={() => { - const activeRoute = encodeURIComponent(Navigation.getActiveRouteWithoutParams()); - Navigation.navigate(ROUTES.EDIT_CURRENCY_REQUEST.getRoute(report.reportID, defaultCurrency, activeRoute)); - }} onSubmit={updateTaxAmount} /> ); From d824c5a0f93facf39c70dc3ac925cfca2bc23310 Mon Sep 17 00:00:00 2001 From: tienifr Date: Wed, 28 Feb 2024 16:25:36 +0700 Subject: [PATCH 166/500] remove storybook warning --- .storybook/webpack.config.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.storybook/webpack.config.js b/.storybook/webpack.config.js index 204f70344b18..89f7699465bf 100644 --- a/.storybook/webpack.config.js +++ b/.storybook/webpack.config.js @@ -37,6 +37,12 @@ module.exports = ({config}) => { config.plugins[definePluginIndex].definitions.__REACT_WEB_CONFIG__ = JSON.stringify(env); config.resolve.extensions = custom.resolve.extensions; + config.stats = { + // We can ignore the "module not installed" warning from lottie-react-native + // because we are not using the library for JSON format of Lottie animations. + warningsFilter: ['../node_modules/lottie-react-native/lib/module/LottieView/index.web.js'], + }; + const babelRulesIndex = _.findIndex(custom.module.rules, (rule) => rule.loader === 'babel-loader'); const babelRule = custom.module.rules[babelRulesIndex]; config.module.rules.push(babelRule); From 61e28f7811003c2b33d78b323e6553e589292320 Mon Sep 17 00:00:00 2001 From: Etotaziba Olei Date: Wed, 28 Feb 2024 11:02:03 +0100 Subject: [PATCH 167/500] update Tax Amount Params --- src/libs/TransactionUtils.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/libs/TransactionUtils.ts b/src/libs/TransactionUtils.ts index 55bb591e3e6a..f8976d1ca450 100644 --- a/src/libs/TransactionUtils.ts +++ b/src/libs/TransactionUtils.ts @@ -306,14 +306,14 @@ function getAmount(transaction: OnyxEntry, isFromExpenseReport = fa /** * Return the tax amount field from the transaction. */ -function getTaxAmount(transaction: OnyxEntry, isFromExpenseReport: boolean): number { +function getTaxAmount(taxAmount: number, isFromExpenseReport: boolean): number { // IOU requests cannot have negative values but they can be stored as negative values, let's return absolute value if (!isFromExpenseReport) { - return Math.abs(transaction?.taxAmount ?? 0); + return Math.abs(taxAmount ?? 0); } // To avoid -0 being shown, lets only change the sign if the value is other than 0. - const amount = transaction?.taxAmount ?? 0; + const amount = taxAmount ?? 0; return amount ? -amount : 0; } From 64408cabc219f5d33db258a3e39163f9c13d4db5 Mon Sep 17 00:00:00 2001 From: Etotaziba Olei Date: Wed, 28 Feb 2024 11:02:35 +0100 Subject: [PATCH 168/500] update getTaxAmount args... --- src/libs/ReportUtils.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index 63d1dd019db2..2a5c2e868c7c 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -2066,7 +2066,7 @@ function getTransactionDetails(transaction: OnyxEntry, createdDateF return { created: TransactionUtils.getCreated(transaction, createdDateFormat), amount: TransactionUtils.getAmount(transaction, !isEmptyObject(report) && isExpenseReport(report)), - taxAmount: TransactionUtils.getTaxAmount(transaction, !isEmptyObject(report) && isExpenseReport(report)), + taxAmount: TransactionUtils.getTaxAmount(transaction?.taxAmount ?? 0, !isEmptyObject(report) && isExpenseReport(report)), taxCode: TransactionUtils.getTaxCode(transaction), currency: TransactionUtils.getCurrency(transaction), comment: TransactionUtils.getDescription(transaction), From 211308e7fc552cf686f2c370d1f4e569f3a37dac Mon Sep 17 00:00:00 2001 From: Etotaziba Olei Date: Wed, 28 Feb 2024 11:03:05 +0100 Subject: [PATCH 169/500] remove duplicate getTaxAmount --- src/libs/ModifiedExpenseMessage.ts | 18 ++---------------- 1 file changed, 2 insertions(+), 16 deletions(-) diff --git a/src/libs/ModifiedExpenseMessage.ts b/src/libs/ModifiedExpenseMessage.ts index a6491d3025fc..2855344899f0 100644 --- a/src/libs/ModifiedExpenseMessage.ts +++ b/src/libs/ModifiedExpenseMessage.ts @@ -98,20 +98,6 @@ function getForDistanceRequest(newDistance: string, oldDistance: string, newAmou }); } -/** - * Return the tax amount field from the transaction. - */ -function getTaxAmount(taxAmount: number, isFromExpenseReport: boolean): number { - // IOU requests cannot have negative values but they can be stored as negative values, let's return absolute value - if (!isFromExpenseReport) { - return Math.abs(taxAmount ?? 0); - } - - // To avoid -0 being shown, lets only change the sign if the value is other than 0. - const amount = taxAmount ?? 0; - return amount ? -amount : 0; -} - /** * Get the report action message when expense has been modified. * @@ -244,8 +230,8 @@ function getForReportAction(reportID: string | undefined, reportAction: OnyxEntr const isFromExpenseReport = ReportUtils.isExpenseReport(iouReport); const currency = iouReport?.currency ?? ''; - const taxAmount = CurrencyUtils.convertToDisplayString(getTaxAmount(reportActionOriginalMessage?.taxAmount ?? 0, isFromExpenseReport), currency); - const oldTaxAmountValue = getTaxAmount(reportActionOriginalMessage?.oldTaxAmount ?? 0, isFromExpenseReport); + const taxAmount = CurrencyUtils.convertToDisplayString(TransactionUtils.getTaxAmount(reportActionOriginalMessage?.taxAmount ?? 0, isFromExpenseReport), currency); + const oldTaxAmountValue = TransactionUtils.getTaxAmount(reportActionOriginalMessage?.oldTaxAmount ?? 0, isFromExpenseReport); const oldTaxAmount = oldTaxAmountValue > 0 ? CurrencyUtils.convertToDisplayString(oldTaxAmountValue, currency) : ''; buildMessageFragmentForValue(taxAmount, oldTaxAmount, Localize.translateLocal('iou.taxAmount'), false, setFragments, removalFragments, changeFragments); From 7b153084d3214db17b0067e5797552eadacc10af Mon Sep 17 00:00:00 2001 From: Yogesh Bhatt <65986357+ygshbht@users.noreply.github.com> Date: Fri, 1 Mar 2024 12:03:54 +0530 Subject: [PATCH 170/500] Incorrect header space fix: src/pages/settings/Profile/PersonalDetails/StateSelectionPage.tsx Co-authored-by: Rajat --- .../settings/Profile/PersonalDetails/StateSelectionPage.tsx | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/pages/settings/Profile/PersonalDetails/StateSelectionPage.tsx b/src/pages/settings/Profile/PersonalDetails/StateSelectionPage.tsx index 804d3de6bf13..05746405adf7 100644 --- a/src/pages/settings/Profile/PersonalDetails/StateSelectionPage.tsx +++ b/src/pages/settings/Profile/PersonalDetails/StateSelectionPage.tsx @@ -86,8 +86,6 @@ function StateSelectionPage() { Date: Fri, 1 Mar 2024 12:07:11 +0530 Subject: [PATCH 171/500] Remove unused code StateSelectionPage.tsx --- .../settings/Profile/PersonalDetails/StateSelectionPage.tsx | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/pages/settings/Profile/PersonalDetails/StateSelectionPage.tsx b/src/pages/settings/Profile/PersonalDetails/StateSelectionPage.tsx index 05746405adf7..b30c9c9967c8 100644 --- a/src/pages/settings/Profile/PersonalDetails/StateSelectionPage.tsx +++ b/src/pages/settings/Profile/PersonalDetails/StateSelectionPage.tsx @@ -7,7 +7,6 @@ import ScreenWrapper from '@components/ScreenWrapper'; import SelectionList from '@components/SelectionList'; import RadioListItem from '@components/SelectionList/RadioListItem'; import useLocalize from '@hooks/useLocalize'; -import useThemeStyles from '@hooks/useThemeStyles'; import Navigation from '@libs/Navigation/Navigation'; import searchCountryOptions from '@libs/searchCountryOptions'; import type {CountryData} from '@libs/searchCountryOptions'; @@ -26,7 +25,6 @@ type RouteParams = { function StateSelectionPage() { const route = useRoute(); const navigation = useNavigation(); - const styles = useThemeStyles(); const {translate} = useLocalize(); const [searchValue, setSearchValue] = useState(''); From 9dd9dad0f9411b07e0943eb216feb0e537c91afc Mon Sep 17 00:00:00 2001 From: Etotaziba Olei Date: Mon, 4 Mar 2024 11:10:00 +0100 Subject: [PATCH 172/500] remove ts-expect-error --- src/pages/EditRequestTaxRatePage.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/pages/EditRequestTaxRatePage.tsx b/src/pages/EditRequestTaxRatePage.tsx index 1c1dc8219ae2..099851e92209 100644 --- a/src/pages/EditRequestTaxRatePage.tsx +++ b/src/pages/EditRequestTaxRatePage.tsx @@ -28,7 +28,6 @@ function EditRequestTaxRatePage({defaultTaxRate, policyID, onSubmit}: EditReques <> Date: Mon, 4 Mar 2024 11:29:19 +0100 Subject: [PATCH 173/500] fix tax picker types --- src/components/TaxPicker.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/components/TaxPicker.tsx b/src/components/TaxPicker.tsx index 50e534dc9f6f..cfccb7604044 100644 --- a/src/components/TaxPicker.tsx +++ b/src/components/TaxPicker.tsx @@ -2,8 +2,6 @@ import React, {useMemo, useState} from 'react'; import {withOnyx} from 'react-native-onyx'; import type {OnyxEntry} from 'react-native-onyx'; import type {EdgeInsets} from 'react-native-safe-area-context'; -import _ from 'underscore'; -import OptionsSelector from '@components/OptionsSelector'; import useLocalize from '@hooks/useLocalize'; import useStyleUtils from '@hooks/useStyleUtils'; import useThemeStyles from '@hooks/useThemeStyles'; @@ -12,6 +10,7 @@ import * as TransactionUtils from '@libs/TransactionUtils'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import type {Policy} from '@src/types/onyx'; +import OptionsSelector from './OptionsSelector'; type TaxPickerOnyxProps = { /** The policy which the user has access to and which the report is tied to */ @@ -23,7 +22,8 @@ type TaxPickerProps = TaxPickerOnyxProps & { selectedTaxRate?: string; /** ID of the policy */ - policyID: string; + // eslint-disable-next-line react/no-unused-prop-types + policyID?: string; /** * Safe area insets required for reflecting the portion of the view, From 2197773a853b801bdabd3cccbb3d3ada812b629d Mon Sep 17 00:00:00 2001 From: tienifr Date: Mon, 4 Mar 2024 22:15:19 +0700 Subject: [PATCH 174/500] middle align the ui --- .../LHNOptionsList/LHNOptionsList.tsx | 51 +++++++++---------- src/libs/SidebarUtils.ts | 8 --- src/styles/index.ts | 4 ++ 3 files changed, 29 insertions(+), 34 deletions(-) diff --git a/src/components/LHNOptionsList/LHNOptionsList.tsx b/src/components/LHNOptionsList/LHNOptionsList.tsx index 40c79c45946c..37354571c2ae 100644 --- a/src/components/LHNOptionsList/LHNOptionsList.tsx +++ b/src/components/LHNOptionsList/LHNOptionsList.tsx @@ -46,6 +46,7 @@ function LHNOptionsList({ const {canUseViolations} = usePermissions(); const {translate} = useLocalize(); const {isExtraSmallScreenHeight, isSmallScreenWidth} = useWindowDimensions(); + const shouldShowEmptyUI = isSmallScreenWidth && data.length === 0; // When the first item renders we want to call the onFirstItemRendered callback. // At this point in time we know that the list is actually displaying items. @@ -157,33 +158,31 @@ function LHNOptionsList({ ], ); - if (isSmallScreenWidth && data.length === 0) { - return ( - - ); - } - return ( - - + + {shouldShowEmptyUI ? ( + + ) : ( + + )} ); } diff --git a/src/libs/SidebarUtils.ts b/src/libs/SidebarUtils.ts index a9cbefddec94..9f6767406c36 100644 --- a/src/libs/SidebarUtils.ts +++ b/src/libs/SidebarUtils.ts @@ -96,14 +96,6 @@ function getOrderedReportIDs( }); }); - if (reportsToDisplay.length === 0) { - // Display Concierge chat report when there is no report to be displayed - const conciergeChatReport = allReportsDictValues.find(ReportUtils.isConciergeChatReport); - if (conciergeChatReport) { - reportsToDisplay.push(conciergeChatReport); - } - } - // The LHN is split into four distinct groups, and each group is sorted a little differently. The groups will ALWAYS be in this order: // 1. Pinned/GBR - Always sorted by reportDisplayName // 2. Drafts - Always sorted by reportDisplayName diff --git a/src/styles/index.ts b/src/styles/index.ts index 5a6266759331..68f9772c6158 100644 --- a/src/styles/index.ts +++ b/src/styles/index.ts @@ -3186,6 +3186,10 @@ const styles = (theme: ThemeColors) => alignItems: 'center', }, + emptyLHNWrapper: { + marginBottom: variables.bottomTabHeight, + }, + emptyLHNAnimation: { width: 180, height: 180, From 84010186e9196a3a4c0fe119fb54dca1f72761f4 Mon Sep 17 00:00:00 2001 From: Etotaziba Olei Date: Wed, 6 Mar 2024 10:29:55 +0100 Subject: [PATCH 175/500] add useCallback for updateTaxAmount and updateTaxRate --- src/pages/EditRequestPage.js | 28 +++++++++++++++++----------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/src/pages/EditRequestPage.js b/src/pages/EditRequestPage.js index 638b3711dc2e..b428e3eaf820 100644 --- a/src/pages/EditRequestPage.js +++ b/src/pages/EditRequestPage.js @@ -125,17 +125,23 @@ function EditRequestPage({report, route, policy, policyCategories, policyTags, p }); }, [parentReportAction, fieldToEdit]); - const updateTaxAmount = (transactionChanges) => { - const newTaxAmount = CurrencyUtils.convertToBackendAmount(Number.parseFloat(transactionChanges.amount)); - IOU.updateMoneyRequestTaxAmount(transaction.transactionID, report.reportID, newTaxAmount, policy, policyTags, policyCategories); - Navigation.dismissModal(report.reportID); - }; - - const updateTaxRate = (transactionChanges) => { - const newTaxCode = transactionChanges.data.code; - IOU.updateMoneyRequestTaxRate(transaction.transactionID, report.reportID, newTaxCode, policy, policyTags, policyCategories); - Navigation.dismissModal(report.reportID); - }; + const updateTaxAmount = useCallback( + (transactionChanges) => { + const newTaxAmount = CurrencyUtils.convertToBackendAmount(Number.parseFloat(transactionChanges.amount)); + IOU.updateMoneyRequestTaxAmount(transaction.transactionID, report.reportID, newTaxAmount, policy, policyTags, policyCategories); + Navigation.dismissModal(report.reportID); + }, + [transaction, report, policy, policyTags, policyCategories], + ); + + const updateTaxRate = useCallback( + (transactionChanges) => { + const newTaxCode = transactionChanges.data.code; + IOU.updateMoneyRequestTaxRate(transaction.transactionID, report.reportID, newTaxCode, policy, policyTags, policyCategories); + Navigation.dismissModal(report.reportID); + }, + [transaction, report, policy, policyTags, policyCategories], + ); const saveAmountAndCurrency = useCallback( ({amount, currency: newCurrency}) => { From 484c8ca9944314e0b7c220646d171ba935d8b1a9 Mon Sep 17 00:00:00 2001 From: Etotaziba Olei Date: Wed, 6 Mar 2024 14:47:34 +0100 Subject: [PATCH 176/500] add tax types to OriginalMessageModifiedExpense --- src/types/onyx/OriginalMessage.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/types/onyx/OriginalMessage.ts b/src/types/onyx/OriginalMessage.ts index 06c2d2e6abce..eaab2ee6f1ba 100644 --- a/src/types/onyx/OriginalMessage.ts +++ b/src/types/onyx/OriginalMessage.ts @@ -251,6 +251,10 @@ type OriginalMessageModifiedExpense = { category?: string; oldTag?: string; tag?: string; + oldTaxAmount?: number; + taxAmount?: number; + oldTaxRate?: string; + taxRate?: string; oldBillable?: string; billable?: string; }; From e7e62a8f6762027ae217246e144318ed6540cfcd Mon Sep 17 00:00:00 2001 From: Etotaziba Olei Date: Wed, 6 Mar 2024 14:48:19 +0100 Subject: [PATCH 177/500] update getTaxAmount func --- src/libs/TransactionUtils.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/libs/TransactionUtils.ts b/src/libs/TransactionUtils.ts index f8976d1ca450..55bb591e3e6a 100644 --- a/src/libs/TransactionUtils.ts +++ b/src/libs/TransactionUtils.ts @@ -306,14 +306,14 @@ function getAmount(transaction: OnyxEntry, isFromExpenseReport = fa /** * Return the tax amount field from the transaction. */ -function getTaxAmount(taxAmount: number, isFromExpenseReport: boolean): number { +function getTaxAmount(transaction: OnyxEntry, isFromExpenseReport: boolean): number { // IOU requests cannot have negative values but they can be stored as negative values, let's return absolute value if (!isFromExpenseReport) { - return Math.abs(taxAmount ?? 0); + return Math.abs(transaction?.taxAmount ?? 0); } // To avoid -0 being shown, lets only change the sign if the value is other than 0. - const amount = taxAmount ?? 0; + const amount = transaction?.taxAmount ?? 0; return amount ? -amount : 0; } From 450f849975995ef04c1cec48ccaf0100870f2042 Mon Sep 17 00:00:00 2001 From: Etotaziba Olei Date: Wed, 6 Mar 2024 14:49:19 +0100 Subject: [PATCH 178/500] add optimistic data for tax amount and code --- src/libs/ReportUtils.ts | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index a82e1fe6b941..77ae89f11e54 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -2097,7 +2097,7 @@ function getTransactionDetails(transaction: OnyxEntry, createdDateF return { created: TransactionUtils.getCreated(transaction, createdDateFormat), amount: TransactionUtils.getAmount(transaction, !isEmptyObject(report) && isExpenseReport(report)), - taxAmount: TransactionUtils.getTaxAmount(transaction?.taxAmount ?? 0, !isEmptyObject(report) && isExpenseReport(report)), + taxAmount: TransactionUtils.getTaxAmount(transaction, !isEmptyObject(report) && isExpenseReport(report)), taxCode: TransactionUtils.getTaxCode(transaction), currency: TransactionUtils.getCurrency(transaction), comment: TransactionUtils.getDescription(transaction), @@ -2474,6 +2474,16 @@ function getModifiedExpenseOriginalMessage(oldTransaction: OnyxEntry Date: Wed, 6 Mar 2024 14:49:45 +0100 Subject: [PATCH 179/500] update modified tax amount --- src/libs/ModifiedExpenseMessage.ts | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/libs/ModifiedExpenseMessage.ts b/src/libs/ModifiedExpenseMessage.ts index 2855344899f0..fad823a8fded 100644 --- a/src/libs/ModifiedExpenseMessage.ts +++ b/src/libs/ModifiedExpenseMessage.ts @@ -62,6 +62,14 @@ function buildMessageFragmentForValue( } } +/** + * Get the absolute value for a tax amount. + */ +function getTaxAmountAbsValue(taxAmount: number): number { + // IOU requests cannot have negative values but they can be stored as negative values, let's return absolute value + return Math.abs(taxAmount ?? 0); +} + /** * Get the message line for a modified expense. */ @@ -227,13 +235,11 @@ function getForReportAction(reportID: string | undefined, reportAction: OnyxEntr if (hasModifiedTaxAmount) { const transactionThread = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${reportID}`] ?? null; const iouReport = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${transactionThread?.parentReportID}`] ?? null; - const isFromExpenseReport = ReportUtils.isExpenseReport(iouReport); const currency = iouReport?.currency ?? ''; - const taxAmount = CurrencyUtils.convertToDisplayString(TransactionUtils.getTaxAmount(reportActionOriginalMessage?.taxAmount ?? 0, isFromExpenseReport), currency); - const oldTaxAmountValue = TransactionUtils.getTaxAmount(reportActionOriginalMessage?.oldTaxAmount ?? 0, isFromExpenseReport); + const taxAmount = CurrencyUtils.convertToDisplayString(getTaxAmountAbsValue(reportActionOriginalMessage?.taxAmount ?? 0), currency); + const oldTaxAmountValue = getTaxAmountAbsValue(reportActionOriginalMessage?.oldTaxAmount ?? 0); const oldTaxAmount = oldTaxAmountValue > 0 ? CurrencyUtils.convertToDisplayString(oldTaxAmountValue, currency) : ''; - buildMessageFragmentForValue(taxAmount, oldTaxAmount, Localize.translateLocal('iou.taxAmount'), false, setFragments, removalFragments, changeFragments); } From 452e0e06f8fa7b91372192e0a5bc7307f50e709d Mon Sep 17 00:00:00 2001 From: Etotaziba Olei Date: Wed, 6 Mar 2024 14:51:16 +0100 Subject: [PATCH 180/500] fix prettier --- src/libs/ReportUtils.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index 77ae89f11e54..9422b20beb02 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -2474,9 +2474,9 @@ function getModifiedExpenseOriginalMessage(oldTransaction: OnyxEntry Date: Wed, 6 Mar 2024 15:23:42 +0100 Subject: [PATCH 181/500] select tax value and pass to original message --- src/libs/ReportUtils.ts | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index 9422b20beb02..a74ef227deb7 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -31,6 +31,7 @@ import type { Transaction, TransactionViolation, } from '@src/types/onyx'; +import type * as OnyxTypes from '@src/types/onyx'; import type {Participant} from '@src/types/onyx/IOU'; import type {Errors, Icon, PendingAction} from '@src/types/onyx/OnyxCommon'; import type { @@ -2438,7 +2439,12 @@ function getReportPreviewMessage( * * At the moment, we only allow changing one transaction field at a time. */ -function getModifiedExpenseOriginalMessage(oldTransaction: OnyxEntry, transactionChanges: TransactionChanges, isFromExpenseReport: boolean): ExpenseOriginalMessage { +function getModifiedExpenseOriginalMessage( + oldTransaction: OnyxEntry, + transactionChanges: TransactionChanges, + isFromExpenseReport: boolean, + policy: OnyxEntry, +): ExpenseOriginalMessage { const originalMessage: ExpenseOriginalMessage = {}; // Remark: Comment field is the only one which has new/old prefixes for the keys (newComment/ oldComment), // all others have old/- pattern such as oldCreated/created @@ -2480,8 +2486,9 @@ function getModifiedExpenseOriginalMessage(oldTransaction: OnyxEntry, transactionChanges: TransactionChanges, isFromExpenseReport: boolean, + policy: OnyxEntry, ): OptimisticModifiedExpenseReportAction { - const originalMessage = getModifiedExpenseOriginalMessage(oldTransaction, transactionChanges, isFromExpenseReport); + const originalMessage = getModifiedExpenseOriginalMessage(oldTransaction, transactionChanges, isFromExpenseReport, policy); return { actionName: CONST.REPORT.ACTIONS.TYPE.MODIFIEDEXPENSE, actorAccountID: currentUserAccountID, From f1e1f92fcbfbb695572a8f79b9190f9a37d770d4 Mon Sep 17 00:00:00 2001 From: Etotaziba Olei Date: Wed, 6 Mar 2024 15:24:10 +0100 Subject: [PATCH 182/500] pass polciy props --- src/libs/actions/IOU.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libs/actions/IOU.ts b/src/libs/actions/IOU.ts index 86b116974091..1161b1e61b65 100644 --- a/src/libs/actions/IOU.ts +++ b/src/libs/actions/IOU.ts @@ -1200,7 +1200,7 @@ function getUpdateMoneyRequestParams( // We don't create a modified report action if we're updating the waypoints, // since there isn't actually any optimistic data we can create for them and the report action is created on the server // with the response from the MapBox API - const updatedReportAction = ReportUtils.buildOptimisticModifiedExpenseReportAction(transactionThread, transaction, transactionChanges, isFromExpenseReport); + const updatedReportAction = ReportUtils.buildOptimisticModifiedExpenseReportAction(transactionThread, transaction, transactionChanges, isFromExpenseReport, policy); if (!hasPendingWaypoints) { params.reportActionID = updatedReportAction.reportActionID; @@ -2664,7 +2664,7 @@ function editRegularMoneyRequest( const isFromExpenseReport = ReportUtils.isExpenseReport(iouReport); // STEP 2: Build new modified expense report action. - const updatedReportAction = ReportUtils.buildOptimisticModifiedExpenseReportAction(transactionThread, transaction, transactionChanges, isFromExpenseReport); + const updatedReportAction = ReportUtils.buildOptimisticModifiedExpenseReportAction(transactionThread, transaction, transactionChanges, isFromExpenseReport, policy); const updatedTransaction = transaction ? TransactionUtils.getUpdatedTransaction(transaction, transactionChanges, isFromExpenseReport) : null; // STEP 3: Compute the IOU total and update the report preview message so LHN amount owed is correct From 89358cb5d9bb3f3b86b06c32b052ebb18f4bd606 Mon Sep 17 00:00:00 2001 From: Etotaziba Olei Date: Wed, 6 Mar 2024 15:40:10 +0100 Subject: [PATCH 183/500] fix lint --- src/libs/ReportUtils.ts | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index a74ef227deb7..2d8bc354c825 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -31,7 +31,6 @@ import type { Transaction, TransactionViolation, } from '@src/types/onyx'; -import type * as OnyxTypes from '@src/types/onyx'; import type {Participant} from '@src/types/onyx/IOU'; import type {Errors, Icon, PendingAction} from '@src/types/onyx/OnyxCommon'; import type { @@ -2443,7 +2442,7 @@ function getModifiedExpenseOriginalMessage( oldTransaction: OnyxEntry, transactionChanges: TransactionChanges, isFromExpenseReport: boolean, - policy: OnyxEntry, + policy: OnyxEntry, ): ExpenseOriginalMessage { const originalMessage: ExpenseOriginalMessage = {}; // Remark: Comment field is the only one which has new/old prefixes for the keys (newComment/ oldComment), @@ -2486,9 +2485,8 @@ function getModifiedExpenseOriginalMessage( } if ('taxCode' in transactionChanges) { - const taxRates = policy?.taxRates?.taxes; - originalMessage.oldTaxRate = taxRates && taxRates[TransactionUtils.getTaxCode(oldTransaction)].value; - originalMessage.taxRate = taxRates && transactionChanges?.taxCode && taxRates[transactionChanges.taxCode].value; + originalMessage.oldTaxRate = policy?.taxRates?.taxes[TransactionUtils.getTaxCode(oldTransaction)].value; + originalMessage.taxRate = transactionChanges?.taxCode && policy?.taxRates?.taxes[transactionChanges?.taxCode].value; } if ('billable' in transactionChanges) { @@ -3332,7 +3330,7 @@ function buildOptimisticModifiedExpenseReportAction( oldTransaction: OnyxEntry, transactionChanges: TransactionChanges, isFromExpenseReport: boolean, - policy: OnyxEntry, + policy: OnyxEntry, ): OptimisticModifiedExpenseReportAction { const originalMessage = getModifiedExpenseOriginalMessage(oldTransaction, transactionChanges, isFromExpenseReport, policy); return { From 9e55810c4787ff1afd70655a835cf62feb7f1f34 Mon Sep 17 00:00:00 2001 From: tienifr Date: Thu, 7 Mar 2024 18:19:12 +0700 Subject: [PATCH 184/500] resolve feedbacks --- .storybook/webpack.config.js | 6 ------ src/components/LHNOptionsList/LHNOptionsList.tsx | 10 +++++----- 2 files changed, 5 insertions(+), 11 deletions(-) diff --git a/.storybook/webpack.config.js b/.storybook/webpack.config.js index 89f7699465bf..204f70344b18 100644 --- a/.storybook/webpack.config.js +++ b/.storybook/webpack.config.js @@ -37,12 +37,6 @@ module.exports = ({config}) => { config.plugins[definePluginIndex].definitions.__REACT_WEB_CONFIG__ = JSON.stringify(env); config.resolve.extensions = custom.resolve.extensions; - config.stats = { - // We can ignore the "module not installed" warning from lottie-react-native - // because we are not using the library for JSON format of Lottie animations. - warningsFilter: ['../node_modules/lottie-react-native/lib/module/LottieView/index.web.js'], - }; - const babelRulesIndex = _.findIndex(custom.module.rules, (rule) => rule.loader === 'babel-loader'); const babelRule = custom.module.rules[babelRulesIndex]; config.module.rules.push(babelRule); diff --git a/src/components/LHNOptionsList/LHNOptionsList.tsx b/src/components/LHNOptionsList/LHNOptionsList.tsx index 17dc4f4bfb6a..df8b1e23f0d4 100644 --- a/src/components/LHNOptionsList/LHNOptionsList.tsx +++ b/src/components/LHNOptionsList/LHNOptionsList.tsx @@ -47,7 +47,7 @@ function LHNOptionsList({ const {canUseViolations} = usePermissions(); const {translate} = useLocalize(); const {isExtraSmallScreenHeight, isSmallScreenWidth} = useWindowDimensions(); - const shouldShowEmptyUI = isSmallScreenWidth && data.length === 0; + const shouldShowEmptyLHN = isSmallScreenWidth && data.length === 0; // When the first item renders we want to call the onFirstItemRendered callback. // At this point in time we know that the list is actually displaying items. @@ -61,7 +61,7 @@ function LHNOptionsList({ onFirstItemRendered(); }, [onFirstItemRendered]); - const renderEmptyStateSubtitle = useCallback( + const renderEmptyLHNSubtitle = useCallback( () => ( - {shouldShowEmptyUI ? ( + + {shouldShowEmptyLHN ? ( ) : ( Date: Thu, 7 Mar 2024 15:24:44 +0100 Subject: [PATCH 185/500] avoid redundant API call if same tax rate is selected --- src/pages/EditRequestPage.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/pages/EditRequestPage.js b/src/pages/EditRequestPage.js index b428e3eaf820..d0436b32c817 100644 --- a/src/pages/EditRequestPage.js +++ b/src/pages/EditRequestPage.js @@ -137,6 +137,12 @@ function EditRequestPage({report, route, policy, policyCategories, policyTags, p const updateTaxRate = useCallback( (transactionChanges) => { const newTaxCode = transactionChanges.data.code; + + if (!newTaxCode) { + Navigation.dismissModal(); + return; + } + IOU.updateMoneyRequestTaxRate(transaction.transactionID, report.reportID, newTaxCode, policy, policyTags, policyCategories); Navigation.dismissModal(report.reportID); }, From f1179ce504f4b27a74b1036a308da93dcc8ce02e Mon Sep 17 00:00:00 2001 From: Etotaziba Olei Date: Thu, 7 Mar 2024 15:27:37 +0100 Subject: [PATCH 186/500] avoid redundant API call if same tax amount inputed is selected --- src/pages/EditRequestPage.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/pages/EditRequestPage.js b/src/pages/EditRequestPage.js index d0436b32c817..c01b65932beb 100644 --- a/src/pages/EditRequestPage.js +++ b/src/pages/EditRequestPage.js @@ -128,6 +128,11 @@ function EditRequestPage({report, route, policy, policyCategories, policyTags, p const updateTaxAmount = useCallback( (transactionChanges) => { const newTaxAmount = CurrencyUtils.convertToBackendAmount(Number.parseFloat(transactionChanges.amount)); + + if (newTaxAmount === TransactionUtils.getTaxAmount(transaction)) { + Navigation.dismissModal(); + return; + } IOU.updateMoneyRequestTaxAmount(transaction.transactionID, report.reportID, newTaxAmount, policy, policyTags, policyCategories); Navigation.dismissModal(report.reportID); }, From 40583469e19a41a331e5d2b94f0c7e7979ce09dc Mon Sep 17 00:00:00 2001 From: Etotaziba Olei Date: Thu, 7 Mar 2024 16:02:35 +0100 Subject: [PATCH 187/500] do not update taxAmount more than default taxAmount based on actual expense amount --- src/pages/EditRequestPage.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/pages/EditRequestPage.js b/src/pages/EditRequestPage.js index c01b65932beb..cfde040cbdf5 100644 --- a/src/pages/EditRequestPage.js +++ b/src/pages/EditRequestPage.js @@ -77,7 +77,7 @@ const defaultProps = { const getTaxAmount = (transactionAmount, transactionTaxCode, taxRates) => { const percentage = (transactionTaxCode ? taxRates.taxes[transactionTaxCode].value : taxRates.defaultValue) || ''; - return CurrencyUtils.convertToBackendAmount(Number.parseFloat(TransactionUtils.calculateTaxAmount(percentage, transactionAmount))); + return CurrencyUtils.convertToBackendAmount(Number.parseFloat(TransactionUtils.calculateTaxAmount(percentage, Math.abs(transactionAmount)))); }; function EditRequestPage({report, route, policy, policyCategories, policyTags, parentReportActions, transaction}) { @@ -221,7 +221,7 @@ function EditRequestPage({report, route, policy, policyCategories, policyTags, p return ( From 5021d915b18ed5307619cd39d63d7ac41a52833a Mon Sep 17 00:00:00 2001 From: Etotaziba Olei Date: Thu, 7 Mar 2024 17:07:23 +0100 Subject: [PATCH 188/500] remove optional chaining --- src/pages/EditRequestPage.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/EditRequestPage.js b/src/pages/EditRequestPage.js index cfde040cbdf5..0498c98b087a 100644 --- a/src/pages/EditRequestPage.js +++ b/src/pages/EditRequestPage.js @@ -221,7 +221,7 @@ function EditRequestPage({report, route, policy, policyCategories, policyTags, p return ( From 397278d3665de12a40768a1ee4e789ac1ee8c56b Mon Sep 17 00:00:00 2001 From: Yogesh Bhatt Date: Thu, 7 Mar 2024 21:54:37 +0530 Subject: [PATCH 189/500] Prettier formatting --- src/pages/settings/Profile/PersonalDetails/AddressPage.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/settings/Profile/PersonalDetails/AddressPage.tsx b/src/pages/settings/Profile/PersonalDetails/AddressPage.tsx index 9d9d16a6a388..77d551b283ec 100644 --- a/src/pages/settings/Profile/PersonalDetails/AddressPage.tsx +++ b/src/pages/settings/Profile/PersonalDetails/AddressPage.tsx @@ -13,8 +13,8 @@ import useThemeStyles from '@hooks/useThemeStyles'; import Navigation from '@libs/Navigation/Navigation'; import type {SettingsNavigatorParamList} from '@libs/Navigation/types'; import * as PersonalDetails from '@userActions/PersonalDetails'; -import CONST from '@src/CONST'; import type {FormOnyxValues} from '@src/components/Form/types'; +import CONST from '@src/CONST'; import type {Country} from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import type SCREENS from '@src/SCREENS'; From 5d223d69037f3e89732c855bc66f8d631f3c5f85 Mon Sep 17 00:00:00 2001 From: Yogesh Bhatt Date: Sat, 9 Mar 2024 02:21:00 +0530 Subject: [PATCH 190/500] fix: handle potential 'undefined' object issue --- .../settings/Profile/PersonalDetails/StateSelectionPage.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/settings/Profile/PersonalDetails/StateSelectionPage.tsx b/src/pages/settings/Profile/PersonalDetails/StateSelectionPage.tsx index b30c9c9967c8..1c3d8dd650c0 100644 --- a/src/pages/settings/Profile/PersonalDetails/StateSelectionPage.tsx +++ b/src/pages/settings/Profile/PersonalDetails/StateSelectionPage.tsx @@ -59,7 +59,7 @@ function StateSelectionPage() { const backTo = params?.backTo ?? ''; // Determine navigation action based on "backTo" presence and route stack length. - if (navigation.getState().routes.length === 1) { + if (navigation.getState()?.routes.length === 1) { // If this is the only page in the navigation stack (examples include direct navigation to this page via URL or page reload). if (_.isEmpty(backTo)) { // No "backTo": default back navigation. From 3a3c767746c199d53b2791ec1d786cb5fc7ff200 Mon Sep 17 00:00:00 2001 From: Yogesh Bhatt <65986357+ygshbht@users.noreply.github.com> Date: Sat, 9 Mar 2024 02:25:20 +0530 Subject: [PATCH 191/500] Typo fix src/components/StateSelector.tsx Co-authored-by: Vit Horacek <36083550+mountiny@users.noreply.github.com> --- src/components/StateSelector.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/StateSelector.tsx b/src/components/StateSelector.tsx index 9ddf2573f857..54a2066c52fa 100644 --- a/src/components/StateSelector.tsx +++ b/src/components/StateSelector.tsx @@ -15,7 +15,7 @@ import MenuItemWithTopDescription from './MenuItemWithTopDescription'; type State = keyof typeof COMMON_CONST.STATES; type StateSelectorProps = { - /** Form error text. e.g when no country is selected */ + /** Form error text. e.g when no state is selected */ errorText?: MaybePhraseKey; /** Current selected state */ From c56f8d52e6e0627f114867e6f2e65c3b44e1983e Mon Sep 17 00:00:00 2001 From: Yogesh Bhatt <65986357+ygshbht@users.noreply.github.com> Date: Sat, 9 Mar 2024 02:26:20 +0530 Subject: [PATCH 192/500] Format src/libs/Url.ts Co-authored-by: Vit Horacek <36083550+mountiny@users.noreply.github.com> --- src/libs/Url.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/Url.ts b/src/libs/Url.ts index 7e028aded60c..1f402fdfc131 100644 --- a/src/libs/Url.ts +++ b/src/libs/Url.ts @@ -45,8 +45,8 @@ function hasSameExpensifyOrigin(url1: string, url2: string): boolean { * Appends or updates a query parameter in a given URL. */ function appendParam(url: string, paramName: string, paramValue: string) { + // If parameter exists, replace it if (url.includes(`${paramName}=`)) { - // If parameter exists, replace it const regex = new RegExp(`${paramName}=([^&]*)`); return url.replace(regex, `${paramName}=${paramValue}`); } From 470be54fc13bebb1596626989db54a3545c27688 Mon Sep 17 00:00:00 2001 From: Yogesh Bhatt <65986357+ygshbht@users.noreply.github.com> Date: Sat, 9 Mar 2024 02:26:48 +0530 Subject: [PATCH 193/500] Format src/libs/Url.ts Co-authored-by: Vit Horacek <36083550+mountiny@users.noreply.github.com> --- src/libs/Url.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/libs/Url.ts b/src/libs/Url.ts index 1f402fdfc131..4e3282e7bdb3 100644 --- a/src/libs/Url.ts +++ b/src/libs/Url.ts @@ -50,6 +50,7 @@ function appendParam(url: string, paramName: string, paramValue: string) { const regex = new RegExp(`${paramName}=([^&]*)`); return url.replace(regex, `${paramName}=${paramValue}`); } + // If parameter doesn't exist, append it const separator = url.includes('?') ? '&' : '?'; return `${url}${separator}${paramName}=${paramValue}`; From 1670b66448c4d92a2eba9f5025ef11bf258c1194 Mon Sep 17 00:00:00 2001 From: Yogesh Bhatt <65986357+ygshbht@users.noreply.github.com> Date: Sat, 9 Mar 2024 02:27:25 +0530 Subject: [PATCH 194/500] Format src/pages/settings/Profile/PersonalDetails/StateSelectionPage.tsx Co-authored-by: Vit Horacek <36083550+mountiny@users.noreply.github.com> --- .../settings/Profile/PersonalDetails/StateSelectionPage.tsx | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/pages/settings/Profile/PersonalDetails/StateSelectionPage.tsx b/src/pages/settings/Profile/PersonalDetails/StateSelectionPage.tsx index 1c3d8dd650c0..275400b27cc0 100644 --- a/src/pages/settings/Profile/PersonalDetails/StateSelectionPage.tsx +++ b/src/pages/settings/Profile/PersonalDetails/StateSelectionPage.tsx @@ -28,9 +28,7 @@ function StateSelectionPage() { const {translate} = useLocalize(); const [searchValue, setSearchValue] = useState(''); - - const params = route.params as RouteParams | undefined; // Type assertion - + const params = route.params as RouteParams | undefined; const currentState = params?.state; const label = params?.label; From 5d2ff1eba9382350854d3512db85d099faca37eb Mon Sep 17 00:00:00 2001 From: Yogesh Bhatt <65986357+ygshbht@users.noreply.github.com> Date: Sat, 9 Mar 2024 02:27:53 +0530 Subject: [PATCH 195/500] Format src/pages/settings/Profile/PersonalDetails/AddressPage.tsx Co-authored-by: Vit Horacek <36083550+mountiny@users.noreply.github.com> --- src/pages/settings/Profile/PersonalDetails/AddressPage.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/pages/settings/Profile/PersonalDetails/AddressPage.tsx b/src/pages/settings/Profile/PersonalDetails/AddressPage.tsx index 77d551b283ec..7706b839b18a 100644 --- a/src/pages/settings/Profile/PersonalDetails/AddressPage.tsx +++ b/src/pages/settings/Profile/PersonalDetails/AddressPage.tsx @@ -48,6 +48,7 @@ function AddressPage({privatePersonalDetails, route}: AddressPageProps) { usePrivatePersonalDetails(); const {translate} = useLocalize(); const address = useMemo(() => privatePersonalDetails?.address, [privatePersonalDetails]); + const countryFromUrlTemp = route?.params?.country; // Check if country is valid const countryFromUrl = CONST.ALL_COUNTRIES[countryFromUrlTemp as keyof typeof CONST.ALL_COUNTRIES] ? countryFromUrlTemp : ''; From 18717ea0f1072ae10b5216481903a4415a84e9a5 Mon Sep 17 00:00:00 2001 From: Yogesh Bhatt Date: Sat, 9 Mar 2024 02:38:28 +0530 Subject: [PATCH 196/500] Comment for function useGeographicalStateFromRoute --- src/hooks/useGeographicalStateFromRoute.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/hooks/useGeographicalStateFromRoute.ts b/src/hooks/useGeographicalStateFromRoute.ts index 39d3cab5f681..5015db12d458 100644 --- a/src/hooks/useGeographicalStateFromRoute.ts +++ b/src/hooks/useGeographicalStateFromRoute.ts @@ -5,6 +5,10 @@ import {CONST as COMMON_CONST} from 'expensify-common/lib/CONST'; type CustomParamList = ParamListBase & Record>; type State = keyof typeof COMMON_CONST.STATES; +/** + * Extracts the 'state' (default) query parameter from the route/ url and validates it against COMMON_CONST.STATES, returning its ISO code or `undefined`. + * Example: const stateISO = useGeographicalStateFromRoute(); // Assuming 'state' param is 'CA' or another valid state, returns the corresponding ISO code or `undefined` if invalid. + */ export default function useGeographicalStateFromRoute(stateParamName = 'state'): State | undefined { const route = useRoute>(); const stateFromUrlTemp = route.params?.[stateParamName] as string | undefined; From 3ba97a550a07ce38adbf5e397bba3e56907552b1 Mon Sep 17 00:00:00 2001 From: Yogesh Bhatt Date: Sat, 9 Mar 2024 03:58:19 +0530 Subject: [PATCH 197/500] hash append for StateSelector edge cases --- src/components/StateSelector.tsx | 12 +++++++++++- src/hooks/useGeographicalStateFromRoute.ts | 7 ++++++- .../Profile/PersonalDetails/StateSelectionPage.tsx | 7 +++++-- 3 files changed, 22 insertions(+), 4 deletions(-) diff --git a/src/components/StateSelector.tsx b/src/components/StateSelector.tsx index 54a2066c52fa..41dcc21bf47c 100644 --- a/src/components/StateSelector.tsx +++ b/src/components/StateSelector.tsx @@ -1,3 +1,5 @@ +import {useRoute} from '@react-navigation/native'; +import type {ParamListBase, RouteProp} from '@react-navigation/native'; import {CONST as COMMON_CONST} from 'expensify-common/lib/CONST'; import React, {useEffect, useState} from 'react'; import type {ForwardedRef} from 'react'; @@ -12,6 +14,8 @@ import FormHelpMessage from './FormHelpMessage'; import type {MenuItemProps} from './MenuItem'; import MenuItemWithTopDescription from './MenuItemWithTopDescription'; +type CustomParamList = ParamListBase & Record>; + type State = keyof typeof COMMON_CONST.STATES; type StateSelectorProps = { @@ -40,6 +44,12 @@ function StateSelector({errorText, shouldUseStateFromUrl = true, value: stateCod const stateFromUrl = useGeographicalStateFromRoute(); const [stateToDisplay, setStateToDisplay] = useState(''); + /** + * See {@link module:src/pages/settings/Profile/PersonalDetails/StateSelectionPage.tsx#withHash} for more information. + */ + const route = useRoute>(); + const rawStateFromUrl = route.params?.state as string | undefined; + useEffect(() => { if (!shouldUseStateFromUrl || !stateFromUrl) { return; @@ -52,7 +62,7 @@ function StateSelector({errorText, shouldUseStateFromUrl = true, value: stateCod setStateToDisplay(stateFromUrl); // eslint-disable-next-line react-hooks/exhaustive-deps - }, [stateFromUrl, shouldUseStateFromUrl]); + }, [rawStateFromUrl, shouldUseStateFromUrl]); useEffect(() => { if (!stateCode) { diff --git a/src/hooks/useGeographicalStateFromRoute.ts b/src/hooks/useGeographicalStateFromRoute.ts index 5015db12d458..6ce67c0ef93a 100644 --- a/src/hooks/useGeographicalStateFromRoute.ts +++ b/src/hooks/useGeographicalStateFromRoute.ts @@ -5,6 +5,11 @@ import {CONST as COMMON_CONST} from 'expensify-common/lib/CONST'; type CustomParamList = ParamListBase & Record>; type State = keyof typeof COMMON_CONST.STATES; +/** + * See {@link module:src/pages/settings/Profile/PersonalDetails/StateSelectionPage.tsx#withHash} for more information. + */ +const removeHash = (arg: string): string => arg.replace(/-hash-.*$/, ''); + /** * Extracts the 'state' (default) query parameter from the route/ url and validates it against COMMON_CONST.STATES, returning its ISO code or `undefined`. * Example: const stateISO = useGeographicalStateFromRoute(); // Assuming 'state' param is 'CA' or another valid state, returns the corresponding ISO code or `undefined` if invalid. @@ -16,5 +21,5 @@ export default function useGeographicalStateFromRoute(stateParamName = 'state'): if (!stateFromUrlTemp) { return; } - return COMMON_CONST.STATES[stateFromUrlTemp as State].stateISO; + return COMMON_CONST.STATES[removeHash(stateFromUrlTemp) as State].stateISO; } diff --git a/src/pages/settings/Profile/PersonalDetails/StateSelectionPage.tsx b/src/pages/settings/Profile/PersonalDetails/StateSelectionPage.tsx index 275400b27cc0..1a9a0fab9069 100644 --- a/src/pages/settings/Profile/PersonalDetails/StateSelectionPage.tsx +++ b/src/pages/settings/Profile/PersonalDetails/StateSelectionPage.tsx @@ -56,6 +56,9 @@ function StateSelectionPage() { (option: CountryData) => { const backTo = params?.backTo ?? ''; + // Add hash to param for rare cases, such as in ReimbursementAccountPage (currentStep = CONST.BANK_ACCOUNT.STEP.COMPANY), when the URL on the form page (which has the StateSelector component) already includes a state param. If the form then updates the StateInput with state data from another source and the user attempts to use StateSelector to select the same state present in the URL, it will cause the StateSelector not to detect the state change, as the URL remains the same. + const withHash = (arg: string): string => `${arg}-hash-${Math.random().toString(36).substring(2, 8)}`; + // Determine navigation action based on "backTo" presence and route stack length. if (navigation.getState()?.routes.length === 1) { // If this is the only page in the navigation stack (examples include direct navigation to this page via URL or page reload). @@ -64,11 +67,11 @@ function StateSelectionPage() { Navigation.goBack(); } else { // "backTo" provided: navigate back to "backTo" with state parameter. - Navigation.goBack(appendParam(backTo, 'state', option.value) as Route); + Navigation.goBack(appendParam(backTo, 'state', withHash(option.value)) as Route); } } else if (!_.isEmpty(backTo)) { // Most common case: Navigation stack has multiple routes and "backTo" is defined: navigate to "backTo" with state parameter. - Navigation.navigate(appendParam(backTo, 'state', option.value) as Route); + Navigation.navigate(appendParam(backTo, 'state', withHash(option.value)) as Route); } else { // This is a fallback block and should never execute if StateSelector is correctly appending the "backTo" route. // Navigation stack has multiple routes but no "backTo" defined: default back navigation. From 0dc7f49ccd5d2169d387c1b2267d7463e0b35c6e Mon Sep 17 00:00:00 2001 From: Yogesh Bhatt Date: Sat, 9 Mar 2024 04:32:25 +0530 Subject: [PATCH 198/500] Comment update useGeographicalStateFromRoute --- src/hooks/useGeographicalStateFromRoute.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/hooks/useGeographicalStateFromRoute.ts b/src/hooks/useGeographicalStateFromRoute.ts index 6ce67c0ef93a..69482b8c3a3b 100644 --- a/src/hooks/useGeographicalStateFromRoute.ts +++ b/src/hooks/useGeographicalStateFromRoute.ts @@ -12,7 +12,10 @@ const removeHash = (arg: string): string => arg.replace(/-hash-.*$/, ''); /** * Extracts the 'state' (default) query parameter from the route/ url and validates it against COMMON_CONST.STATES, returning its ISO code or `undefined`. - * Example: const stateISO = useGeographicalStateFromRoute(); // Assuming 'state' param is 'CA' or another valid state, returns the corresponding ISO code or `undefined` if invalid. + * Example 1: Url: https://dev.new.expensify.com:8082/settings/profile/address?state=MO Returns: MO + * Example 2: Url: https://dev.new.expensify.com:8082/settings/profile/address?state=ASDF Returns: undefined + * Example 3: Url: https://dev.new.expensify.com:8082/settings/profile/address Returns: undefined + * Example 4: Url: https://dev.new.expensify.com:8082/settings/profile/address?state=MO-hash-a12341 Returns: MO */ export default function useGeographicalStateFromRoute(stateParamName = 'state'): State | undefined { const route = useRoute>(); From 57bcee217ff3a01861f0a0f8ac6a09bba7a4bfc3 Mon Sep 17 00:00:00 2001 From: Yogesh Bhatt Date: Sat, 9 Mar 2024 06:19:39 +0530 Subject: [PATCH 199/500] Add StateSelector to MoneyRequest Navigation stack to work for /send/new/add-debit-card --- src/ROUTES.ts | 10 ++++++++++ src/SCREENS.ts | 1 + src/components/StateSelector.tsx | 10 ++++++++-- .../Navigation/AppNavigator/ModalStackNavigators.tsx | 1 + src/libs/Navigation/linkingConfig/config.ts | 1 + src/pages/settings/Wallet/AddDebitCardPage.tsx | 5 +++++ 6 files changed, 26 insertions(+), 2 deletions(-) diff --git a/src/ROUTES.ts b/src/ROUTES.ts index 371691b7b0fd..a1b28c33d51b 100644 --- a/src/ROUTES.ts +++ b/src/ROUTES.ts @@ -423,6 +423,16 @@ const ROUTES = { getRoute: (iouType: ValueOf, transactionID: string, reportID: string) => `create/${iouType}/start/${transactionID}/${reportID}/scan` as const, }, + MONEY_REQUEST_STATE_SELECTOR: { + route: 'moneyrequest/state', + + getRoute: (state?: string, backTo?: string, label?: string) => + `${getUrlWithBackToParam(`moneyrequest/state${state ? `?state=${encodeURIComponent(state)}` : ''}`, backTo)}${ + // Nullish operator ?? doesnt seem to be a replacement for || here + // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing + label ? `${backTo || state ? '&' : '?'}label=${encodeURIComponent(label)}` : '' + }` as const, + }, IOU_REQUEST: 'request/new', IOU_SEND: 'send/new', IOU_SEND_ADD_BANK_ACCOUNT: 'send/new/add-bank-account', diff --git a/src/SCREENS.ts b/src/SCREENS.ts index 5121068f273f..f04b521b9876 100644 --- a/src/SCREENS.ts +++ b/src/SCREENS.ts @@ -161,6 +161,7 @@ const SCREENS = { EDIT_WAYPOINT: 'Money_Request_Edit_Waypoint', DISTANCE: 'Money_Request_Distance', RECEIPT: 'Money_Request_Receipt', + STATE_SELECTOR: 'Money_Request_State_Selector', }, IOU_SEND: { diff --git a/src/components/StateSelector.tsx b/src/components/StateSelector.tsx index 41dcc21bf47c..9de9c746c415 100644 --- a/src/components/StateSelector.tsx +++ b/src/components/StateSelector.tsx @@ -36,9 +36,15 @@ type StateSelectorProps = { /** whether to use state from url, for cases when url value is passed from parent */ shouldUseStateFromUrl?: boolean; + + /** object to get route details from */ + stateSelectorRoute?: typeof ROUTES.SETTINGS_ADDRESS_STATE | typeof ROUTES.MONEY_REQUEST_STATE_SELECTOR; }; -function StateSelector({errorText, shouldUseStateFromUrl = true, value: stateCode, label, onInputChange, wrapperStyle}: StateSelectorProps, ref: ForwardedRef) { +function StateSelector( + {errorText, shouldUseStateFromUrl = true, value: stateCode, label, onInputChange, wrapperStyle, stateSelectorRoute = ROUTES.SETTINGS_ADDRESS_STATE}: StateSelectorProps, + ref: ForwardedRef, +) { const styles = useThemeStyles(); const {translate} = useLocalize(); const stateFromUrl = useGeographicalStateFromRoute(); @@ -93,7 +99,7 @@ function StateSelector({errorText, shouldUseStateFromUrl = true, value: stateCod description={label || translate('common.state')} onPress={() => { const activeRoute = Navigation.getActiveRoute(); - Navigation.navigate(ROUTES.SETTINGS_ADDRESS_STATE.getRoute(stateCode, activeRoute, label)); + Navigation.navigate(stateSelectorRoute.getRoute(stateCode, activeRoute, label)); }} wrapperStyle={wrapperStyle} /> diff --git a/src/libs/Navigation/AppNavigator/ModalStackNavigators.tsx b/src/libs/Navigation/AppNavigator/ModalStackNavigators.tsx index b030165aca11..4ae6ddb03434 100644 --- a/src/libs/Navigation/AppNavigator/ModalStackNavigators.tsx +++ b/src/libs/Navigation/AppNavigator/ModalStackNavigators.tsx @@ -106,6 +106,7 @@ const MoneyRequestModalStackNavigator = createModalStackNavigator require('../../../pages/iou/MoneyRequestWaypointPage').default as React.ComponentType, [SCREENS.MONEY_REQUEST.DISTANCE]: () => require('../../../pages/iou/NewDistanceRequestPage').default as React.ComponentType, [SCREENS.MONEY_REQUEST.RECEIPT]: () => require('../../../pages/EditRequestReceiptPage').default as React.ComponentType, + [SCREENS.MONEY_REQUEST.STATE_SELECTOR]: () => require('../../../pages/settings/Profile/PersonalDetails/StateSelectionPage').default as React.ComponentType, }); const SplitDetailsModalStackNavigator = createModalStackNavigator({ diff --git a/src/libs/Navigation/linkingConfig/config.ts b/src/libs/Navigation/linkingConfig/config.ts index 3c00bd3c453c..cc5ae141b5a4 100644 --- a/src/libs/Navigation/linkingConfig/config.ts +++ b/src/libs/Navigation/linkingConfig/config.ts @@ -490,6 +490,7 @@ const config: LinkingOptions['config'] = { [SCREENS.IOU_SEND.ENABLE_PAYMENTS]: ROUTES.IOU_SEND_ENABLE_PAYMENTS, [SCREENS.IOU_SEND.ADD_BANK_ACCOUNT]: ROUTES.IOU_SEND_ADD_BANK_ACCOUNT, [SCREENS.IOU_SEND.ADD_DEBIT_CARD]: ROUTES.IOU_SEND_ADD_DEBIT_CARD, + [SCREENS.MONEY_REQUEST.STATE_SELECTOR]: {path: ROUTES.MONEY_REQUEST_STATE_SELECTOR.route, exact: true}, }, }, [SCREENS.RIGHT_MODAL.SPLIT_DETAILS]: { diff --git a/src/pages/settings/Wallet/AddDebitCardPage.tsx b/src/pages/settings/Wallet/AddDebitCardPage.tsx index 99f2a90abd8e..0beb3c16018d 100644 --- a/src/pages/settings/Wallet/AddDebitCardPage.tsx +++ b/src/pages/settings/Wallet/AddDebitCardPage.tsx @@ -1,3 +1,4 @@ +import {useRoute} from '@react-navigation/native'; import React, {useEffect, useRef} from 'react'; import {View} from 'react-native'; import type {OnyxEntry} from 'react-native-onyx'; @@ -22,6 +23,8 @@ import * as ValidationUtils from '@libs/ValidationUtils'; import * as PaymentMethods from '@userActions/PaymentMethods'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; +import ROUTES from '@src/ROUTES'; +import SCREENS from '@src/SCREENS'; import type {AddDebitCardForm} from '@src/types/form'; import INPUT_IDS from '@src/types/form/AddDebitCardForm'; @@ -58,6 +61,7 @@ function DebitCardPage({formData}: DebitCardPageProps) { const {translate} = useLocalize(); const prevFormDataSetupComplete = usePrevious(!!formData?.setupComplete); const nameOnCardRef = useRef(null); + const route = useRoute(); /** * Reset the form values on the mount and unmount so that old errors don't show when this form is displayed again. @@ -200,6 +204,7 @@ function DebitCardPage({formData}: DebitCardPageProps) { /> From 9ffea05a449afe435ee0bee1ece0e8202b9f918a Mon Sep 17 00:00:00 2001 From: Yogesh Bhatt <65986357+ygshbht@users.noreply.github.com> Date: Sat, 9 Mar 2024 06:30:49 +0530 Subject: [PATCH 200/500] Formatting src/pages/settings/Profile/PersonalDetails/AddressPage.tsx Co-authored-by: Vit Horacek <36083550+mountiny@users.noreply.github.com> --- src/pages/settings/Profile/PersonalDetails/AddressPage.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/settings/Profile/PersonalDetails/AddressPage.tsx b/src/pages/settings/Profile/PersonalDetails/AddressPage.tsx index 7706b839b18a..ae724ce218ad 100644 --- a/src/pages/settings/Profile/PersonalDetails/AddressPage.tsx +++ b/src/pages/settings/Profile/PersonalDetails/AddressPage.tsx @@ -48,8 +48,8 @@ function AddressPage({privatePersonalDetails, route}: AddressPageProps) { usePrivatePersonalDetails(); const {translate} = useLocalize(); const address = useMemo(() => privatePersonalDetails?.address, [privatePersonalDetails]); - const countryFromUrlTemp = route?.params?.country; + // Check if country is valid const countryFromUrl = CONST.ALL_COUNTRIES[countryFromUrlTemp as keyof typeof CONST.ALL_COUNTRIES] ? countryFromUrlTemp : ''; const stateFromUrl = useGeographicalStateFromRoute(); From d25904ca74e906302784150dd143bdbf7d0ebb33 Mon Sep 17 00:00:00 2001 From: Yogesh Bhatt <65986357+ygshbht@users.noreply.github.com> Date: Sat, 9 Mar 2024 06:31:30 +0530 Subject: [PATCH 201/500] Commnet udpate src/ROUTES.ts Co-authored-by: Vit Horacek <36083550+mountiny@users.noreply.github.com> --- src/ROUTES.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ROUTES.ts b/src/ROUTES.ts index a1b28c33d51b..9c697f4f4cfd 100644 --- a/src/ROUTES.ts +++ b/src/ROUTES.ts @@ -136,7 +136,7 @@ const ROUTES = { getRoute: (state?: string, backTo?: string, label?: string) => `${getUrlWithBackToParam(`settings/profile/address/state${state ? `?state=${encodeURIComponent(state)}` : ''}`, backTo)}${ - // Nullish operator ?? doesnt seem to be a replacement for || here + // the label param can be an empty string so we cannot use a nullish ?? operator // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing label ? `${backTo || state ? '&' : '?'}label=${encodeURIComponent(label)}` : '' }` as const, From 81f452c650071377cbaa17ab74b215ab79835f9a Mon Sep 17 00:00:00 2001 From: Yogesh Bhatt Date: Sat, 9 Mar 2024 06:33:01 +0530 Subject: [PATCH 202/500] Comment update ROUTES.ts --- src/ROUTES.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ROUTES.ts b/src/ROUTES.ts index 9c697f4f4cfd..de06c07a0834 100644 --- a/src/ROUTES.ts +++ b/src/ROUTES.ts @@ -428,7 +428,7 @@ const ROUTES = { getRoute: (state?: string, backTo?: string, label?: string) => `${getUrlWithBackToParam(`moneyrequest/state${state ? `?state=${encodeURIComponent(state)}` : ''}`, backTo)}${ - // Nullish operator ?? doesnt seem to be a replacement for || here + // the label param can be an empty string so we cannot use a nullish ?? operator // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing label ? `${backTo || state ? '&' : '?'}label=${encodeURIComponent(label)}` : '' }` as const, From ecc13ca4e7964813f6f76d018d92e9fca19bca2b Mon Sep 17 00:00:00 2001 From: tienifr Date: Mon, 11 Mar 2024 16:56:17 +0700 Subject: [PATCH 203/500] remove custom web style --- src/components/BlockingViews/BlockingView.tsx | 1 + src/components/LHNOptionsList/LHNOptionsList.tsx | 4 ++-- src/styles/index.ts | 5 ----- 3 files changed, 3 insertions(+), 7 deletions(-) diff --git a/src/components/BlockingViews/BlockingView.tsx b/src/components/BlockingViews/BlockingView.tsx index 05ffbd6a0d58..3733075c2025 100644 --- a/src/components/BlockingViews/BlockingView.tsx +++ b/src/components/BlockingViews/BlockingView.tsx @@ -67,6 +67,7 @@ type BlockingViewProps = RequiredIllustrationProps & { /** Style for the animation */ animationStyles?: StyleProp; + /** Style for the animation on web */ animationWebStyle?: WebStyle; /** Render custom subtitle */ diff --git a/src/components/LHNOptionsList/LHNOptionsList.tsx b/src/components/LHNOptionsList/LHNOptionsList.tsx index 1481b309949f..2005b5493202 100644 --- a/src/components/LHNOptionsList/LHNOptionsList.tsx +++ b/src/components/LHNOptionsList/LHNOptionsList.tsx @@ -44,7 +44,7 @@ function LHNOptionsList({ const styles = useThemeStyles(); const {canUseViolations} = usePermissions(); const {translate} = useLocalize(); - const {isExtraSmallScreenHeight, isSmallScreenWidth} = useWindowDimensions(); + const {isSmallScreenWidth} = useWindowDimensions(); const shouldShowEmptyLHN = isSmallScreenWidth && data.length === 0; // When the first item renders we want to call the onFirstItemRendered callback. @@ -167,7 +167,7 @@ function LHNOptionsList({ height: 180, }, - emptyLHNAnimationWeb: (isExtraSmallScreenHeight: boolean) => ({ - width: isExtraSmallScreenHeight ? 160 : 180, - height: isExtraSmallScreenHeight ? 160 : 180, - }), - locationErrorLinkText: { textAlignVertical: 'center', fontSize: variables.fontSizeLabel, From 840ac30b22dda0a10b34dc88031d9eb7aee8a1bf Mon Sep 17 00:00:00 2001 From: Tomasz Misiukiewicz Date: Mon, 11 Mar 2024 12:17:28 +0100 Subject: [PATCH 204/500] add function to create all reports and personal details options --- src/libs/OptionsListUtils.ts | 60 ++++++++++++++++++++++++++++++++++++ 1 file changed, 60 insertions(+) diff --git a/src/libs/OptionsListUtils.ts b/src/libs/OptionsListUtils.ts index 3dd23752d5db..9d309877ae75 100644 --- a/src/libs/OptionsListUtils.ts +++ b/src/libs/OptionsListUtils.ts @@ -1329,6 +1329,65 @@ function isReportSelected(reportOption: ReportUtils.OptionData, selectedOptions: return selectedOptions.some((option) => (option.accountID && option.accountID === reportOption.accountID) || (option.reportID && option.reportID === reportOption.reportID)); } +function createOptionList(reports: OnyxCollection, personalDetails: OnyxEntry, {reportActions = {}}: Partial) { + const reportMapForAccountIDs: Record = {}; + // Sorting the reports works like this: + // - Order everything by the last message timestamp (descending) + // - All archived reports should remain at the bottom + const orderedReports = lodashSortBy(reports, (report) => { + if (ReportUtils.isArchivedRoom(report)) { + return CONST.DATE.UNIX_EPOCH; + } + + return report?.lastVisibleActionCreated; + }); + orderedReports.reverse(); + const allReportOptions: ReportUtils.OptionData[] = []; + + orderedReports.forEach((report) => { + if (!report) { + return; + } + + const isSelfDM = ReportUtils.isSelfDM(report); + // Currently, currentUser is not included in visibleChatMemberAccountIDs, so for selfDM we need to add the currentUser as participants. + const accountIDs = isSelfDM ? [currentUserAccountID ?? 0] : report.visibleChatMemberAccountIDs ?? []; + + if (!accountIDs || accountIDs.length === 0) { + return; + } + + // Save the report in the map if this is a single participant so we can associate the reportID with the + // personal detail option later. Individuals should not be associated with single participant + // policyExpenseChats or chatRooms since those are not people. + if (accountIDs.length <= 1) { + reportMapForAccountIDs[accountIDs[0]] = report; + } + + allReportOptions.push( + createOption(accountIDs, personalDetails, report, reportActions, { + showChatPreviewLine: true, + forcePolicyNamePreview: true, + }), + ); + }); + + const havingLoginPersonalDetails = Object.fromEntries( + Object.entries(personalDetails ?? {}).filter(([, detail]) => !!detail?.login && !!detail.accountID && !detail?.isOptimisticPersonalDetail), + ); + const allPersonalDetailsOptions = Object.values(havingLoginPersonalDetails).map((personalDetail) => + createOption([personalDetail?.accountID ?? -1], personalDetails, reportMapForAccountIDs[personalDetail?.accountID ?? -1], reportActions, { + showChatPreviewLine: true, + forcePolicyNamePreview: true, + }), + ); + + return { + reportOptions: allReportOptions, + personalDetailsOptions: allPersonalDetailsOptions, + }; +} + /** * Build the options */ @@ -2063,6 +2122,7 @@ export { formatSectionsFromSearchTerm, transformedTaxRates, getShareLogOptions, + createOptionList, }; export type {MemberForList, CategorySection, GetOptions}; From 68e364c017cca6df15f913e8c0c218093801db1d Mon Sep 17 00:00:00 2001 From: Tomasz Misiukiewicz Date: Mon, 11 Mar 2024 13:14:25 +0100 Subject: [PATCH 205/500] use context to initialize all options --- src/App.tsx | 2 + src/components/OnyxProvider.tsx | 5 ++ src/components/OptionListContextProvider.tsx | 60 ++++++++++++++++++++ src/libs/OptionsListUtils.ts | 34 +++++++---- src/pages/SearchPage/index.js | 7 ++- 5 files changed, 96 insertions(+), 12 deletions(-) create mode 100644 src/components/OptionListContextProvider.tsx diff --git a/src/App.tsx b/src/App.tsx index 0e247d5faa53..e53ba387d4d6 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -15,6 +15,7 @@ import ErrorBoundary from './components/ErrorBoundary'; import HTMLEngineProvider from './components/HTMLEngineProvider'; import {LocaleContextProvider} from './components/LocaleContextProvider'; import OnyxProvider from './components/OnyxProvider'; +import {OptionsListContextProvider} from './components/OptionListContextProvider'; import PopoverContextProvider from './components/PopoverProvider'; import SafeArea from './components/SafeArea'; import ThemeIllustrationsProvider from './components/ThemeIllustrationsProvider'; @@ -78,6 +79,7 @@ function App({url}: AppProps) { PlaybackContextProvider, VolumeContextProvider, VideoPopoverMenuContextProvider, + OptionsListContextProvider, ]} > diff --git a/src/components/OnyxProvider.tsx b/src/components/OnyxProvider.tsx index 0bc9130ea4a8..c57c38c3eba1 100644 --- a/src/components/OnyxProvider.tsx +++ b/src/components/OnyxProvider.tsx @@ -15,6 +15,7 @@ const [withReportCommentDrafts, ReportCommentDraftsProvider] = createOnyxContext const [withPreferredTheme, PreferredThemeProvider, PreferredThemeContext] = createOnyxContext(ONYXKEYS.PREFERRED_THEME); const [withFrequentlyUsedEmojis, FrequentlyUsedEmojisProvider, , useFrequentlyUsedEmojis] = createOnyxContext(ONYXKEYS.FREQUENTLY_USED_EMOJIS); const [withPreferredEmojiSkinTone, PreferredEmojiSkinToneProvider, PreferredEmojiSkinToneContext] = createOnyxContext(ONYXKEYS.PREFERRED_EMOJI_SKIN_TONE); +const [withReports, ReportsProvider, ReportsContext, useReports] = createOnyxContext(ONYXKEYS.COLLECTION.REPORT); const [, SessionProvider, , useSession] = createOnyxContext(ONYXKEYS.SESSION); type OnyxProviderProps = { @@ -37,6 +38,7 @@ function OnyxProvider(props: OnyxProviderProps) { FrequentlyUsedEmojisProvider, PreferredEmojiSkinToneProvider, SessionProvider, + ReportsProvider, ]} > {props.children} @@ -69,4 +71,7 @@ export { useBlockedFromConcierge, useReportActionsDrafts, useSession, + withReports, + ReportsContext, + useReports, }; diff --git a/src/components/OptionListContextProvider.tsx b/src/components/OptionListContextProvider.tsx new file mode 100644 index 000000000000..6c7e96d68078 --- /dev/null +++ b/src/components/OptionListContextProvider.tsx @@ -0,0 +1,60 @@ +import React, {createContext, useCallback, useContext, useMemo, useRef, useState} from 'react'; +import * as OptionsListUtils from '@libs/OptionsListUtils'; +import type {OptionData} from '@libs/ReportUtils'; +import {usePersonalDetails, useReports} from './OnyxProvider'; + +type Options = { + reports: OptionData[]; + personalDetails: OptionData[]; +}; + +type OptionsListContextProps = { + options: Options; + initializeOptions: () => void; +}; + +type OptionsListProviderProps = { + /** Actual content wrapped by this component */ + children: React.ReactNode; +}; + +const OptionsListContext = createContext({ + options: { + reports: [], + personalDetails: [], + }, + initializeOptions: () => {}, +}); + +function OptionsListContextProvider({children}: OptionsListProviderProps) { + const areOptionsInitialized = useRef(false); + const [options, setOptions] = useState({ + reports: [], + personalDetails: [], + }); + const personalDetails = usePersonalDetails(); + const reports = useReports(); + + const loadOptions = useCallback(() => { + const optionLists = OptionsListUtils.createOptionList(reports, personalDetails); + setOptions({ + reports: optionLists.reports, + personalDetails: optionLists.personalDetails, + }); + }, [personalDetails, reports]); + + const initializeOptions = useCallback(() => { + if (areOptionsInitialized.current) { + return; + } + + areOptionsInitialized.current = true; + loadOptions(); + }, [loadOptions]); + + return ({options, initializeOptions}), [options, initializeOptions])}>{children}; +} + +const useOptionsListContext = () => useContext(OptionsListContext); + +export {OptionsListContextProvider, useOptionsListContext}; diff --git a/src/libs/OptionsListUtils.ts b/src/libs/OptionsListUtils.ts index 9d309877ae75..3b08aa923aba 100644 --- a/src/libs/OptionsListUtils.ts +++ b/src/libs/OptionsListUtils.ts @@ -1329,7 +1329,7 @@ function isReportSelected(reportOption: ReportUtils.OptionData, selectedOptions: return selectedOptions.some((option) => (option.accountID && option.accountID === reportOption.accountID) || (option.reportID && option.reportID === reportOption.reportID)); } -function createOptionList(reports: OnyxCollection, personalDetails: OnyxEntry, {reportActions = {}}: Partial) { +function createOptionList(reports: OnyxCollection, personalDetails: OnyxEntry) { const reportMapForAccountIDs: Record = {}; // Sorting the reports works like this: // - Order everything by the last message timestamp (descending) @@ -1365,10 +1365,16 @@ function createOptionList(reports: OnyxCollection, personalDetails: Onyx } allReportOptions.push( - createOption(accountIDs, personalDetails, report, reportActions, { - showChatPreviewLine: true, - forcePolicyNamePreview: true, - }), + createOption( + accountIDs, + personalDetails, + report, + {}, + { + showChatPreviewLine: true, + forcePolicyNamePreview: true, + }, + ), ); }); @@ -1376,15 +1382,21 @@ function createOptionList(reports: OnyxCollection, personalDetails: Onyx Object.entries(personalDetails ?? {}).filter(([, detail]) => !!detail?.login && !!detail.accountID && !detail?.isOptimisticPersonalDetail), ); const allPersonalDetailsOptions = Object.values(havingLoginPersonalDetails).map((personalDetail) => - createOption([personalDetail?.accountID ?? -1], personalDetails, reportMapForAccountIDs[personalDetail?.accountID ?? -1], reportActions, { - showChatPreviewLine: true, - forcePolicyNamePreview: true, - }), + createOption( + [personalDetail?.accountID ?? -1], + personalDetails, + reportMapForAccountIDs[personalDetail?.accountID ?? -1], + {}, + { + showChatPreviewLine: true, + forcePolicyNamePreview: true, + }, + ), ); return { - reportOptions: allReportOptions, - personalDetailsOptions: allPersonalDetailsOptions, + reports: allReportOptions, + personalDetails: allPersonalDetailsOptions, }; } diff --git a/src/pages/SearchPage/index.js b/src/pages/SearchPage/index.js index 2a6308d27294..e0f547ba4eae 100644 --- a/src/pages/SearchPage/index.js +++ b/src/pages/SearchPage/index.js @@ -5,6 +5,7 @@ import {withOnyx} from 'react-native-onyx'; import _ from 'underscore'; import HeaderWithBackButton from '@components/HeaderWithBackButton'; import {usePersonalDetails} from '@components/OnyxProvider'; +import {useOptionsListContext} from '@components/OptionListContextProvider'; import ScreenWrapper from '@components/ScreenWrapper'; import SelectionList from '@components/SelectionList'; import UserListItem from '@components/SelectionList/UserListItem'; @@ -63,7 +64,7 @@ function SearchPage({betas, reports, isSearchingForReports, navigation}) { const {isOffline} = useNetwork(); const themeStyles = useThemeStyles(); const personalDetails = usePersonalDetails(); - + const {initializeOptions} = useOptionsListContext(); const offlineMessage = isOffline ? [`${translate('common.youAppearToBeOffline')} ${translate('search.resultsAreLimited')}`, {isTranslated: true}] : ''; const [searchValue, debouncedSearchValue, setSearchValue] = useDebouncedState(''); @@ -96,6 +97,10 @@ function SearchPage({betas, reports, isSearchingForReports, navigation}) { return {...options, headerMessage: header}; }, [debouncedSearchValue, reports, personalDetails, betas, isScreenTransitionEnd]); + useEffect(() => { + initializeOptions(); + }, [initializeOptions]); + const sections = useMemo(() => { const newSections = []; let indexOffset = 0; From d32dd3a0fdc796c97308df658164ce978f63d449 Mon Sep 17 00:00:00 2001 From: Tomasz Misiukiewicz Date: Mon, 11 Mar 2024 16:15:09 +0100 Subject: [PATCH 206/500] update getOptions, use options generation in search --- src/components/OptionListContextProvider.tsx | 8 +- src/libs/OptionsListUtils.ts | 239 +++++++++---------- src/pages/SearchPage/index.js | 20 +- 3 files changed, 132 insertions(+), 135 deletions(-) diff --git a/src/components/OptionListContextProvider.tsx b/src/components/OptionListContextProvider.tsx index 6c7e96d68078..d218e4873210 100644 --- a/src/components/OptionListContextProvider.tsx +++ b/src/components/OptionListContextProvider.tsx @@ -11,6 +11,7 @@ type Options = { type OptionsListContextProps = { options: Options; initializeOptions: () => void; + areOptionsInitialized: boolean; }; type OptionsListProviderProps = { @@ -24,6 +25,7 @@ const OptionsListContext = createContext({ personalDetails: [], }, initializeOptions: () => {}, + areOptionsInitialized: false, }); function OptionsListContextProvider({children}: OptionsListProviderProps) { @@ -52,7 +54,11 @@ function OptionsListContextProvider({children}: OptionsListProviderProps) { loadOptions(); }, [loadOptions]); - return ({options, initializeOptions}), [options, initializeOptions])}>{children}; + return ( + ({options, initializeOptions, areOptionsInitialized: areOptionsInitialized.current}), [options, initializeOptions])}> + {children} + + ); } const useOptionsListContext = () => useContext(OptionsListContext); diff --git a/src/libs/OptionsListUtils.ts b/src/libs/OptionsListUtils.ts index 3b08aa923aba..a142f8abcfc3 100644 --- a/src/libs/OptionsListUtils.ts +++ b/src/libs/OptionsListUtils.ts @@ -1329,6 +1329,10 @@ function isReportSelected(reportOption: ReportUtils.OptionData, selectedOptions: return selectedOptions.some((option) => (option.accountID && option.accountID === reportOption.accountID) || (option.reportID && option.reportID === reportOption.reportID)); } +type ReportOption = ReportUtils.OptionData & { + item: Report | PersonalDetails; +}; + function createOptionList(reports: OnyxCollection, personalDetails: OnyxEntry) { const reportMapForAccountIDs: Record = {}; // Sorting the reports works like this: @@ -1342,7 +1346,7 @@ function createOptionList(reports: OnyxCollection, personalDetails: Onyx return report?.lastVisibleActionCreated; }); orderedReports.reverse(); - const allReportOptions: ReportUtils.OptionData[] = []; + const allReportOptions: ReportOption[] = []; orderedReports.forEach((report) => { if (!report) { @@ -1364,8 +1368,9 @@ function createOptionList(reports: OnyxCollection, personalDetails: Onyx reportMapForAccountIDs[accountIDs[0]] = report; } - allReportOptions.push( - createOption( + allReportOptions.push({ + item: report, + ...createOption( accountIDs, personalDetails, report, @@ -1375,14 +1380,15 @@ function createOptionList(reports: OnyxCollection, personalDetails: Onyx forcePolicyNamePreview: true, }, ), - ); + }); }); const havingLoginPersonalDetails = Object.fromEntries( Object.entries(personalDetails ?? {}).filter(([, detail]) => !!detail?.login && !!detail.accountID && !detail?.isOptimisticPersonalDetail), ); - const allPersonalDetailsOptions = Object.values(havingLoginPersonalDetails).map((personalDetail) => - createOption( + const allPersonalDetailsOptions = Object.values(havingLoginPersonalDetails).map((personalDetail) => ({ + item: personalDetail, + ...createOption( [personalDetail?.accountID ?? -1], personalDetails, reportMapForAccountIDs[personalDetail?.accountID ?? -1], @@ -1392,7 +1398,7 @@ function createOptionList(reports: OnyxCollection, personalDetails: Onyx forcePolicyNamePreview: true, }, ), - ); + })); return { reports: allReportOptions, @@ -1401,11 +1407,13 @@ function createOptionList(reports: OnyxCollection, personalDetails: Onyx } /** - * Build the options + * filter options based on specific conditions */ function getOptions( - reports: OnyxCollection, - personalDetails: OnyxEntry, + options: { + reports: ReportOption[]; + personalDetails: ReportOption[]; + }, { reportActions = {}, betas = [], @@ -1483,26 +1491,13 @@ function getOptions( }; } - if (!isPersonalDetailsReady(personalDetails)) { - return { - recentReports: [], - personalDetails: [], - userToInvite: null, - currentUserOption: null, - categoryOptions: [], - tagOptions: [], - taxRatesOptions: [], - }; - } - - let recentReportOptions = []; - let personalDetailsOptions: ReportUtils.OptionData[] = []; - const reportMapForAccountIDs: Record = {}; const parsedPhoneNumber = PhoneNumber.parsePhoneNumber(LoginUtils.appendCountryCode(Str.removeSMSDomain(searchInputValue))); const searchValue = parsedPhoneNumber.possible ? parsedPhoneNumber.number?.e164 : searchInputValue.toLowerCase(); + const topmostReportId = Navigation.getTopmostReportId() ?? ''; // Filter out all the reports that shouldn't be displayed - const filteredReports = Object.values(reports ?? {}).filter((report) => { + const filteredReportOptions = options.reports.filter((option) => { + const report = option.item as Report; const {parentReportID, parentReportActionID} = report ?? {}; const canGetParentReport = parentReportID && parentReportActionID && allReportActions; const parentReportAction = canGetParentReport ? allReportActions[parentReportID]?.[parentReportActionID] ?? null : null; @@ -1511,7 +1506,7 @@ function getOptions( return ReportUtils.shouldReportBeInOptionList({ report, - currentReportId: Navigation.getTopmostReportId() ?? '', + currentReportId: topmostReportId, betas, policies, doesReportHaveViolations, @@ -1524,23 +1519,24 @@ function getOptions( // Sorting the reports works like this: // - Order everything by the last message timestamp (descending) // - All archived reports should remain at the bottom - const orderedReports = lodashSortBy(filteredReports, (report) => { + const orderedReportOptions = lodashSortBy(filteredReportOptions, (option) => { + const report = option.item as Report; if (ReportUtils.isArchivedRoom(report)) { return CONST.DATE.UNIX_EPOCH; } return report?.lastVisibleActionCreated; }); - orderedReports.reverse(); + orderedReportOptions.reverse(); + + const allReportOptions = orderedReportOptions.filter((option) => { + const report = option.item as Report; - const allReportOptions: ReportUtils.OptionData[] = []; - orderedReports.forEach((report) => { if (!report) { return; } const isThread = ReportUtils.isChatThread(report); - const isChatRoom = ReportUtils.isChatRoom(report); const isTaskReport = ReportUtils.isTaskReport(report); const isPolicyExpenseChat = ReportUtils.isPolicyExpenseChat(report); const isMoneyRequestReport = ReportUtils.isMoneyRequestReport(report); @@ -1582,33 +1578,12 @@ function getOptions( return; } - // Save the report in the map if this is a single participant so we can associate the reportID with the - // personal detail option later. Individuals should not be associated with single participant - // policyExpenseChats or chatRooms since those are not people. - if (accountIDs.length <= 1 && !isPolicyExpenseChat && !isChatRoom) { - reportMapForAccountIDs[accountIDs[0]] = report; - } - - allReportOptions.push( - createOption(accountIDs, personalDetails, report, reportActions, { - showChatPreviewLine, - forcePolicyNamePreview, - }), - ); + return option; }); - // We're only picking personal details that have logins set - // This is a temporary fix for all the logic that's been breaking because of the new privacy changes - // See https://github.com/Expensify/Expensify/issues/293465 for more context - // Moreover, we should not override the personalDetails object, otherwise the createOption util won't work properly, it returns incorrect tooltipText - const havingLoginPersonalDetails = !includeP2P - ? {} - : Object.fromEntries(Object.entries(personalDetails ?? {}).filter(([, detail]) => !!detail?.login && !!detail.accountID && !detail?.isOptimisticPersonalDetail)); - let allPersonalDetailsOptions = Object.values(havingLoginPersonalDetails).map((personalDetail) => - createOption([personalDetail?.accountID ?? -1], personalDetails, reportMapForAccountIDs[personalDetail?.accountID ?? -1], reportActions, { - showChatPreviewLine, - forcePolicyNamePreview, - }), - ); + + // HERE + + let allPersonalDetailsOptions = options.personalDetails; if (sortPersonalDetailsByAlphaAsc) { // PersonalDetails should be ordered Alphabetically by default - https://github.com/Expensify/App/issues/8220#issuecomment-1104009435 @@ -1629,6 +1604,9 @@ function getOptions( optionsToExclude.push({login}); }); + let recentReportOptions = []; + let personalDetailsOptions: ReportUtils.OptionData[] = []; + if (includeRecentReports) { for (const reportOption of allReportOptions) { // Stop adding options to the recentReports array when we reach the maxRecentReportsToShow value @@ -1794,10 +1772,17 @@ function getOptions( /** * Build the options for the Search view */ -function getSearchOptions(reports: Record, personalDetails: OnyxEntry, searchValue = '', betas: Beta[] = []): GetOptions { +function getSearchOptions( + options: { + reports: ReportOption[]; + personalDetails: ReportOption[]; + }, + searchValue = '', + betas: Beta[] = [], +): GetOptions { Timing.start(CONST.TIMING.LOAD_SEARCH_OPTIONS); Performance.markStart(CONST.TIMING.LOAD_SEARCH_OPTIONS); - const options = getOptions(reports, personalDetails, { + const optionList = getOptions(options, { betas, searchInputValue: searchValue.trim(), includeRecentReports: true, @@ -1816,11 +1801,18 @@ function getSearchOptions(reports: Record, personalDetails: Onyx Timing.end(CONST.TIMING.LOAD_SEARCH_OPTIONS); Performance.markEnd(CONST.TIMING.LOAD_SEARCH_OPTIONS); - return options; + return optionList; } -function getShareLogOptions(reports: OnyxCollection, personalDetails: OnyxEntry, searchValue = '', betas: Beta[] = []): GetOptions { - return getOptions(reports, personalDetails, { +function getShareLogOptions( + options: { + reports: ReportOption[]; + personalDetails: ReportOption[]; + }, + searchValue = '', + betas: Beta[] = [], +): GetOptions { + return getOptions(options, { betas, searchInputValue: searchValue.trim(), includeRecentReports: true, @@ -1869,8 +1861,8 @@ function getIOUConfirmationOptionsFromParticipants(participants: Participant[], * Build the options for the New Group view */ function getFilteredOptions( - reports: OnyxCollection, - personalDetails: OnyxEntry, + reports: ReportOption[] = [], + personalDetails: ReportOption[] = [], betas: OnyxEntry = [], searchValue = '', selectedOptions: Array> = [], @@ -1889,28 +1881,31 @@ function getFilteredOptions( taxRates: TaxRatesWithDefault = {} as TaxRatesWithDefault, includeSelfDM = false, ) { - return getOptions(reports, personalDetails, { - betas, - searchInputValue: searchValue.trim(), - selectedOptions, - includeRecentReports: true, - includePersonalDetails: true, - maxRecentReportsToShow: 5, - excludeLogins, - includeOwnedWorkspaceChats, - includeP2P, - includeCategories, - categories, - recentlyUsedCategories, - includeTags, - tags, - recentlyUsedTags, - canInviteUser, - includeSelectedOptions, - includeTaxRates, - taxRates, - includeSelfDM, - }); + return getOptions( + {reports, personalDetails}, + { + betas, + searchInputValue: searchValue.trim(), + selectedOptions, + includeRecentReports: true, + includePersonalDetails: true, + maxRecentReportsToShow: 5, + excludeLogins, + includeOwnedWorkspaceChats, + includeP2P, + includeCategories, + categories, + recentlyUsedCategories, + includeTags, + tags, + recentlyUsedTags, + canInviteUser, + includeSelectedOptions, + includeTaxRates, + taxRates, + includeSelfDM, + }, + ); } /** @@ -1918,8 +1913,8 @@ function getFilteredOptions( */ function getShareDestinationOptions( - reports: Record, - personalDetails: OnyxEntry, + reports: ReportOption[], + personalDetails: ReportOption[], betas: OnyxEntry = [], searchValue = '', selectedOptions: Array> = [], @@ -1927,24 +1922,27 @@ function getShareDestinationOptions( includeOwnedWorkspaceChats = true, excludeUnknownUsers = true, ) { - return getOptions(reports, personalDetails, { - betas, - searchInputValue: searchValue.trim(), - selectedOptions, - maxRecentReportsToShow: 0, // Unlimited - includeRecentReports: true, - includeMultipleParticipantReports: true, - includePersonalDetails: false, - showChatPreviewLine: true, - forcePolicyNamePreview: true, - includeThreads: true, - includeMoneyRequests: true, - includeTasks: true, - excludeLogins, - includeOwnedWorkspaceChats, - excludeUnknownUsers, - includeSelfDM: true, - }); + return getOptions( + {reports, personalDetails}, + { + betas, + searchInputValue: searchValue.trim(), + selectedOptions, + maxRecentReportsToShow: 0, // Unlimited + includeRecentReports: true, + includeMultipleParticipantReports: true, + includePersonalDetails: false, + showChatPreviewLine: true, + forcePolicyNamePreview: true, + includeThreads: true, + includeMoneyRequests: true, + includeTasks: true, + excludeLogins, + includeOwnedWorkspaceChats, + excludeUnknownUsers, + includeSelfDM: true, + }, + ); } /** @@ -1976,21 +1974,18 @@ function formatMemberForList(member: ReportUtils.OptionData): MemberForList { /** * Build the options for the Workspace Member Invite view */ -function getMemberInviteOptions( - personalDetails: OnyxEntry, - betas: Beta[] = [], - searchValue = '', - excludeLogins: string[] = [], - includeSelectedOptions = false, -): GetOptions { - return getOptions({}, personalDetails, { - betas, - searchInputValue: searchValue.trim(), - includePersonalDetails: true, - excludeLogins, - sortPersonalDetailsByAlphaAsc: true, - includeSelectedOptions, - }); +function getMemberInviteOptions(personalDetails: ReportOption[], betas: Beta[] = [], searchValue = '', excludeLogins: string[] = [], includeSelectedOptions = false): GetOptions { + return getOptions( + {reports: [], personalDetails}, + { + betas, + searchInputValue: searchValue.trim(), + includePersonalDetails: true, + excludeLogins, + sortPersonalDetailsByAlphaAsc: true, + includeSelectedOptions, + }, + ); } /** diff --git a/src/pages/SearchPage/index.js b/src/pages/SearchPage/index.js index e0f547ba4eae..bd0650c1d231 100644 --- a/src/pages/SearchPage/index.js +++ b/src/pages/SearchPage/index.js @@ -17,7 +17,6 @@ import Navigation from '@libs/Navigation/Navigation'; import * as OptionsListUtils from '@libs/OptionsListUtils'; import Performance from '@libs/Performance'; import * as ReportUtils from '@libs/ReportUtils'; -import reportPropTypes from '@pages/reportPropTypes'; import * as Report from '@userActions/Report'; import Timing from '@userActions/Timing'; import CONST from '@src/CONST'; @@ -30,9 +29,6 @@ const propTypes = { /** Beta features list */ betas: PropTypes.arrayOf(PropTypes.string), - /** All reports shared with the user */ - reports: PropTypes.objectOf(reportPropTypes), - /** Whether or not we are searching for reports on the server */ isSearchingForReports: PropTypes.bool, @@ -46,7 +42,6 @@ const propTypes = { const defaultProps = { betas: [], - reports: {}, isSearchingForReports: false, navigation: {}, }; @@ -58,13 +53,13 @@ const setPerformanceTimersEnd = () => { const SearchPageFooterInstance = ; -function SearchPage({betas, reports, isSearchingForReports, navigation}) { +function SearchPage({betas, isSearchingForReports, navigation}) { const [isScreenTransitionEnd, setIsScreenTransitionEnd] = useState(false); const {translate} = useLocalize(); const {isOffline} = useNetwork(); const themeStyles = useThemeStyles(); const personalDetails = usePersonalDetails(); - const {initializeOptions} = useOptionsListContext(); + const {options, initializeOptions, areOptionsInitialized} = useOptionsListContext(); const offlineMessage = isOffline ? [`${translate('common.youAppearToBeOffline')} ${translate('search.resultsAreLimited')}`, {isTranslated: true}] : ''; const [searchValue, debouncedSearchValue, setSearchValue] = useDebouncedState(''); @@ -84,7 +79,7 @@ function SearchPage({betas, reports, isSearchingForReports, navigation}) { userToInvite, headerMessage, } = useMemo(() => { - if (!isScreenTransitionEnd) { + if (!areOptionsInitialized && !isScreenTransitionEnd) { return { recentReports: {}, personalDetails: {}, @@ -92,10 +87,11 @@ function SearchPage({betas, reports, isSearchingForReports, navigation}) { headerMessage: '', }; } - const options = OptionsListUtils.getSearchOptions(reports, personalDetails, debouncedSearchValue.trim(), betas); - const header = OptionsListUtils.getHeaderMessage(options.recentReports.length + options.personalDetails.length !== 0, Boolean(options.userToInvite), debouncedSearchValue); - return {...options, headerMessage: header}; - }, [debouncedSearchValue, reports, personalDetails, betas, isScreenTransitionEnd]); + + const optionList = OptionsListUtils.getSearchOptions(options, debouncedSearchValue.trim(), betas); + const header = OptionsListUtils.getHeaderMessage(options.reports.length + options.personalDetails.length !== 0, Boolean(options.userToInvite), debouncedSearchValue); + return {...optionList, headerMessage: header}; + }, [areOptionsInitialized, isScreenTransitionEnd, options, betas, debouncedSearchValue]); useEffect(() => { initializeOptions(); From dc35055073b34f237c9d4d6a3c589bf53ac8f892 Mon Sep 17 00:00:00 2001 From: Yogesh Bhatt <65986357+ygshbht@users.noreply.github.com> Date: Tue, 12 Mar 2024 03:11:46 +0530 Subject: [PATCH 207/500] Example update src/hooks/useGeographicalStateFromRoute.ts Co-authored-by: Vit Horacek <36083550+mountiny@users.noreply.github.com> --- src/hooks/useGeographicalStateFromRoute.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/hooks/useGeographicalStateFromRoute.ts b/src/hooks/useGeographicalStateFromRoute.ts index 69482b8c3a3b..bddc2b5c9a07 100644 --- a/src/hooks/useGeographicalStateFromRoute.ts +++ b/src/hooks/useGeographicalStateFromRoute.ts @@ -12,10 +12,10 @@ const removeHash = (arg: string): string => arg.replace(/-hash-.*$/, ''); /** * Extracts the 'state' (default) query parameter from the route/ url and validates it against COMMON_CONST.STATES, returning its ISO code or `undefined`. - * Example 1: Url: https://dev.new.expensify.com:8082/settings/profile/address?state=MO Returns: MO - * Example 2: Url: https://dev.new.expensify.com:8082/settings/profile/address?state=ASDF Returns: undefined - * Example 3: Url: https://dev.new.expensify.com:8082/settings/profile/address Returns: undefined - * Example 4: Url: https://dev.new.expensify.com:8082/settings/profile/address?state=MO-hash-a12341 Returns: MO + * Example 1: Url: https://new.expensify.com/settings/profile/address?state=MO Returns: MO + * Example 2: Url: https://new.expensify.com/settings/profile/address?state=ASDF Returns: undefined + * Example 3: Url: https://new.expensify.com/settings/profile/address Returns: undefined + * Example 4: Url: https://new.expensify.com/settings/profile/address?state=MO-hash-a12341 Returns: MO */ export default function useGeographicalStateFromRoute(stateParamName = 'state'): State | undefined { const route = useRoute>(); From 78afcd5da67d02e4a51518b2edc15319ed0bf967 Mon Sep 17 00:00:00 2001 From: Yogesh Bhatt Date: Tue, 12 Mar 2024 03:15:29 +0530 Subject: [PATCH 208/500] Refactor linking config StateSelector --- src/libs/Navigation/linkingConfig/config.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/Navigation/linkingConfig/config.ts b/src/libs/Navigation/linkingConfig/config.ts index 34526091e471..787a6054db80 100644 --- a/src/libs/Navigation/linkingConfig/config.ts +++ b/src/libs/Navigation/linkingConfig/config.ts @@ -490,10 +490,10 @@ const config: LinkingOptions['config'] = { [SCREENS.MONEY_REQUEST.CURRENCY]: ROUTES.MONEY_REQUEST_CURRENCY.route, [SCREENS.MONEY_REQUEST.RECEIPT]: ROUTES.MONEY_REQUEST_RECEIPT.route, [SCREENS.MONEY_REQUEST.DISTANCE]: ROUTES.MONEY_REQUEST_DISTANCE.route, + [SCREENS.MONEY_REQUEST.STATE_SELECTOR]: {path: ROUTES.MONEY_REQUEST_STATE_SELECTOR.route, exact: true}, [SCREENS.IOU_SEND.ENABLE_PAYMENTS]: ROUTES.IOU_SEND_ENABLE_PAYMENTS, [SCREENS.IOU_SEND.ADD_BANK_ACCOUNT]: ROUTES.IOU_SEND_ADD_BANK_ACCOUNT, [SCREENS.IOU_SEND.ADD_DEBIT_CARD]: ROUTES.IOU_SEND_ADD_DEBIT_CARD, - [SCREENS.MONEY_REQUEST.STATE_SELECTOR]: {path: ROUTES.MONEY_REQUEST_STATE_SELECTOR.route, exact: true}, }, }, [SCREENS.RIGHT_MODAL.SPLIT_DETAILS]: { From b52b28c227ee0e69d0dd9e4586b05548e2d3f6e7 Mon Sep 17 00:00:00 2001 From: Etotaziba Olei Date: Tue, 12 Mar 2024 10:29:14 +0100 Subject: [PATCH 209/500] check and return if same tax rate is selected --- src/pages/EditRequestPage.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/EditRequestPage.js b/src/pages/EditRequestPage.js index 0498c98b087a..91694f5cefc3 100644 --- a/src/pages/EditRequestPage.js +++ b/src/pages/EditRequestPage.js @@ -143,7 +143,7 @@ function EditRequestPage({report, route, policy, policyCategories, policyTags, p (transactionChanges) => { const newTaxCode = transactionChanges.data.code; - if (!newTaxCode) { + if (newTaxCode === undefined || newTaxCode === TransactionUtils.getTaxCode(transaction)) { Navigation.dismissModal(); return; } From 5626316a710f8e0024d2553d7e1fcb94211c5c25 Mon Sep 17 00:00:00 2001 From: Etotaziba Olei Date: Tue, 12 Mar 2024 11:29:56 +0100 Subject: [PATCH 210/500] add option for transaction draft --- src/libs/actions/IOU.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libs/actions/IOU.ts b/src/libs/actions/IOU.ts index b630e94a0742..3b6b306c08f3 100644 --- a/src/libs/actions/IOU.ts +++ b/src/libs/actions/IOU.ts @@ -4119,8 +4119,8 @@ function setMoneyRequestTaxRate(transactionID: string, taxRate: TaxRate) { Onyx.merge(`${ONYXKEYS.COLLECTION.TRANSACTION_DRAFT}${transactionID}`, {taxRate}); } -function setMoneyRequestTaxAmount(transactionID: string, taxAmount: number) { - Onyx.merge(`${ONYXKEYS.COLLECTION.TRANSACTION_DRAFT}${transactionID}`, {taxAmount}); +function setMoneyRequestTaxAmount(transactionID: string, taxAmount: number, isDraft: boolean) { + Onyx.merge(`${isDraft ? ONYXKEYS.COLLECTION.TRANSACTION_DRAFT : ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`, {taxAmount}); } function setMoneyRequestBillable(billable: boolean) { From 9ecc9838099ceae49f1512ffb80cfc74f3d7e86c Mon Sep 17 00:00:00 2001 From: Etotaziba Olei Date: Tue, 12 Mar 2024 11:31:40 +0100 Subject: [PATCH 211/500] optimistically update tax amount when expense amount is edited --- src/pages/EditRequestPage.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/pages/EditRequestPage.js b/src/pages/EditRequestPage.js index 91694f5cefc3..86cd99ed276b 100644 --- a/src/pages/EditRequestPage.js +++ b/src/pages/EditRequestPage.js @@ -77,7 +77,7 @@ const defaultProps = { const getTaxAmount = (transactionAmount, transactionTaxCode, taxRates) => { const percentage = (transactionTaxCode ? taxRates.taxes[transactionTaxCode].value : taxRates.defaultValue) || ''; - return CurrencyUtils.convertToBackendAmount(Number.parseFloat(TransactionUtils.calculateTaxAmount(percentage, Math.abs(transactionAmount)))); + return CurrencyUtils.convertToBackendAmount(Number.parseFloat(TransactionUtils.calculateTaxAmount(percentage, transactionAmount))); }; function EditRequestPage({report, route, policy, policyCategories, policyTags, parentReportActions, transaction}) { @@ -163,7 +163,8 @@ function EditRequestPage({report, route, policy, policyCategories, policyTags, p Navigation.dismissModal(); return; } - + // optimisticall set tax amount. + IOU.setMoneyRequestTaxAmount(transaction.transactionID, getTaxAmount(-newAmount, transactionTaxCode, taxRates)); IOU.updateMoneyRequestAmountAndCurrency(transaction.transactionID, report.reportID, newCurrency, newAmount, policy, policyTags, policyCategories); Navigation.dismissModal(); }, From dea2b668a900b5940d802d9c75aea19866da6a8c Mon Sep 17 00:00:00 2001 From: Etotaziba Olei Date: Tue, 12 Mar 2024 11:32:40 +0100 Subject: [PATCH 212/500] add isDraft option for IOU.setMoneyRequestTaxAmount --- src/pages/iou/request/step/IOURequestStepTaxAmountPage.js | 2 +- src/pages/iou/request/step/IOURequestStepTaxRatePage.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/pages/iou/request/step/IOURequestStepTaxAmountPage.js b/src/pages/iou/request/step/IOURequestStepTaxAmountPage.js index d28d350677fd..a43d3bc01c09 100644 --- a/src/pages/iou/request/step/IOURequestStepTaxAmountPage.js +++ b/src/pages/iou/request/step/IOURequestStepTaxAmountPage.js @@ -112,7 +112,7 @@ function IOURequestStepTaxAmountPage({ const updateTaxAmount = (currentAmount) => { isSaveButtonPressed.current = true; const amountInSmallestCurrencyUnits = CurrencyUtils.convertToBackendAmount(Number.parseFloat(currentAmount.amount)); - IOU.setMoneyRequestTaxAmount(transactionID, amountInSmallestCurrencyUnits); + IOU.setMoneyRequestTaxAmount(transactionID, amountInSmallestCurrencyUnits, true); IOU.setMoneyRequestCurrency_temporaryForRefactor(transactionID, currency || CONST.CURRENCY.USD, true); diff --git a/src/pages/iou/request/step/IOURequestStepTaxRatePage.js b/src/pages/iou/request/step/IOURequestStepTaxRatePage.js index f374b6986ef2..d4a2c10d24b0 100644 --- a/src/pages/iou/request/step/IOURequestStepTaxRatePage.js +++ b/src/pages/iou/request/step/IOURequestStepTaxRatePage.js @@ -71,7 +71,7 @@ function IOURequestStepTaxRatePage({ const taxAmount = getTaxAmount(taxRates, taxes.text, transaction.amount); const amountInSmallestCurrencyUnits = CurrencyUtils.convertToBackendAmount(Number.parseFloat(taxAmount)); IOU.setMoneyRequestTaxRate(transaction.transactionID, taxes); - IOU.setMoneyRequestTaxAmount(transaction.transactionID, amountInSmallestCurrencyUnits); + IOU.setMoneyRequestTaxAmount(transaction.transactionID, amountInSmallestCurrencyUnits, true); Navigation.goBack(backTo); }; From 72e7b681d6234096fd796317845919537e886f8c Mon Sep 17 00:00:00 2001 From: Etotaziba Olei Date: Tue, 12 Mar 2024 11:42:11 +0100 Subject: [PATCH 213/500] add useCallback missing deps --- src/pages/EditRequestPage.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/EditRequestPage.js b/src/pages/EditRequestPage.js index 86cd99ed276b..7fcfab735248 100644 --- a/src/pages/EditRequestPage.js +++ b/src/pages/EditRequestPage.js @@ -168,7 +168,7 @@ function EditRequestPage({report, route, policy, policyCategories, policyTags, p IOU.updateMoneyRequestAmountAndCurrency(transaction.transactionID, report.reportID, newCurrency, newAmount, policy, policyTags, policyCategories); Navigation.dismissModal(); }, - [transaction, report, policy, policyTags, policyCategories], + [transaction, report, policy, policyTags, policyCategories, taxRates, transactionTaxCode], ); const saveTag = useCallback( From 50c823c7ab61d2743dd5ebec6e1c8dc666057123 Mon Sep 17 00:00:00 2001 From: Tomasz Misiukiewicz Date: Tue, 12 Mar 2024 13:36:08 +0100 Subject: [PATCH 214/500] update usage in search page --- src/components/OptionListContextProvider.tsx | 11 ++--- src/libs/OptionsListUtils.ts | 42 +++++++------------- src/pages/SearchPage/index.tsx | 18 +++------ 3 files changed, 24 insertions(+), 47 deletions(-) diff --git a/src/components/OptionListContextProvider.tsx b/src/components/OptionListContextProvider.tsx index d218e4873210..36ce61ec615f 100644 --- a/src/components/OptionListContextProvider.tsx +++ b/src/components/OptionListContextProvider.tsx @@ -1,15 +1,10 @@ import React, {createContext, useCallback, useContext, useMemo, useRef, useState} from 'react'; import * as OptionsListUtils from '@libs/OptionsListUtils'; -import type {OptionData} from '@libs/ReportUtils'; +import type {OptionList} from '@libs/OptionsListUtils'; import {usePersonalDetails, useReports} from './OnyxProvider'; -type Options = { - reports: OptionData[]; - personalDetails: OptionData[]; -}; - type OptionsListContextProps = { - options: Options; + options: OptionList; initializeOptions: () => void; areOptionsInitialized: boolean; }; @@ -30,7 +25,7 @@ const OptionsListContext = createContext({ function OptionsListContextProvider({children}: OptionsListProviderProps) { const areOptionsInitialized = useRef(false); - const [options, setOptions] = useState({ + const [options, setOptions] = useState({ reports: [], personalDetails: [], }); diff --git a/src/libs/OptionsListUtils.ts b/src/libs/OptionsListUtils.ts index 2d18010fc434..95c99fa2f890 100644 --- a/src/libs/OptionsListUtils.ts +++ b/src/libs/OptionsListUtils.ts @@ -53,6 +53,15 @@ import * as TaskUtils from './TaskUtils'; import * as TransactionUtils from './TransactionUtils'; import * as UserUtils from './UserUtils'; +type ReportOption = ReportUtils.OptionData & { + item: Report | PersonalDetails; +}; + +type OptionList = { + reports: ReportOption[]; + personalDetails: ReportOption[]; +}; + type Tag = { enabled: boolean; name: string; @@ -1329,10 +1338,6 @@ function isReportSelected(reportOption: ReportUtils.OptionData, selectedOptions: return selectedOptions.some((option) => (option.accountID && option.accountID === reportOption.accountID) || (option.reportID && option.reportID === reportOption.reportID)); } -type ReportOption = ReportUtils.OptionData & { - item: Report | PersonalDetails; -}; - function createOptionList(reports: OnyxCollection, personalDetails: OnyxEntry) { const reportMapForAccountIDs: Record = {}; // Sorting the reports works like this: @@ -1402,7 +1407,7 @@ function createOptionList(reports: OnyxCollection, personalDetails: Onyx return { reports: allReportOptions, - personalDetails: allPersonalDetailsOptions, + personalDetails: allPersonalDetailsOptions as ReportOption[], }; } @@ -1410,10 +1415,7 @@ function createOptionList(reports: OnyxCollection, personalDetails: Onyx * filter options based on specific conditions */ function getOptions( - options: { - reports: ReportOption[]; - personalDetails: ReportOption[]; - }, + options: OptionList, { reportActions = {}, betas = [], @@ -1704,7 +1706,7 @@ function getOptions( // Generates an optimistic account ID for new users not yet saved in Onyx const optimisticAccountID = UserUtils.generateAccountID(searchValue); const personalDetailsExtended = { - ...personalDetails, + ...allPersonalDetails, [optimisticAccountID]: { accountID: optimisticAccountID, login: searchValue, @@ -1772,14 +1774,7 @@ function getOptions( /** * Build the options for the Search view */ -function getSearchOptions( - options: { - reports: ReportOption[]; - personalDetails: ReportOption[]; - }, - searchValue = '', - betas: Beta[] = [], -): GetOptions { +function getSearchOptions(options: OptionList, searchValue = '', betas: Beta[] = []): GetOptions { Timing.start(CONST.TIMING.LOAD_SEARCH_OPTIONS); Performance.markStart(CONST.TIMING.LOAD_SEARCH_OPTIONS); const optionList = getOptions(options, { @@ -1804,14 +1799,7 @@ function getSearchOptions( return optionList; } -function getShareLogOptions( - options: { - reports: ReportOption[]; - personalDetails: ReportOption[]; - }, - searchValue = '', - betas: Beta[] = [], -): GetOptions { +function getShareLogOptions(options: OptionList, searchValue = '', betas: Beta[] = []): GetOptions { return getOptions(options, { betas, searchInputValue: searchValue.trim(), @@ -2132,4 +2120,4 @@ export { createOptionList, }; -export type {MemberForList, CategorySection, GetOptions}; +export type {MemberForList, CategorySection, GetOptions, OptionList}; diff --git a/src/pages/SearchPage/index.tsx b/src/pages/SearchPage/index.tsx index 8d2080995dd2..0fec77506793 100644 --- a/src/pages/SearchPage/index.tsx +++ b/src/pages/SearchPage/index.tsx @@ -1,7 +1,7 @@ import type {StackScreenProps} from '@react-navigation/stack'; import React, {useEffect, useMemo, 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 HeaderWithBackButton from '@components/HeaderWithBackButton'; import {usePersonalDetails} from '@components/OnyxProvider'; @@ -31,9 +31,6 @@ type SearchPageOnyxProps = { /** Beta features list */ betas: OnyxEntry; - /** All reports shared with the user */ - reports: OnyxCollection; - /** Whether or not we are searching for reports on the server */ isSearchingForReports: OnyxEntry; }; @@ -55,7 +52,7 @@ const setPerformanceTimersEnd = () => { const SearchPageFooterInstance = ; -function SearchPage({betas, reports, isSearchingForReports, navigation}: SearchPageProps) { +function SearchPage({betas, isSearchingForReports, navigation}: SearchPageProps) { const [isScreenTransitionEnd, setIsScreenTransitionEnd] = useState(false); const {translate} = useLocalize(); const {isOffline} = useNetwork(); @@ -89,8 +86,8 @@ function SearchPage({betas, reports, isSearchingForReports, navigation}: SearchP headerMessage: '', }; } - const optionList = OptionsListUtils.getSearchOptions(options, debouncedSearchValue.trim(), betas); - const header = OptionsListUtils.getHeaderMessage(options.reports.length + options.personalDetails.length !== 0, Boolean(options.userToInvite), debouncedSearchValue); + const optionList = OptionsListUtils.getSearchOptions(options, debouncedSearchValue.trim(), betas ?? []); + const header = OptionsListUtils.getHeaderMessage(optionList.recentReports.length + optionList.personalDetails.length !== 0, Boolean(optionList.userToInvite), debouncedSearchValue); return {...optionList, headerMessage: header}; }, [areOptionsInitialized, isScreenTransitionEnd, options, debouncedSearchValue, betas]); @@ -166,7 +163,7 @@ function SearchPage({betas, reports, isSearchingForReports, navigation}: SearchP /> - sections={didScreenTransitionEnd && isOptionsDataReady ? sections : CONST.EMPTY_ARRAY} + sections={(areOptionsInitialized || didScreenTransitionEnd) && isOptionsDataReady ? sections : CONST.EMPTY_ARRAY} ListItem={UserListItem} textInputValue={searchValue} textInputLabel={translate('optionsSelector.nameEmailOrPhoneNumber')} @@ -176,7 +173,7 @@ function SearchPage({betas, reports, isSearchingForReports, navigation}: SearchP onLayout={setPerformanceTimersEnd} autoFocus onSelectRow={selectReport} - showLoadingPlaceholder={!didScreenTransitionEnd || !isOptionsDataReady} + showLoadingPlaceholder={(!areOptionsInitialized && !didScreenTransitionEnd) || !isOptionsDataReady} // showLoadingPlaceholder={(!areOptionsInitialized && !didScreenTransitionEnd) || !isOptionsDataReady} footerContent={SearchPageFooterInstance} isLoadingNewOptions={isSearchingForReports ?? undefined} /> @@ -190,9 +187,6 @@ function SearchPage({betas, reports, isSearchingForReports, navigation}: SearchP SearchPage.displayName = 'SearchPage'; export default withOnyx({ - reports: { - key: ONYXKEYS.COLLECTION.REPORT, - }, betas: { key: ONYXKEYS.BETAS, }, From 2c7d2313828ba9c9190fd5edb41bd16c734ae24e Mon Sep 17 00:00:00 2001 From: Tomasz Misiukiewicz Date: Tue, 12 Mar 2024 14:23:56 +0100 Subject: [PATCH 215/500] update options list utils --- src/libs/OptionsListUtils.ts | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/libs/OptionsListUtils.ts b/src/libs/OptionsListUtils.ts index 95c99fa2f890..b6804d0a6ff1 100644 --- a/src/libs/OptionsListUtils.ts +++ b/src/libs/OptionsListUtils.ts @@ -1500,6 +1500,7 @@ function getOptions( // Filter out all the reports that shouldn't be displayed const filteredReportOptions = options.reports.filter((option) => { const report = option.item as Report; + const {parentReportID, parentReportActionID} = report ?? {}; const canGetParentReport = parentReportID && parentReportActionID && allReportActions; const parentReportAction = canGetParentReport ? allReportActions[parentReportID]?.[parentReportActionID] ?? null : null; @@ -1583,8 +1584,6 @@ function getOptions( return option; }); - // HERE - let allPersonalDetailsOptions = options.personalDetails; if (sortPersonalDetailsByAlphaAsc) { @@ -1611,6 +1610,11 @@ function getOptions( if (includeRecentReports) { for (const reportOption of allReportOptions) { + // update the alternate text if needed + if ((!!reportOption.isChatRoom || reportOption.isPolicyExpenseChat) && forcePolicyNamePreview) { + reportOption.alternateText = ReportUtils.getChatRoomSubtitle(reportOption.item as Report); + } + // Stop adding options to the recentReports array when we reach the maxRecentReportsToShow value if (recentReportOptions.length > 0 && recentReportOptions.length === maxRecentReportsToShow) { break; From b1bd11fa5b43ab4419376511fa6037d68dc62a59 Mon Sep 17 00:00:00 2001 From: Tomasz Misiukiewicz Date: Tue, 12 Mar 2024 14:29:53 +0100 Subject: [PATCH 216/500] create reusable initializer for search pages --- src/components/OptionListContextProvider.tsx | 18 ++++++++++++++++-- src/pages/SearchPage/index.tsx | 8 ++------ 2 files changed, 18 insertions(+), 8 deletions(-) diff --git a/src/components/OptionListContextProvider.tsx b/src/components/OptionListContextProvider.tsx index 36ce61ec615f..15f63eccda72 100644 --- a/src/components/OptionListContextProvider.tsx +++ b/src/components/OptionListContextProvider.tsx @@ -1,4 +1,4 @@ -import React, {createContext, useCallback, useContext, useMemo, useRef, useState} from 'react'; +import React, {createContext, useCallback, useContext, useEffect, useMemo, useRef, useState} from 'react'; import * as OptionsListUtils from '@libs/OptionsListUtils'; import type {OptionList} from '@libs/OptionsListUtils'; import {usePersonalDetails, useReports} from './OnyxProvider'; @@ -58,4 +58,18 @@ function OptionsListContextProvider({children}: OptionsListProviderProps) { const useOptionsListContext = () => useContext(OptionsListContext); -export {OptionsListContextProvider, useOptionsListContext}; +// Hook to use the OptionsListContext with an initializer to load the options +const useOptionsList = () => { + const {initializeOptions, ...optionsListContext} = useOptionsListContext(); + + useEffect(() => { + initializeOptions(); + }, [initializeOptions, optionsListContext]); + + return { + initializeOptions, + ...optionsListContext, + }; +}; + +export {OptionsListContextProvider, useOptionsListContext, useOptionsList}; diff --git a/src/pages/SearchPage/index.tsx b/src/pages/SearchPage/index.tsx index 0fec77506793..b588fa88d0e4 100644 --- a/src/pages/SearchPage/index.tsx +++ b/src/pages/SearchPage/index.tsx @@ -5,7 +5,7 @@ import type {OnyxEntry} from 'react-native-onyx'; import {withOnyx} from 'react-native-onyx'; import HeaderWithBackButton from '@components/HeaderWithBackButton'; import {usePersonalDetails} from '@components/OnyxProvider'; -import {useOptionsListContext} from '@components/OptionListContextProvider'; +import {useOptionsList} from '@components/OptionListContextProvider'; import ScreenWrapper from '@components/ScreenWrapper'; import SelectionList from '@components/SelectionList'; import UserListItem from '@components/SelectionList/UserListItem'; @@ -58,7 +58,7 @@ function SearchPage({betas, isSearchingForReports, navigation}: SearchPageProps) const {isOffline} = useNetwork(); const themeStyles = useThemeStyles(); const personalDetails = usePersonalDetails(); - const {options, initializeOptions, areOptionsInitialized} = useOptionsListContext(); + const {options, areOptionsInitialized} = useOptionsList(); const offlineMessage: MaybePhraseKey = isOffline ? [`${translate('common.youAppearToBeOffline')} ${translate('search.resultsAreLimited')}`, {isTranslated: true}] : ''; const [searchValue, debouncedSearchValue, setSearchValue] = useDebouncedState(''); @@ -91,10 +91,6 @@ function SearchPage({betas, isSearchingForReports, navigation}: SearchPageProps) return {...optionList, headerMessage: header}; }, [areOptionsInitialized, isScreenTransitionEnd, options, debouncedSearchValue, betas]); - useEffect(() => { - initializeOptions(); - }, [initializeOptions]); - const sections = useMemo((): SearchPageSectionList => { const newSections: SearchPageSectionList = []; let indexOffset = 0; From 499eb8406364e1432546eed700d7411a86b09f02 Mon Sep 17 00:00:00 2001 From: Tomasz Misiukiewicz Date: Tue, 12 Mar 2024 14:42:31 +0100 Subject: [PATCH 217/500] add options to new chat page --- src/pages/NewChatPage.tsx | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/pages/NewChatPage.tsx b/src/pages/NewChatPage.tsx index 72393e89ae1a..807df128a590 100755 --- a/src/pages/NewChatPage.tsx +++ b/src/pages/NewChatPage.tsx @@ -4,6 +4,7 @@ import {withOnyx} from 'react-native-onyx'; import type {OnyxCollection, OnyxEntry} from 'react-native-onyx'; import KeyboardAvoidingView from '@components/KeyboardAvoidingView'; import OfflineIndicator from '@components/OfflineIndicator'; +import {useOptionsList} from '@components/OptionListContextProvider'; import OptionsSelector from '@components/OptionsSelector'; import ScreenWrapper from '@components/ScreenWrapper'; import useAutoFocusInput from '@hooks/useAutoFocusInput'; @@ -48,6 +49,7 @@ const excludedGroupEmails = CONST.EXPENSIFY_EMAILS.filter((value) => value !== C function NewChatPage({betas, isGroupChat, personalDetails, reports, isSearchingForReports, dismissedReferralBanners}: NewChatPageProps) { const {translate} = useLocalize(); + const {options} = useOptionsList(); const styles = useThemeStyles(); const [searchTerm, setSearchTerm] = useState(''); const [filteredRecentReports, setFilteredRecentReports] = useState([]); @@ -131,8 +133,8 @@ function NewChatPage({betas, isGroupChat, personalDetails, reports, isSearchingF personalDetails: newChatPersonalDetails, userToInvite, } = OptionsListUtils.getFilteredOptions( - reports, - personalDetails, + options.reports ?? [], + options.personalDetails ?? [], betas ?? [], searchTerm, newSelectedOptions, @@ -185,8 +187,8 @@ function NewChatPage({betas, isGroupChat, personalDetails, reports, isSearchingF personalDetails: newChatPersonalDetails, userToInvite, } = OptionsListUtils.getFilteredOptions( - reports, - personalDetails, + options.reports ?? [], + options.personalDetails ?? [], betas ?? [], searchTerm, selectedOptions, From 122a6fb97bd8ec04a7b79618f32161077fb40db8 Mon Sep 17 00:00:00 2001 From: Tomasz Misiukiewicz Date: Tue, 12 Mar 2024 15:35:15 +0100 Subject: [PATCH 218/500] update other occurencies of getoptions --- src/components/CategoryPicker.tsx | 4 +-- src/components/TaxPicker.tsx | 2 +- src/libs/OptionsListUtils.ts | 2 +- src/pages/RoomInvitePage.tsx | 4 ++- .../ShareLogList/BaseShareLogList.tsx | 8 +++-- src/pages/tasks/TaskAssigneeSelectorModal.tsx | 19 +++++------- .../TaskShareDestinationSelectorModal.tsx | 29 ++++++++----------- src/pages/workspace/WorkspaceInvitePage.tsx | 4 ++- 8 files changed, 35 insertions(+), 37 deletions(-) diff --git a/src/components/CategoryPicker.tsx b/src/components/CategoryPicker.tsx index 3033bf118e8f..0307b67114e5 100644 --- a/src/components/CategoryPicker.tsx +++ b/src/components/CategoryPicker.tsx @@ -47,8 +47,8 @@ function CategoryPicker({selectedCategory, policyCategories, policyRecentlyUsedC const [sections, headerMessage, shouldShowTextInput] = useMemo(() => { const validPolicyRecentlyUsedCategories = policyRecentlyUsedCategories?.filter((p) => !isEmptyObject(p)); const {categoryOptions} = OptionsListUtils.getFilteredOptions( - {}, - {}, + [], + [], [], debouncedSearchValue, selectedOptions, diff --git a/src/components/TaxPicker.tsx b/src/components/TaxPicker.tsx index 664aa741c400..5e65a2c940be 100644 --- a/src/components/TaxPicker.tsx +++ b/src/components/TaxPicker.tsx @@ -52,7 +52,7 @@ function TaxPicker({selectedTaxRate = '', taxRates, insets, onSubmit}: TaxPicker }, [selectedTaxRate]); const sections = useMemo(() => { - const {taxRatesOptions} = OptionsListUtils.getFilteredOptions({}, {}, [], searchValue, selectedOptions, [], false, false, false, {}, [], false, {}, [], false, false, true, taxRates); + const {taxRatesOptions} = OptionsListUtils.getFilteredOptions([], [], [], searchValue, selectedOptions, [], false, false, false, {}, [], false, {}, [], false, false, true, taxRates); return taxRatesOptions; }, [taxRates, searchValue, selectedOptions]); diff --git a/src/libs/OptionsListUtils.ts b/src/libs/OptionsListUtils.ts index b6804d0a6ff1..3987d6ebe626 100644 --- a/src/libs/OptionsListUtils.ts +++ b/src/libs/OptionsListUtils.ts @@ -2124,4 +2124,4 @@ export { createOptionList, }; -export type {MemberForList, CategorySection, GetOptions, OptionList}; +export type {MemberForList, CategorySection, GetOptions, OptionList, ReportOption}; diff --git a/src/pages/RoomInvitePage.tsx b/src/pages/RoomInvitePage.tsx index 7bcd64397e20..1c0597a34ed9 100644 --- a/src/pages/RoomInvitePage.tsx +++ b/src/pages/RoomInvitePage.tsx @@ -9,6 +9,7 @@ import {withOnyx} from 'react-native-onyx'; import FullPageNotFoundView from '@components/BlockingViews/FullPageNotFoundView'; import FormAlertWithSubmitButton from '@components/FormAlertWithSubmitButton'; import HeaderWithBackButton from '@components/HeaderWithBackButton'; +import {useOptionsList} from '@components/OptionListContextProvider'; import ScreenWrapper from '@components/ScreenWrapper'; import SelectionList from '@components/SelectionList'; import type {Section} from '@components/SelectionList/types'; @@ -51,6 +52,7 @@ function RoomInvitePage({betas, personalDetails, report, policies}: RoomInvitePa const [userToInvite, setUserToInvite] = useState(null); const [didScreenTransitionEnd, setDidScreenTransitionEnd] = useState(false); const navigation: StackNavigationProp = useNavigation(); + const {options} = useOptionsList(); // Any existing participants and Expensify emails should not be eligible for invitation const excludedUsers = useMemo( @@ -62,7 +64,7 @@ function RoomInvitePage({betas, personalDetails, report, policies}: RoomInvitePa ); useEffect(() => { - const inviteOptions = OptionsListUtils.getMemberInviteOptions(personalDetails, betas ?? [], searchTerm, excludedUsers); + const inviteOptions = OptionsListUtils.getMemberInviteOptions(options.personalDetails, betas ?? [], searchTerm, excludedUsers); // Update selectedOptions with the latest personalDetails information const detailsMap: Record = {}; diff --git a/src/pages/settings/AboutPage/ShareLogList/BaseShareLogList.tsx b/src/pages/settings/AboutPage/ShareLogList/BaseShareLogList.tsx index 1a7b23477349..d200e5312016 100644 --- a/src/pages/settings/AboutPage/ShareLogList/BaseShareLogList.tsx +++ b/src/pages/settings/AboutPage/ShareLogList/BaseShareLogList.tsx @@ -3,6 +3,7 @@ import {View} from 'react-native'; import {withOnyx} from 'react-native-onyx'; import HeaderWithBackButton from '@components/HeaderWithBackButton'; import {usePersonalDetails} from '@components/OnyxProvider'; +import {useOptionsList} from '@components/OptionListContextProvider'; import OptionsSelector from '@components/OptionsSelector'; import ScreenWrapper from '@components/ScreenWrapper'; import useLocalize from '@hooks/useLocalize'; @@ -17,7 +18,7 @@ import ROUTES from '@src/ROUTES'; import type {Report} from '@src/types/onyx'; import type {BaseShareLogListOnyxProps, BaseShareLogListProps} from './types'; -function BaseShareLogList({betas, reports, onAttachLogToReport}: BaseShareLogListProps) { +function BaseShareLogList({betas, onAttachLogToReport}: BaseShareLogListProps) { const [searchValue, setSearchValue] = useState(''); const [searchOptions, setSearchOptions] = useState>({ recentReports: [], @@ -30,20 +31,21 @@ function BaseShareLogList({betas, reports, onAttachLogToReport}: BaseShareLogLis const styles = useThemeStyles(); const isMounted = useRef(false); const personalDetails = usePersonalDetails(); + const {options} = useOptionsList(); const updateOptions = useCallback(() => { const { recentReports: localRecentReports, personalDetails: localPersonalDetails, userToInvite: localUserToInvite, - } = OptionsListUtils.getShareLogOptions(reports, personalDetails, searchValue.trim(), betas ?? []); + } = OptionsListUtils.getShareLogOptions(options, searchValue.trim(), betas ?? []); setSearchOptions({ recentReports: localRecentReports, personalDetails: localPersonalDetails, userToInvite: localUserToInvite, }); - }, [betas, personalDetails, reports, searchValue]); + }, [betas, options, searchValue]); const isOptionsDataReady = ReportUtils.isReportDataReady() && OptionsListUtils.isPersonalDetailsReady(personalDetails); diff --git a/src/pages/tasks/TaskAssigneeSelectorModal.tsx b/src/pages/tasks/TaskAssigneeSelectorModal.tsx index 0ffb33b7590b..ef097ffcfd71 100644 --- a/src/pages/tasks/TaskAssigneeSelectorModal.tsx +++ b/src/pages/tasks/TaskAssigneeSelectorModal.tsx @@ -7,7 +7,8 @@ import {withOnyx} from 'react-native-onyx'; import type {OnyxCollection, OnyxEntry} from 'react-native-onyx'; import FullPageNotFoundView from '@components/BlockingViews/FullPageNotFoundView'; import HeaderWithBackButton from '@components/HeaderWithBackButton'; -import {useBetas, usePersonalDetails, useSession} from '@components/OnyxProvider'; +import {useBetas, useSession} from '@components/OnyxProvider'; +import {useOptionsList} from '@components/OptionListContextProvider'; import ScreenWrapper from '@components/ScreenWrapper'; import SelectionList from '@components/SelectionList'; import type {ListItem} from '@components/SelectionList/types'; @@ -37,22 +38,18 @@ type TaskAssigneeSelectorModalOnyxProps = { task: OnyxEntry; }; -type UseOptions = { - reports: OnyxCollection; -}; - type TaskAssigneeSelectorModalProps = TaskAssigneeSelectorModalOnyxProps & WithCurrentUserPersonalDetailsProps; -function useOptions({reports}: UseOptions) { - const allPersonalDetails = usePersonalDetails() || CONST.EMPTY_OBJECT; +function useOptions() { const betas = useBetas(); const [isLoading, setIsLoading] = useState(true); const [searchValue, debouncedSearchValue, setSearchValue] = useDebouncedState(''); + const {options: optionsList} = useOptionsList(); const options = useMemo(() => { const {recentReports, personalDetails, userToInvite, currentUserOption} = OptionsListUtils.getFilteredOptions( - reports, - allPersonalDetails, + optionsList.reports, + optionsList.personalDetails, betas, debouncedSearchValue.trim(), [], @@ -85,7 +82,7 @@ function useOptions({reports}: UseOptions) { currentUserOption, headerMessage, }; - }, [debouncedSearchValue, allPersonalDetails, isLoading, betas, reports]); + }, [optionsList.reports, optionsList.personalDetails, betas, debouncedSearchValue, isLoading]); return {...options, isLoading, searchValue, debouncedSearchValue, setSearchValue}; } @@ -96,7 +93,7 @@ function TaskAssigneeSelectorModal({reports, task}: TaskAssigneeSelectorModalPro const {translate} = useLocalize(); const session = useSession(); const currentUserPersonalDetails = useCurrentUserPersonalDetails(); - const {userToInvite, recentReports, personalDetails, currentUserOption, isLoading, searchValue, setSearchValue, headerMessage} = useOptions({reports}); + const {userToInvite, recentReports, personalDetails, currentUserOption, isLoading, searchValue, setSearchValue, headerMessage} = useOptions(); const onChangeText = (newSearchTerm = '') => { setSearchValue(newSearchTerm); diff --git a/src/pages/tasks/TaskShareDestinationSelectorModal.tsx b/src/pages/tasks/TaskShareDestinationSelectorModal.tsx index 5b56e58752ac..4d3b9d8a573b 100644 --- a/src/pages/tasks/TaskShareDestinationSelectorModal.tsx +++ b/src/pages/tasks/TaskShareDestinationSelectorModal.tsx @@ -1,9 +1,9 @@ import React, {useEffect, useMemo} from 'react'; import {View} from 'react-native'; import {withOnyx} from 'react-native-onyx'; -import type {OnyxCollection, OnyxEntry} from 'react-native-onyx'; +import type {OnyxEntry} from 'react-native-onyx'; import HeaderWithBackButton from '@components/HeaderWithBackButton'; -import {usePersonalDetails} from '@components/OnyxProvider'; +import {useOptionsList} from '@components/OptionListContextProvider'; import ScreenWrapper from '@components/ScreenWrapper'; import SelectionList from '@components/SelectionList'; import UserListItem from '@components/SelectionList/UserListItem'; @@ -22,8 +22,6 @@ import ROUTES from '@src/ROUTES'; import type {Report} from '@src/types/onyx'; type TaskShareDestinationSelectorModalOnyxProps = { - reports: OnyxCollection; - isSearchingForReports: OnyxEntry; }; @@ -40,28 +38,28 @@ const selectReportHandler = (option: unknown) => { Navigation.goBack(ROUTES.NEW_TASK); }; -const reportFilter = (reports: OnyxCollection) => - Object.keys(reports ?? {}).reduce((filtered, reportKey) => { - const report: OnyxEntry = reports?.[reportKey] ?? null; +const reportFilter = (reportOptions: OptionsListUtils.ReportOption[]) => + (reportOptions ?? []).reduce((filtered: OptionsListUtils.ReportOption[], option) => { + const report = option.item as Report; if (ReportUtils.canUserPerformWriteAction(report) && ReportUtils.canCreateTaskInReport(report) && !ReportUtils.isCanceledTaskReport(report)) { - return {...filtered, [reportKey]: report}; + filtered.push(option); } return filtered; - }, {}); + }, []); -function TaskShareDestinationSelectorModal({reports, isSearchingForReports}: TaskShareDestinationSelectorModalProps) { +function TaskShareDestinationSelectorModal({isSearchingForReports}: TaskShareDestinationSelectorModalProps) { const styles = useThemeStyles(); const [searchValue, debouncedSearchValue, setSearchValue] = useDebouncedState(''); const {translate} = useLocalize(); - const personalDetails = usePersonalDetails(); const {isOffline} = useNetwork(); + const {options: optionList} = useOptionsList(); const textInputHint = useMemo(() => (isOffline ? `${translate('common.youAppearToBeOffline')} ${translate('search.resultsAreLimited')}` : ''), [isOffline, translate]); const options = useMemo(() => { - const filteredReports = reportFilter(reports); + const filteredReports = reportFilter(optionList.reports); - const {recentReports} = OptionsListUtils.getShareDestinationOptions(filteredReports, personalDetails, [], debouncedSearchValue.trim(), [], CONST.EXPENSIFY_EMAILS, true); + const {recentReports} = OptionsListUtils.getShareDestinationOptions(filteredReports, optionList.personalDetails, [], debouncedSearchValue.trim(), [], CONST.EXPENSIFY_EMAILS, true); const headerMessage = OptionsListUtils.getHeaderMessage(recentReports && recentReports.length !== 0, false, debouncedSearchValue); @@ -84,7 +82,7 @@ function TaskShareDestinationSelectorModal({reports, isSearchingForReports}: Tas : []; return {sections, headerMessage}; - }, [personalDetails, reports, debouncedSearchValue]); + }, [optionList.reports, optionList.personalDetails, debouncedSearchValue]); useEffect(() => { ReportActions.searchInServer(debouncedSearchValue); @@ -124,9 +122,6 @@ function TaskShareDestinationSelectorModal({reports, isSearchingForReports}: Tas TaskShareDestinationSelectorModal.displayName = 'TaskShareDestinationSelectorModal'; export default withOnyx({ - reports: { - key: ONYXKEYS.COLLECTION.REPORT, - }, isSearchingForReports: { key: ONYXKEYS.IS_SEARCHING_FOR_REPORTS, initWithStoredValues: false, diff --git a/src/pages/workspace/WorkspaceInvitePage.tsx b/src/pages/workspace/WorkspaceInvitePage.tsx index 67bf6f8064da..7984a47bee35 100644 --- a/src/pages/workspace/WorkspaceInvitePage.tsx +++ b/src/pages/workspace/WorkspaceInvitePage.tsx @@ -8,6 +8,7 @@ import type {OnyxEntry} from 'react-native-onyx'; import FullPageNotFoundView from '@components/BlockingViews/FullPageNotFoundView'; import FormAlertWithSubmitButton from '@components/FormAlertWithSubmitButton'; import HeaderWithBackButton from '@components/HeaderWithBackButton'; +import {useOptionsList} from '@components/OptionListContextProvider'; import ScreenWrapper from '@components/ScreenWrapper'; import SelectionList from '@components/SelectionList'; import type {Section} from '@components/SelectionList/types'; @@ -72,6 +73,7 @@ function WorkspaceInvitePage({ const policyMemberEmailsToAccountIDs = PolicyUtils.getMemberAccountIDsForWorkspace(policyMembers, personalDetailsProp); Policy.openWorkspaceInvitePage(route.params.policyID, Object.keys(policyMemberEmailsToAccountIDs)); }; + const {options} = useOptionsList(); useEffect(() => { setSearchTerm(SearchInputManager.searchInput); @@ -107,7 +109,7 @@ function WorkspaceInvitePage({ const newPersonalDetailsDict: Record = {}; const newSelectedOptionsDict: Record = {}; - const inviteOptions = OptionsListUtils.getMemberInviteOptions(personalDetailsProp, betas ?? [], searchTerm, excludedUsers, true); + const inviteOptions = OptionsListUtils.getMemberInviteOptions(options.personalDetails, betas ?? [], searchTerm, excludedUsers, true); // Update selectedOptions with the latest personalDetails and policyMembers information const detailsMap: Record = {}; From ecc39530deb63835402a3163ac028d2da3fd5164 Mon Sep 17 00:00:00 2001 From: Yogesh Bhatt Date: Wed, 13 Mar 2024 02:59:18 +0530 Subject: [PATCH 219/500] Revert "hash append for StateSelector edge cases" This reverts commit 3ba97a550a07ce38adbf5e397bba3e56907552b1. --- src/components/StateSelector.tsx | 12 +----------- src/hooks/useGeographicalStateFromRoute.ts | 7 +------ .../Profile/PersonalDetails/StateSelectionPage.tsx | 7 ++----- 3 files changed, 4 insertions(+), 22 deletions(-) diff --git a/src/components/StateSelector.tsx b/src/components/StateSelector.tsx index 9de9c746c415..3aff71cd9f90 100644 --- a/src/components/StateSelector.tsx +++ b/src/components/StateSelector.tsx @@ -1,5 +1,3 @@ -import {useRoute} from '@react-navigation/native'; -import type {ParamListBase, RouteProp} from '@react-navigation/native'; import {CONST as COMMON_CONST} from 'expensify-common/lib/CONST'; import React, {useEffect, useState} from 'react'; import type {ForwardedRef} from 'react'; @@ -14,8 +12,6 @@ import FormHelpMessage from './FormHelpMessage'; import type {MenuItemProps} from './MenuItem'; import MenuItemWithTopDescription from './MenuItemWithTopDescription'; -type CustomParamList = ParamListBase & Record>; - type State = keyof typeof COMMON_CONST.STATES; type StateSelectorProps = { @@ -50,12 +46,6 @@ function StateSelector( const stateFromUrl = useGeographicalStateFromRoute(); const [stateToDisplay, setStateToDisplay] = useState(''); - /** - * See {@link module:src/pages/settings/Profile/PersonalDetails/StateSelectionPage.tsx#withHash} for more information. - */ - const route = useRoute>(); - const rawStateFromUrl = route.params?.state as string | undefined; - useEffect(() => { if (!shouldUseStateFromUrl || !stateFromUrl) { return; @@ -68,7 +58,7 @@ function StateSelector( setStateToDisplay(stateFromUrl); // eslint-disable-next-line react-hooks/exhaustive-deps - }, [rawStateFromUrl, shouldUseStateFromUrl]); + }, [stateFromUrl, shouldUseStateFromUrl]); useEffect(() => { if (!stateCode) { diff --git a/src/hooks/useGeographicalStateFromRoute.ts b/src/hooks/useGeographicalStateFromRoute.ts index bddc2b5c9a07..3e85bfc2a368 100644 --- a/src/hooks/useGeographicalStateFromRoute.ts +++ b/src/hooks/useGeographicalStateFromRoute.ts @@ -5,11 +5,6 @@ import {CONST as COMMON_CONST} from 'expensify-common/lib/CONST'; type CustomParamList = ParamListBase & Record>; type State = keyof typeof COMMON_CONST.STATES; -/** - * See {@link module:src/pages/settings/Profile/PersonalDetails/StateSelectionPage.tsx#withHash} for more information. - */ -const removeHash = (arg: string): string => arg.replace(/-hash-.*$/, ''); - /** * Extracts the 'state' (default) query parameter from the route/ url and validates it against COMMON_CONST.STATES, returning its ISO code or `undefined`. * Example 1: Url: https://new.expensify.com/settings/profile/address?state=MO Returns: MO @@ -24,5 +19,5 @@ export default function useGeographicalStateFromRoute(stateParamName = 'state'): if (!stateFromUrlTemp) { return; } - return COMMON_CONST.STATES[removeHash(stateFromUrlTemp) as State].stateISO; + return COMMON_CONST.STATES[stateFromUrlTemp as State].stateISO; } diff --git a/src/pages/settings/Profile/PersonalDetails/StateSelectionPage.tsx b/src/pages/settings/Profile/PersonalDetails/StateSelectionPage.tsx index 1a9a0fab9069..275400b27cc0 100644 --- a/src/pages/settings/Profile/PersonalDetails/StateSelectionPage.tsx +++ b/src/pages/settings/Profile/PersonalDetails/StateSelectionPage.tsx @@ -56,9 +56,6 @@ function StateSelectionPage() { (option: CountryData) => { const backTo = params?.backTo ?? ''; - // Add hash to param for rare cases, such as in ReimbursementAccountPage (currentStep = CONST.BANK_ACCOUNT.STEP.COMPANY), when the URL on the form page (which has the StateSelector component) already includes a state param. If the form then updates the StateInput with state data from another source and the user attempts to use StateSelector to select the same state present in the URL, it will cause the StateSelector not to detect the state change, as the URL remains the same. - const withHash = (arg: string): string => `${arg}-hash-${Math.random().toString(36).substring(2, 8)}`; - // Determine navigation action based on "backTo" presence and route stack length. if (navigation.getState()?.routes.length === 1) { // If this is the only page in the navigation stack (examples include direct navigation to this page via URL or page reload). @@ -67,11 +64,11 @@ function StateSelectionPage() { Navigation.goBack(); } else { // "backTo" provided: navigate back to "backTo" with state parameter. - Navigation.goBack(appendParam(backTo, 'state', withHash(option.value)) as Route); + Navigation.goBack(appendParam(backTo, 'state', option.value) as Route); } } else if (!_.isEmpty(backTo)) { // Most common case: Navigation stack has multiple routes and "backTo" is defined: navigate to "backTo" with state parameter. - Navigation.navigate(appendParam(backTo, 'state', withHash(option.value)) as Route); + Navigation.navigate(appendParam(backTo, 'state', option.value) as Route); } else { // This is a fallback block and should never execute if StateSelector is correctly appending the "backTo" route. // Navigation stack has multiple routes but no "backTo" defined: default back navigation. From 713a91ce5f252da0afe33a88a3ddf030504700bb Mon Sep 17 00:00:00 2001 From: Yogesh Bhatt Date: Wed, 13 Mar 2024 03:31:40 +0530 Subject: [PATCH 220/500] Sync URL 'state' param with StateSelector value --- src/components/StateSelector.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/components/StateSelector.tsx b/src/components/StateSelector.tsx index 3aff71cd9f90..5e1e8591498d 100644 --- a/src/components/StateSelector.tsx +++ b/src/components/StateSelector.tsx @@ -69,6 +69,7 @@ function StateSelector( // This will cause the form to revalidate and remove any error related to state name onInputChange(stateCode); } + Navigation.setParams({state: stateCode}); setStateToDisplay(stateCode); // eslint-disable-next-line react-hooks/exhaustive-deps From 4c0cb3eb75f47ce1c638d23b42b3ea25bd52af7a Mon Sep 17 00:00:00 2001 From: Tomasz Misiukiewicz Date: Wed, 13 Mar 2024 09:52:13 +0100 Subject: [PATCH 221/500] update initializing in search page --- src/components/OptionListContextProvider.tsx | 11 ++++++--- src/pages/SearchPage/index.tsx | 24 +++++++++----------- 2 files changed, 19 insertions(+), 16 deletions(-) diff --git a/src/components/OptionListContextProvider.tsx b/src/components/OptionListContextProvider.tsx index 15f63eccda72..dbaafa7f6215 100644 --- a/src/components/OptionListContextProvider.tsx +++ b/src/components/OptionListContextProvider.tsx @@ -45,8 +45,8 @@ function OptionsListContextProvider({children}: OptionsListProviderProps) { return; } - areOptionsInitialized.current = true; loadOptions(); + areOptionsInitialized.current = true; }, [loadOptions]); return ( @@ -59,12 +59,17 @@ function OptionsListContextProvider({children}: OptionsListProviderProps) { const useOptionsListContext = () => useContext(OptionsListContext); // Hook to use the OptionsListContext with an initializer to load the options -const useOptionsList = () => { +const useOptionsList = (options?: {shouldInitialize: boolean}) => { + const {shouldInitialize = true} = options ?? {}; const {initializeOptions, ...optionsListContext} = useOptionsListContext(); useEffect(() => { + if (!shouldInitialize || optionsListContext.areOptionsInitialized) { + return; + } + initializeOptions(); - }, [initializeOptions, optionsListContext]); + }, [shouldInitialize, initializeOptions, optionsListContext.areOptionsInitialized]); return { initializeOptions, diff --git a/src/pages/SearchPage/index.tsx b/src/pages/SearchPage/index.tsx index b588fa88d0e4..3374a6f410c2 100644 --- a/src/pages/SearchPage/index.tsx +++ b/src/pages/SearchPage/index.tsx @@ -4,7 +4,6 @@ import {View} from 'react-native'; import type {OnyxEntry} from 'react-native-onyx'; import {withOnyx} from 'react-native-onyx'; import HeaderWithBackButton from '@components/HeaderWithBackButton'; -import {usePersonalDetails} from '@components/OnyxProvider'; import {useOptionsList} from '@components/OptionListContextProvider'; import ScreenWrapper from '@components/ScreenWrapper'; import SelectionList from '@components/SelectionList'; @@ -18,7 +17,7 @@ import Navigation from '@libs/Navigation/Navigation'; import type {RootStackParamList} from '@libs/Navigation/types'; import * as OptionsListUtils from '@libs/OptionsListUtils'; import Performance from '@libs/Performance'; -import * as ReportUtils from '@libs/ReportUtils'; +import type {OptionData} from '@libs/ReportUtils'; import * as Report from '@userActions/Report'; import Timing from '@userActions/Timing'; import CONST from '@src/CONST'; @@ -38,7 +37,7 @@ type SearchPageOnyxProps = { type SearchPageProps = SearchPageOnyxProps & StackScreenProps; type SearchPageSectionItem = { - data: ReportUtils.OptionData[]; + data: OptionData[]; shouldShow: boolean; indexOffset: number; }; @@ -57,8 +56,9 @@ function SearchPage({betas, isSearchingForReports, navigation}: SearchPageProps) const {translate} = useLocalize(); const {isOffline} = useNetwork(); const themeStyles = useThemeStyles(); - const personalDetails = usePersonalDetails(); - const {options, areOptionsInitialized} = useOptionsList(); + const {options, areOptionsInitialized} = useOptionsList({ + shouldInitialize: isScreenTransitionEnd, + }); const offlineMessage: MaybePhraseKey = isOffline ? [`${translate('common.youAppearToBeOffline')} ${translate('search.resultsAreLimited')}`, {isTranslated: true}] : ''; const [searchValue, debouncedSearchValue, setSearchValue] = useDebouncedState(''); @@ -78,7 +78,7 @@ function SearchPage({betas, isSearchingForReports, navigation}: SearchPageProps) userToInvite, headerMessage, } = useMemo(() => { - if (!areOptionsInitialized && !isScreenTransitionEnd) { + if (!isScreenTransitionEnd) { return { recentReports: [], personalDetails: [], @@ -89,7 +89,7 @@ function SearchPage({betas, isSearchingForReports, navigation}: SearchPageProps) const optionList = OptionsListUtils.getSearchOptions(options, debouncedSearchValue.trim(), betas ?? []); const header = OptionsListUtils.getHeaderMessage(optionList.recentReports.length + optionList.personalDetails.length !== 0, Boolean(optionList.userToInvite), debouncedSearchValue); return {...optionList, headerMessage: header}; - }, [areOptionsInitialized, isScreenTransitionEnd, options, debouncedSearchValue, betas]); + }, [isScreenTransitionEnd, options, debouncedSearchValue, betas]); const sections = useMemo((): SearchPageSectionList => { const newSections: SearchPageSectionList = []; @@ -124,7 +124,7 @@ function SearchPage({betas, isSearchingForReports, navigation}: SearchPageProps) return newSections; }, [localPersonalDetails, recentReports, userToInvite]); - const selectReport = (option: ReportUtils.OptionData) => { + const selectReport = (option: OptionData) => { if (!option) { return; } @@ -141,8 +141,6 @@ function SearchPage({betas, isSearchingForReports, navigation}: SearchPageProps) setIsScreenTransitionEnd(true); }; - const isOptionsDataReady = useMemo(() => ReportUtils.isReportDataReady() && OptionsListUtils.isPersonalDetailsReady(personalDetails), [personalDetails]); - return ( - - sections={(areOptionsInitialized || didScreenTransitionEnd) && isOptionsDataReady ? sections : CONST.EMPTY_ARRAY} + + sections={didScreenTransitionEnd || areOptionsInitialized ? sections : CONST.EMPTY_ARRAY} ListItem={UserListItem} textInputValue={searchValue} textInputLabel={translate('optionsSelector.nameEmailOrPhoneNumber')} @@ -169,7 +167,7 @@ function SearchPage({betas, isSearchingForReports, navigation}: SearchPageProps) onLayout={setPerformanceTimersEnd} autoFocus onSelectRow={selectReport} - showLoadingPlaceholder={(!areOptionsInitialized && !didScreenTransitionEnd) || !isOptionsDataReady} // showLoadingPlaceholder={(!areOptionsInitialized && !didScreenTransitionEnd) || !isOptionsDataReady} + showLoadingPlaceholder={!didScreenTransitionEnd} footerContent={SearchPageFooterInstance} isLoadingNewOptions={isSearchingForReports ?? undefined} /> From 0a7925e747fcc406269572a7eee3dc7e3cf88d70 Mon Sep 17 00:00:00 2001 From: Tomasz Misiukiewicz Date: Wed, 13 Mar 2024 10:08:29 +0100 Subject: [PATCH 222/500] update types for options --- src/libs/OptionsListUtils.ts | 38 +++++++++++-------- .../TaskShareDestinationSelectorModal.tsx | 6 +-- 2 files changed, 25 insertions(+), 19 deletions(-) diff --git a/src/libs/OptionsListUtils.ts b/src/libs/OptionsListUtils.ts index 3987d6ebe626..d0b79d752c74 100644 --- a/src/libs/OptionsListUtils.ts +++ b/src/libs/OptionsListUtils.ts @@ -53,13 +53,13 @@ import * as TaskUtils from './TaskUtils'; import * as TransactionUtils from './TransactionUtils'; import * as UserUtils from './UserUtils'; -type ReportOption = ReportUtils.OptionData & { - item: Report | PersonalDetails; +type SearchOption = ReportUtils.OptionData & { + item: T; }; type OptionList = { - reports: ReportOption[]; - personalDetails: ReportOption[]; + reports: Array>; + personalDetails: Array>; }; type Tag = { @@ -1351,7 +1351,7 @@ function createOptionList(reports: OnyxCollection, personalDetails: Onyx return report?.lastVisibleActionCreated; }); orderedReports.reverse(); - const allReportOptions: ReportOption[] = []; + const allReportOptions: Array> = []; orderedReports.forEach((report) => { if (!report) { @@ -1407,7 +1407,7 @@ function createOptionList(reports: OnyxCollection, personalDetails: Onyx return { reports: allReportOptions, - personalDetails: allPersonalDetailsOptions as ReportOption[], + personalDetails: allPersonalDetailsOptions as Array>, }; } @@ -1499,7 +1499,7 @@ function getOptions( // Filter out all the reports that shouldn't be displayed const filteredReportOptions = options.reports.filter((option) => { - const report = option.item as Report; + const report = option.item; const {parentReportID, parentReportActionID} = report ?? {}; const canGetParentReport = parentReportID && parentReportActionID && allReportActions; @@ -1523,7 +1523,7 @@ function getOptions( // - Order everything by the last message timestamp (descending) // - All archived reports should remain at the bottom const orderedReportOptions = lodashSortBy(filteredReportOptions, (option) => { - const report = option.item as Report; + const report = option.item; if (ReportUtils.isArchivedRoom(report)) { return CONST.DATE.UNIX_EPOCH; } @@ -1533,7 +1533,7 @@ function getOptions( orderedReportOptions.reverse(); const allReportOptions = orderedReportOptions.filter((option) => { - const report = option.item as Report; + const report = option.item; if (!report) { return; @@ -1612,7 +1612,7 @@ function getOptions( for (const reportOption of allReportOptions) { // update the alternate text if needed if ((!!reportOption.isChatRoom || reportOption.isPolicyExpenseChat) && forcePolicyNamePreview) { - reportOption.alternateText = ReportUtils.getChatRoomSubtitle(reportOption.item as Report); + reportOption.alternateText = ReportUtils.getChatRoomSubtitle(reportOption.item); } // Stop adding options to the recentReports array when we reach the maxRecentReportsToShow value @@ -1853,8 +1853,8 @@ function getIOUConfirmationOptionsFromParticipants(participants: Participant[], * Build the options for the New Group view */ function getFilteredOptions( - reports: ReportOption[] = [], - personalDetails: ReportOption[] = [], + reports: Array> = [], + personalDetails: Array> = [], betas: OnyxEntry = [], searchValue = '', selectedOptions: Array> = [], @@ -1905,8 +1905,8 @@ function getFilteredOptions( */ function getShareDestinationOptions( - reports: ReportOption[], - personalDetails: ReportOption[], + reports: Array> = [], + personalDetails: Array> = [], betas: OnyxEntry = [], searchValue = '', selectedOptions: Array> = [], @@ -1966,7 +1966,13 @@ function formatMemberForList(member: ReportUtils.OptionData): MemberForList { /** * Build the options for the Workspace Member Invite view */ -function getMemberInviteOptions(personalDetails: ReportOption[], betas: Beta[] = [], searchValue = '', excludeLogins: string[] = [], includeSelectedOptions = false): GetOptions { +function getMemberInviteOptions( + personalDetails: Array>, + betas: Beta[] = [], + searchValue = '', + excludeLogins: string[] = [], + includeSelectedOptions = false, +): GetOptions { return getOptions( {reports: [], personalDetails}, { @@ -2124,4 +2130,4 @@ export { createOptionList, }; -export type {MemberForList, CategorySection, GetOptions, OptionList, ReportOption}; +export type {MemberForList, CategorySection, GetOptions, OptionList, SearchOption}; diff --git a/src/pages/tasks/TaskShareDestinationSelectorModal.tsx b/src/pages/tasks/TaskShareDestinationSelectorModal.tsx index 4d3b9d8a573b..bf640fe5982a 100644 --- a/src/pages/tasks/TaskShareDestinationSelectorModal.tsx +++ b/src/pages/tasks/TaskShareDestinationSelectorModal.tsx @@ -38,9 +38,9 @@ const selectReportHandler = (option: unknown) => { Navigation.goBack(ROUTES.NEW_TASK); }; -const reportFilter = (reportOptions: OptionsListUtils.ReportOption[]) => - (reportOptions ?? []).reduce((filtered: OptionsListUtils.ReportOption[], option) => { - const report = option.item as Report; +const reportFilter = (reportOptions: Array>) => + (reportOptions ?? []).reduce((filtered: Array>, option) => { + const report = option.item; if (ReportUtils.canUserPerformWriteAction(report) && ReportUtils.canCreateTaskInReport(report) && !ReportUtils.isCanceledTaskReport(report)) { filtered.push(option); } From 47795b96cc535193e6499132db672d7da702591a Mon Sep 17 00:00:00 2001 From: Tomasz Misiukiewicz Date: Wed, 13 Mar 2024 12:28:15 +0100 Subject: [PATCH 223/500] update loading in search page --- src/pages/SearchPage/index.tsx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/pages/SearchPage/index.tsx b/src/pages/SearchPage/index.tsx index 3374a6f410c2..6e06024521fd 100644 --- a/src/pages/SearchPage/index.tsx +++ b/src/pages/SearchPage/index.tsx @@ -78,7 +78,7 @@ function SearchPage({betas, isSearchingForReports, navigation}: SearchPageProps) userToInvite, headerMessage, } = useMemo(() => { - if (!isScreenTransitionEnd) { + if (!areOptionsInitialized) { return { recentReports: [], personalDetails: [], @@ -89,7 +89,7 @@ function SearchPage({betas, isSearchingForReports, navigation}: SearchPageProps) const optionList = OptionsListUtils.getSearchOptions(options, debouncedSearchValue.trim(), betas ?? []); const header = OptionsListUtils.getHeaderMessage(optionList.recentReports.length + optionList.personalDetails.length !== 0, Boolean(optionList.userToInvite), debouncedSearchValue); return {...optionList, headerMessage: header}; - }, [isScreenTransitionEnd, options, debouncedSearchValue, betas]); + }, [areOptionsInitialized, options, debouncedSearchValue, betas]); const sections = useMemo((): SearchPageSectionList => { const newSections: SearchPageSectionList = []; @@ -157,7 +157,7 @@ function SearchPage({betas, isSearchingForReports, navigation}: SearchPageProps) /> - sections={didScreenTransitionEnd || areOptionsInitialized ? sections : CONST.EMPTY_ARRAY} + sections={(!areOptionsInitialized && didScreenTransitionEnd) || areOptionsInitialized ? sections : CONST.EMPTY_ARRAY} ListItem={UserListItem} textInputValue={searchValue} textInputLabel={translate('optionsSelector.nameEmailOrPhoneNumber')} @@ -167,7 +167,7 @@ function SearchPage({betas, isSearchingForReports, navigation}: SearchPageProps) onLayout={setPerformanceTimersEnd} autoFocus onSelectRow={selectReport} - showLoadingPlaceholder={!didScreenTransitionEnd} + showLoadingPlaceholder={areOptionsInitialized && debouncedSearchValue.trim() === '' ? sections.length === 0 : !didScreenTransitionEnd} footerContent={SearchPageFooterInstance} isLoadingNewOptions={isSearchingForReports ?? undefined} /> From ffaee2a78035b85cd78c54fe0e3db6a83c28ab13 Mon Sep 17 00:00:00 2001 From: Tomasz Misiukiewicz Date: Wed, 13 Mar 2024 12:47:40 +0100 Subject: [PATCH 224/500] update new chat page skeleton displaying --- src/pages/NewChatPage.tsx | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/src/pages/NewChatPage.tsx b/src/pages/NewChatPage.tsx index 807df128a590..ab24d641fe90 100755 --- a/src/pages/NewChatPage.tsx +++ b/src/pages/NewChatPage.tsx @@ -16,7 +16,6 @@ import useWindowDimensions from '@hooks/useWindowDimensions'; import * as DeviceCapabilities from '@libs/DeviceCapabilities'; import doInteractionTask from '@libs/DoInteractionTask'; import * as OptionsListUtils from '@libs/OptionsListUtils'; -import * as ReportUtils from '@libs/ReportUtils'; import type {OptionData} from '@libs/ReportUtils'; import variables from '@styles/variables'; import * as Report from '@userActions/Report'; @@ -49,12 +48,12 @@ const excludedGroupEmails = CONST.EXPENSIFY_EMAILS.filter((value) => value !== C function NewChatPage({betas, isGroupChat, personalDetails, reports, isSearchingForReports, dismissedReferralBanners}: NewChatPageProps) { const {translate} = useLocalize(); - const {options} = useOptionsList(); + const {options, areOptionsInitialized} = useOptionsList(); const styles = useThemeStyles(); const [searchTerm, setSearchTerm] = useState(''); - const [filteredRecentReports, setFilteredRecentReports] = useState([]); - const [filteredPersonalDetails, setFilteredPersonalDetails] = useState([]); - const [filteredUserToInvite, setFilteredUserToInvite] = useState(); + const [filteredRecentReports, setFilteredRecentReports] = useState([]); + const [filteredPersonalDetails, setFilteredPersonalDetails] = useState([]); + const [filteredUserToInvite, setFilteredUserToInvite] = useState(); const [selectedOptions, setSelectedOptions] = useState([]); const {isOffline} = useNetwork(); const {isSmallScreenWidth} = useWindowDimensions(); @@ -71,8 +70,6 @@ function NewChatPage({betas, isGroupChat, personalDetails, reports, isSearchingF selectedOptions.some((participant) => participant?.searchText?.toLowerCase().includes(searchTerm.trim().toLowerCase())), ); - const isOptionsDataReady = ReportUtils.isReportDataReady() && OptionsListUtils.isPersonalDetailsReady(personalDetails); - const sections = useMemo((): OptionsListUtils.CategorySection[] => { const sectionsList: OptionsListUtils.CategorySection[] = []; let indexOffset = 0; @@ -265,7 +262,7 @@ function NewChatPage({betas, isGroupChat, personalDetails, reports, isSearchingF headerMessage={headerMessage} boldStyle shouldPreventDefaultFocusOnSelectRow={!DeviceCapabilities.canUseTouchScreen()} - shouldShowOptions={isOptionsDataReady && didScreenTransitionEnd} + shouldShowOptions={areOptionsInitialized && didScreenTransitionEnd} shouldShowConfirmButton shouldShowReferralCTA={!dismissedReferralBanners[CONST.REFERRAL_PROGRAM.CONTENT_TYPES.START_CHAT]} referralContentType={CONST.REFERRAL_PROGRAM.CONTENT_TYPES.START_CHAT} From 0cdee35813facc8da61e83e06ffbae738e7a7dc2 Mon Sep 17 00:00:00 2001 From: Etotaziba Olei Date: Wed, 13 Mar 2024 13:35:38 +0100 Subject: [PATCH 225/500] pass isDraft argument --- .../MoneyTemporaryForRefactorRequestConfirmationList.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/MoneyTemporaryForRefactorRequestConfirmationList.js b/src/components/MoneyTemporaryForRefactorRequestConfirmationList.js index aef5468241c1..64507f80fdcd 100755 --- a/src/components/MoneyTemporaryForRefactorRequestConfirmationList.js +++ b/src/components/MoneyTemporaryForRefactorRequestConfirmationList.js @@ -375,7 +375,7 @@ function MoneyTemporaryForRefactorRequestConfirmationList({ if (previousTransactionTaxAmount !== transaction.taxAmount && amountInSmallestCurrencyUnits !== transaction.taxAmount) { return; } - IOU.setMoneyRequestTaxAmount(transaction.transactionID, amountInSmallestCurrencyUnits); + IOU.setMoneyRequestTaxAmount(transaction.transactionID, amountInSmallestCurrencyUnits, true); }, [taxRates.defaultValue, transaction, previousTransactionTaxAmount]); /** From 47fc6795159d2cddebb3db0694d32bfb9f7a6b2a Mon Sep 17 00:00:00 2001 From: Tomasz Misiukiewicz Date: Wed, 13 Mar 2024 14:48:38 +0100 Subject: [PATCH 226/500] update caching in all selection lists --- src/pages/NewChatPage.tsx | 6 +- src/pages/RoomInvitePage.tsx | 4 +- .../ShareLogList/BaseShareLogList.tsx | 15 ++- src/pages/tasks/TaskAssigneeSelectorModal.tsx | 21 ++-- .../TaskShareDestinationSelectorModal.tsx | 20 +++- src/pages/workspace/WorkspaceInvitePage.tsx | 110 +++++++++--------- 6 files changed, 94 insertions(+), 82 deletions(-) diff --git a/src/pages/NewChatPage.tsx b/src/pages/NewChatPage.tsx index ab24d641fe90..9523c31298e3 100755 --- a/src/pages/NewChatPage.tsx +++ b/src/pages/NewChatPage.tsx @@ -47,8 +47,11 @@ type NewChatPageProps = NewChatPageWithOnyxProps & { const excludedGroupEmails = CONST.EXPENSIFY_EMAILS.filter((value) => value !== CONST.EMAIL.CONCIERGE); function NewChatPage({betas, isGroupChat, personalDetails, reports, isSearchingForReports, dismissedReferralBanners}: NewChatPageProps) { + const [isScreenTransitionEnd, setIsScreenTransitionEnd] = useState(false); const {translate} = useLocalize(); - const {options, areOptionsInitialized} = useOptionsList(); + const {options, areOptionsInitialized} = useOptionsList({ + shouldInitialize: isScreenTransitionEnd, + }); const styles = useThemeStyles(); const [searchTerm, setSearchTerm] = useState(''); const [filteredRecentReports, setFilteredRecentReports] = useState([]); @@ -238,6 +241,7 @@ function NewChatPage({betas, isGroupChat, personalDetails, reports, isSearchingF includePaddingTop={false} shouldEnableMaxHeight testID={NewChatPage.displayName} + onEntryTransitionEnd={() => setIsScreenTransitionEnd(true)} > {({safeAreaPaddingBottomStyle, insets}) => ( (null); const [didScreenTransitionEnd, setDidScreenTransitionEnd] = useState(false); const navigation: StackNavigationProp = useNavigation(); - const {options} = useOptionsList(); + const {options, areOptionsInitialized} = useOptionsList(); // Any existing participants and Expensify emails should not be eligible for invitation const excludedUsers = useMemo( @@ -239,7 +239,7 @@ function RoomInvitePage({betas, personalDetails, report, policies}: RoomInvitePa onConfirm={inviteUsers} showScrollIndicator shouldPreventDefaultFocusOnSelectRow={!DeviceCapabilities.canUseTouchScreen()} - showLoadingPlaceholder={!didScreenTransitionEnd || !OptionsListUtils.isPersonalDetailsReady(personalDetails)} + showLoadingPlaceholder={!didScreenTransitionEnd && !areOptionsInitialized} /> { const { @@ -47,12 +44,14 @@ function BaseShareLogList({betas, onAttachLogToReport}: BaseShareLogListProps) { }); }, [betas, options, searchValue]); - const isOptionsDataReady = ReportUtils.isReportDataReady() && OptionsListUtils.isPersonalDetailsReady(personalDetails); - useEffect(() => { + if (!areOptionsInitialized) { + return; + } + updateOptions(); // eslint-disable-next-line react-hooks/exhaustive-deps - }, []); + }, [areOptionsInitialized]); useEffect(() => { if (!isMounted.current) { @@ -134,7 +133,7 @@ function BaseShareLogList({betas, onAttachLogToReport}: BaseShareLogListProps) { value={searchValue} headerMessage={headerMessage} showTitleTooltip - shouldShowOptions={isOptionsDataReady} + shouldShowOptions={areOptionsInitialized} textInputLabel={translate('optionsSelector.nameEmailOrPhoneNumber')} textInputAlert={isOffline ? `${translate('common.youAppearToBeOffline')} ${translate('search.resultsAreLimited')}` : ''} safeAreaPaddingBottomStyle={safeAreaPaddingBottomStyle} diff --git a/src/pages/tasks/TaskAssigneeSelectorModal.tsx b/src/pages/tasks/TaskAssigneeSelectorModal.tsx index ef097ffcfd71..d367a06cb60e 100644 --- a/src/pages/tasks/TaskAssigneeSelectorModal.tsx +++ b/src/pages/tasks/TaskAssigneeSelectorModal.tsx @@ -41,10 +41,12 @@ type TaskAssigneeSelectorModalOnyxProps = { type TaskAssigneeSelectorModalProps = TaskAssigneeSelectorModalOnyxProps & WithCurrentUserPersonalDetailsProps; function useOptions() { + const [isScreenTransitionEnd, setIsScreenTransitionEnd] = useState(false); const betas = useBetas(); - const [isLoading, setIsLoading] = useState(true); const [searchValue, debouncedSearchValue, setSearchValue] = useDebouncedState(''); - const {options: optionsList} = useOptionsList(); + const {options: optionsList, areOptionsInitialized} = useOptionsList({ + shouldInitialize: isScreenTransitionEnd, + }); const options = useMemo(() => { const {recentReports, personalDetails, userToInvite, currentUserOption} = OptionsListUtils.getFilteredOptions( @@ -71,10 +73,6 @@ function useOptions() { debouncedSearchValue, ); - if (isLoading) { - setIsLoading(false); - } - return { userToInvite, recentReports, @@ -82,9 +80,9 @@ function useOptions() { currentUserOption, headerMessage, }; - }, [optionsList.reports, optionsList.personalDetails, betas, debouncedSearchValue, isLoading]); + }, [optionsList.reports, optionsList.personalDetails, betas, debouncedSearchValue]); - return {...options, isLoading, searchValue, debouncedSearchValue, setSearchValue}; + return {...options, searchValue, debouncedSearchValue, setSearchValue, areOptionsInitialized, setIsScreenTransitionEnd}; } function TaskAssigneeSelectorModal({reports, task}: TaskAssigneeSelectorModalProps) { @@ -93,7 +91,7 @@ function TaskAssigneeSelectorModal({reports, task}: TaskAssigneeSelectorModalPro const {translate} = useLocalize(); const session = useSession(); const currentUserPersonalDetails = useCurrentUserPersonalDetails(); - const {userToInvite, recentReports, personalDetails, currentUserOption, isLoading, searchValue, setSearchValue, headerMessage} = useOptions(); + const {userToInvite, recentReports, personalDetails, currentUserOption, searchValue, setSearchValue, headerMessage, areOptionsInitialized, setIsScreenTransitionEnd} = useOptions(); const onChangeText = (newSearchTerm = '') => { setSearchValue(newSearchTerm); @@ -208,6 +206,7 @@ function TaskAssigneeSelectorModal({reports, task}: TaskAssigneeSelectorModalPro setIsScreenTransitionEnd(true)} > {({didScreenTransitionEnd}) => ( @@ -217,14 +216,14 @@ function TaskAssigneeSelectorModal({reports, task}: TaskAssigneeSelectorModalPro /> diff --git a/src/pages/tasks/TaskShareDestinationSelectorModal.tsx b/src/pages/tasks/TaskShareDestinationSelectorModal.tsx index bf640fe5982a..0c31058fe329 100644 --- a/src/pages/tasks/TaskShareDestinationSelectorModal.tsx +++ b/src/pages/tasks/TaskShareDestinationSelectorModal.tsx @@ -1,4 +1,4 @@ -import React, {useEffect, useMemo} from 'react'; +import React, {useEffect, useMemo, useState} from 'react'; import {View} from 'react-native'; import {withOnyx} from 'react-native-onyx'; import type {OnyxEntry} from 'react-native-onyx'; @@ -48,15 +48,24 @@ const reportFilter = (reportOptions: Array }, []); function TaskShareDestinationSelectorModal({isSearchingForReports}: TaskShareDestinationSelectorModalProps) { + const [isScreenTransitionEnd, setIsScreenTransitionEnd] = useState(false); const styles = useThemeStyles(); const [searchValue, debouncedSearchValue, setSearchValue] = useDebouncedState(''); const {translate} = useLocalize(); const {isOffline} = useNetwork(); - const {options: optionList} = useOptionsList(); + const {options: optionList, areOptionsInitialized} = useOptionsList({ + shouldInitialize: isScreenTransitionEnd, + }); const textInputHint = useMemo(() => (isOffline ? `${translate('common.youAppearToBeOffline')} ${translate('search.resultsAreLimited')}` : ''), [isOffline, translate]); const options = useMemo(() => { + if (!areOptionsInitialized) { + return { + sections: [], + headerMessage: '', + }; + } const filteredReports = reportFilter(optionList.reports); const {recentReports} = OptionsListUtils.getShareDestinationOptions(filteredReports, optionList.personalDetails, [], debouncedSearchValue.trim(), [], CONST.EXPENSIFY_EMAILS, true); @@ -82,7 +91,7 @@ function TaskShareDestinationSelectorModal({isSearchingForReports}: TaskShareDes : []; return {sections, headerMessage}; - }, [optionList.reports, optionList.personalDetails, debouncedSearchValue]); + }, [areOptionsInitialized, optionList.reports, optionList.personalDetails, debouncedSearchValue]); useEffect(() => { ReportActions.searchInServer(debouncedSearchValue); @@ -92,6 +101,7 @@ function TaskShareDestinationSelectorModal({isSearchingForReports}: TaskShareDes setIsScreenTransitionEnd(true)} > {({didScreenTransitionEnd}) => ( <> @@ -102,13 +112,13 @@ function TaskShareDestinationSelectorModal({isSearchingForReports}: TaskShareDes diff --git a/src/pages/workspace/WorkspaceInvitePage.tsx b/src/pages/workspace/WorkspaceInvitePage.tsx index 7984a47bee35..50a5301160a5 100644 --- a/src/pages/workspace/WorkspaceInvitePage.tsx +++ b/src/pages/workspace/WorkspaceInvitePage.tsx @@ -67,13 +67,15 @@ function WorkspaceInvitePage({ const [selectedOptions, setSelectedOptions] = useState([]); const [personalDetails, setPersonalDetails] = useState([]); const [usersToInvite, setUsersToInvite] = useState([]); - const [didScreenTransitionEnd, setDidScreenTransitionEnd] = useState(false); + const [isScreenTransitionEnd, setIsScreenTransitionEnd] = useState(false); const navigation = useNavigation>(); const openWorkspaceInvitePage = () => { const policyMemberEmailsToAccountIDs = PolicyUtils.getMemberAccountIDsForWorkspace(policyMembers, personalDetailsProp); Policy.openWorkspaceInvitePage(route.params.policyID, Object.keys(policyMemberEmailsToAccountIDs)); }; - const {options} = useOptionsList(); + const {options, areOptionsInitialized} = useOptionsList({ + shouldInitialize: isScreenTransitionEnd, + }); useEffect(() => { setSearchTerm(SearchInputManager.searchInput); @@ -90,7 +92,7 @@ function WorkspaceInvitePage({ useEffect(() => { const unsubscribeTransitionEnd = navigation.addListener('transitionEnd', () => { - setDidScreenTransitionEnd(true); + setIsScreenTransitionEnd(true); }); return () => { @@ -110,7 +112,6 @@ function WorkspaceInvitePage({ const newSelectedOptionsDict: Record = {}; const inviteOptions = OptionsListUtils.getMemberInviteOptions(options.personalDetails, betas ?? [], searchTerm, excludedUsers, true); - // Update selectedOptions with the latest personalDetails and policyMembers information const detailsMap: Record = {}; inviteOptions.personalDetails.forEach((detail) => { @@ -161,16 +162,12 @@ function WorkspaceInvitePage({ setSelectedOptions(Object.values(newSelectedOptionsDict)); // eslint-disable-next-line react-hooks/exhaustive-deps -- we don't want to recalculate when selectedOptions change - }, [personalDetailsProp, policyMembers, betas, searchTerm, excludedUsers]); + }, [options.personalDetails, policyMembers, betas, searchTerm, excludedUsers]); const sections: MembersSection[] = useMemo(() => { const sectionsArr: MembersSection[] = []; let indexOffset = 0; - if (!didScreenTransitionEnd) { - return []; - } - // Filter all options that is a part of the search term or in the personal details let filterSelectedOptions = selectedOptions; if (searchTerm !== '') { @@ -220,7 +217,7 @@ function WorkspaceInvitePage({ }); return sectionsArr; - }, [personalDetails, searchTerm, selectedOptions, usersToInvite, translate, didScreenTransitionEnd]); + }, [selectedOptions, searchTerm, personalDetails, translate, usersToInvite]); const toggleOption = (option: MemberForList) => { Policy.clearErrors(route.params.policyID); @@ -290,53 +287,56 @@ function WorkspaceInvitePage({ shouldEnableMaxHeight shouldUseCachedViewportHeight testID={WorkspaceInvitePage.displayName} + onEntryTransitionEnd={() => setIsScreenTransitionEnd(true)} > - - { - Policy.clearErrors(route.params.policyID); - Navigation.goBack(); - }} - /> - { - SearchInputManager.searchInput = value; - setSearchTerm(value); - }} - headerMessage={headerMessage} - onSelectRow={toggleOption} - onConfirm={inviteUser} - showScrollIndicator - showLoadingPlaceholder={!didScreenTransitionEnd || !OptionsListUtils.isPersonalDetailsReady(personalDetailsProp)} - shouldPreventDefaultFocusOnSelectRow={!DeviceCapabilities.canUseTouchScreen()} - /> - - ( + + { + Policy.clearErrors(route.params.policyID); + Navigation.goBack(); + }} + /> + { + SearchInputManager.searchInput = value; + setSearchTerm(value); + }} + headerMessage={headerMessage} + onSelectRow={toggleOption} + onConfirm={inviteUser} + showScrollIndicator + showLoadingPlaceholder={areOptionsInitialized && searchTerm.trim() === '' ? sections.length === 0 : !didScreenTransitionEnd} + shouldPreventDefaultFocusOnSelectRow={!DeviceCapabilities.canUseTouchScreen()} /> - - + + + + + )} ); } From 623810f5bc08b2e6b2a24552ee30f84ee5f1809d Mon Sep 17 00:00:00 2001 From: Tomasz Misiukiewicz Date: Wed, 13 Mar 2024 15:41:04 +0100 Subject: [PATCH 227/500] update options in the background --- src/App.tsx | 2 +- src/components/OptionListContextProvider.tsx | 45 +++++++++++++++++--- src/libs/OptionsListUtils.ts | 19 +++++++++ src/libs/ReportUtils.ts | 18 ++++++++ 4 files changed, 78 insertions(+), 6 deletions(-) diff --git a/src/App.tsx b/src/App.tsx index e53ba387d4d6..23ee2f879136 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -15,7 +15,7 @@ import ErrorBoundary from './components/ErrorBoundary'; import HTMLEngineProvider from './components/HTMLEngineProvider'; import {LocaleContextProvider} from './components/LocaleContextProvider'; import OnyxProvider from './components/OnyxProvider'; -import {OptionsListContextProvider} from './components/OptionListContextProvider'; +import OptionsListContextProvider from './components/OptionListContextProvider'; import PopoverContextProvider from './components/PopoverProvider'; import SafeArea from './components/SafeArea'; import ThemeIllustrationsProvider from './components/ThemeIllustrationsProvider'; diff --git a/src/components/OptionListContextProvider.tsx b/src/components/OptionListContextProvider.tsx index dbaafa7f6215..ffc72d684f64 100644 --- a/src/components/OptionListContextProvider.tsx +++ b/src/components/OptionListContextProvider.tsx @@ -1,7 +1,12 @@ import React, {createContext, useCallback, useContext, useEffect, useMemo, useRef, useState} from 'react'; +import {withOnyx} from 'react-native-onyx'; +import type {OnyxCollection} from 'react-native-onyx'; import * as OptionsListUtils from '@libs/OptionsListUtils'; import type {OptionList} from '@libs/OptionsListUtils'; -import {usePersonalDetails, useReports} from './OnyxProvider'; +import * as ReportUtils from '@libs/ReportUtils'; +import ONYXKEYS from '@src/ONYXKEYS'; +import type {Report} from '@src/types/onyx'; +import {usePersonalDetails} from './OnyxProvider'; type OptionsListContextProps = { options: OptionList; @@ -9,7 +14,11 @@ type OptionsListContextProps = { areOptionsInitialized: boolean; }; -type OptionsListProviderProps = { +type OptionsListProviderOnyxProps = { + reports: OnyxCollection; +}; + +type OptionsListProviderProps = OptionsListProviderOnyxProps & { /** Actual content wrapped by this component */ children: React.ReactNode; }; @@ -23,14 +32,34 @@ const OptionsListContext = createContext({ areOptionsInitialized: false, }); -function OptionsListContextProvider({children}: OptionsListProviderProps) { +function OptionsListContextProvider({reports, children}: OptionsListProviderProps) { const areOptionsInitialized = useRef(false); const [options, setOptions] = useState({ reports: [], personalDetails: [], }); const personalDetails = usePersonalDetails(); - const reports = useReports(); + + useEffect(() => { + // there is no need to update the options if the options are not initialized + if (!areOptionsInitialized.current) { + return; + } + + const lastUpdatedReport = ReportUtils.getLastUpdatedReport(); + const newOption = OptionsListUtils.createOptionFromReport(lastUpdatedReport, personalDetails); + const replaceIndex = options.reports.findIndex((option) => option.reportID === lastUpdatedReport.reportID); + + if (replaceIndex === undefined) { + return; + } + + setOptions((prevOptions) => { + const newOptions = {...prevOptions}; + newOptions.reports[replaceIndex] = newOption; + return newOptions; + }); + }, [options.reports, personalDetails, reports]); const loadOptions = useCallback(() => { const optionLists = OptionsListUtils.createOptionList(reports, personalDetails); @@ -77,4 +106,10 @@ const useOptionsList = (options?: {shouldInitialize: boolean}) => { }; }; -export {OptionsListContextProvider, useOptionsListContext, useOptionsList}; +export default withOnyx({ + reports: { + key: ONYXKEYS.COLLECTION.REPORT, + }, +})(OptionsListContextProvider); + +export {useOptionsListContext, useOptionsList}; diff --git a/src/libs/OptionsListUtils.ts b/src/libs/OptionsListUtils.ts index c4e9c1c28816..2288cff7a254 100644 --- a/src/libs/OptionsListUtils.ts +++ b/src/libs/OptionsListUtils.ts @@ -1422,6 +1422,24 @@ function createOptionList(reports: OnyxCollection, personalDetails: Onyx }; } +function createOptionFromReport(report: Report, personalDetails: OnyxEntry) { + const accountIDs = report.participantAccountIDs ?? []; + + return { + item: report, + ...createOption( + accountIDs, + personalDetails, + report, + {}, + { + showChatPreviewLine: true, + forcePolicyNamePreview: true, + }, + ), + }; +} + /** * filter options based on specific conditions */ @@ -2139,6 +2157,7 @@ export { transformedTaxRates, getShareLogOptions, createOptionList, + createOptionFromReport, }; export type {MemberForList, CategorySection, GetOptions, OptionList, SearchOption, PayeePersonalDetails}; diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index 8dc1c9967f13..1362974df4f4 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -521,6 +521,23 @@ Onyx.connect({ }, }); +let lastUpdatedReport: Report; + +Onyx.connect({ + key: ONYXKEYS.COLLECTION.REPORT, + callback: (value) => { + if (!value) { + return; + } + + lastUpdatedReport = value; + }, +}); + +function getLastUpdatedReport() { + return lastUpdatedReport; +} + function getCurrentUserAvatarOrDefault(): UserUtils.AvatarSource { return currentUserPersonalDetails?.avatar ?? UserUtils.getDefaultAvatarURL(currentUserAccountID); } @@ -5497,6 +5514,7 @@ export { isJoinRequestInAdminRoom, canAddOrDeleteTransactions, shouldCreateNewMoneyRequestReport, + getLastUpdatedReport, }; export type { From f44142deaae113814ef6792086b1e9a701abdb07 Mon Sep 17 00:00:00 2001 From: Abdullah Alsigar Date: Wed, 13 Mar 2024 23:36:20 +0300 Subject: [PATCH 228/500] lock capturePhoto --- .../step/IOURequestStepScan/index.native.js | 21 ++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/src/pages/iou/request/step/IOURequestStepScan/index.native.js b/src/pages/iou/request/step/IOURequestStepScan/index.native.js index 338444d473c6..eec79d969ce5 100644 --- a/src/pages/iou/request/step/IOURequestStepScan/index.native.js +++ b/src/pages/iou/request/step/IOURequestStepScan/index.native.js @@ -30,6 +30,7 @@ import reportPropTypes from '@pages/reportPropTypes'; import * as IOU from '@userActions/IOU'; import CONST from '@src/CONST'; import ROUTES from '@src/ROUTES'; +import {useNavigation} from "@react-navigation/native"; import * as CameraPermission from './CameraPermission'; import NavigationAwareCamera from './NavigationAwareCamera'; @@ -61,12 +62,24 @@ function IOURequestStepScan({ const styles = useThemeStyles(); const devices = useCameraDevices('wide-angle-camera'); const device = devices.back; + const navigation = useNavigation(); const camera = useRef(null); const [flash, setFlash] = useState(false); const [cameraPermissionStatus, setCameraPermissionStatus] = useState(undefined); const askedForPermission = useRef(false); + const [didCapturePhoto, setDidCapturePhoto] = useState(false); + useEffect(() => { + const unsubscribe = navigation.addListener('focus', () => { + setDidCapturePhoto(false); + }); + + return () => { + unsubscribe(); + } + }, [navigation]); + const {translate} = useLocalize(); const askForPermissions = (showPermissionsAlert = true) => { @@ -238,6 +251,10 @@ function IOURequestStepScan({ return; } + if (didCapturePhoto) { + return; + } + return camera.current .takePhoto({ qualityPrioritization: 'speed', @@ -255,13 +272,15 @@ function IOURequestStepScan({ return; } + setDidCapturePhoto(true); navigateToConfirmationStep(); }) .catch((error) => { + setDidCapturePhoto(false); showCameraAlert(); Log.warn('Error taking photo', error); }); - }, [flash, action, translate, transactionID, updateScanAndNavigate, navigateToConfirmationStep, cameraPermissionStatus]); + }, [flash, action, translate, transactionID, updateScanAndNavigate, navigateToConfirmationStep, cameraPermissionStatus, didCapturePhoto]); // Wait for camera permission status to render if (cameraPermissionStatus == null) { From 2a76bed788590cad8f75e07a502b4ed5d745e5b9 Mon Sep 17 00:00:00 2001 From: Yogesh Bhatt <65986357+ygshbht@users.noreply.github.com> Date: Thu, 14 Mar 2024 03:48:47 +0530 Subject: [PATCH 229/500] change moneyrequest/state url to request/state Co-authored-by: Rajat --- src/ROUTES.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ROUTES.ts b/src/ROUTES.ts index 255afc50a862..f0be85bf553f 100644 --- a/src/ROUTES.ts +++ b/src/ROUTES.ts @@ -431,7 +431,7 @@ const ROUTES = { }, MONEY_REQUEST_STATE_SELECTOR: { - route: 'moneyrequest/state', + route: 'request/state', getRoute: (state?: string, backTo?: string, label?: string) => `${getUrlWithBackToParam(`moneyrequest/state${state ? `?state=${encodeURIComponent(state)}` : ''}`, backTo)}${ From 49f2e467e50da30687754138f3da0f8e01e90e45 Mon Sep 17 00:00:00 2001 From: Yogesh Bhatt <65986357+ygshbht@users.noreply.github.com> Date: Thu, 14 Mar 2024 03:48:59 +0530 Subject: [PATCH 230/500] change moneyrequest/state url to request/state Co-authored-by: Rajat --- src/ROUTES.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ROUTES.ts b/src/ROUTES.ts index f0be85bf553f..b19ba22d2bf3 100644 --- a/src/ROUTES.ts +++ b/src/ROUTES.ts @@ -434,7 +434,7 @@ const ROUTES = { route: 'request/state', getRoute: (state?: string, backTo?: string, label?: string) => - `${getUrlWithBackToParam(`moneyrequest/state${state ? `?state=${encodeURIComponent(state)}` : ''}`, backTo)}${ + `${getUrlWithBackToParam(`request/state${state ? `?state=${encodeURIComponent(state)}` : ''}`, backTo)}${ // the label param can be an empty string so we cannot use a nullish ?? operator // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing label ? `${backTo || state ? '&' : '?'}label=${encodeURIComponent(label)}` : '' From d4621e9757e53247d067ee8d2b0a3c92689354ec Mon Sep 17 00:00:00 2001 From: Tomasz Misiukiewicz Date: Thu, 14 Mar 2024 09:33:03 +0100 Subject: [PATCH 231/500] update changed personal details --- src/components/OptionListContextProvider.tsx | 21 ++++- src/libs/OptionsListUtils.ts | 85 ++++++++++---------- 2 files changed, 63 insertions(+), 43 deletions(-) diff --git a/src/components/OptionListContextProvider.tsx b/src/components/OptionListContextProvider.tsx index ffc72d684f64..5ee76a23c3ef 100644 --- a/src/components/OptionListContextProvider.tsx +++ b/src/components/OptionListContextProvider.tsx @@ -59,10 +59,27 @@ function OptionsListContextProvider({reports, children}: OptionsListProviderProp newOptions.reports[replaceIndex] = newOption; return newOptions; }); - }, [options.reports, personalDetails, reports]); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [reports]); + + useEffect(() => { + // there is no need to update the options if the options are not initialized + if (!areOptionsInitialized.current) { + return; + } + + // since personal details are not a collection, we need to recreate the whole list from scratch + const newPersonalDetailsOptions = OptionsListUtils.createOptionList(personalDetails).personalDetails; + + setOptions((prevOptions) => { + const newOptions = {...prevOptions}; + newOptions.personalDetails = newPersonalDetailsOptions; + return newOptions; + }); + }, [personalDetails]); const loadOptions = useCallback(() => { - const optionLists = OptionsListUtils.createOptionList(reports, personalDetails); + const optionLists = OptionsListUtils.createOptionList(personalDetails, reports); setOptions({ reports: optionLists.reports, personalDetails: optionLists.personalDetails, diff --git a/src/libs/OptionsListUtils.ts b/src/libs/OptionsListUtils.ts index 2288cff7a254..1958015097f4 100644 --- a/src/libs/OptionsListUtils.ts +++ b/src/libs/OptionsListUtils.ts @@ -1349,55 +1349,58 @@ function isReportSelected(reportOption: ReportUtils.OptionData, selectedOptions: return selectedOptions.some((option) => (option.accountID && option.accountID === reportOption.accountID) || (option.reportID && option.reportID === reportOption.reportID)); } -function createOptionList(reports: OnyxCollection, personalDetails: OnyxEntry) { +function createOptionList(personalDetails: OnyxEntry, reports?: OnyxCollection) { const reportMapForAccountIDs: Record = {}; - // Sorting the reports works like this: - // - Order everything by the last message timestamp (descending) - // - All archived reports should remain at the bottom - const orderedReports = lodashSortBy(reports, (report) => { - if (ReportUtils.isArchivedRoom(report)) { - return CONST.DATE.UNIX_EPOCH; - } - - return report?.lastVisibleActionCreated; - }); - orderedReports.reverse(); const allReportOptions: Array> = []; - orderedReports.forEach((report) => { - if (!report) { - return; - } + if (reports) { + // Sorting the reports works like this: + // - Order everything by the last message timestamp (descending) + // - All archived reports should remain at the bottom + const orderedReports = lodashSortBy(reports, (report) => { + if (ReportUtils.isArchivedRoom(report)) { + return CONST.DATE.UNIX_EPOCH; + } - const isSelfDM = ReportUtils.isSelfDM(report); - // Currently, currentUser is not included in visibleChatMemberAccountIDs, so for selfDM we need to add the currentUser as participants. - const accountIDs = isSelfDM ? [currentUserAccountID ?? 0] : report.visibleChatMemberAccountIDs ?? []; + return report?.lastVisibleActionCreated; + }); + orderedReports.reverse(); - if (!accountIDs || accountIDs.length === 0) { - return; - } + orderedReports.forEach((report) => { + if (!report) { + return; + } - // Save the report in the map if this is a single participant so we can associate the reportID with the - // personal detail option later. Individuals should not be associated with single participant - // policyExpenseChats or chatRooms since those are not people. - if (accountIDs.length <= 1) { - reportMapForAccountIDs[accountIDs[0]] = report; - } + const isSelfDM = ReportUtils.isSelfDM(report); + // Currently, currentUser is not included in visibleChatMemberAccountIDs, so for selfDM we need to add the currentUser as participants. + const accountIDs = isSelfDM ? [currentUserAccountID ?? 0] : report.visibleChatMemberAccountIDs ?? []; - allReportOptions.push({ - item: report, - ...createOption( - accountIDs, - personalDetails, - report, - {}, - { - showChatPreviewLine: true, - forcePolicyNamePreview: true, - }, - ), + if (!accountIDs || accountIDs.length === 0) { + return; + } + + // Save the report in the map if this is a single participant so we can associate the reportID with the + // personal detail option later. Individuals should not be associated with single participant + // policyExpenseChats or chatRooms since those are not people. + if (accountIDs.length <= 1) { + reportMapForAccountIDs[accountIDs[0]] = report; + } + + allReportOptions.push({ + item: report, + ...createOption( + accountIDs, + personalDetails, + report, + {}, + { + showChatPreviewLine: true, + forcePolicyNamePreview: true, + }, + ), + }); }); - }); + } const havingLoginPersonalDetails = Object.fromEntries( Object.entries(personalDetails ?? {}).filter(([, detail]) => !!detail?.login && !!detail.accountID && !detail?.isOptimisticPersonalDetail), From 653c9cb99263ff38ab53474fcd85b50dd8f4cee3 Mon Sep 17 00:00:00 2001 From: Tomasz Misiukiewicz Date: Thu, 14 Mar 2024 09:54:51 +0100 Subject: [PATCH 232/500] fix loader on new chat page --- src/pages/NewChatPage.tsx | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/pages/NewChatPage.tsx b/src/pages/NewChatPage.tsx index 9523c31298e3..36d51533b917 100755 --- a/src/pages/NewChatPage.tsx +++ b/src/pages/NewChatPage.tsx @@ -52,6 +52,7 @@ function NewChatPage({betas, isGroupChat, personalDetails, reports, isSearchingF const {options, areOptionsInitialized} = useOptionsList({ shouldInitialize: isScreenTransitionEnd, }); + const styles = useThemeStyles(); const [searchTerm, setSearchTerm] = useState(''); const [filteredRecentReports, setFilteredRecentReports] = useState([]); @@ -60,7 +61,6 @@ function NewChatPage({betas, isGroupChat, personalDetails, reports, isSearchingF const [selectedOptions, setSelectedOptions] = useState([]); const {isOffline} = useNetwork(); const {isSmallScreenWidth} = useWindowDimensions(); - const [didScreenTransitionEnd, setDidScreenTransitionEnd] = useState(false); const maxParticipantsReached = selectedOptions.length === CONST.REPORT.MAXIMUM_PARTICIPANTS; const setSearchTermAndSearchInServer = useSearchTermAndSearch(setSearchTerm, maxParticipantsReached); @@ -212,7 +212,7 @@ function NewChatPage({betas, isGroupChat, personalDetails, reports, isSearchingF useEffect(() => { const interactionTask = doInteractionTask(() => { - setDidScreenTransitionEnd(true); + setIsScreenTransitionEnd(true); }); return () => { @@ -225,11 +225,11 @@ function NewChatPage({betas, isGroupChat, personalDetails, reports, isSearchingF }, []); useEffect(() => { - if (!didScreenTransitionEnd) { + if (!isScreenTransitionEnd) { return; } updateOptions(); - }, [didScreenTransitionEnd, updateOptions]); + }, [isScreenTransitionEnd, updateOptions]); const {inputCallbackRef} = useAutoFocusInput(); @@ -241,7 +241,6 @@ function NewChatPage({betas, isGroupChat, personalDetails, reports, isSearchingF includePaddingTop={false} shouldEnableMaxHeight testID={NewChatPage.displayName} - onEntryTransitionEnd={() => setIsScreenTransitionEnd(true)} > {({safeAreaPaddingBottomStyle, insets}) => ( Date: Thu, 14 Mar 2024 12:25:40 +0100 Subject: [PATCH 233/500] filter personal details with login --- src/libs/OptionsListUtils.ts | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/libs/OptionsListUtils.ts b/src/libs/OptionsListUtils.ts index 1958015097f4..6002b8c6ab4a 100644 --- a/src/libs/OptionsListUtils.ts +++ b/src/libs/OptionsListUtils.ts @@ -1402,10 +1402,7 @@ function createOptionList(personalDetails: OnyxEntry, repor }); } - const havingLoginPersonalDetails = Object.fromEntries( - Object.entries(personalDetails ?? {}).filter(([, detail]) => !!detail?.login && !!detail.accountID && !detail?.isOptimisticPersonalDetail), - ); - const allPersonalDetailsOptions = Object.values(havingLoginPersonalDetails).map((personalDetail) => ({ + const allPersonalDetailsOptions = Object.values(personalDetails ?? {}).map((personalDetail) => ({ item: personalDetail, ...createOption( [personalDetail?.accountID ?? -1], @@ -1616,7 +1613,9 @@ function getOptions( return option; }); - let allPersonalDetailsOptions = options.personalDetails; + const havingLoginPersonalDetails = options.personalDetails.filter((detail) => !!detail?.login && !!detail.accountID && !detail?.isOptimisticPersonalDetail); + + let allPersonalDetailsOptions = havingLoginPersonalDetails; if (sortPersonalDetailsByAlphaAsc) { // PersonalDetails should be ordered Alphabetically by default - https://github.com/Expensify/App/issues/8220#issuecomment-1104009435 From 7ad28722bd623417db85048fda9d7481e87bfacf Mon Sep 17 00:00:00 2001 From: Tomasz Misiukiewicz Date: Thu, 14 Mar 2024 15:54:58 +0100 Subject: [PATCH 234/500] generate alternate text on the fly if needed --- src/libs/OptionsListUtils.ts | 54 ++++++++++++++++++++++------------ src/pages/SearchPage/index.tsx | 1 + 2 files changed, 37 insertions(+), 18 deletions(-) diff --git a/src/libs/OptionsListUtils.ts b/src/libs/OptionsListUtils.ts index 6002b8c6ab4a..1f976055086e 100644 --- a/src/libs/OptionsListUtils.ts +++ b/src/libs/OptionsListUtils.ts @@ -534,6 +534,25 @@ function getLastActorDisplayName(lastActorDetails: Partial | nu : ''; } +/** + * Update alternate text option when applicable + */ +function getAlternateTextOption( + option: ReportUtils.OptionData, + {showChatPreviewLine = false, forcePolicyNamePreview = false, lastMessageTextFromReport = ''}: PreviewConfig & {lastMessageTextFromReport?: string}, +) { + if (!!option.isThread || !!option.isMoneyRequestReport) { + return lastMessageTextFromReport.length > 0 ? option.lastMessageText : Localize.translate(preferredLocale, 'report.noActivityYet'); + } + if (!!option.isChatRoom || !!option.isPolicyExpenseChat) { + return showChatPreviewLine && !forcePolicyNamePreview && option.lastMessageText ? option.lastMessageText : option.subtitle; + } + if (option.isTaskReport) { + return showChatPreviewLine && option.lastMessageText ? option.lastMessageText : Localize.translate(preferredLocale, 'report.noActivityYet'); + } + return showChatPreviewLine && option.lastMessageText ? option.lastMessageText : LocalePhoneNumber.formatPhoneNumber((option.participantsList ?? [])[0].login ?? ''); +} + /** * Get the last message text from the report directly or from other sources for special cases. */ @@ -646,6 +665,7 @@ function createOption( isExpenseReport: false, policyID: undefined, isOptimisticPersonalDetail: false, + lastMessageText: '', }; const personalDetailMap = getPersonalDetailsForAccountIDs(accountIDs, personalDetails); @@ -699,14 +719,11 @@ function createOption( lastMessageText = `${lastActorDisplayName}: ${lastMessageTextFromReport}`; } - if (result.isThread || result.isMoneyRequestReport) { - result.alternateText = lastMessageTextFromReport.length > 0 ? lastMessageText : Localize.translate(preferredLocale, 'report.noActivityYet'); - } else if (result.isChatRoom || result.isPolicyExpenseChat) { - result.alternateText = showChatPreviewLine && !forcePolicyNamePreview && lastMessageText ? lastMessageText : subtitle; - } else if (result.isTaskReport) { - result.alternateText = showChatPreviewLine && lastMessageText ? lastMessageText : Localize.translate(preferredLocale, 'report.noActivityYet'); - } else { - result.alternateText = showChatPreviewLine && lastMessageText ? lastMessageText : LocalePhoneNumber.formatPhoneNumber(personalDetail?.login ?? ''); + result.lastMessageText = lastMessageText; + + // If displaying chat preview line is needed, let's overwrite the default alternate text + if (showChatPreviewLine || forcePolicyNamePreview) { + result.alternateText = getAlternateTextOption(result, {showChatPreviewLine, forcePolicyNamePreview, lastMessageTextFromReport}); } reportName = ReportUtils.getReportName(report); } else { @@ -1394,8 +1411,8 @@ function createOptionList(personalDetails: OnyxEntry, repor report, {}, { - showChatPreviewLine: true, - forcePolicyNamePreview: true, + showChatPreviewLine: false, + forcePolicyNamePreview: false, }, ), }); @@ -1410,8 +1427,8 @@ function createOptionList(personalDetails: OnyxEntry, repor reportMapForAccountIDs[personalDetail?.accountID ?? -1], {}, { - showChatPreviewLine: true, - forcePolicyNamePreview: true, + showChatPreviewLine: false, + forcePolicyNamePreview: false, }, ), })); @@ -1433,8 +1450,8 @@ function createOptionFromReport(report: Report, personalDetails: OnyxEntry 0 && recentReportOptions.length === maxRecentReportsToShow) { diff --git a/src/pages/SearchPage/index.tsx b/src/pages/SearchPage/index.tsx index 6e06024521fd..d08d68c37cdc 100644 --- a/src/pages/SearchPage/index.tsx +++ b/src/pages/SearchPage/index.tsx @@ -59,6 +59,7 @@ function SearchPage({betas, isSearchingForReports, navigation}: SearchPageProps) const {options, areOptionsInitialized} = useOptionsList({ shouldInitialize: isScreenTransitionEnd, }); + const offlineMessage: MaybePhraseKey = isOffline ? [`${translate('common.youAppearToBeOffline')} ${translate('search.resultsAreLimited')}`, {isTranslated: true}] : ''; const [searchValue, debouncedSearchValue, setSearchValue] = useDebouncedState(''); From 67a0ae29aeb32bd5a9efc3a6c05c80b34a2371c0 Mon Sep 17 00:00:00 2001 From: Tomasz Misiukiewicz Date: Thu, 14 Mar 2024 15:59:48 +0100 Subject: [PATCH 235/500] freeze the option list when search is open --- src/components/OptionListContextProvider.tsx | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/components/OptionListContextProvider.tsx b/src/components/OptionListContextProvider.tsx index 5ee76a23c3ef..00cdaa6dc1ac 100644 --- a/src/components/OptionListContextProvider.tsx +++ b/src/components/OptionListContextProvider.tsx @@ -107,19 +107,23 @@ const useOptionsListContext = () => useContext(OptionsListContext); // Hook to use the OptionsListContext with an initializer to load the options const useOptionsList = (options?: {shouldInitialize: boolean}) => { const {shouldInitialize = true} = options ?? {}; - const {initializeOptions, ...optionsListContext} = useOptionsListContext(); + const {initializeOptions, options: optionList, areOptionsInitialized} = useOptionsListContext(); + + // freeze the options, so they won't update when the context updates and the screen is open + const optionsRef = useRef(optionList); useEffect(() => { - if (!shouldInitialize || optionsListContext.areOptionsInitialized) { + if (!shouldInitialize || areOptionsInitialized) { return; } initializeOptions(); - }, [shouldInitialize, initializeOptions, optionsListContext.areOptionsInitialized]); + }, [shouldInitialize, initializeOptions, areOptionsInitialized]); return { initializeOptions, - ...optionsListContext, + options: optionsRef.current, + areOptionsInitialized, }; }; From 0b0847d863c8e2e43ed4d564f197f13080b07620 Mon Sep 17 00:00:00 2001 From: Abdullah Alsigar Date: Thu, 14 Mar 2024 18:37:46 +0300 Subject: [PATCH 236/500] prettier --- src/pages/iou/request/step/IOURequestStepScan/index.native.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/pages/iou/request/step/IOURequestStepScan/index.native.js b/src/pages/iou/request/step/IOURequestStepScan/index.native.js index eec79d969ce5..22b54b1247ef 100644 --- a/src/pages/iou/request/step/IOURequestStepScan/index.native.js +++ b/src/pages/iou/request/step/IOURequestStepScan/index.native.js @@ -1,3 +1,4 @@ +import {useNavigation} from '@react-navigation/native'; import lodashGet from 'lodash/get'; import React, {useCallback, useEffect, useRef, useState} from 'react'; import {ActivityIndicator, Alert, AppState, View} from 'react-native'; @@ -30,7 +31,6 @@ import reportPropTypes from '@pages/reportPropTypes'; import * as IOU from '@userActions/IOU'; import CONST from '@src/CONST'; import ROUTES from '@src/ROUTES'; -import {useNavigation} from "@react-navigation/native"; import * as CameraPermission from './CameraPermission'; import NavigationAwareCamera from './NavigationAwareCamera'; @@ -77,7 +77,7 @@ function IOURequestStepScan({ return () => { unsubscribe(); - } + }; }, [navigation]); const {translate} = useLocalize(); From 47c9e6bbd151dd4d6971b93e50d5a75fe2eff155 Mon Sep 17 00:00:00 2001 From: Tomasz Misiukiewicz Date: Fri, 15 Mar 2024 11:08:04 +0100 Subject: [PATCH 237/500] remove unnecessary code --- src/components/OnyxProvider.tsx | 5 - src/components/OptionListContextProvider.tsx | 13 ++- src/pages/NewChatPage.tsx | 16 +-- .../ShareLogList/BaseShareLogList.tsx | 6 +- src/pages/tasks/NewTaskPage.tsx | 2 +- src/pages/tasks/TaskAssigneeSelectorModal.tsx | 17 +-- .../TaskShareDestinationSelectorModal.tsx | 8 +- src/pages/workspace/WorkspaceInvitePage.tsx | 109 +++++++++--------- 8 files changed, 88 insertions(+), 88 deletions(-) diff --git a/src/components/OnyxProvider.tsx b/src/components/OnyxProvider.tsx index c57c38c3eba1..0bc9130ea4a8 100644 --- a/src/components/OnyxProvider.tsx +++ b/src/components/OnyxProvider.tsx @@ -15,7 +15,6 @@ const [withReportCommentDrafts, ReportCommentDraftsProvider] = createOnyxContext const [withPreferredTheme, PreferredThemeProvider, PreferredThemeContext] = createOnyxContext(ONYXKEYS.PREFERRED_THEME); const [withFrequentlyUsedEmojis, FrequentlyUsedEmojisProvider, , useFrequentlyUsedEmojis] = createOnyxContext(ONYXKEYS.FREQUENTLY_USED_EMOJIS); const [withPreferredEmojiSkinTone, PreferredEmojiSkinToneProvider, PreferredEmojiSkinToneContext] = createOnyxContext(ONYXKEYS.PREFERRED_EMOJI_SKIN_TONE); -const [withReports, ReportsProvider, ReportsContext, useReports] = createOnyxContext(ONYXKEYS.COLLECTION.REPORT); const [, SessionProvider, , useSession] = createOnyxContext(ONYXKEYS.SESSION); type OnyxProviderProps = { @@ -38,7 +37,6 @@ function OnyxProvider(props: OnyxProviderProps) { FrequentlyUsedEmojisProvider, PreferredEmojiSkinToneProvider, SessionProvider, - ReportsProvider, ]} > {props.children} @@ -71,7 +69,4 @@ export { useBlockedFromConcierge, useReportActionsDrafts, useSession, - withReports, - ReportsContext, - useReports, }; diff --git a/src/components/OptionListContextProvider.tsx b/src/components/OptionListContextProvider.tsx index 00cdaa6dc1ac..11906c6274c9 100644 --- a/src/components/OptionListContextProvider.tsx +++ b/src/components/OptionListContextProvider.tsx @@ -107,10 +107,10 @@ const useOptionsListContext = () => useContext(OptionsListContext); // Hook to use the OptionsListContext with an initializer to load the options const useOptionsList = (options?: {shouldInitialize: boolean}) => { const {shouldInitialize = true} = options ?? {}; - const {initializeOptions, options: optionList, areOptionsInitialized} = useOptionsListContext(); + const {initializeOptions, options: optionsList, areOptionsInitialized} = useOptionsListContext(); // freeze the options, so they won't update when the context updates and the screen is open - const optionsRef = useRef(optionList); + const optionsRef = useRef(optionsList); useEffect(() => { if (!shouldInitialize || areOptionsInitialized) { @@ -120,6 +120,15 @@ const useOptionsList = (options?: {shouldInitialize: boolean}) => { initializeOptions(); }, [shouldInitialize, initializeOptions, areOptionsInitialized]); + useEffect(() => { + if (!areOptionsInitialized) { + return; + } + + optionsRef.current = optionsList; + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [areOptionsInitialized]); + return { initializeOptions, options: optionsRef.current, diff --git a/src/pages/NewChatPage.tsx b/src/pages/NewChatPage.tsx index 36d51533b917..2b072d2a0480 100755 --- a/src/pages/NewChatPage.tsx +++ b/src/pages/NewChatPage.tsx @@ -47,11 +47,7 @@ type NewChatPageProps = NewChatPageWithOnyxProps & { const excludedGroupEmails = CONST.EXPENSIFY_EMAILS.filter((value) => value !== CONST.EMAIL.CONCIERGE); function NewChatPage({betas, isGroupChat, personalDetails, reports, isSearchingForReports, dismissedReferralBanners}: NewChatPageProps) { - const [isScreenTransitionEnd, setIsScreenTransitionEnd] = useState(false); const {translate} = useLocalize(); - const {options, areOptionsInitialized} = useOptionsList({ - shouldInitialize: isScreenTransitionEnd, - }); const styles = useThemeStyles(); const [searchTerm, setSearchTerm] = useState(''); @@ -61,6 +57,10 @@ function NewChatPage({betas, isGroupChat, personalDetails, reports, isSearchingF const [selectedOptions, setSelectedOptions] = useState([]); const {isOffline} = useNetwork(); const {isSmallScreenWidth} = useWindowDimensions(); + const [didScreenTransitionEnd, setDidScreenTransitionEnd] = useState(false); + const {options, areOptionsInitialized} = useOptionsList({ + shouldInitialize: didScreenTransitionEnd, + }); const maxParticipantsReached = selectedOptions.length === CONST.REPORT.MAXIMUM_PARTICIPANTS; const setSearchTermAndSearchInServer = useSearchTermAndSearch(setSearchTerm, maxParticipantsReached); @@ -212,7 +212,7 @@ function NewChatPage({betas, isGroupChat, personalDetails, reports, isSearchingF useEffect(() => { const interactionTask = doInteractionTask(() => { - setIsScreenTransitionEnd(true); + setDidScreenTransitionEnd(true); }); return () => { @@ -225,11 +225,11 @@ function NewChatPage({betas, isGroupChat, personalDetails, reports, isSearchingF }, []); useEffect(() => { - if (!isScreenTransitionEnd) { + if (!didScreenTransitionEnd) { return; } updateOptions(); - }, [isScreenTransitionEnd, updateOptions]); + }, [didScreenTransitionEnd, updateOptions]); const {inputCallbackRef} = useAutoFocusInput(); @@ -265,7 +265,7 @@ function NewChatPage({betas, isGroupChat, personalDetails, reports, isSearchingF headerMessage={headerMessage} boldStyle shouldPreventDefaultFocusOnSelectRow={!DeviceCapabilities.canUseTouchScreen()} - shouldShowOptions={areOptionsInitialized && isScreenTransitionEnd} + shouldShowOptions={areOptionsInitialized && didScreenTransitionEnd} shouldShowConfirmButton shouldShowReferralCTA={!dismissedReferralBanners[CONST.REFERRAL_PROGRAM.CONTENT_TYPES.START_CHAT]} referralContentType={CONST.REFERRAL_PROGRAM.CONTENT_TYPES.START_CHAT} diff --git a/src/pages/settings/AboutPage/ShareLogList/BaseShareLogList.tsx b/src/pages/settings/AboutPage/ShareLogList/BaseShareLogList.tsx index 8c84ba12b153..cc9f1564c25e 100644 --- a/src/pages/settings/AboutPage/ShareLogList/BaseShareLogList.tsx +++ b/src/pages/settings/AboutPage/ShareLogList/BaseShareLogList.tsx @@ -23,13 +23,13 @@ function BaseShareLogList({betas, onAttachLogToReport}: BaseShareLogListProps) { personalDetails: [], userToInvite: null, }); - + console.log({searchOptions}); const {isOffline} = useNetwork(); const {translate} = useLocalize(); const styles = useThemeStyles(); const isMounted = useRef(false); const {options, areOptionsInitialized} = useOptionsList(); - + console.log({options}); const updateOptions = useCallback(() => { const { recentReports: localRecentReports, @@ -51,7 +51,7 @@ function BaseShareLogList({betas, onAttachLogToReport}: BaseShareLogListProps) { updateOptions(); // eslint-disable-next-line react-hooks/exhaustive-deps - }, [areOptionsInitialized]); + }, [options, areOptionsInitialized]); useEffect(() => { if (!isMounted.current) { diff --git a/src/pages/tasks/NewTaskPage.tsx b/src/pages/tasks/NewTaskPage.tsx index 64c46e75c91d..87541d82e094 100644 --- a/src/pages/tasks/NewTaskPage.tsx +++ b/src/pages/tasks/NewTaskPage.tsx @@ -178,7 +178,7 @@ function NewTaskPage({task, reports, personalDetails}: NewTaskPageProps) { description={shareDestination?.displayName ? shareDestination.subtitle : translate('newTaskPage.shareSomewhere')} icon={shareDestination?.icons} onPress={() => Navigation.navigate(ROUTES.NEW_TASK_SHARE_DESTINATION)} - interactive={!task?.parentReportID} + interactive shouldShowRightIcon={!task?.parentReportID} titleWithTooltips={shareDestination?.shouldUseFullTitleToDisplay ? shareDestination?.displayNamesWithTooltips : []} /> diff --git a/src/pages/tasks/TaskAssigneeSelectorModal.tsx b/src/pages/tasks/TaskAssigneeSelectorModal.tsx index d367a06cb60e..4185621398dd 100644 --- a/src/pages/tasks/TaskAssigneeSelectorModal.tsx +++ b/src/pages/tasks/TaskAssigneeSelectorModal.tsx @@ -41,12 +41,10 @@ type TaskAssigneeSelectorModalOnyxProps = { type TaskAssigneeSelectorModalProps = TaskAssigneeSelectorModalOnyxProps & WithCurrentUserPersonalDetailsProps; function useOptions() { - const [isScreenTransitionEnd, setIsScreenTransitionEnd] = useState(false); const betas = useBetas(); + const [isLoading, setIsLoading] = useState(true); const [searchValue, debouncedSearchValue, setSearchValue] = useDebouncedState(''); - const {options: optionsList, areOptionsInitialized} = useOptionsList({ - shouldInitialize: isScreenTransitionEnd, - }); + const {options: optionsList, areOptionsInitialized} = useOptionsList(); const options = useMemo(() => { const {recentReports, personalDetails, userToInvite, currentUserOption} = OptionsListUtils.getFilteredOptions( @@ -73,6 +71,10 @@ function useOptions() { debouncedSearchValue, ); + if (isLoading) { + setIsLoading(false); + } + return { userToInvite, recentReports, @@ -80,9 +82,9 @@ function useOptions() { currentUserOption, headerMessage, }; - }, [optionsList.reports, optionsList.personalDetails, betas, debouncedSearchValue]); + }, [optionsList.reports, optionsList.personalDetails, betas, debouncedSearchValue, isLoading]); - return {...options, searchValue, debouncedSearchValue, setSearchValue, areOptionsInitialized, setIsScreenTransitionEnd}; + return {...options, searchValue, debouncedSearchValue, setSearchValue, areOptionsInitialized}; } function TaskAssigneeSelectorModal({reports, task}: TaskAssigneeSelectorModalProps) { @@ -91,7 +93,7 @@ function TaskAssigneeSelectorModal({reports, task}: TaskAssigneeSelectorModalPro const {translate} = useLocalize(); const session = useSession(); const currentUserPersonalDetails = useCurrentUserPersonalDetails(); - const {userToInvite, recentReports, personalDetails, currentUserOption, searchValue, setSearchValue, headerMessage, areOptionsInitialized, setIsScreenTransitionEnd} = useOptions(); + const {userToInvite, recentReports, personalDetails, currentUserOption, searchValue, setSearchValue, headerMessage, areOptionsInitialized} = useOptions(); const onChangeText = (newSearchTerm = '') => { setSearchValue(newSearchTerm); @@ -206,7 +208,6 @@ function TaskAssigneeSelectorModal({reports, task}: TaskAssigneeSelectorModalPro setIsScreenTransitionEnd(true)} > {({didScreenTransitionEnd}) => ( diff --git a/src/pages/tasks/TaskShareDestinationSelectorModal.tsx b/src/pages/tasks/TaskShareDestinationSelectorModal.tsx index 0c31058fe329..9a1dac648887 100644 --- a/src/pages/tasks/TaskShareDestinationSelectorModal.tsx +++ b/src/pages/tasks/TaskShareDestinationSelectorModal.tsx @@ -1,4 +1,4 @@ -import React, {useEffect, useMemo, useState} from 'react'; +import React, {useEffect, useMemo} from 'react'; import {View} from 'react-native'; import {withOnyx} from 'react-native-onyx'; import type {OnyxEntry} from 'react-native-onyx'; @@ -48,14 +48,11 @@ const reportFilter = (reportOptions: Array }, []); function TaskShareDestinationSelectorModal({isSearchingForReports}: TaskShareDestinationSelectorModalProps) { - const [isScreenTransitionEnd, setIsScreenTransitionEnd] = useState(false); const styles = useThemeStyles(); const [searchValue, debouncedSearchValue, setSearchValue] = useDebouncedState(''); const {translate} = useLocalize(); const {isOffline} = useNetwork(); - const {options: optionList, areOptionsInitialized} = useOptionsList({ - shouldInitialize: isScreenTransitionEnd, - }); + const {options: optionList, areOptionsInitialized} = useOptionsList(); const textInputHint = useMemo(() => (isOffline ? `${translate('common.youAppearToBeOffline')} ${translate('search.resultsAreLimited')}` : ''), [isOffline, translate]); @@ -101,7 +98,6 @@ function TaskShareDestinationSelectorModal({isSearchingForReports}: TaskShareDes setIsScreenTransitionEnd(true)} > {({didScreenTransitionEnd}) => ( <> diff --git a/src/pages/workspace/WorkspaceInvitePage.tsx b/src/pages/workspace/WorkspaceInvitePage.tsx index d01a73fdd9ff..da6c00472306 100644 --- a/src/pages/workspace/WorkspaceInvitePage.tsx +++ b/src/pages/workspace/WorkspaceInvitePage.tsx @@ -67,15 +67,13 @@ function WorkspaceInvitePage({ const [selectedOptions, setSelectedOptions] = useState([]); const [personalDetails, setPersonalDetails] = useState([]); const [usersToInvite, setUsersToInvite] = useState([]); - const [isScreenTransitionEnd, setIsScreenTransitionEnd] = useState(false); + const [didScreenTransitionEnd, setDidScreenTransitionEnd] = useState(false); const navigation = useNavigation>(); const openWorkspaceInvitePage = () => { const policyMemberEmailsToAccountIDs = PolicyUtils.getMemberAccountIDsForWorkspace(policyMembers, personalDetailsProp); Policy.openWorkspaceInvitePage(route.params.policyID, Object.keys(policyMemberEmailsToAccountIDs)); }; - const {options, areOptionsInitialized} = useOptionsList({ - shouldInitialize: isScreenTransitionEnd, - }); + const {options, areOptionsInitialized} = useOptionsList(); useEffect(() => { setSearchTerm(SearchInputManager.searchInput); @@ -92,7 +90,7 @@ function WorkspaceInvitePage({ useEffect(() => { const unsubscribeTransitionEnd = navigation.addListener('transitionEnd', () => { - setIsScreenTransitionEnd(true); + setDidScreenTransitionEnd(true); }); return () => { @@ -168,6 +166,10 @@ function WorkspaceInvitePage({ const sectionsArr: MembersSection[] = []; let indexOffset = 0; + if (!didScreenTransitionEnd) { + return []; + } + // Filter all options that is a part of the search term or in the personal details let filterSelectedOptions = selectedOptions; if (searchTerm !== '') { @@ -217,7 +219,7 @@ function WorkspaceInvitePage({ }); return sectionsArr; - }, [selectedOptions, searchTerm, personalDetails, translate, usersToInvite]); + }, [didScreenTransitionEnd, selectedOptions, searchTerm, personalDetails, translate, usersToInvite]); const toggleOption = (option: MemberForList) => { Policy.clearErrors(route.params.policyID); @@ -287,57 +289,54 @@ function WorkspaceInvitePage({ shouldEnableMaxHeight shouldUseCachedViewportHeight testID={WorkspaceInvitePage.displayName} - onEntryTransitionEnd={() => setIsScreenTransitionEnd(true)} > - {({didScreenTransitionEnd}) => ( - - { - Policy.clearErrors(route.params.policyID); - Navigation.goBack(); - }} - /> - { - SearchInputManager.searchInput = value; - setSearchTerm(value); - }} - headerMessage={headerMessage} - onSelectRow={toggleOption} - onConfirm={inviteUser} - showScrollIndicator - showLoadingPlaceholder={areOptionsInitialized && searchTerm.trim() === '' ? sections.length === 0 : !didScreenTransitionEnd} - shouldPreventDefaultFocusOnSelectRow={!DeviceCapabilities.canUseTouchScreen()} - checkmarkPosition={CONST.DIRECTION.RIGHT} + + { + Policy.clearErrors(route.params.policyID); + Navigation.goBack(); + }} + /> + { + SearchInputManager.searchInput = value; + setSearchTerm(value); + }} + headerMessage={headerMessage} + onSelectRow={toggleOption} + onConfirm={inviteUser} + showScrollIndicator + showLoadingPlaceholder={areOptionsInitialized && searchTerm.trim() === '' ? sections.length === 0 : !didScreenTransitionEnd} + shouldPreventDefaultFocusOnSelectRow={!DeviceCapabilities.canUseTouchScreen()} + checkmarkPosition={CONST.DIRECTION.RIGHT} + /> + + - - - - - )} + + ); } From 0377ba6965974a52181360ad0776646cfe6f410d Mon Sep 17 00:00:00 2001 From: Tomasz Misiukiewicz Date: Fri, 15 Mar 2024 11:31:26 +0100 Subject: [PATCH 238/500] remove console logs --- src/pages/settings/AboutPage/ShareLogList/BaseShareLogList.tsx | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/pages/settings/AboutPage/ShareLogList/BaseShareLogList.tsx b/src/pages/settings/AboutPage/ShareLogList/BaseShareLogList.tsx index cc9f1564c25e..75e0879398ac 100644 --- a/src/pages/settings/AboutPage/ShareLogList/BaseShareLogList.tsx +++ b/src/pages/settings/AboutPage/ShareLogList/BaseShareLogList.tsx @@ -23,13 +23,11 @@ function BaseShareLogList({betas, onAttachLogToReport}: BaseShareLogListProps) { personalDetails: [], userToInvite: null, }); - console.log({searchOptions}); const {isOffline} = useNetwork(); const {translate} = useLocalize(); const styles = useThemeStyles(); const isMounted = useRef(false); const {options, areOptionsInitialized} = useOptionsList(); - console.log({options}); const updateOptions = useCallback(() => { const { recentReports: localRecentReports, From c6e95419d8c5e4b0570dfa84aaadf7d4dd7ddb0c Mon Sep 17 00:00:00 2001 From: Tomasz Misiukiewicz Date: Fri, 15 Mar 2024 13:57:28 +0100 Subject: [PATCH 239/500] fix for generating alternate text --- src/components/OptionListContextProvider.tsx | 14 +----- src/libs/OptionsListUtils.ts | 49 ++++---------------- 2 files changed, 9 insertions(+), 54 deletions(-) diff --git a/src/components/OptionListContextProvider.tsx b/src/components/OptionListContextProvider.tsx index 11906c6274c9..feabc0464913 100644 --- a/src/components/OptionListContextProvider.tsx +++ b/src/components/OptionListContextProvider.tsx @@ -109,9 +109,6 @@ const useOptionsList = (options?: {shouldInitialize: boolean}) => { const {shouldInitialize = true} = options ?? {}; const {initializeOptions, options: optionsList, areOptionsInitialized} = useOptionsListContext(); - // freeze the options, so they won't update when the context updates and the screen is open - const optionsRef = useRef(optionsList); - useEffect(() => { if (!shouldInitialize || areOptionsInitialized) { return; @@ -120,18 +117,9 @@ const useOptionsList = (options?: {shouldInitialize: boolean}) => { initializeOptions(); }, [shouldInitialize, initializeOptions, areOptionsInitialized]); - useEffect(() => { - if (!areOptionsInitialized) { - return; - } - - optionsRef.current = optionsList; - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [areOptionsInitialized]); - return { initializeOptions, - options: optionsRef.current, + options: optionsList, areOptionsInitialized, }; }; diff --git a/src/libs/OptionsListUtils.ts b/src/libs/OptionsListUtils.ts index 1f976055086e..bb20dfeb645f 100644 --- a/src/libs/OptionsListUtils.ts +++ b/src/libs/OptionsListUtils.ts @@ -631,8 +631,10 @@ function createOption( personalDetails: OnyxEntry, report: OnyxEntry, reportActions: ReportActions, - {showChatPreviewLine = false, forcePolicyNamePreview = false}: PreviewConfig, + config?: PreviewConfig, ): ReportUtils.OptionData { + const {showChatPreviewLine = false, forcePolicyNamePreview = false} = config ?? {}; + const result: ReportUtils.OptionData = { text: undefined, alternateText: null, @@ -725,6 +727,7 @@ function createOption( if (showChatPreviewLine || forcePolicyNamePreview) { result.alternateText = getAlternateTextOption(result, {showChatPreviewLine, forcePolicyNamePreview, lastMessageTextFromReport}); } + reportName = ReportUtils.getReportName(report); } else { // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing @@ -765,16 +768,7 @@ function createOption( function getPolicyExpenseReportOption(report: Report): ReportUtils.OptionData { const expenseReport = policyExpenseReports?.[`${ONYXKEYS.COLLECTION.REPORT}${report.reportID}`]; - const option = createOption( - expenseReport?.visibleChatMemberAccountIDs ?? [], - allPersonalDetails ?? {}, - expenseReport ?? null, - {}, - { - showChatPreviewLine: false, - forcePolicyNamePreview: false, - }, - ); + const option = createOption(expenseReport?.visibleChatMemberAccountIDs ?? [], allPersonalDetails ?? {}, expenseReport ?? null, {}); // Update text & alternateText because createOption returns workspace name only if report is owned by the user option.text = ReportUtils.getPolicyName(expenseReport); @@ -1405,32 +1399,14 @@ function createOptionList(personalDetails: OnyxEntry, repor allReportOptions.push({ item: report, - ...createOption( - accountIDs, - personalDetails, - report, - {}, - { - showChatPreviewLine: false, - forcePolicyNamePreview: false, - }, - ), + ...createOption(accountIDs, personalDetails, report, {}), }); }); } const allPersonalDetailsOptions = Object.values(personalDetails ?? {}).map((personalDetail) => ({ item: personalDetail, - ...createOption( - [personalDetail?.accountID ?? -1], - personalDetails, - reportMapForAccountIDs[personalDetail?.accountID ?? -1], - {}, - { - showChatPreviewLine: false, - forcePolicyNamePreview: false, - }, - ), + ...createOption([personalDetail?.accountID ?? -1], personalDetails, reportMapForAccountIDs[personalDetail?.accountID ?? -1], {}), })); return { @@ -1444,16 +1420,7 @@ function createOptionFromReport(report: Report, personalDetails: OnyxEntry Date: Fri, 15 Mar 2024 18:26:03 +0100 Subject: [PATCH 240/500] fix jest tests --- src/libs/OptionsListUtils.ts | 14 +- tests/unit/OptionsListUtilsTest.js | 209 +++++++++++++++++------------ 2 files changed, 123 insertions(+), 100 deletions(-) diff --git a/src/libs/OptionsListUtils.ts b/src/libs/OptionsListUtils.ts index bb20dfeb645f..a5906d45b1cc 100644 --- a/src/libs/OptionsListUtils.ts +++ b/src/libs/OptionsListUtils.ts @@ -1365,19 +1365,7 @@ function createOptionList(personalDetails: OnyxEntry, repor const allReportOptions: Array> = []; if (reports) { - // Sorting the reports works like this: - // - Order everything by the last message timestamp (descending) - // - All archived reports should remain at the bottom - const orderedReports = lodashSortBy(reports, (report) => { - if (ReportUtils.isArchivedRoom(report)) { - return CONST.DATE.UNIX_EPOCH; - } - - return report?.lastVisibleActionCreated; - }); - orderedReports.reverse(); - - orderedReports.forEach((report) => { + Object.values(reports).forEach((report) => { if (!report) { return; } diff --git a/tests/unit/OptionsListUtilsTest.js b/tests/unit/OptionsListUtilsTest.js index 7244b7830a29..ea06c1d99db1 100644 --- a/tests/unit/OptionsListUtilsTest.js +++ b/tests/unit/OptionsListUtilsTest.js @@ -311,25 +311,38 @@ describe('OptionsListUtils', () => { return waitForBatchedUpdates().then(() => Onyx.set(ONYXKEYS.PERSONAL_DETAILS_LIST, PERSONAL_DETAILS)); }); + let OPTIONS = {}; + let OPTIONS_WITH_CONCIERGE = {}; + let OPTIONS_WITH_CHRONOS = {}; + let OPTIONS_WITH_RECEIPTS = {}; + let OPTIONS_WITH_WORKSPACES = {}; + + beforeEach(() => { + OPTIONS = OptionsListUtils.createOptionList(PERSONAL_DETAILS, REPORTS); + OPTIONS_WITH_CONCIERGE = OptionsListUtils.createOptionList(PERSONAL_DETAILS_WITH_CONCIERGE, REPORTS_WITH_CONCIERGE); + OPTIONS_WITH_CHRONOS = OptionsListUtils.createOptionList(PERSONAL_DETAILS_WITH_CHRONOS, REPORTS_WITH_CHRONOS); + OPTIONS_WITH_RECEIPTS = OptionsListUtils.createOptionList(PERSONAL_DETAILS_WITH_RECEIPTS, REPORTS_WITH_RECEIPTS); + OPTIONS_WITH_WORKSPACES = OptionsListUtils.createOptionList(PERSONAL_DETAILS, REPORTS_WITH_WORKSPACE_ROOMS); + }); + it('getSearchOptions()', () => { // When we filter in the Search view without providing a searchValue - let results = OptionsListUtils.getSearchOptions(REPORTS, PERSONAL_DETAILS, '', [CONST.BETAS.ALL]); - + let results = OptionsListUtils.getSearchOptions(OPTIONS, '', [CONST.BETAS.ALL]); // Then the 2 personalDetails that don't have reports should be returned expect(results.personalDetails.length).toBe(2); // Then all of the reports should be shown including the archived rooms. - expect(results.recentReports.length).toBe(_.size(REPORTS)); + expect(results.recentReports.length).toBe(_.size(OPTIONS.reports)); // When we filter again but provide a searchValue - results = OptionsListUtils.getSearchOptions(REPORTS, PERSONAL_DETAILS, 'spider'); + results = OptionsListUtils.getSearchOptions(OPTIONS, 'spider'); // Then only one option should be returned and it's the one matching the search value expect(results.recentReports.length).toBe(1); expect(results.recentReports[0].login).toBe('peterparker@expensify.com'); // When we filter again but provide a searchValue that should match multiple times - results = OptionsListUtils.getSearchOptions(REPORTS, PERSONAL_DETAILS, 'fantastic'); + results = OptionsListUtils.getSearchOptions(OPTIONS, 'fantastic'); // Value with latest lastVisibleActionCreated should be at the top. expect(results.recentReports.length).toBe(2); @@ -339,9 +352,9 @@ describe('OptionsListUtils', () => { return waitForBatchedUpdates() .then(() => Onyx.set(ONYXKEYS.PERSONAL_DETAILS_LIST, PERSONAL_DETAILS_WITH_PERIODS)) .then(() => { + const OPTIONS_WITH_PERIODS = OptionsListUtils.createOptionList(PERSONAL_DETAILS_WITH_PERIODS, REPORTS); // When we filter again but provide a searchValue that should match with periods - results = OptionsListUtils.getSearchOptions(REPORTS, PERSONAL_DETAILS_WITH_PERIODS, 'barry.allen@expensify.com'); - + results = OptionsListUtils.getSearchOptions(OPTIONS_WITH_PERIODS, 'barry.allen@expensify.com'); // Then we expect to have the personal detail with period filtered expect(results.recentReports.length).toBe(1); expect(results.recentReports[0].text).toBe('The Flash'); @@ -353,14 +366,14 @@ describe('OptionsListUtils', () => { const MAX_RECENT_REPORTS = 5; // When we call getFilteredOptions() with no search value - let results = OptionsListUtils.getFilteredOptions(REPORTS, PERSONAL_DETAILS, [], ''); + let results = OptionsListUtils.getFilteredOptions(OPTIONS.reports, OPTIONS.personalDetails, [], ''); // We should expect maximimum of 5 recent reports to be returned expect(results.recentReports.length).toBe(MAX_RECENT_REPORTS); // We should expect all personalDetails to be returned, // minus the currently logged in user and recent reports count - expect(results.personalDetails.length).toBe(_.size(PERSONAL_DETAILS) - 1 - MAX_RECENT_REPORTS); + expect(results.personalDetails.length).toBe(_.size(OPTIONS.personalDetails) - 1 - MAX_RECENT_REPORTS); // We should expect personal details sorted alphabetically expect(results.personalDetails[0].text).toBe('Black Widow'); @@ -373,7 +386,7 @@ describe('OptionsListUtils', () => { expect(personalDetailWithExistingReport.reportID).toBe(2); // When we only pass personal details - results = OptionsListUtils.getFilteredOptions([], PERSONAL_DETAILS, [], ''); + results = OptionsListUtils.getFilteredOptions([], OPTIONS.personalDetails, [], ''); // We should expect personal details sorted alphabetically expect(results.personalDetails[0].text).toBe('Black Panther'); @@ -382,13 +395,13 @@ describe('OptionsListUtils', () => { expect(results.personalDetails[3].text).toBe('Invisible Woman'); // When we provide a search value that does not match any personal details - results = OptionsListUtils.getFilteredOptions(REPORTS, PERSONAL_DETAILS, [], 'magneto'); + results = OptionsListUtils.getFilteredOptions(OPTIONS.reports, OPTIONS.personalDetails, [], 'magneto'); // Then no options will be returned expect(results.personalDetails.length).toBe(0); // When we provide a search value that matches an email - results = OptionsListUtils.getFilteredOptions(REPORTS, PERSONAL_DETAILS, [], 'peterparker@expensify.com'); + results = OptionsListUtils.getFilteredOptions(OPTIONS.reports, OPTIONS.personalDetails, [], 'peterparker@expensify.com'); // Then one recentReports will be returned and it will be the correct option // personalDetails should be empty array @@ -397,7 +410,7 @@ describe('OptionsListUtils', () => { expect(results.personalDetails.length).toBe(0); // When we provide a search value that matches a partial display name or email - results = OptionsListUtils.getFilteredOptions(REPORTS, PERSONAL_DETAILS, [], '.com'); + results = OptionsListUtils.getFilteredOptions(OPTIONS.reports, OPTIONS.personalDetails, [], '.com'); // Then several options will be returned and they will be each have the search string in their email or name // even though the currently logged in user matches they should not show. @@ -410,45 +423,46 @@ describe('OptionsListUtils', () => { expect(results.recentReports[2].text).toBe('Black Panther'); // Test for Concierge's existence in chat options - results = OptionsListUtils.getFilteredOptions(REPORTS_WITH_CONCIERGE, PERSONAL_DETAILS_WITH_CONCIERGE); + + results = OptionsListUtils.getFilteredOptions(OPTIONS_WITH_CONCIERGE.reports, OPTIONS_WITH_CONCIERGE.personalDetails); // Concierge is included in the results by default. We should expect all the personalDetails to show // (minus the 5 that are already showing and the currently logged in user) - expect(results.personalDetails.length).toBe(_.size(PERSONAL_DETAILS_WITH_CONCIERGE) - 1 - MAX_RECENT_REPORTS); + expect(results.personalDetails.length).toBe(_.size(OPTIONS_WITH_CONCIERGE.personalDetails) - 1 - MAX_RECENT_REPORTS); expect(results.recentReports).toEqual(expect.arrayContaining([expect.objectContaining({login: 'concierge@expensify.com'})])); // Test by excluding Concierge from the results - results = OptionsListUtils.getFilteredOptions(REPORTS_WITH_CONCIERGE, PERSONAL_DETAILS_WITH_CONCIERGE, [], '', [], [CONST.EMAIL.CONCIERGE]); + results = OptionsListUtils.getFilteredOptions(OPTIONS_WITH_CONCIERGE.reports, OPTIONS_WITH_CONCIERGE.personalDetails, [], '', [], [CONST.EMAIL.CONCIERGE]); // All the personalDetails should be returned minus the currently logged in user and Concierge - expect(results.personalDetails.length).toBe(_.size(PERSONAL_DETAILS_WITH_CONCIERGE) - 2 - MAX_RECENT_REPORTS); + expect(results.personalDetails.length).toBe(_.size(OPTIONS_WITH_CONCIERGE.personalDetails) - 2 - MAX_RECENT_REPORTS); expect(results.personalDetails).not.toEqual(expect.arrayContaining([expect.objectContaining({login: 'concierge@expensify.com'})])); // Test by excluding Chronos from the results - results = OptionsListUtils.getFilteredOptions(REPORTS_WITH_CHRONOS, PERSONAL_DETAILS_WITH_CHRONOS, [], '', [], [CONST.EMAIL.CHRONOS]); + results = OptionsListUtils.getFilteredOptions(OPTIONS_WITH_CHRONOS.reports, OPTIONS_WITH_CHRONOS.personalDetails, [], '', [], [CONST.EMAIL.CHRONOS]); // All the personalDetails should be returned minus the currently logged in user and Concierge - expect(results.personalDetails.length).toBe(_.size(PERSONAL_DETAILS_WITH_CHRONOS) - 2 - MAX_RECENT_REPORTS); + expect(results.personalDetails.length).toBe(_.size(OPTIONS_WITH_CHRONOS.personalDetails) - 2 - MAX_RECENT_REPORTS); expect(results.personalDetails).not.toEqual(expect.arrayContaining([expect.objectContaining({login: 'chronos@expensify.com'})])); // Test by excluding Receipts from the results - results = OptionsListUtils.getFilteredOptions(REPORTS_WITH_RECEIPTS, PERSONAL_DETAILS_WITH_RECEIPTS, [], '', [], [CONST.EMAIL.RECEIPTS]); + results = OptionsListUtils.getFilteredOptions(OPTIONS_WITH_RECEIPTS.reports, OPTIONS_WITH_RECEIPTS.personalDetails, [], '', [], [CONST.EMAIL.RECEIPTS]); // All the personalDetails should be returned minus the currently logged in user and Concierge - expect(results.personalDetails.length).toBe(_.size(PERSONAL_DETAILS_WITH_RECEIPTS) - 2 - MAX_RECENT_REPORTS); + expect(results.personalDetails.length).toBe(_.size(OPTIONS_WITH_RECEIPTS.personalDetails) - 2 - MAX_RECENT_REPORTS); expect(results.personalDetails).not.toEqual(expect.arrayContaining([expect.objectContaining({login: 'receipts@expensify.com'})])); }); it('getFilteredOptions() for group Chat', () => { // When we call getFilteredOptions() with no search value - let results = OptionsListUtils.getFilteredOptions(REPORTS, PERSONAL_DETAILS, [], ''); + let results = OptionsListUtils.getFilteredOptions(OPTIONS.reports, OPTIONS.personalDetails, [], ''); // Then we should expect only a maxmimum of 5 recent reports to be returned expect(results.recentReports.length).toBe(5); // And we should expect all the personalDetails to show (minus the 5 that are already // showing and the currently logged in user) - expect(results.personalDetails.length).toBe(_.size(PERSONAL_DETAILS) - 6); + expect(results.personalDetails.length).toBe(_.size(OPTIONS.personalDetails) - 6); // We should expect personal details sorted alphabetically expect(results.personalDetails[0].text).toBe('Black Widow'); @@ -462,7 +476,7 @@ describe('OptionsListUtils', () => { expect(personalDetailsOverlapWithReports).toBe(false); // When we search for an option that is only in a personalDetail with no existing report - results = OptionsListUtils.getFilteredOptions(REPORTS, PERSONAL_DETAILS, [], 'hulk'); + results = OptionsListUtils.getFilteredOptions(OPTIONS.reports, OPTIONS.personalDetails, [], 'hulk'); // Then reports should return no results expect(results.recentReports.length).toBe(0); @@ -472,7 +486,7 @@ describe('OptionsListUtils', () => { expect(results.personalDetails[0].login).toBe('brucebanner@expensify.com'); // When we search for an option that matches things in both personalDetails and reports - results = OptionsListUtils.getFilteredOptions(REPORTS, PERSONAL_DETAILS, [], '.com'); + results = OptionsListUtils.getFilteredOptions(OPTIONS.reports, OPTIONS.personalDetails, [], '.com'); // Then all single participant reports that match will show up in the recentReports array, Recently used contact should be at the top expect(results.recentReports.length).toBe(5); @@ -483,7 +497,7 @@ describe('OptionsListUtils', () => { expect(results.personalDetails[0].login).toBe('natasharomanoff@expensify.com'); // When we provide no selected options to getFilteredOptions() - results = OptionsListUtils.getFilteredOptions(REPORTS, PERSONAL_DETAILS, [], '', []); + results = OptionsListUtils.getFilteredOptions(OPTIONS.reports, OPTIONS.personalDetails, [], '', []); // Then one of our older report options (not in our five most recent) should appear in the personalDetails // but not in recentReports @@ -491,7 +505,7 @@ describe('OptionsListUtils', () => { expect(_.every(results.personalDetails, (option) => option.login !== 'peterparker@expensify.com')).toBe(false); // When we provide a "selected" option to getFilteredOptions() - results = OptionsListUtils.getFilteredOptions(REPORTS, PERSONAL_DETAILS, [], '', [{login: 'peterparker@expensify.com'}]); + results = OptionsListUtils.getFilteredOptions(OPTIONS.reports, OPTIONS.personalDetails, [], '', [{login: 'peterparker@expensify.com'}]); // Then the option should not appear anywhere in either list expect(_.every(results.recentReports, (option) => option.login !== 'peterparker@expensify.com')).toBe(true); @@ -499,7 +513,7 @@ describe('OptionsListUtils', () => { // When we add a search term for which no options exist and the searchValue itself // is not a potential email or phone - results = OptionsListUtils.getFilteredOptions(REPORTS, PERSONAL_DETAILS, [], 'marc@expensify'); + results = OptionsListUtils.getFilteredOptions(OPTIONS.reports, OPTIONS.personalDetails, [], 'marc@expensify'); // Then we should have no options or personal details at all and also that there is no userToInvite expect(results.recentReports.length).toBe(0); @@ -508,7 +522,7 @@ describe('OptionsListUtils', () => { // When we add a search term for which no options exist and the searchValue itself // is a potential email - results = OptionsListUtils.getFilteredOptions(REPORTS, PERSONAL_DETAILS, [], 'marc@expensify.com'); + results = OptionsListUtils.getFilteredOptions(OPTIONS.reports, OPTIONS.personalDetails, [], 'marc@expensify.com'); // Then we should have no options or personal details at all but there should be a userToInvite expect(results.recentReports.length).toBe(0); @@ -516,7 +530,7 @@ describe('OptionsListUtils', () => { expect(results.userToInvite).not.toBe(null); // When we add a search term with a period, with options for it that don't contain the period - results = OptionsListUtils.getFilteredOptions(REPORTS, PERSONAL_DETAILS, [], 'peter.parker@expensify.com'); + results = OptionsListUtils.getFilteredOptions(OPTIONS.reports, OPTIONS.personalDetails, [], 'peter.parker@expensify.com'); // Then we should have no options at all but there should be a userToInvite expect(results.recentReports.length).toBe(0); @@ -524,7 +538,7 @@ describe('OptionsListUtils', () => { // When we add a search term for which no options exist and the searchValue itself // is a potential phone number without country code added - results = OptionsListUtils.getFilteredOptions(REPORTS, PERSONAL_DETAILS, [], '5005550006'); + results = OptionsListUtils.getFilteredOptions(OPTIONS.reports, OPTIONS.personalDetails, [], '5005550006'); // Then we should have no options or personal details at all but there should be a userToInvite and the login // should have the country code included @@ -535,7 +549,7 @@ describe('OptionsListUtils', () => { // When we add a search term for which no options exist and the searchValue itself // is a potential phone number with country code added - results = OptionsListUtils.getFilteredOptions(REPORTS, PERSONAL_DETAILS, [], '+15005550006'); + results = OptionsListUtils.getFilteredOptions(OPTIONS.reports, OPTIONS.personalDetails, [], '+15005550006'); // Then we should have no options or personal details at all but there should be a userToInvite and the login // should have the country code included @@ -546,7 +560,7 @@ describe('OptionsListUtils', () => { // When we add a search term for which no options exist and the searchValue itself // is a potential phone number with special characters added - results = OptionsListUtils.getFilteredOptions(REPORTS, PERSONAL_DETAILS, [], '+1 (800)324-3233'); + results = OptionsListUtils.getFilteredOptions(OPTIONS.reports, OPTIONS.personalDetails, [], '+1 (800)324-3233'); // Then we should have no options or personal details at all but there should be a userToInvite and the login // should have the country code included @@ -556,7 +570,7 @@ describe('OptionsListUtils', () => { expect(results.userToInvite.login).toBe('+18003243233'); // When we use a search term for contact number that contains alphabet characters - results = OptionsListUtils.getFilteredOptions(REPORTS, PERSONAL_DETAILS, [], '998243aaaa'); + results = OptionsListUtils.getFilteredOptions(OPTIONS.reports, OPTIONS.personalDetails, [], '998243aaaa'); // Then we shouldn't have any results or user to invite expect(results.recentReports.length).toBe(0); @@ -564,93 +578,100 @@ describe('OptionsListUtils', () => { expect(results.userToInvite).toBe(null); // Test Concierge's existence in new group options - results = OptionsListUtils.getFilteredOptions(REPORTS_WITH_CONCIERGE, PERSONAL_DETAILS_WITH_CONCIERGE); + results = OptionsListUtils.getFilteredOptions(OPTIONS_WITH_CONCIERGE.reports, OPTIONS_WITH_CONCIERGE.personalDetails); // Concierge is included in the results by default. We should expect all the personalDetails to show // (minus the 5 that are already showing and the currently logged in user) - expect(results.personalDetails.length).toBe(_.size(PERSONAL_DETAILS_WITH_CONCIERGE) - 6); + expect(results.personalDetails.length).toBe(_.size(OPTIONS_WITH_CONCIERGE.personalDetails) - 6); expect(results.recentReports).toEqual(expect.arrayContaining([expect.objectContaining({login: 'concierge@expensify.com'})])); // Test by excluding Concierge from the results - results = OptionsListUtils.getFilteredOptions(REPORTS_WITH_CONCIERGE, PERSONAL_DETAILS_WITH_CONCIERGE, [], '', [], [CONST.EMAIL.CONCIERGE]); + results = OptionsListUtils.getFilteredOptions(OPTIONS_WITH_CONCIERGE.reports, OPTIONS_WITH_CONCIERGE.personalDetails, [], '', [], [CONST.EMAIL.CONCIERGE]); // We should expect all the personalDetails to show (minus the 5 that are already showing, // the currently logged in user and Concierge) - expect(results.personalDetails.length).toBe(_.size(PERSONAL_DETAILS_WITH_CONCIERGE) - 7); + expect(results.personalDetails.length).toBe(_.size(OPTIONS_WITH_CONCIERGE.personalDetails) - 7); expect(results.personalDetails).not.toEqual(expect.arrayContaining([expect.objectContaining({login: 'concierge@expensify.com'})])); expect(results.recentReports).not.toEqual(expect.arrayContaining([expect.objectContaining({login: 'concierge@expensify.com'})])); // Test by excluding Chronos from the results - results = OptionsListUtils.getFilteredOptions(REPORTS_WITH_CHRONOS, PERSONAL_DETAILS_WITH_CHRONOS, [], '', [], [CONST.EMAIL.CHRONOS]); + results = OptionsListUtils.getFilteredOptions(OPTIONS_WITH_CHRONOS.reports, OPTIONS_WITH_CHRONOS.personalDetails, [], '', [], [CONST.EMAIL.CHRONOS]); // We should expect all the personalDetails to show (minus the 5 that are already showing, // the currently logged in user and Concierge) - expect(results.personalDetails.length).toBe(_.size(PERSONAL_DETAILS_WITH_CHRONOS) - 7); + expect(results.personalDetails.length).toBe(_.size(OPTIONS_WITH_CHRONOS.personalDetails) - 7); expect(results.personalDetails).not.toEqual(expect.arrayContaining([expect.objectContaining({login: 'chronos@expensify.com'})])); expect(results.recentReports).not.toEqual(expect.arrayContaining([expect.objectContaining({login: 'chronos@expensify.com'})])); // Test by excluding Receipts from the results - results = OptionsListUtils.getFilteredOptions(REPORTS_WITH_RECEIPTS, PERSONAL_DETAILS_WITH_RECEIPTS, [], '', [], [CONST.EMAIL.RECEIPTS]); + results = OptionsListUtils.getFilteredOptions(OPTIONS_WITH_RECEIPTS.reports, OPTIONS_WITH_RECEIPTS.personalDetails, [], '', [], [CONST.EMAIL.RECEIPTS]); // We should expect all the personalDetails to show (minus the 5 that are already showing, // the currently logged in user and Concierge) - expect(results.personalDetails.length).toBe(_.size(PERSONAL_DETAILS_WITH_RECEIPTS) - 7); + expect(results.personalDetails.length).toBe(_.size(OPTIONS_WITH_RECEIPTS.personalDetails) - 7); expect(results.personalDetails).not.toEqual(expect.arrayContaining([expect.objectContaining({login: 'receipts@expensify.com'})])); expect(results.recentReports).not.toEqual(expect.arrayContaining([expect.objectContaining({login: 'receipts@expensify.com'})])); }); it('getShareDestinationsOptions()', () => { // Filter current REPORTS as we do in the component, before getting share destination options - const filteredReports = {}; - _.keys(REPORTS).forEach((reportKey) => { - if (!ReportUtils.canUserPerformWriteAction(REPORTS[reportKey]) || ReportUtils.isExpensifyOnlyParticipantInReport(REPORTS[reportKey])) { - return; - } - filteredReports[reportKey] = REPORTS[reportKey]; - }); + const filteredReports = _.reduce( + OPTIONS.reports, + (filtered, option) => { + const report = option.item; + if (ReportUtils.canUserPerformWriteAction(report) && ReportUtils.canCreateTaskInReport(report) && !ReportUtils.isCanceledTaskReport(report)) { + filtered.push(option); + } + return filtered; + }, + [], + ); // When we pass an empty search value - let results = OptionsListUtils.getShareDestinationOptions(filteredReports, PERSONAL_DETAILS, [], ''); + let results = OptionsListUtils.getShareDestinationOptions(filteredReports, OPTIONS.personalDetails, [], ''); // Then we should expect all the recent reports to show but exclude the archived rooms - expect(results.recentReports.length).toBe(_.size(REPORTS) - 1); + expect(results.recentReports.length).toBe(_.size(OPTIONS.reports) - 1); // When we pass a search value that doesn't match the group chat name - results = OptionsListUtils.getShareDestinationOptions(filteredReports, PERSONAL_DETAILS, [], 'mutants'); + results = OptionsListUtils.getShareDestinationOptions(filteredReports, OPTIONS.personalDetails, [], 'mutants'); // Then we should expect no recent reports to show expect(results.recentReports.length).toBe(0); // When we pass a search value that matches the group chat name - results = OptionsListUtils.getShareDestinationOptions(filteredReports, PERSONAL_DETAILS, [], 'Iron Man, Fantastic'); + results = OptionsListUtils.getShareDestinationOptions(filteredReports, OPTIONS.personalDetails, [], 'Iron Man, Fantastic'); // Then we should expect the group chat to show along with the contacts matching the search expect(results.recentReports.length).toBe(1); // Filter current REPORTS_WITH_WORKSPACE_ROOMS as we do in the component, before getting share destination options - const filteredReportsWithWorkspaceRooms = {}; - _.keys(REPORTS_WITH_WORKSPACE_ROOMS).forEach((reportKey) => { - if (!ReportUtils.canUserPerformWriteAction(REPORTS_WITH_WORKSPACE_ROOMS[reportKey]) || ReportUtils.isExpensifyOnlyParticipantInReport(REPORTS_WITH_WORKSPACE_ROOMS[reportKey])) { - return; - } - filteredReportsWithWorkspaceRooms[reportKey] = REPORTS_WITH_WORKSPACE_ROOMS[reportKey]; - }); + const filteredReportsWithWorkspaceRooms = _.reduce( + OPTIONS_WITH_WORKSPACES.reports, + (filtered, option) => { + const report = option.item; + if (ReportUtils.canUserPerformWriteAction(report) || ReportUtils.isExpensifyOnlyParticipantInReport(report)) { + filtered.push(option); + } + return filtered; + }, + [], + ); // When we also have a policy to return rooms in the results - results = OptionsListUtils.getShareDestinationOptions(filteredReportsWithWorkspaceRooms, PERSONAL_DETAILS, [], ''); - + results = OptionsListUtils.getShareDestinationOptions(filteredReportsWithWorkspaceRooms, OPTIONS.personalDetails, [], ''); // Then we should expect the DMS, the group chats and the workspace room to show // We should expect all the recent reports to show, excluding the archived rooms - expect(results.recentReports.length).toBe(_.size(REPORTS_WITH_WORKSPACE_ROOMS) - 1); + expect(results.recentReports.length).toBe(_.size(OPTIONS_WITH_WORKSPACES.reports) - 1); // When we search for a workspace room - results = OptionsListUtils.getShareDestinationOptions(filteredReportsWithWorkspaceRooms, PERSONAL_DETAILS, [], 'Avengers Room'); + results = OptionsListUtils.getShareDestinationOptions(filteredReportsWithWorkspaceRooms, OPTIONS.personalDetails, [], 'Avengers Room'); // Then we should expect only the workspace room to show expect(results.recentReports.length).toBe(1); // When we search for a workspace room that doesn't exist - results = OptionsListUtils.getShareDestinationOptions(filteredReportsWithWorkspaceRooms, PERSONAL_DETAILS, [], 'Mutants Lair'); + results = OptionsListUtils.getShareDestinationOptions(filteredReportsWithWorkspaceRooms, OPTIONS.personalDetails, [], 'Mutants Lair'); // Then we should expect no results to show expect(results.recentReports.length).toBe(0); @@ -658,7 +679,7 @@ describe('OptionsListUtils', () => { it('getMemberInviteOptions()', () => { // When we only pass personal details - let results = OptionsListUtils.getMemberInviteOptions(PERSONAL_DETAILS, [], ''); + let results = OptionsListUtils.getMemberInviteOptions(OPTIONS.personalDetails, [], ''); // We should expect personal details to be sorted alphabetically expect(results.personalDetails[0].text).toBe('Black Panther'); @@ -667,13 +688,13 @@ describe('OptionsListUtils', () => { expect(results.personalDetails[3].text).toBe('Invisible Woman'); // When we provide a search value that does not match any personal details - results = OptionsListUtils.getMemberInviteOptions(PERSONAL_DETAILS, [], 'magneto'); + results = OptionsListUtils.getMemberInviteOptions(OPTIONS.personalDetails, [], 'magneto'); // Then no options will be returned expect(results.personalDetails.length).toBe(0); // When we provide a search value that matches an email - results = OptionsListUtils.getMemberInviteOptions(PERSONAL_DETAILS, [], 'peterparker@expensify.com'); + results = OptionsListUtils.getMemberInviteOptions(OPTIONS.personalDetails, [], 'peterparker@expensify.com'); // Then one personal should be in personalDetails list expect(results.personalDetails.length).toBe(1); @@ -1020,18 +1041,18 @@ describe('OptionsListUtils', () => { }, ]; - const smallResult = OptionsListUtils.getFilteredOptions(REPORTS, PERSONAL_DETAILS, [], emptySearch, [], [], false, false, true, smallCategoriesList); + const smallResult = OptionsListUtils.getFilteredOptions(OPTIONS.reports, OPTIONS.personalDetails, [], emptySearch, [], [], false, false, true, smallCategoriesList); expect(smallResult.categoryOptions).toStrictEqual(smallResultList); - const smallSearchResult = OptionsListUtils.getFilteredOptions(REPORTS, PERSONAL_DETAILS, [], search, [], [], false, false, true, smallCategoriesList); + const smallSearchResult = OptionsListUtils.getFilteredOptions(OPTIONS.reports, OPTIONS.personalDetails, [], search, [], [], false, false, true, smallCategoriesList); expect(smallSearchResult.categoryOptions).toStrictEqual(smallSearchResultList); - const smallWrongSearchResult = OptionsListUtils.getFilteredOptions(REPORTS, PERSONAL_DETAILS, [], wrongSearch, [], [], false, false, true, smallCategoriesList); + const smallWrongSearchResult = OptionsListUtils.getFilteredOptions(OPTIONS.reports, OPTIONS.personalDetails, [], wrongSearch, [], [], false, false, true, smallCategoriesList); expect(smallWrongSearchResult.categoryOptions).toStrictEqual(smallWrongSearchResultList); const largeResult = OptionsListUtils.getFilteredOptions( - REPORTS, - PERSONAL_DETAILS, + OPTIONS.reports, + OPTIONS.personalDetails, [], emptySearch, selectedOptions, @@ -1045,8 +1066,8 @@ describe('OptionsListUtils', () => { expect(largeResult.categoryOptions).toStrictEqual(largeResultList); const largeSearchResult = OptionsListUtils.getFilteredOptions( - REPORTS, - PERSONAL_DETAILS, + OPTIONS.reports, + OPTIONS.personalDetails, [], search, selectedOptions, @@ -1060,8 +1081,8 @@ describe('OptionsListUtils', () => { expect(largeSearchResult.categoryOptions).toStrictEqual(largeSearchResultList); const largeWrongSearchResult = OptionsListUtils.getFilteredOptions( - REPORTS, - PERSONAL_DETAILS, + OPTIONS.reports, + OPTIONS.personalDetails, [], wrongSearch, selectedOptions, @@ -1074,7 +1095,7 @@ describe('OptionsListUtils', () => { ); expect(largeWrongSearchResult.categoryOptions).toStrictEqual(largeWrongSearchResultList); - const emptyResult = OptionsListUtils.getFilteredOptions(REPORTS, PERSONAL_DETAILS, [], search, selectedOptions, [], false, false, true, emptyCategoriesList); + const emptyResult = OptionsListUtils.getFilteredOptions(OPTIONS.reports, OPTIONS.personalDetails, [], search, selectedOptions, [], false, false, true, emptyCategoriesList); expect(emptyResult.categoryOptions).toStrictEqual(emptySelectedResultList); }); @@ -1327,18 +1348,32 @@ describe('OptionsListUtils', () => { }, ]; - const smallResult = OptionsListUtils.getFilteredOptions(REPORTS, PERSONAL_DETAILS, [], emptySearch, [], [], false, false, false, {}, [], true, smallTagsList); + const smallResult = OptionsListUtils.getFilteredOptions(OPTIONS.reports, OPTIONS.personalDetails, [], emptySearch, [], [], false, false, false, {}, [], true, smallTagsList); expect(smallResult.tagOptions).toStrictEqual(smallResultList); - const smallSearchResult = OptionsListUtils.getFilteredOptions(REPORTS, PERSONAL_DETAILS, [], search, [], [], false, false, false, {}, [], true, smallTagsList); + const smallSearchResult = OptionsListUtils.getFilteredOptions(OPTIONS.reports, OPTIONS.personalDetails, [], search, [], [], false, false, false, {}, [], true, smallTagsList); expect(smallSearchResult.tagOptions).toStrictEqual(smallSearchResultList); - const smallWrongSearchResult = OptionsListUtils.getFilteredOptions(REPORTS, PERSONAL_DETAILS, [], wrongSearch, [], [], false, false, false, {}, [], true, smallTagsList); + const smallWrongSearchResult = OptionsListUtils.getFilteredOptions( + OPTIONS.reports, + OPTIONS.personalDetails, + [], + wrongSearch, + [], + [], + false, + false, + false, + {}, + [], + true, + smallTagsList, + ); expect(smallWrongSearchResult.tagOptions).toStrictEqual(smallWrongSearchResultList); const largeResult = OptionsListUtils.getFilteredOptions( - REPORTS, - PERSONAL_DETAILS, + OPTIONS.reports, + OPTIONS.personalDetails, [], emptySearch, selectedOptions, @@ -1355,8 +1390,8 @@ describe('OptionsListUtils', () => { expect(largeResult.tagOptions).toStrictEqual(largeResultList); const largeSearchResult = OptionsListUtils.getFilteredOptions( - REPORTS, - PERSONAL_DETAILS, + OPTIONS.reports, + OPTIONS.personalDetails, [], search, selectedOptions, @@ -1373,8 +1408,8 @@ describe('OptionsListUtils', () => { expect(largeSearchResult.tagOptions).toStrictEqual(largeSearchResultList); const largeWrongSearchResult = OptionsListUtils.getFilteredOptions( - REPORTS, - PERSONAL_DETAILS, + OPTIONS.reports, + OPTIONS.personalDetails, [], wrongSearch, selectedOptions, From d494a03f64fd1807a6f3f5bdda1b9c369a316ca2 Mon Sep 17 00:00:00 2001 From: Shridhar Goel <35566748+ShridharGoel@users.noreply.github.com> Date: Sat, 16 Mar 2024 00:59:56 +0530 Subject: [PATCH 241/500] Fix video replay issue in iOS --- src/components/VideoPlayer/BaseVideoPlayer.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/VideoPlayer/BaseVideoPlayer.js b/src/components/VideoPlayer/BaseVideoPlayer.js index 92d829e9d0db..14f72e389029 100644 --- a/src/components/VideoPlayer/BaseVideoPlayer.js +++ b/src/components/VideoPlayer/BaseVideoPlayer.js @@ -102,7 +102,7 @@ function BaseVideoPlayer({ const currentDuration = e.durationMillis || videoDuration * 1000; const currentPositon = e.positionMillis || 0; - if (shouldReplayVideo(e, isVideoPlaying, currentDuration, currentPositon)) { + if (shouldReplayVideo(e, isPlaying, currentDuration, currentPositon)) { videoPlayerRef.current.setStatusAsync({positionMillis: 0, shouldPlay: true}); } From d71b283ad16dbfccd8d1faa8ff2ac4441917c05e Mon Sep 17 00:00:00 2001 From: Shubham Agrawal Date: Sun, 17 Mar 2024 18:57:51 +0530 Subject: [PATCH 242/500] Refactored selection list items --- .../CalendarPicker/YearPickerModal.tsx | 1 - src/components/SelectionList/BaseListItem.tsx | 51 ------------------ .../SelectionList/RadioListItem.tsx | 40 ++++++++++++-- .../SelectionList/TableListItem.tsx | 38 +++++++++++-- src/components/SelectionList/UserListItem.tsx | 54 +++++++++++++++++-- src/components/SelectionList/index.ios.tsx | 20 ------- .../{index.android.tsx => index.native.tsx} | 1 - src/components/SelectionList/types.ts | 14 ++--- src/pages/SearchPage/index.tsx | 1 - 9 files changed, 121 insertions(+), 99 deletions(-) delete mode 100644 src/components/SelectionList/index.ios.tsx rename src/components/SelectionList/{index.android.tsx => index.native.tsx} (96%) diff --git a/src/components/DatePicker/CalendarPicker/YearPickerModal.tsx b/src/components/DatePicker/CalendarPicker/YearPickerModal.tsx index 8f5487f9c68b..6170b81073a2 100644 --- a/src/components/DatePicker/CalendarPicker/YearPickerModal.tsx +++ b/src/components/DatePicker/CalendarPicker/YearPickerModal.tsx @@ -65,7 +65,6 @@ function YearPickerModal({isVisible, years, currentYear = new Date().getFullYear onBackButtonPress={onClose} /> ({ item, pressableStyle, wrapperStyle, - selectMultipleStyle, isDisabled = false, shouldPreventDefaultFocusOnSelectRow = false, canSelectMultiple = false, onSelectRow, - onCheckboxPress, onDismissError = () => {}, rightHandSideComponent, - checkmarkPosition = CONST.DIRECTION.LEFT, keyForList, errors, pendingAction, @@ -33,7 +28,6 @@ function BaseListItem({ }: BaseListItemProps) { const theme = useTheme(); const styles = useThemeStyles(); - const StyleUtils = useStyleUtils(); const {hovered, bind} = useHover(); const rightHandSideComponentRender = () => { @@ -48,14 +42,6 @@ function BaseListItem({ return rightHandSideComponent; }; - const handleCheckboxPress = () => { - if (onCheckboxPress) { - onCheckboxPress(item); - } else { - onSelectRow(item); - } - }; - return ( onDismissError(item)} @@ -78,45 +64,8 @@ function BaseListItem({ style={pressableStyle} > - {canSelectMultiple && checkmarkPosition === CONST.DIRECTION.LEFT && ( - - - {item.isSelected && ( - - )} - - - )} - {typeof children === 'function' ? children(hovered) : children} - {canSelectMultiple && checkmarkPosition === CONST.DIRECTION.RIGHT && ( - - - - )} - {!canSelectMultiple && item.isSelected && !rightHandSideComponent && ( { + if (onCheckboxPress) { + onCheckboxPress(item); + } else { + onSelectRow(item); + } + }, [item, onCheckboxPress, onSelectRow]); + return ( <> + {canSelectMultiple && ( + + + {item.isSelected && ( + + )} + + + )} { + if (onCheckboxPress) { + onCheckboxPress(item); + } else { + onSelectRow(item); + } + }, [item, onCheckboxPress, onSelectRow]); + return ( {(hovered) => ( <> + {canSelectMultiple && ( + + + {item.isSelected && ( + + )} + + + )} {!!item.icons && ( { + if (onCheckboxPress) { + onCheckboxPress(item); + } else { + onSelectRow(item); + } + }, [item, onCheckboxPress, onSelectRow]); + return ( {(hovered?: boolean) => ( <> + {canSelectMultiple && checkmarkPosition === CONST.DIRECTION.LEFT && ( + + + {item.isSelected && ( + + )} + + + )} {!!item.icons && (item.shouldShowSubscript ? ( {!!item.rightElement && item.rightElement} + {canSelectMultiple && checkmarkPosition === CONST.DIRECTION.RIGHT && ( + + + + )} )} diff --git a/src/components/SelectionList/index.ios.tsx b/src/components/SelectionList/index.ios.tsx deleted file mode 100644 index afa1ce499ecc..000000000000 --- a/src/components/SelectionList/index.ios.tsx +++ /dev/null @@ -1,20 +0,0 @@ -import React, {forwardRef} from 'react'; -import type {ForwardedRef} from 'react'; -import {Keyboard} from 'react-native'; -import BaseSelectionList from './BaseSelectionList'; -import type {BaseSelectionListProps, ListItem, SelectionListHandle} from './types'; - -function SelectionList(props: BaseSelectionListProps, ref: ForwardedRef) { - return ( - - // eslint-disable-next-line react/jsx-props-no-spreading - {...props} - ref={ref} - onScrollBeginDrag={() => Keyboard.dismiss()} - /> - ); -} - -SelectionList.displayName = 'SelectionList'; - -export default forwardRef(SelectionList); diff --git a/src/components/SelectionList/index.android.tsx b/src/components/SelectionList/index.native.tsx similarity index 96% rename from src/components/SelectionList/index.android.tsx rename to src/components/SelectionList/index.native.tsx index f8e54b219f5b..baccdf7c6024 100644 --- a/src/components/SelectionList/index.android.tsx +++ b/src/components/SelectionList/index.native.tsx @@ -10,7 +10,6 @@ function SelectionList(props: BaseSelectionListProps Keyboard.dismiss()} /> ); diff --git a/src/components/SelectionList/types.ts b/src/components/SelectionList/types.ts index 9e9ba7e5fc27..9251eb1399f6 100644 --- a/src/components/SelectionList/types.ts +++ b/src/components/SelectionList/types.ts @@ -151,6 +151,8 @@ type RadioListItemProps = ListItemProps; type TableListItemProps = ListItemProps; +type ValidListItem = typeof RadioListItem | typeof UserListItem | typeof TableListItem; + type Section = { /** Title of the section */ title?: string; @@ -173,7 +175,7 @@ type BaseSelectionListProps = Partial & { sections: Array>> | typeof CONST.EMPTY_ARRAY; /** Default renderer for every item in the list */ - ListItem: typeof RadioListItem | typeof UserListItem | typeof TableListItem; + ListItem: ValidListItem; /** Whether this is a multi-select list */ canSelectMultiple?: boolean; @@ -259,18 +261,12 @@ type BaseSelectionListProps = Partial & { /** Whether keyboard shortcuts should be disabled */ disableKeyboardShortcuts?: boolean; - /** Whether to disable initial styling for focused option */ - disableInitialFocusOptionStyle?: boolean; - /** Styles to apply to SelectionList container */ containerStyle?: ViewStyle; /** Whether keyboard is visible on the screen */ isKeyboardShown?: boolean; - /** Whether focus event should be delayed */ - shouldDelayFocus?: boolean; - /** Component to display on the right side of each child */ rightHandSideComponent?: ((item: ListItem) => ReactElement) | ReactElement | null; @@ -289,9 +285,6 @@ type BaseSelectionListProps = Partial & { /** Styles for the list header wrapper */ listHeaderWrapperStyle?: StyleProp; - /** Whether to auto focus the Search Input */ - autoFocus?: boolean; - /** Whether to wrap long text up to 2 lines */ isRowMultilineSupported?: boolean; @@ -335,4 +328,5 @@ export type { ButtonOrCheckBoxRoles, SectionListDataType, SelectionListHandle, + ValidListItem, }; diff --git a/src/pages/SearchPage/index.tsx b/src/pages/SearchPage/index.tsx index 07096ce6c2d5..7a42db3ff2cd 100644 --- a/src/pages/SearchPage/index.tsx +++ b/src/pages/SearchPage/index.tsx @@ -169,7 +169,6 @@ function SearchPage({betas, reports, isSearchingForReports, navigation}: SearchP onChangeText={setSearchValue} headerMessage={headerMessage} onLayout={setPerformanceTimersEnd} - autoFocus onSelectRow={selectReport} showLoadingPlaceholder={!didScreenTransitionEnd || !isOptionsDataReady} footerContent={SearchPageFooterInstance} From 95c5553f67c0d2afd46c0596559cc5dea4b26382 Mon Sep 17 00:00:00 2001 From: Shubham Agrawal Date: Sun, 17 Mar 2024 19:18:51 +0530 Subject: [PATCH 243/500] Fixed icons import --- src/components/SelectionList/RadioListItem.tsx | 1 + src/components/SelectionList/TableListItem.tsx | 1 + src/components/SelectionList/UserListItem.tsx | 1 + 3 files changed, 3 insertions(+) diff --git a/src/components/SelectionList/RadioListItem.tsx b/src/components/SelectionList/RadioListItem.tsx index 489076e7b1e6..5931f76f16e4 100644 --- a/src/components/SelectionList/RadioListItem.tsx +++ b/src/components/SelectionList/RadioListItem.tsx @@ -1,6 +1,7 @@ import React, {useCallback} from 'react'; import {View} from 'react-native'; import Icon from '@components/Icon'; +import * as Expensicons from '@components/Icon/Expensicons'; import PressableWithFeedback from '@components/Pressable/PressableWithFeedback'; import TextWithTooltip from '@components/TextWithTooltip'; import useStyleUtils from '@hooks/useStyleUtils'; diff --git a/src/components/SelectionList/TableListItem.tsx b/src/components/SelectionList/TableListItem.tsx index a6490dbcd136..d8ded2ada697 100644 --- a/src/components/SelectionList/TableListItem.tsx +++ b/src/components/SelectionList/TableListItem.tsx @@ -1,6 +1,7 @@ import React, {useCallback} from 'react'; import {View} from 'react-native'; import Icon from '@components/Icon'; +import * as Expensicons from '@components/Icon/Expensicons'; import MultipleAvatars from '@components/MultipleAvatars'; import PressableWithFeedback from '@components/Pressable/PressableWithFeedback'; import TextWithTooltip from '@components/TextWithTooltip'; diff --git a/src/components/SelectionList/UserListItem.tsx b/src/components/SelectionList/UserListItem.tsx index 8de089cb9313..081d49d27d68 100644 --- a/src/components/SelectionList/UserListItem.tsx +++ b/src/components/SelectionList/UserListItem.tsx @@ -2,6 +2,7 @@ import Str from 'expensify-common/lib/str'; import React, {useCallback} from 'react'; import {View} from 'react-native'; import Icon from '@components/Icon'; +import * as Expensicons from '@components/Icon/Expensicons'; import MultipleAvatars from '@components/MultipleAvatars'; import PressableWithFeedback from '@components/Pressable/PressableWithFeedback'; import SelectCircle from '@components/SelectCircle'; From 69cd8cebb4af2d84717e6351bbe85b9934c3001b Mon Sep 17 00:00:00 2001 From: Tomasz Misiukiewicz Date: Mon, 18 Mar 2024 09:22:57 +0100 Subject: [PATCH 244/500] update reassure tests --- tests/perf-test/SearchPage.perf-test.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/perf-test/SearchPage.perf-test.js b/tests/perf-test/SearchPage.perf-test.js index be6b6a5d78f9..b25eb9c0ec05 100644 --- a/tests/perf-test/SearchPage.perf-test.js +++ b/tests/perf-test/SearchPage.perf-test.js @@ -5,6 +5,7 @@ import {measurePerformance} from 'reassure'; import _ from 'underscore'; import {LocaleContextProvider} from '@components/LocaleContextProvider'; import SearchPage from '@pages/SearchPage'; +import OptionListContextProvider from '@components/OptionListContextProvider'; import ComposeProviders from '../../src/components/ComposeProviders'; import OnyxProvider from '../../src/components/OnyxProvider'; import CONST from '../../src/CONST'; @@ -110,7 +111,7 @@ afterEach(() => { function SearchPageWrapper(args) { return ( - + Date: Mon, 18 Mar 2024 10:02:34 +0100 Subject: [PATCH 245/500] fix prettier --- tests/perf-test/SearchPage.perf-test.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/perf-test/SearchPage.perf-test.js b/tests/perf-test/SearchPage.perf-test.js index b25eb9c0ec05..bc3b32dbba96 100644 --- a/tests/perf-test/SearchPage.perf-test.js +++ b/tests/perf-test/SearchPage.perf-test.js @@ -4,8 +4,8 @@ import Onyx from 'react-native-onyx'; import {measurePerformance} from 'reassure'; import _ from 'underscore'; import {LocaleContextProvider} from '@components/LocaleContextProvider'; -import SearchPage from '@pages/SearchPage'; import OptionListContextProvider from '@components/OptionListContextProvider'; +import SearchPage from '@pages/SearchPage'; import ComposeProviders from '../../src/components/ComposeProviders'; import OnyxProvider from '../../src/components/OnyxProvider'; import CONST from '../../src/CONST'; From cfc67146623d405ca9f253539b335047b15fd954 Mon Sep 17 00:00:00 2001 From: Shubham Agrawal Date: Mon, 18 Mar 2024 17:19:51 +0530 Subject: [PATCH 246/500] Removed unecessary checkmark from RadioListItem --- .../SelectionList/RadioListItem.tsx | 42 +------------------ 1 file changed, 1 insertion(+), 41 deletions(-) diff --git a/src/components/SelectionList/RadioListItem.tsx b/src/components/SelectionList/RadioListItem.tsx index 5931f76f16e4..0b1f74b9d68e 100644 --- a/src/components/SelectionList/RadioListItem.tsx +++ b/src/components/SelectionList/RadioListItem.tsx @@ -1,13 +1,7 @@ -import React, {useCallback} from 'react'; +import React from 'react'; import {View} from 'react-native'; -import Icon from '@components/Icon'; -import * as Expensicons from '@components/Icon/Expensicons'; -import PressableWithFeedback from '@components/Pressable/PressableWithFeedback'; import TextWithTooltip from '@components/TextWithTooltip'; -import useStyleUtils from '@hooks/useStyleUtils'; -import useTheme from '@hooks/useTheme'; import useThemeStyles from '@hooks/useThemeStyles'; -import CONST from '@src/CONST'; import BaseListItem from './BaseListItem'; import type {RadioListItemProps} from './types'; @@ -16,25 +10,13 @@ function RadioListItem({ isFocused, showTooltip, isDisabled, - canSelectMultiple, onSelectRow, - onCheckboxPress, onDismissError, shouldPreventDefaultFocusOnSelectRow, rightHandSideComponent, isMultilineSupported = false, }: RadioListItemProps) { const styles = useThemeStyles(); - const theme = useTheme(); - const StyleUtils = useStyleUtils(); - - const handleCheckboxPress = useCallback(() => { - if (onCheckboxPress) { - onCheckboxPress(item); - } else { - onSelectRow(item); - } - }, [item, onCheckboxPress, onSelectRow]); return ( <> - {canSelectMultiple && ( - - - {item.isSelected && ( - - )} - - - )} Date: Mon, 18 Mar 2024 17:26:05 +0530 Subject: [PATCH 247/500] Removed unused file --- .../SelectionList/selectionListPropTypes.js | 203 ------------------ 1 file changed, 203 deletions(-) delete mode 100644 src/components/SelectionList/selectionListPropTypes.js diff --git a/src/components/SelectionList/selectionListPropTypes.js b/src/components/SelectionList/selectionListPropTypes.js deleted file mode 100644 index f5178112a4c3..000000000000 --- a/src/components/SelectionList/selectionListPropTypes.js +++ /dev/null @@ -1,203 +0,0 @@ -import PropTypes from 'prop-types'; -import _ from 'underscore'; -import sourcePropTypes from '@components/Image/sourcePropTypes'; -import CONST from '@src/CONST'; - -const commonListItemPropTypes = { - /** Whether this item is focused (for arrow key controls) */ - isFocused: PropTypes.bool, - - /** Style to be applied to Text */ - textStyles: PropTypes.arrayOf(PropTypes.object), - - /** Style to be applied on the alternate text */ - alternateTextStyles: PropTypes.arrayOf(PropTypes.object), - - /** Whether this item is disabled */ - isDisabled: PropTypes.bool, - - /** Whether this item should show Tooltip */ - showTooltip: PropTypes.bool.isRequired, - - /** Whether to use the Checkbox (multiple selection) instead of the Checkmark (single selection) */ - canSelectMultiple: PropTypes.bool, - - /** Callback to fire when the item is selected */ - onSelectRow: PropTypes.func.isRequired, - - /** Callback to fire when an error is dismissed */ - onDismissError: PropTypes.func, -}; - -const userListItemPropTypes = { - ...commonListItemPropTypes, - - /** The section list item */ - item: PropTypes.shape({ - /** Text to display */ - text: PropTypes.string.isRequired, - - /** Alternate text to display */ - alternateText: PropTypes.string, - - /** Key used internally by React */ - keyForList: PropTypes.string.isRequired, - - /** Whether this option is selected */ - isSelected: PropTypes.bool, - - /** Whether this option is disabled for selection */ - isDisabled: PropTypes.bool, - - /** User accountID */ - accountID: PropTypes.number, - - /** User login */ - login: PropTypes.string, - - /** Element to show on the right side of the item */ - rightElement: PropTypes.element, - - /** Icons for the user (can be multiple if it's a Workspace) */ - icons: PropTypes.arrayOf( - PropTypes.shape({ - source: PropTypes.oneOfType([PropTypes.string, sourcePropTypes]).isRequired, - name: PropTypes.string, - type: PropTypes.string, - }), - ), - - /** Errors that this user may contain */ - errors: PropTypes.objectOf(PropTypes.string), - - /** The type of action that's pending */ - pendingAction: PropTypes.oneOf(_.values(CONST.RED_BRICK_ROAD_PENDING_ACTION)), - }).isRequired, -}; - -const radioListItemPropTypes = { - ...commonListItemPropTypes, - - /** The section list item */ - item: PropTypes.shape({ - /** Text to display */ - text: PropTypes.string.isRequired, - - /** Alternate text to display */ - alternateText: PropTypes.string, - - /** Key used internally by React */ - keyForList: PropTypes.string.isRequired, - - /** Whether this option is selected */ - isSelected: PropTypes.bool, - }).isRequired, -}; - -const baseListItemPropTypes = { - ...commonListItemPropTypes, - item: PropTypes.oneOfType([PropTypes.shape(userListItemPropTypes.item), PropTypes.shape(radioListItemPropTypes.item)]), - shouldPreventDefaultFocusOnSelectRow: PropTypes.bool, -}; - -const propTypes = { - /** Sections for the section list */ - sections: PropTypes.arrayOf( - PropTypes.shape({ - /** Title of the section */ - title: PropTypes.string, - - /** The initial index of this section given the total number of options in each section's data array */ - indexOffset: PropTypes.number, - - /** Array of options */ - data: PropTypes.arrayOf(PropTypes.oneOfType([PropTypes.shape(userListItemPropTypes.item), PropTypes.shape(radioListItemPropTypes.item)])), - - /** Whether this section items disabled for selection */ - isDisabled: PropTypes.bool, - }), - ).isRequired, - - /** Whether this is a multi-select list */ - canSelectMultiple: PropTypes.bool, - - /** Callback to fire when a row is pressed */ - onSelectRow: PropTypes.func.isRequired, - - /** Callback to fire when "Select All" checkbox is pressed. Only use along with `canSelectMultiple` */ - onSelectAll: PropTypes.func, - - /** Callback to fire when an error is dismissed */ - onDismissError: PropTypes.func, - - /** Label for the text input */ - textInputLabel: PropTypes.string, - - /** Placeholder for the text input */ - textInputPlaceholder: PropTypes.string, - - /** Value for the text input */ - textInputValue: PropTypes.string, - - /** Max length for the text input */ - textInputMaxLength: PropTypes.number, - - /** Callback to fire when the text input changes */ - onChangeText: PropTypes.func, - - /** Input mode for the text input */ - inputMode: PropTypes.string, - - /** Item `keyForList` to focus initially */ - initiallyFocusedOptionKey: PropTypes.string, - - /** Callback to fire when the list is scrolled */ - onScroll: PropTypes.func, - - /** Callback to fire when the list is scrolled and the user begins dragging */ - onScrollBeginDrag: PropTypes.func, - - /** Message to display at the top of the list */ - headerMessage: PropTypes.string, - - /** Text to display on the confirm button */ - confirmButtonText: PropTypes.string, - - /** Callback to fire when the confirm button is pressed */ - onConfirm: PropTypes.func, - - /** Whether to show the vertical scroll indicator */ - showScrollIndicator: PropTypes.bool, - - /** Whether to show the loading placeholder */ - showLoadingPlaceholder: PropTypes.bool, - - /** Whether to show the default confirm button */ - showConfirmButton: PropTypes.bool, - - /** Whether to stop automatic form submission on pressing enter key or not */ - shouldStopPropagation: PropTypes.bool, - - /** Whether to prevent default focusing of options and focus the textinput when selecting an option */ - shouldPreventDefaultFocusOnSelectRow: PropTypes.bool, - - /** A ref to forward to the TextInput */ - inputRef: PropTypes.oneOfType([PropTypes.object]), - - /** Custom content to display in the header */ - headerContent: PropTypes.oneOfType([PropTypes.func, PropTypes.node]), - - /** Custom content to display in the footer */ - footerContent: PropTypes.oneOfType([PropTypes.func, PropTypes.node]), - - /** Whether to show the toolip text */ - shouldShowTooltips: PropTypes.bool, - - /** Whether to use dynamic maxToRenderPerBatch depending on the visible number of elements */ - shouldUseDynamicMaxToRenderPerBatch: PropTypes.bool, - - /** Right hand side component to display in the list item. Function has list item passed as the param */ - rightHandSideComponent: PropTypes.oneOfType([PropTypes.func, PropTypes.node]), -}; - -export {propTypes, baseListItemPropTypes, radioListItemPropTypes, userListItemPropTypes}; From 73dff71e03d9163de1e41d69a77f22575f715bb3 Mon Sep 17 00:00:00 2001 From: DylanDylann Date: Mon, 18 Mar 2024 19:42:55 +0700 Subject: [PATCH 248/500] merge main --- src/pages/iou/MoneyRequestCategoryPage.js | 106 ------------ src/pages/iou/MoneyRequestDatePage.js | 126 -------------- src/pages/iou/MoneyRequestDescriptionPage.js | 165 ------------------- src/pages/iou/MoneyRequestMerchantPage.js | 140 ---------------- src/pages/iou/MoneyRequestTagPage.js | 127 -------------- 5 files changed, 664 deletions(-) delete mode 100644 src/pages/iou/MoneyRequestCategoryPage.js delete mode 100644 src/pages/iou/MoneyRequestDatePage.js delete mode 100644 src/pages/iou/MoneyRequestDescriptionPage.js delete mode 100644 src/pages/iou/MoneyRequestMerchantPage.js delete mode 100644 src/pages/iou/MoneyRequestTagPage.js diff --git a/src/pages/iou/MoneyRequestCategoryPage.js b/src/pages/iou/MoneyRequestCategoryPage.js deleted file mode 100644 index b3ca0a5d7ae0..000000000000 --- a/src/pages/iou/MoneyRequestCategoryPage.js +++ /dev/null @@ -1,106 +0,0 @@ -import lodashGet from 'lodash/get'; -import PropTypes from 'prop-types'; -import React from 'react'; -import {withOnyx} from 'react-native-onyx'; -import CategoryPicker from '@components/CategoryPicker'; -import HeaderWithBackButton from '@components/HeaderWithBackButton'; -import ScreenWrapper from '@components/ScreenWrapper'; -import Text from '@components/Text'; -import useLocalize from '@hooks/useLocalize'; -import useThemeStyles from '@hooks/useThemeStyles'; -import compose from '@libs/compose'; -import Navigation from '@libs/Navigation/Navigation'; -import reportPropTypes from '@pages/reportPropTypes'; -import * as IOU from '@userActions/IOU'; -import ONYXKEYS from '@src/ONYXKEYS'; -import ROUTES from '@src/ROUTES'; -import {iouDefaultProps, iouPropTypes} from './propTypes'; - -const propTypes = { - /** Navigation route context info provided by react navigation */ - route: PropTypes.shape({ - /** Route specific parameters used on this screen via route :iouType/new/category/:reportID? */ - params: PropTypes.shape({ - /** The type of IOU report, i.e. bill, request, send */ - iouType: PropTypes.string, - - /** The report ID of the IOU */ - reportID: PropTypes.string, - }), - }).isRequired, - - /* Onyx Props */ - /** The report currently being used */ - report: reportPropTypes, - - /** Holds data related to Money Request view state, rather than the underlying Money Request data. */ - iou: iouPropTypes, -}; - -const defaultProps = { - report: {}, - iou: iouDefaultProps, -}; - -function MoneyRequestCategoryPage({route, report, iou}) { - const styles = useThemeStyles(); - const {translate} = useLocalize(); - - const reportID = lodashGet(route, 'params.reportID', ''); - const iouType = lodashGet(route, 'params.iouType', ''); - - const navigateBack = () => { - Navigation.goBack(ROUTES.MONEY_REQUEST_STEP_CONFIRMATION.getRoute(iouType, '1', reportID)); - }; - - const updateCategory = (category) => { - if (category.searchText === iou.category) { - IOU.resetMoneyRequestCategory(); - } else { - IOU.setMoneyRequestCategory(category.searchText); - } - - Navigation.goBack(ROUTES.MONEY_REQUEST_STEP_CONFIRMATION.getRoute(iouType, '1', reportID)); - }; - - return ( - - - {translate('iou.categorySelection')} - - - ); -} - -MoneyRequestCategoryPage.displayName = 'MoneyRequestCategoryPage'; -MoneyRequestCategoryPage.propTypes = propTypes; -MoneyRequestCategoryPage.defaultProps = defaultProps; - -export default compose( - withOnyx({ - iou: { - key: ONYXKEYS.IOU, - }, - }), - // eslint-disable-next-line rulesdir/no-multiple-onyx-in-file - withOnyx({ - report: { - key: ({route, iou}) => { - const reportID = IOU.getIOUReportID(iou, route); - - return `${ONYXKEYS.COLLECTION.REPORT}${reportID}`; - }, - }, - }), -)(MoneyRequestCategoryPage); diff --git a/src/pages/iou/MoneyRequestDatePage.js b/src/pages/iou/MoneyRequestDatePage.js deleted file mode 100644 index c99c07a80624..000000000000 --- a/src/pages/iou/MoneyRequestDatePage.js +++ /dev/null @@ -1,126 +0,0 @@ -import lodashGet from 'lodash/get'; -import PropTypes from 'prop-types'; -import React, {useEffect} from 'react'; -import {withOnyx} from 'react-native-onyx'; -import _ from 'underscore'; -import DatePicker from '@components/DatePicker'; -import FormProvider from '@components/Form/FormProvider'; -import InputWrapper from '@components/Form/InputWrapper'; -import HeaderWithBackButton from '@components/HeaderWithBackButton'; -import ScreenWrapper from '@components/ScreenWrapper'; -import useLocalize from '@hooks/useLocalize'; -import useThemeStyles from '@hooks/useThemeStyles'; -import * as MoneyRequestUtils from '@libs/MoneyRequestUtils'; -import Navigation from '@libs/Navigation/Navigation'; -import * as IOU from '@userActions/IOU'; -import CONST from '@src/CONST'; -import ONYXKEYS from '@src/ONYXKEYS'; -import ROUTES from '@src/ROUTES'; -import {iouDefaultProps, iouPropTypes} from './propTypes'; - -const propTypes = { - /** Onyx Props */ - /** Holds data related to Money Request view state, rather than the underlying Money Request data. */ - iou: iouPropTypes, - - /** Route from navigation */ - route: PropTypes.shape({ - /** Params from the route */ - params: PropTypes.shape({ - /** The type of IOU report, i.e. bill, request, send */ - iouType: PropTypes.string, - - /** The report ID of the IOU */ - reportID: PropTypes.string, - - /** Which field we are editing */ - field: PropTypes.string, - - /** reportID for the "transaction thread" */ - threadReportID: PropTypes.string, - }), - }).isRequired, - - /** The current tab we have navigated to in the request modal. String that corresponds to the request type. */ - selectedTab: PropTypes.oneOf(_.values(CONST.TAB_REQUEST)).isRequired, -}; - -const defaultProps = { - iou: iouDefaultProps, -}; - -function MoneyRequestDatePage({iou, route, selectedTab}) { - const styles = useThemeStyles(); - const {translate} = useLocalize(); - const iouType = lodashGet(route, 'params.iouType', ''); - const reportID = lodashGet(route, 'params.reportID', ''); - const isDistanceRequest = MoneyRequestUtils.isDistanceRequest(iouType, selectedTab); - - useEffect(() => { - const moneyRequestId = `${iouType}${reportID}`; - const shouldReset = iou.id !== moneyRequestId; - if (shouldReset) { - IOU.resetMoneyRequestInfo(moneyRequestId); - } - - if (!isDistanceRequest && (_.isEmpty(iou.participants) || (iou.amount === 0 && !iou.receiptPath) || shouldReset)) { - Navigation.goBack(ROUTES.MONEY_REQUEST.getRoute(iouType, reportID), true); - } - }, [iou.id, iou.participants, iou.amount, iou.receiptPath, iouType, reportID, isDistanceRequest]); - - function navigateBack() { - Navigation.goBack(ROUTES.MONEY_REQUEST_STEP_CONFIRMATION.getRoute(iouType, '1', reportID)); - } - - /** - * Sets the money request comment by saving it to Onyx. - * - * @param {Object} value - * @param {String} value.moneyRequestCreated - */ - function updateDate(value) { - IOU.setMoneyRequestCreated(value.moneyRequestCreated); - navigateBack(); - } - - return ( - - navigateBack()} - /> - updateDate(value)} - submitButtonText={translate('common.save')} - enabledWhenOffline - > - - - - ); -} - -MoneyRequestDatePage.propTypes = propTypes; -MoneyRequestDatePage.defaultProps = defaultProps; -MoneyRequestDatePage.displayName = 'MoneyRequestDatePage'; - -export default withOnyx({ - iou: { - key: ONYXKEYS.IOU, - }, - selectedTab: { - key: `${ONYXKEYS.COLLECTION.SELECTED_TAB}${CONST.TAB.RECEIPT_TAB_ID}`, - }, -})(MoneyRequestDatePage); diff --git a/src/pages/iou/MoneyRequestDescriptionPage.js b/src/pages/iou/MoneyRequestDescriptionPage.js deleted file mode 100644 index a2209818ee1a..000000000000 --- a/src/pages/iou/MoneyRequestDescriptionPage.js +++ /dev/null @@ -1,165 +0,0 @@ -import {useFocusEffect} from '@react-navigation/native'; -import lodashGet from 'lodash/get'; -import PropTypes from 'prop-types'; -import React, {useCallback, useEffect, useRef} from 'react'; -import {View} from 'react-native'; -import {withOnyx} from 'react-native-onyx'; -import _ from 'underscore'; -import FormProvider from '@components/Form/FormProvider'; -import InputWrapperWithRef from '@components/Form/InputWrapper'; -import HeaderWithBackButton from '@components/HeaderWithBackButton'; -import ScreenWrapper from '@components/ScreenWrapper'; -import TextInput from '@components/TextInput'; -import useLocalize from '@hooks/useLocalize'; -import useThemeStyles from '@hooks/useThemeStyles'; -import * as IOU from '@libs/actions/IOU'; -import * as Browser from '@libs/Browser'; -import * as MoneyRequestUtils from '@libs/MoneyRequestUtils'; -import Navigation from '@libs/Navigation/Navigation'; -import updateMultilineInputRange from '@libs/updateMultilineInputRange'; -import CONST from '@src/CONST'; -import ONYXKEYS from '@src/ONYXKEYS'; -import ROUTES from '@src/ROUTES'; -import {iouDefaultProps, iouPropTypes} from './propTypes'; - -const propTypes = { - /** Onyx Props */ - /** Holds data related to Money Request view state, rather than the underlying Money Request data. */ - iou: iouPropTypes, - - /** Route from navigation */ - route: PropTypes.shape({ - /** Params from the route */ - params: PropTypes.shape({ - /** The type of IOU report, i.e. bill, request, send */ - iouType: PropTypes.string, - - /** The report ID of the IOU */ - reportID: PropTypes.string, - - /** Which field we are editing */ - field: PropTypes.string, - - /** reportID for the "transaction thread" */ - threadReportID: PropTypes.string, - }), - }).isRequired, - - /** The current tab we have navigated to in the request modal. String that corresponds to the request type. */ - selectedTab: PropTypes.oneOf(_.values(CONST.TAB_REQUEST)), -}; - -const defaultProps = { - iou: iouDefaultProps, - selectedTab: '', -}; - -function MoneyRequestDescriptionPage({iou, route, selectedTab}) { - const styles = useThemeStyles(); - const {translate} = useLocalize(); - const inputRef = useRef(null); - const focusTimeoutRef = useRef(null); - const iouType = lodashGet(route, 'params.iouType', ''); - const reportID = lodashGet(route, 'params.reportID', ''); - const isDistanceRequest = MoneyRequestUtils.isDistanceRequest(iouType, selectedTab); - - useFocusEffect( - useCallback(() => { - focusTimeoutRef.current = setTimeout(() => { - if (inputRef.current) { - inputRef.current.focus(); - } - return () => { - if (!focusTimeoutRef.current) { - return; - } - clearTimeout(focusTimeoutRef.current); - }; - }, CONST.ANIMATED_TRANSITION); - }, []), - ); - - useEffect(() => { - const moneyRequestId = `${iouType}${reportID}`; - const shouldReset = iou.id !== moneyRequestId; - if (shouldReset) { - IOU.resetMoneyRequestInfo(moneyRequestId); - } - - if (!isDistanceRequest && (_.isEmpty(iou.participants) || (iou.amount === 0 && !iou.receiptPath) || shouldReset)) { - Navigation.goBack(ROUTES.MONEY_REQUEST.getRoute(iouType, reportID), true); - } - }, [iou.id, iou.participants, iou.amount, iou.receiptPath, iouType, reportID, isDistanceRequest]); - - function navigateBack() { - Navigation.goBack(ROUTES.MONEY_REQUEST_STEP_CONFIRMATION.getRoute(iouType, '1', reportID)); - } - - /** - * Sets the money request comment by saving it to Onyx. - * - * @param {Object} value - * @param {String} value.moneyRequestComment - */ - function updateComment(value) { - IOU.setMoneyRequestDescription(value.moneyRequestComment); - navigateBack(); - } - - return ( - - <> - navigateBack()} - /> - updateComment(value)} - submitButtonText={translate('common.save')} - enabledWhenOffline - > - - { - if (!el) { - return; - } - inputRef.current = el; - updateMultilineInputRange(inputRef.current); - }} - autoGrowHeight - containerStyles={[styles.autoGrowHeightMultilineInput]} - submitOnEnter={!Browser.isMobile()} - /> - - - - - ); -} - -MoneyRequestDescriptionPage.propTypes = propTypes; -MoneyRequestDescriptionPage.defaultProps = defaultProps; -MoneyRequestDescriptionPage.displayName = 'MoneyRequestDescriptionPage'; - -export default withOnyx({ - iou: { - key: ONYXKEYS.IOU, - }, - selectedTab: { - key: `${ONYXKEYS.COLLECTION.SELECTED_TAB}${CONST.TAB.RECEIPT_TAB_ID}`, - }, -})(MoneyRequestDescriptionPage); diff --git a/src/pages/iou/MoneyRequestMerchantPage.js b/src/pages/iou/MoneyRequestMerchantPage.js deleted file mode 100644 index 1d3fc6f19584..000000000000 --- a/src/pages/iou/MoneyRequestMerchantPage.js +++ /dev/null @@ -1,140 +0,0 @@ -import lodashGet from 'lodash/get'; -import PropTypes from 'prop-types'; -import React, {useCallback, useEffect} from 'react'; -import {View} from 'react-native'; -import {withOnyx} from 'react-native-onyx'; -import _ from 'underscore'; -import FormProvider from '@components/Form/FormProvider'; -import InputWrapperWithRef from '@components/Form/InputWrapper'; -import HeaderWithBackButton from '@components/HeaderWithBackButton'; -import ScreenWrapper from '@components/ScreenWrapper'; -import TextInput from '@components/TextInput'; -import useAutoFocusInput from '@hooks/useAutoFocusInput'; -import useLocalize from '@hooks/useLocalize'; -import useThemeStyles from '@hooks/useThemeStyles'; -import Navigation from '@libs/Navigation/Navigation'; -import * as IOU from '@userActions/IOU'; -import CONST from '@src/CONST'; -import ONYXKEYS from '@src/ONYXKEYS'; -import ROUTES from '@src/ROUTES'; -import INPUT_IDS from '@src/types/form/MoneyRequestMerchantForm'; -import {iouDefaultProps, iouPropTypes} from './propTypes'; - -const propTypes = { - /** Onyx Props */ - /** Holds data related to Money Request view state, rather than the underlying Money Request data. */ - iou: iouPropTypes, - - /** Route from navigation */ - route: PropTypes.shape({ - /** Params from the route */ - params: PropTypes.shape({ - /** The type of IOU report, i.e. bill, request, send */ - iouType: PropTypes.string, - - /** The report ID of the IOU */ - reportID: PropTypes.string, - - /** Which field we are editing */ - field: PropTypes.string, - - /** reportID for the "transaction thread" */ - threadReportID: PropTypes.string, - }), - }).isRequired, -}; - -const defaultProps = { - iou: iouDefaultProps, -}; - -function MoneyRequestMerchantPage({iou, route}) { - const styles = useThemeStyles(); - const {translate} = useLocalize(); - const {inputCallbackRef} = useAutoFocusInput(); - const iouType = lodashGet(route, 'params.iouType', ''); - const reportID = lodashGet(route, 'params.reportID', ''); - const isEmptyMerchant = iou.merchant === '' || iou.merchant === CONST.TRANSACTION.PARTIAL_TRANSACTION_MERCHANT; - - useEffect(() => { - const moneyRequestId = `${iouType}${reportID}`; - const shouldReset = iou.id !== moneyRequestId; - if (shouldReset) { - IOU.resetMoneyRequestInfo(moneyRequestId); - } - - if (_.isEmpty(iou.participants) || (iou.amount === 0 && !iou.receiptPath) || shouldReset) { - Navigation.goBack(ROUTES.MONEY_REQUEST.getRoute(iouType, reportID), true); - } - }, [iou.id, iou.participants, iou.amount, iou.receiptPath, iouType, reportID]); - - function navigateBack() { - Navigation.goBack(ROUTES.MONEY_REQUEST__STEP_CONFIRMATION.getRoute(iouType, '1', reportID)); - } - - const validate = useCallback((value) => { - const errors = {}; - - if (_.isEmpty(value.moneyRequestMerchant)) { - errors.moneyRequestMerchant = 'common.error.fieldRequired'; - } - - return errors; - }, []); - - /** - * Sets the money request comment by saving it to Onyx. - * - * @param {Object} value - * @param {String} value.moneyRequestMerchant - */ - function updateMerchant(value) { - IOU.setMoneyRequestMerchant(value.moneyRequestMerchant); - navigateBack(); - } - - return ( - - navigateBack()} - /> - updateMerchant(value)} - validate={validate} - submitButtonText={translate('common.save')} - enabledWhenOffline - > - - - - - - ); -} - -MoneyRequestMerchantPage.propTypes = propTypes; -MoneyRequestMerchantPage.defaultProps = defaultProps; -MoneyRequestMerchantPage.displayName = 'MoneyRequestMerchantPage'; - -export default withOnyx({ - iou: { - key: ONYXKEYS.IOU, - }, -})(MoneyRequestMerchantPage); diff --git a/src/pages/iou/MoneyRequestTagPage.js b/src/pages/iou/MoneyRequestTagPage.js deleted file mode 100644 index 7fd08f20d652..000000000000 --- a/src/pages/iou/MoneyRequestTagPage.js +++ /dev/null @@ -1,127 +0,0 @@ -import lodashGet from 'lodash/get'; -import PropTypes from 'prop-types'; -import React from 'react'; -import {withOnyx} from 'react-native-onyx'; -import _ from 'underscore'; -import HeaderWithBackButton from '@components/HeaderWithBackButton'; -import ScreenWrapper from '@components/ScreenWrapper'; -import TagPicker from '@components/TagPicker'; -import tagPropTypes from '@components/tagPropTypes'; -import Text from '@components/Text'; -import useLocalize from '@hooks/useLocalize'; -import useThemeStyles from '@hooks/useThemeStyles'; -import compose from '@libs/compose'; -import Navigation from '@libs/Navigation/Navigation'; -import * as PolicyUtils from '@libs/PolicyUtils'; -import reportPropTypes from '@pages/reportPropTypes'; -import * as IOU from '@userActions/IOU'; -import ONYXKEYS from '@src/ONYXKEYS'; -import ROUTES from '@src/ROUTES'; -import {iouDefaultProps, iouPropTypes} from './propTypes'; - -const propTypes = { - /** Navigation route context info provided by react navigation */ - route: PropTypes.shape({ - /** Route specific parameters used on this screen via route :iouType/new/tag/:reportID? */ - params: PropTypes.shape({ - /** The type of IOU report, i.e. bill, request, send */ - iouType: PropTypes.string, - - /** The report ID of the IOU */ - reportID: PropTypes.string, - }), - }).isRequired, - - /* Onyx props */ - /** The report currently being used */ - report: reportPropTypes, - - /** Collection of tags attached to a policy */ - policyTags: tagPropTypes, - - /** Holds data related to Money Request view state, rather than the underlying Money Request data. */ - iou: iouPropTypes, -}; - -const defaultProps = { - report: {}, - policyTags: {}, - iou: iouDefaultProps, -}; - -function MoneyRequestTagPage({route, report, policyTags, iou}) { - const styles = useThemeStyles(); - const {translate} = useLocalize(); - - const iouType = lodashGet(route, 'params.iouType', ''); - - // Fetches the first tag list of the policy - const tagListKey = _.first(_.keys(policyTags)); - const policyTagListName = PolicyUtils.getTagListName(policyTags) || translate('common.tag'); - - const navigateBack = () => { - Navigation.goBack(ROUTES.MONEY_REQUEST_STEP_CONFIRMATION.getRoute(iouType, '1', report.reportID)); - }; - - const updateTag = (selectedTag) => { - if (selectedTag.searchText === iou.tag) { - IOU.resetMoneyRequestTag(); - } else { - IOU.setMoneyRequestTag(selectedTag.searchText); - } - navigateBack(); - }; - - return ( - - {({insets}) => ( - <> - - {translate('iou.tagSelection', {tagName: policyTagListName})} - - - )} - - ); -} - -MoneyRequestTagPage.displayName = 'MoneyRequestTagPage'; -MoneyRequestTagPage.propTypes = propTypes; -MoneyRequestTagPage.defaultProps = defaultProps; - -export default compose( - withOnyx({ - iou: { - key: ONYXKEYS.IOU, - }, - }), - // eslint-disable-next-line rulesdir/no-multiple-onyx-in-file - withOnyx({ - report: { - key: ({route, iou}) => { - const reportID = IOU.getIOUReportID(iou, route); - - return `${ONYXKEYS.COLLECTION.REPORT}${reportID}`; - }, - }, - }), - // eslint-disable-next-line rulesdir/no-multiple-onyx-in-file - withOnyx({ - policyTags: { - key: ({report}) => `${ONYXKEYS.COLLECTION.POLICY_TAGS}${report ? report.policyID : '0'}`, - }, - }), -)(MoneyRequestTagPage); From 26801e73a92c0e86bb9e379ab3cfe037dcb2f156 Mon Sep 17 00:00:00 2001 From: DylanDylann Date: Mon, 18 Mar 2024 19:45:19 +0700 Subject: [PATCH 249/500] merge main --- src/pages/EditRequestMerchantPage.js | 85 ---------------------------- 1 file changed, 85 deletions(-) delete mode 100644 src/pages/EditRequestMerchantPage.js diff --git a/src/pages/EditRequestMerchantPage.js b/src/pages/EditRequestMerchantPage.js deleted file mode 100644 index 33c04df39e3e..000000000000 --- a/src/pages/EditRequestMerchantPage.js +++ /dev/null @@ -1,85 +0,0 @@ -import PropTypes from 'prop-types'; -import React, {useCallback, useRef} from 'react'; -import {View} from 'react-native'; -import _ from 'underscore'; -import FormProvider from '@components/Form/FormProvider'; -import InputWrapper from '@components/Form/InputWrapper'; -import HeaderWithBackButton from '@components/HeaderWithBackButton'; -import ScreenWrapper from '@components/ScreenWrapper'; -import TextInput from '@components/TextInput'; -import useLocalize from '@hooks/useLocalize'; -import useThemeStyles from '@hooks/useThemeStyles'; -import CONST from '@src/CONST'; -import ONYXKEYS from '@src/ONYXKEYS'; -import INPUT_IDS from '@src/types/form/MoneyRequestMerchantForm'; - -const propTypes = { - /** Transaction default merchant value */ - defaultMerchant: PropTypes.string.isRequired, - - /** Callback to fire when the Save button is pressed */ - onSubmit: PropTypes.func.isRequired, - - /** Boolean to enable validation */ - isPolicyExpenseChat: PropTypes.bool, -}; - -const defaultProps = { - isPolicyExpenseChat: false, -}; - -function EditRequestMerchantPage({defaultMerchant, onSubmit, isPolicyExpenseChat}) { - const styles = useThemeStyles(); - const {translate} = useLocalize(); - const merchantInputRef = useRef(null); - const isEmptyMerchant = defaultMerchant === '' || defaultMerchant === CONST.TRANSACTION.PARTIAL_TRANSACTION_MERCHANT; - - const validate = useCallback( - (value) => { - const errors = {}; - if (_.isEmpty(value.merchant) && value.merchant.trim() === '' && isPolicyExpenseChat) { - errors.merchant = 'common.error.fieldRequired'; - } - return errors; - }, - [isPolicyExpenseChat], - ); - - return ( - merchantInputRef.current && merchantInputRef.current.focus()} - testID={EditRequestMerchantPage.displayName} - > - - - - (merchantInputRef.current = e)} - /> - - - - ); -} - -EditRequestMerchantPage.propTypes = propTypes; -EditRequestMerchantPage.defaultProps = defaultProps; -EditRequestMerchantPage.displayName = 'EditRequestMerchantPage'; - -export default EditRequestMerchantPage; From 0abc91308b5bd675562a1689bfa795e904b9e5be Mon Sep 17 00:00:00 2001 From: DylanDylann Date: Mon, 18 Mar 2024 19:49:33 +0700 Subject: [PATCH 250/500] merge main --- src/libs/Navigation/linkingConfig.ts | 506 --------------------------- 1 file changed, 506 deletions(-) delete mode 100644 src/libs/Navigation/linkingConfig.ts diff --git a/src/libs/Navigation/linkingConfig.ts b/src/libs/Navigation/linkingConfig.ts deleted file mode 100644 index d4e04d5402e2..000000000000 --- a/src/libs/Navigation/linkingConfig.ts +++ /dev/null @@ -1,506 +0,0 @@ -/* eslint-disable @typescript-eslint/naming-convention */ -import type {LinkingOptions} from '@react-navigation/native'; -import CONST from '@src/CONST'; -import NAVIGATORS from '@src/NAVIGATORS'; -import ROUTES from '@src/ROUTES'; -import SCREENS from '@src/SCREENS'; -import type {RootStackParamList} from './types'; - -const linkingConfig: LinkingOptions = { - prefixes: [ - 'app://-/', - 'new-expensify://', - 'https://www.expensify.cash', - 'https://staging.expensify.cash', - 'https://dev.new.expensify.com', - CONST.NEW_EXPENSIFY_URL, - CONST.STAGING_NEW_EXPENSIFY_URL, - ], - config: { - initialRouteName: SCREENS.HOME, - screens: { - // Main Routes - [SCREENS.VALIDATE_LOGIN]: ROUTES.VALIDATE_LOGIN, - [SCREENS.UNLINK_LOGIN]: ROUTES.UNLINK_LOGIN, - [SCREENS.TRANSITION_BETWEEN_APPS]: ROUTES.TRANSITION_BETWEEN_APPS, - [SCREENS.CONCIERGE]: ROUTES.CONCIERGE, - [SCREENS.SIGN_IN_WITH_APPLE_DESKTOP]: ROUTES.APPLE_SIGN_IN, - [SCREENS.SIGN_IN_WITH_GOOGLE_DESKTOP]: ROUTES.GOOGLE_SIGN_IN, - [SCREENS.SAML_SIGN_IN]: ROUTES.SAML_SIGN_IN, - [SCREENS.DESKTOP_SIGN_IN_REDIRECT]: ROUTES.DESKTOP_SIGN_IN_REDIRECT, - [SCREENS.REPORT_ATTACHMENTS]: ROUTES.REPORT_ATTACHMENTS.route, - [SCREENS.PROFILE_AVATAR]: ROUTES.PROFILE_AVATAR.route, - [SCREENS.WORKSPACE_AVATAR]: ROUTES.WORKSPACE_AVATAR.route, - [SCREENS.REPORT_AVATAR]: ROUTES.REPORT_AVATAR.route, - - // Sidebar - [SCREENS.HOME]: { - path: ROUTES.HOME, - }, - - [NAVIGATORS.CENTRAL_PANE_NAVIGATOR]: { - screens: { - [SCREENS.REPORT]: ROUTES.REPORT_WITH_ID.route, - }, - }, - [SCREENS.NOT_FOUND]: '*', - [NAVIGATORS.LEFT_MODAL_NAVIGATOR]: { - screens: { - [SCREENS.LEFT_MODAL.SEARCH]: { - screens: { - [SCREENS.SEARCH_ROOT]: ROUTES.SEARCH, - }, - }, - }, - }, - [NAVIGATORS.RIGHT_MODAL_NAVIGATOR]: { - screens: { - [SCREENS.RIGHT_MODAL.SETTINGS]: { - screens: { - [SCREENS.SETTINGS.ROOT]: { - path: ROUTES.SETTINGS, - }, - [SCREENS.SETTINGS.WORKSPACES]: { - path: ROUTES.SETTINGS_WORKSPACES, - exact: true, - }, - [SCREENS.SETTINGS.PREFERENCES.ROOT]: { - path: ROUTES.SETTINGS_PREFERENCES, - exact: true, - }, - [SCREENS.SETTINGS.PREFERENCES.PRIORITY_MODE]: { - path: ROUTES.SETTINGS_PRIORITY_MODE, - exact: true, - }, - [SCREENS.SETTINGS.PREFERENCES.LANGUAGE]: { - path: ROUTES.SETTINGS_LANGUAGE, - exact: true, - }, - [SCREENS.SETTINGS.PREFERENCES.THEME]: { - path: ROUTES.SETTINGS_THEME, - exact: true, - }, - [SCREENS.SETTINGS.CLOSE]: { - path: ROUTES.SETTINGS_CLOSE, - exact: true, - }, - [SCREENS.SETTINGS.SECURITY]: { - path: ROUTES.SETTINGS_SECURITY, - exact: true, - }, - [SCREENS.SETTINGS.WALLET.ROOT]: { - path: ROUTES.SETTINGS_WALLET, - exact: true, - }, - [SCREENS.SETTINGS.WALLET.DOMAIN_CARD]: { - path: ROUTES.SETTINGS_WALLET_DOMAINCARD.route, - exact: true, - }, - [SCREENS.SETTINGS.WALLET.REPORT_VIRTUAL_CARD_FRAUD]: { - path: ROUTES.SETTINGS_REPORT_FRAUD.route, - exact: true, - }, - [SCREENS.SETTINGS.WALLET.CARD_GET_PHYSICAL.NAME]: { - path: ROUTES.SETTINGS_WALLET_CARD_GET_PHYSICAL_NAME.route, - exact: true, - }, - [SCREENS.SETTINGS.WALLET.CARD_GET_PHYSICAL.PHONE]: { - path: ROUTES.SETTINGS_WALLET_CARD_GET_PHYSICAL_PHONE.route, - exact: true, - }, - [SCREENS.SETTINGS.WALLET.CARD_GET_PHYSICAL.ADDRESS]: { - path: ROUTES.SETTINGS_WALLET_CARD_GET_PHYSICAL_ADDRESS.route, - exact: true, - }, - [SCREENS.SETTINGS.WALLET.CARD_GET_PHYSICAL.CONFIRM]: { - path: ROUTES.SETTINGS_WALLET_CARD_GET_PHYSICAL_CONFIRM.route, - exact: true, - }, - [SCREENS.SETTINGS.WALLET.ENABLE_PAYMENTS]: { - path: ROUTES.SETTINGS_ENABLE_PAYMENTS, - exact: true, - }, - [SCREENS.SETTINGS.WALLET.TRANSFER_BALANCE]: { - path: ROUTES.SETTINGS_WALLET_TRANSFER_BALANCE, - exact: true, - }, - [SCREENS.SETTINGS.WALLET.CHOOSE_TRANSFER_ACCOUNT]: { - path: ROUTES.SETTINGS_WALLET_CHOOSE_TRANSFER_ACCOUNT, - exact: true, - }, - [SCREENS.SETTINGS.REPORT_CARD_LOST_OR_DAMAGED]: { - path: ROUTES.SETTINGS_WALLET_REPORT_CARD_LOST_OR_DAMAGED.route, - exact: true, - }, - [SCREENS.SETTINGS.WALLET.CARD_ACTIVATE]: { - path: ROUTES.SETTINGS_WALLET_CARD_ACTIVATE.route, - exact: true, - }, - [SCREENS.SETTINGS.WALLET.CARDS_DIGITAL_DETAILS_UPDATE_ADDRESS]: { - path: ROUTES.SETTINGS_WALLET_CARD_DIGITAL_DETAILS_UPDATE_ADDRESS.route, - exact: true, - }, - [SCREENS.SETTINGS.ADD_DEBIT_CARD]: { - path: ROUTES.SETTINGS_ADD_DEBIT_CARD, - exact: true, - }, - [SCREENS.SETTINGS.ADD_BANK_ACCOUNT]: { - path: ROUTES.SETTINGS_ADD_BANK_ACCOUNT, - exact: true, - }, - [SCREENS.SETTINGS.PROFILE.ROOT]: { - path: ROUTES.SETTINGS_PROFILE, - exact: true, - }, - [SCREENS.SETTINGS.PROFILE.PRONOUNS]: { - path: ROUTES.SETTINGS_PRONOUNS, - exact: true, - }, - [SCREENS.SETTINGS.PROFILE.DISPLAY_NAME]: { - path: ROUTES.SETTINGS_DISPLAY_NAME, - exact: true, - }, - [SCREENS.SETTINGS.PROFILE.TIMEZONE]: { - path: ROUTES.SETTINGS_TIMEZONE, - exact: true, - }, - [SCREENS.SETTINGS.PROFILE.TIMEZONE_SELECT]: { - path: ROUTES.SETTINGS_TIMEZONE_SELECT, - exact: true, - }, - [SCREENS.SETTINGS.ABOUT]: { - path: ROUTES.SETTINGS_ABOUT, - exact: true, - }, - [SCREENS.SETTINGS.APP_DOWNLOAD_LINKS]: { - path: ROUTES.SETTINGS_APP_DOWNLOAD_LINKS, - exact: true, - }, - [SCREENS.SETTINGS.PROFILE.CONTACT_METHODS]: { - path: ROUTES.SETTINGS_CONTACT_METHODS.route, - exact: true, - }, - [SCREENS.SETTINGS.PROFILE.CONTACT_METHOD_DETAILS]: { - path: ROUTES.SETTINGS_CONTACT_METHOD_DETAILS.route, - }, - [SCREENS.SETTINGS.LOUNGE_ACCESS]: { - path: ROUTES.SETTINGS_LOUNGE_ACCESS, - }, - [SCREENS.SETTINGS.PROFILE.NEW_CONTACT_METHOD]: { - path: ROUTES.SETTINGS_NEW_CONTACT_METHOD.route, - exact: true, - }, - [SCREENS.SETTINGS.PROFILE.PERSONAL_DETAILS.INITIAL]: { - path: ROUTES.SETTINGS_PERSONAL_DETAILS, - exact: true, - }, - [SCREENS.SETTINGS.PROFILE.PERSONAL_DETAILS.LEGAL_NAME]: { - path: ROUTES.SETTINGS_PERSONAL_DETAILS_LEGAL_NAME, - exact: true, - }, - [SCREENS.SETTINGS.PROFILE.PERSONAL_DETAILS.DATE_OF_BIRTH]: { - path: ROUTES.SETTINGS_PERSONAL_DETAILS_DATE_OF_BIRTH, - exact: true, - }, - [SCREENS.SETTINGS.PROFILE.PERSONAL_DETAILS.ADDRESS]: { - path: ROUTES.SETTINGS_PERSONAL_DETAILS_ADDRESS, - exact: true, - }, - [SCREENS.SETTINGS.PROFILE.PERSONAL_DETAILS.ADDRESS_COUNTRY]: { - path: ROUTES.SETTINGS_PERSONAL_DETAILS_ADDRESS_COUNTRY.route, - exact: true, - }, - [SCREENS.SETTINGS.TWO_FACTOR_AUTH]: { - path: ROUTES.SETTINGS_2FA.route, - exact: true, - }, - [SCREENS.SETTINGS.SHARE_CODE]: { - path: ROUTES.SETTINGS_SHARE_CODE, - exact: true, - }, - [SCREENS.SETTINGS.PROFILE.STATUS]: { - path: ROUTES.SETTINGS_STATUS, - exact: true, - }, - [SCREENS.SETTINGS.PROFILE.STATUS_CLEAR_AFTER]: { - path: ROUTES.SETTINGS_STATUS_CLEAR_AFTER, - }, - [SCREENS.SETTINGS.PROFILE.STATUS_CLEAR_AFTER_DATE]: { - path: ROUTES.SETTINGS_STATUS_CLEAR_AFTER_DATE, - }, - [SCREENS.SETTINGS.PROFILE.STATUS_CLEAR_AFTER_TIME]: { - path: ROUTES.SETTINGS_STATUS_CLEAR_AFTER_TIME, - }, - [SCREENS.WORKSPACE.INITIAL]: { - path: ROUTES.WORKSPACE_INITIAL.route, - }, - [SCREENS.WORKSPACE.SETTINGS]: { - path: ROUTES.WORKSPACE_SETTINGS.route, - }, - [SCREENS.WORKSPACE.CURRENCY]: { - path: ROUTES.WORKSPACE_SETTINGS_CURRENCY.route, - }, - [SCREENS.WORKSPACE.CARD]: { - path: ROUTES.WORKSPACE_CARD.route, - }, - [SCREENS.WORKSPACE.REIMBURSE]: { - path: ROUTES.WORKSPACE_REIMBURSE.route, - }, - [SCREENS.WORKSPACE.RATE_AND_UNIT]: { - path: ROUTES.WORKSPACE_RATE_AND_UNIT.route, - }, - [SCREENS.WORKSPACE.BILLS]: { - path: ROUTES.WORKSPACE_BILLS.route, - }, - [SCREENS.WORKSPACE.INVOICES]: { - path: ROUTES.WORKSPACE_INVOICES.route, - }, - [SCREENS.WORKSPACE.TRAVEL]: { - path: ROUTES.WORKSPACE_TRAVEL.route, - }, - [SCREENS.WORKSPACE.MEMBERS]: { - path: ROUTES.WORKSPACE_MEMBERS.route, - }, - [SCREENS.WORKSPACE.INVITE]: { - path: ROUTES.WORKSPACE_INVITE.route, - }, - [SCREENS.WORKSPACE.INVITE_MESSAGE]: { - path: ROUTES.WORKSPACE_INVITE_MESSAGE.route, - }, - [SCREENS.REIMBURSEMENT_ACCOUNT]: { - path: ROUTES.BANK_ACCOUNT_WITH_STEP_TO_OPEN.route, - exact: true, - }, - [SCREENS.GET_ASSISTANCE]: { - path: ROUTES.GET_ASSISTANCE.route, - }, - [SCREENS.KEYBOARD_SHORTCUTS]: { - path: ROUTES.KEYBOARD_SHORTCUTS, - }, - }, - }, - [SCREENS.RIGHT_MODAL.PRIVATE_NOTES]: { - screens: { - [SCREENS.PRIVATE_NOTES.LIST]: ROUTES.PRIVATE_NOTES_LIST.route, - [SCREENS.PRIVATE_NOTES.EDIT]: ROUTES.PRIVATE_NOTES_EDIT.route, - }, - }, - [SCREENS.RIGHT_MODAL.REPORT_DETAILS]: { - screens: { - [SCREENS.REPORT_DETAILS.ROOT]: ROUTES.REPORT_WITH_ID_DETAILS.route, - [SCREENS.REPORT_DETAILS.SHARE_CODE]: ROUTES.REPORT_WITH_ID_DETAILS_SHARE_CODE.route, - }, - }, - [SCREENS.RIGHT_MODAL.REPORT_SETTINGS]: { - screens: { - [SCREENS.REPORT_SETTINGS.ROOT]: { - path: ROUTES.REPORT_SETTINGS.route, - }, - [SCREENS.REPORT_SETTINGS.ROOM_NAME]: { - path: ROUTES.REPORT_SETTINGS_ROOM_NAME.route, - }, - [SCREENS.REPORT_SETTINGS.NOTIFICATION_PREFERENCES]: { - path: ROUTES.REPORT_SETTINGS_NOTIFICATION_PREFERENCES.route, - }, - [SCREENS.REPORT_SETTINGS.WRITE_CAPABILITY]: { - path: ROUTES.REPORT_SETTINGS_WRITE_CAPABILITY.route, - }, - }, - }, - [SCREENS.RIGHT_MODAL.REPORT_WELCOME_MESSAGE]: { - screens: { - [SCREENS.REPORT_WELCOME_MESSAGE_ROOT]: ROUTES.REPORT_WELCOME_MESSAGE.route, - }, - }, - [SCREENS.RIGHT_MODAL.NEW_CHAT]: { - screens: { - [SCREENS.NEW_CHAT.ROOT]: { - path: ROUTES.NEW, - exact: true, - screens: { - [SCREENS.NEW_CHAT.NEW_CHAT]: { - path: ROUTES.NEW_CHAT, - exact: true, - }, - [SCREENS.NEW_CHAT.NEW_ROOM]: { - path: ROUTES.NEW_ROOM, - exact: true, - }, - }, - }, - }, - }, - [SCREENS.RIGHT_MODAL.NEW_TASK]: { - screens: { - [SCREENS.NEW_TASK.ROOT]: ROUTES.NEW_TASK, - [SCREENS.NEW_TASK.TASK_ASSIGNEE_SELECTOR]: ROUTES.NEW_TASK_ASSIGNEE, - [SCREENS.NEW_TASK.TASK_SHARE_DESTINATION_SELECTOR]: ROUTES.NEW_TASK_SHARE_DESTINATION, - [SCREENS.NEW_TASK.DETAILS]: ROUTES.NEW_TASK_DETAILS, - [SCREENS.NEW_TASK.TITLE]: ROUTES.NEW_TASK_TITLE, - [SCREENS.NEW_TASK.DESCRIPTION]: ROUTES.NEW_TASK_DESCRIPTION, - }, - }, - [SCREENS.RIGHT_MODAL.TEACHERS_UNITE]: { - screens: { - [SCREENS.SAVE_THE_WORLD.ROOT]: ROUTES.TEACHERS_UNITE, - [SCREENS.I_KNOW_A_TEACHER]: ROUTES.I_KNOW_A_TEACHER, - [SCREENS.INTRO_SCHOOL_PRINCIPAL]: ROUTES.INTRO_SCHOOL_PRINCIPAL, - [SCREENS.I_AM_A_TEACHER]: ROUTES.I_AM_A_TEACHER, - }, - }, - [SCREENS.RIGHT_MODAL.DETAILS]: { - screens: { - [SCREENS.DETAILS_ROOT]: ROUTES.DETAILS.route, - }, - }, - [SCREENS.RIGHT_MODAL.PROFILE]: { - screens: { - [SCREENS.PROFILE_ROOT]: ROUTES.PROFILE.route, - }, - }, - [SCREENS.RIGHT_MODAL.PARTICIPANTS]: { - screens: { - [SCREENS.REPORT_PARTICIPANTS_ROOT]: ROUTES.REPORT_PARTICIPANTS.route, - }, - }, - [SCREENS.RIGHT_MODAL.ROOM_INVITE]: { - screens: { - [SCREENS.ROOM_INVITE_ROOT]: ROUTES.ROOM_INVITE.route, - }, - }, - [SCREENS.RIGHT_MODAL.ROOM_MEMBERS]: { - screens: { - [SCREENS.ROOM_MEMBERS_ROOT]: ROUTES.ROOM_MEMBERS.route, - }, - }, - [SCREENS.RIGHT_MODAL.MONEY_REQUEST]: { - screens: { - [SCREENS.MONEY_REQUEST.START]: ROUTES.MONEY_REQUEST_START.route, - [SCREENS.MONEY_REQUEST.CREATE]: { - path: ROUTES.MONEY_REQUEST_CREATE.route, - exact: true, - screens: { - distance: { - path: ROUTES.MONEY_REQUEST_CREATE_TAB_DISTANCE.route, - exact: true, - }, - manual: { - path: ROUTES.MONEY_REQUEST_CREATE_TAB_MANUAL.route, - exact: true, - }, - scan: { - path: ROUTES.MONEY_REQUEST_CREATE_TAB_SCAN.route, - exact: true, - }, - }, - }, - [SCREENS.MONEY_REQUEST.STEP_AMOUNT]: ROUTES.MONEY_REQUEST_STEP_AMOUNT.route, - [SCREENS.MONEY_REQUEST.STEP_CATEGORY]: ROUTES.MONEY_REQUEST_STEP_CATEGORY.route, - [SCREENS.MONEY_REQUEST.STEP_CONFIRMATION]: ROUTES.MONEY_REQUEST_STEP_CONFIRMATION.route, - [SCREENS.MONEY_REQUEST.STEP_CURRENCY]: ROUTES.MONEY_REQUEST_STEP_CURRENCY.route, - [SCREENS.MONEY_REQUEST.STEP_DATE]: ROUTES.MONEY_REQUEST_STEP_DATE.route, - [SCREENS.MONEY_REQUEST.STEP_DESCRIPTION]: ROUTES.MONEY_REQUEST_STEP_DESCRIPTION.route, - [SCREENS.MONEY_REQUEST.STEP_DISTANCE]: ROUTES.MONEY_REQUEST_STEP_DISTANCE.route, - [SCREENS.MONEY_REQUEST.STEP_MERCHANT]: ROUTES.MONEY_REQUEST_STEP_MERCHANT.route, - [SCREENS.MONEY_REQUEST.STEP_PARTICIPANTS]: ROUTES.MONEY_REQUEST_STEP_PARTICIPANTS.route, - [SCREENS.MONEY_REQUEST.STEP_SCAN]: ROUTES.MONEY_REQUEST_STEP_SCAN.route, - [SCREENS.MONEY_REQUEST.STEP_TAG]: ROUTES.MONEY_REQUEST_STEP_TAG.route, - [SCREENS.MONEY_REQUEST.STEP_WAYPOINT]: ROUTES.MONEY_REQUEST_STEP_WAYPOINT.route, - [SCREENS.MONEY_REQUEST.ROOT]: { - path: ROUTES.MONEY_REQUEST.route, - exact: true, - screens: { - [SCREENS.MONEY_REQUEST.MANUAL_TAB]: { - path: ROUTES.MONEY_REQUEST_MANUAL_TAB, - exact: true, - }, - [SCREENS.MONEY_REQUEST.SCAN_TAB]: { - path: ROUTES.MONEY_REQUEST_SCAN_TAB, - exact: true, - }, - [SCREENS.MONEY_REQUEST.DISTANCE_TAB]: { - path: ROUTES.MONEY_REQUEST_DISTANCE_TAB.route, - exact: true, - }, - }, - }, - [SCREENS.MONEY_REQUEST.AMOUNT]: ROUTES.MONEY_REQUEST_AMOUNT.route, - [SCREENS.MONEY_REQUEST.STEP_TAX_AMOUNT]: ROUTES.MONEY_REQUEST_STEP_TAX_AMOUNT.route, - [SCREENS.MONEY_REQUEST.STEP_TAX_RATE]: ROUTES.MONEY_REQUEST_STEP_TAX_RATE.route, - [SCREENS.MONEY_REQUEST.PARTICIPANTS]: ROUTES.MONEY_REQUEST_PARTICIPANTS.route, - [SCREENS.MONEY_REQUEST.DATE]: ROUTES.MONEY_REQUEST_DATE.route, - [SCREENS.MONEY_REQUEST.CURRENCY]: ROUTES.MONEY_REQUEST_CURRENCY.route, - [SCREENS.MONEY_REQUEST.DESCRIPTION]: ROUTES.MONEY_REQUEST_DESCRIPTION.route, - [SCREENS.MONEY_REQUEST.CATEGORY]: ROUTES.MONEY_REQUEST_CATEGORY.route, - [SCREENS.MONEY_REQUEST.TAG]: ROUTES.MONEY_REQUEST_TAG.route, - [SCREENS.MONEY_REQUEST.MERCHANT]: ROUTES.MONEY_REQUEST_MERCHANT.route, - [SCREENS.MONEY_REQUEST.RECEIPT]: ROUTES.MONEY_REQUEST_RECEIPT.route, - [SCREENS.MONEY_REQUEST.DISTANCE]: ROUTES.MONEY_REQUEST_DISTANCE.route, - [SCREENS.IOU_SEND.ENABLE_PAYMENTS]: ROUTES.IOU_SEND_ENABLE_PAYMENTS, - [SCREENS.IOU_SEND.ADD_BANK_ACCOUNT]: ROUTES.IOU_SEND_ADD_BANK_ACCOUNT, - [SCREENS.IOU_SEND.ADD_DEBIT_CARD]: ROUTES.IOU_SEND_ADD_DEBIT_CARD, - }, - }, - [SCREENS.RIGHT_MODAL.SPLIT_DETAILS]: { - screens: { - [SCREENS.SPLIT_DETAILS.ROOT]: ROUTES.SPLIT_BILL_DETAILS.route, - [SCREENS.SPLIT_DETAILS.EDIT_REQUEST]: ROUTES.EDIT_SPLIT_BILL.route, - [SCREENS.SPLIT_DETAILS.EDIT_CURRENCY]: ROUTES.EDIT_SPLIT_BILL_CURRENCY.route, - }, - }, - [SCREENS.RIGHT_MODAL.TASK_DETAILS]: { - screens: { - [SCREENS.TASK.TITLE]: ROUTES.TASK_TITLE.route, - [SCREENS.TASK.DESCRIPTION]: ROUTES.TASK_DESCRIPTION.route, - [SCREENS.TASK.ASSIGNEE]: ROUTES.TASK_ASSIGNEE.route, - }, - }, - [SCREENS.RIGHT_MODAL.ADD_PERSONAL_BANK_ACCOUNT]: { - screens: { - [SCREENS.ADD_PERSONAL_BANK_ACCOUNT_ROOT]: ROUTES.BANK_ACCOUNT_PERSONAL, - }, - }, - [SCREENS.RIGHT_MODAL.ENABLE_PAYMENTS]: { - screens: { - [SCREENS.ENABLE_PAYMENTS_ROOT]: ROUTES.ENABLE_PAYMENTS, - }, - }, - [SCREENS.RIGHT_MODAL.WALLET_STATEMENT]: { - screens: { - [SCREENS.WALLET_STATEMENT_ROOT]: ROUTES.WALLET_STATEMENT_WITH_DATE, - }, - }, - [SCREENS.RIGHT_MODAL.FLAG_COMMENT]: { - screens: { - [SCREENS.FLAG_COMMENT_ROOT]: ROUTES.FLAG_COMMENT.route, - }, - }, - [SCREENS.RIGHT_MODAL.EDIT_REQUEST]: { - screens: { - [SCREENS.EDIT_REQUEST.ROOT]: ROUTES.EDIT_REQUEST.route, - [SCREENS.EDIT_REQUEST.CURRENCY]: ROUTES.EDIT_CURRENCY_REQUEST.route, - [SCREENS.EDIT_REQUEST.REPORT_FIELD]: ROUTES.EDIT_REPORT_FIELD_REQUEST.route, - }, - }, - [SCREENS.RIGHT_MODAL.SIGN_IN]: { - screens: { - [SCREENS.SIGN_IN_ROOT]: ROUTES.SIGN_IN_MODAL, - }, - }, - [SCREENS.RIGHT_MODAL.REFERRAL]: { - screens: { - [SCREENS.REFERRAL_DETAILS]: ROUTES.REFERRAL_DETAILS_MODAL.route, - }, - }, - ProcessMoneyRequestHold: { - screens: { - ProcessMoneyRequestHold_Root: ROUTES.PROCESS_MONEY_REQUEST_HOLD, - }, - }, - }, - }, - }, - }, -}; - -export default linkingConfig; From 2eaad6f31c061b53af666a2f7f4951063181966a Mon Sep 17 00:00:00 2001 From: Abdullah Alsigar Date: Mon, 18 Mar 2024 17:27:08 +0300 Subject: [PATCH 251/500] useFocusEffect --- .../step/IOURequestStepScan/index.native.js | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/src/pages/iou/request/step/IOURequestStepScan/index.native.js b/src/pages/iou/request/step/IOURequestStepScan/index.native.js index 22b54b1247ef..b52a71e30de6 100644 --- a/src/pages/iou/request/step/IOURequestStepScan/index.native.js +++ b/src/pages/iou/request/step/IOURequestStepScan/index.native.js @@ -1,4 +1,4 @@ -import {useNavigation} from '@react-navigation/native'; +import {useFocusEffect} from '@react-navigation/native'; import lodashGet from 'lodash/get'; import React, {useCallback, useEffect, useRef, useState} from 'react'; import {ActivityIndicator, Alert, AppState, View} from 'react-native'; @@ -62,7 +62,6 @@ function IOURequestStepScan({ const styles = useThemeStyles(); const devices = useCameraDevices('wide-angle-camera'); const device = devices.back; - const navigation = useNavigation(); const camera = useRef(null); const [flash, setFlash] = useState(false); @@ -70,15 +69,11 @@ function IOURequestStepScan({ const askedForPermission = useRef(false); const [didCapturePhoto, setDidCapturePhoto] = useState(false); - useEffect(() => { - const unsubscribe = navigation.addListener('focus', () => { + useFocusEffect( + useCallback(() => { setDidCapturePhoto(false); - }); - - return () => { - unsubscribe(); - }; - }, [navigation]); + }, []), + ); const {translate} = useLocalize(); From 5d56fb096ca6cf1d7c346b5581fcd5939c05ad18 Mon Sep 17 00:00:00 2001 From: Jakub Szymczak Date: Mon, 18 Mar 2024 17:36:07 +0100 Subject: [PATCH 252/500] migrate jest.config --- workflow_tests/jest.config.js | 9 --------- workflow_tests/jest.config.ts | 13 +++++++++++++ 2 files changed, 13 insertions(+), 9 deletions(-) delete mode 100644 workflow_tests/jest.config.js create mode 100644 workflow_tests/jest.config.ts diff --git a/workflow_tests/jest.config.js b/workflow_tests/jest.config.js deleted file mode 100644 index cecdf8589d7f..000000000000 --- a/workflow_tests/jest.config.js +++ /dev/null @@ -1,9 +0,0 @@ -module.exports = { - verbose: true, - transform: { - '^.+\\.jsx?$': 'babel-jest', - '^.+\\.tsx?$': 'ts-jest', - }, - clearMocks: true, - resetMocks: true, -}; diff --git a/workflow_tests/jest.config.ts b/workflow_tests/jest.config.ts new file mode 100644 index 000000000000..da9dd2e15794 --- /dev/null +++ b/workflow_tests/jest.config.ts @@ -0,0 +1,13 @@ +/* eslint-disable @typescript-eslint/naming-convention */ +import type {Config} from 'jest'; + +const config: Config = { + verbose: true, + transform: { + '^.+\\.jsx?$': 'babel-jest', + }, + clearMocks: true, + resetMocks: true, +}; + +export default config; From 94702fbd9564b31afd94e78d37e54d374edd9cf2 Mon Sep 17 00:00:00 2001 From: Jakub Szymczak Date: Mon, 18 Mar 2024 17:36:27 +0100 Subject: [PATCH 253/500] migrate testBuild --- .../{testBuild.test.js => testBuild.test.ts} | 58 ++++++++++--------- 1 file changed, 31 insertions(+), 27 deletions(-) rename workflow_tests/{testBuild.test.js => testBuild.test.ts} (97%) diff --git a/workflow_tests/testBuild.test.js b/workflow_tests/testBuild.test.ts similarity index 97% rename from workflow_tests/testBuild.test.js rename to workflow_tests/testBuild.test.ts index 371759607ed5..2c676a63adfe 100644 --- a/workflow_tests/testBuild.test.js +++ b/workflow_tests/testBuild.test.ts @@ -1,12 +1,16 @@ -const path = require('path'); -const kieMockGithub = require('@kie/mock-github'); -const utils = require('./utils/utils'); -const assertions = require('./assertions/testBuildAssertions'); -const mocks = require('./mocks/testBuildMocks'); -const eAct = require('./utils/ExtendedAct'); +/* eslint-disable @typescript-eslint/naming-convention */ + +/* eslint-disable @typescript-eslint/require-await */ +import type {MockGithub} from '@kie/mock-github'; +import kieMockGithub from '@kie/mock-github'; +import path from 'path'; +import assertions from './assertions/testBuildAssertions'; +import mocks from './mocks/testBuildMocks'; +import * as eAct from './utils/ExtendedAct'; +import * as utils from './utils/utils'; jest.setTimeout(90 * 1000); -let mockGithub; +let mockGithub: MockGithub; const FILES_TO_COPY_INTO_TEST_REPO = [ ...utils.deepCopy(utils.FILES_TO_COPY_INTO_TEST_REPO), { @@ -60,7 +64,7 @@ describe('test workflow testBuild', () => { PULL_REQUEST_NUMBER: '1234', }; it('executes workflow', async () => { - const repoPath = mockGithub.repo.getPath('testTestBuildWorkflowRepo') || ''; + const repoPath = mockGithub.repo.getPath('testTestBuildWorkflowRepo') ?? ''; const workflowPath = path.join(repoPath, '.github', 'workflows', 'testBuild.yml'); let act = new eAct.ExtendedAct(repoPath, workflowPath); act = utils.setUpActParams(act, event, {}, secrets, githubToken, {}, inputs); @@ -91,7 +95,7 @@ describe('test workflow testBuild', () => { }); describe('actor is not a team member', () => { it('stops the workflow after validation', async () => { - const repoPath = mockGithub.repo.getPath('testTestBuildWorkflowRepo') || ''; + const repoPath = mockGithub.repo.getPath('testTestBuildWorkflowRepo') ?? ''; const workflowPath = path.join(repoPath, '.github', 'workflows', 'testBuild.yml'); let act = new eAct.ExtendedAct(repoPath, workflowPath); act = utils.setUpActParams(act, event, {}, secrets, githubToken, {}, inputs); @@ -123,7 +127,7 @@ describe('test workflow testBuild', () => { }); describe('PR does not have READY_TO_BUILD label', () => { it('stops the workflow after validation', async () => { - const repoPath = mockGithub.repo.getPath('testTestBuildWorkflowRepo') || ''; + const repoPath = mockGithub.repo.getPath('testTestBuildWorkflowRepo') ?? ''; const workflowPath = path.join(repoPath, '.github', 'workflows', 'testBuild.yml'); let act = new eAct.ExtendedAct(repoPath, workflowPath); act = utils.setUpActParams(act, event, {}, secrets, githubToken, {}, inputs); @@ -155,7 +159,7 @@ describe('test workflow testBuild', () => { }); describe('actor is not a team member and PR does not have READY_TO_BUILD label', () => { it('stops the workflow after validation', async () => { - const repoPath = mockGithub.repo.getPath('testTestBuildWorkflowRepo') || ''; + const repoPath = mockGithub.repo.getPath('testTestBuildWorkflowRepo') ?? ''; const workflowPath = path.join(repoPath, '.github', 'workflows', 'testBuild.yml'); let act = new eAct.ExtendedAct(repoPath, workflowPath); act = utils.setUpActParams(act, event, {}, secrets, githubToken, {}, inputs); @@ -187,7 +191,7 @@ describe('test workflow testBuild', () => { }); describe('android fails', () => { it('executes workflow, failure reflected', async () => { - const repoPath = mockGithub.repo.getPath('testTestBuildWorkflowRepo') || ''; + const repoPath = mockGithub.repo.getPath('testTestBuildWorkflowRepo') ?? ''; const workflowPath = path.join(repoPath, '.github', 'workflows', 'testBuild.yml'); let act = new eAct.ExtendedAct(repoPath, workflowPath); act = utils.setUpActParams(act, event, {}, secrets, githubToken, {}, inputs); @@ -220,7 +224,7 @@ describe('test workflow testBuild', () => { }); describe('iOS fails', () => { it('executes workflow, failure reflected', async () => { - const repoPath = mockGithub.repo.getPath('testTestBuildWorkflowRepo') || ''; + const repoPath = mockGithub.repo.getPath('testTestBuildWorkflowRepo') ?? ''; const workflowPath = path.join(repoPath, '.github', 'workflows', 'testBuild.yml'); let act = new eAct.ExtendedAct(repoPath, workflowPath); act = utils.setUpActParams(act, event, {}, secrets, githubToken, {}, inputs); @@ -253,7 +257,7 @@ describe('test workflow testBuild', () => { }); describe('desktop fails', () => { it('executes workflow, failure reflected', async () => { - const repoPath = mockGithub.repo.getPath('testTestBuildWorkflowRepo') || ''; + const repoPath = mockGithub.repo.getPath('testTestBuildWorkflowRepo') ?? ''; const workflowPath = path.join(repoPath, '.github', 'workflows', 'testBuild.yml'); let act = new eAct.ExtendedAct(repoPath, workflowPath); act = utils.setUpActParams(act, event, {}, secrets, githubToken, {}, inputs); @@ -295,7 +299,7 @@ describe('test workflow testBuild', () => { }); describe('web fails', () => { it('executes workflow, failure reflected', async () => { - const repoPath = mockGithub.repo.getPath('testTestBuildWorkflowRepo') || ''; + const repoPath = mockGithub.repo.getPath('testTestBuildWorkflowRepo') ?? ''; const workflowPath = path.join(repoPath, '.github', 'workflows', 'testBuild.yml'); let act = new eAct.ExtendedAct(repoPath, workflowPath); act = utils.setUpActParams(act, event, {}, secrets, githubToken, {}, inputs); @@ -348,7 +352,7 @@ describe('test workflow testBuild', () => { }, }; it('executes workflow, without getBranchRef', async () => { - const repoPath = mockGithub.repo.getPath('testTestBuildWorkflowRepo') || ''; + const repoPath = mockGithub.repo.getPath('testTestBuildWorkflowRepo') ?? ''; const workflowPath = path.join(repoPath, '.github', 'workflows', 'testBuild.yml'); let act = new eAct.ExtendedAct(repoPath, workflowPath); act = utils.setUpActParams(act, event, eventOptions, secrets, githubToken, {}); @@ -379,7 +383,7 @@ describe('test workflow testBuild', () => { }); describe('actor is not a team member', () => { it('stops the workflow after validation', async () => { - const repoPath = mockGithub.repo.getPath('testTestBuildWorkflowRepo') || ''; + const repoPath = mockGithub.repo.getPath('testTestBuildWorkflowRepo') ?? ''; const workflowPath = path.join(repoPath, '.github', 'workflows', 'testBuild.yml'); let act = new eAct.ExtendedAct(repoPath, workflowPath); act = utils.setUpActParams(act, event, eventOptions, secrets, githubToken, {}); @@ -411,7 +415,7 @@ describe('test workflow testBuild', () => { }); describe('PR does not have READY_TO_BUILD label', () => { it('stops the workflow after validation', async () => { - const repoPath = mockGithub.repo.getPath('testTestBuildWorkflowRepo') || ''; + const repoPath = mockGithub.repo.getPath('testTestBuildWorkflowRepo') ?? ''; const workflowPath = path.join(repoPath, '.github', 'workflows', 'testBuild.yml'); let act = new eAct.ExtendedAct(repoPath, workflowPath); act = utils.setUpActParams(act, event, eventOptions, secrets, githubToken, {}); @@ -443,7 +447,7 @@ describe('test workflow testBuild', () => { }); describe('actor is not a team member and PR does not have READY_TO_BUILD label', () => { it('stops the workflow after validation', async () => { - const repoPath = mockGithub.repo.getPath('testTestBuildWorkflowRepo') || ''; + const repoPath = mockGithub.repo.getPath('testTestBuildWorkflowRepo') ?? ''; const workflowPath = path.join(repoPath, '.github', 'workflows', 'testBuild.yml'); let act = new eAct.ExtendedAct(repoPath, workflowPath); act = utils.setUpActParams(act, event, eventOptions, secrets, githubToken, {}); @@ -486,7 +490,7 @@ describe('test workflow testBuild', () => { }, }; it('executes workflow, without getBranchRef', async () => { - const repoPath = mockGithub.repo.getPath('testTestBuildWorkflowRepo') || ''; + const repoPath = mockGithub.repo.getPath('testTestBuildWorkflowRepo') ?? ''; const workflowPath = path.join(repoPath, '.github', 'workflows', 'testBuild.yml'); let act = new eAct.ExtendedAct(repoPath, workflowPath); act = utils.setUpActParams(act, event, eventOptions, secrets, githubToken, {}); @@ -517,7 +521,7 @@ describe('test workflow testBuild', () => { }); describe('actor is not a team member', () => { it('stops the workflow after validation', async () => { - const repoPath = mockGithub.repo.getPath('testTestBuildWorkflowRepo') || ''; + const repoPath = mockGithub.repo.getPath('testTestBuildWorkflowRepo') ?? ''; const workflowPath = path.join(repoPath, '.github', 'workflows', 'testBuild.yml'); let act = new eAct.ExtendedAct(repoPath, workflowPath); act = utils.setUpActParams(act, event, eventOptions, secrets, githubToken, {}); @@ -549,7 +553,7 @@ describe('test workflow testBuild', () => { }); describe('PR does not have READY_TO_BUILD label', () => { it('stops the workflow after validation', async () => { - const repoPath = mockGithub.repo.getPath('testTestBuildWorkflowRepo') || ''; + const repoPath = mockGithub.repo.getPath('testTestBuildWorkflowRepo') ?? ''; const workflowPath = path.join(repoPath, '.github', 'workflows', 'testBuild.yml'); let act = new eAct.ExtendedAct(repoPath, workflowPath); act = utils.setUpActParams(act, event, eventOptions, secrets, githubToken, {}); @@ -581,7 +585,7 @@ describe('test workflow testBuild', () => { }); describe('actor is not a team member and PR does not have READY_TO_BUILD label', () => { it('stops the workflow after validation', async () => { - const repoPath = mockGithub.repo.getPath('testTestBuildWorkflowRepo') || ''; + const repoPath = mockGithub.repo.getPath('testTestBuildWorkflowRepo') ?? ''; const workflowPath = path.join(repoPath, '.github', 'workflows', 'testBuild.yml'); let act = new eAct.ExtendedAct(repoPath, workflowPath); act = utils.setUpActParams(act, event, eventOptions, secrets, githubToken, {}); @@ -624,7 +628,7 @@ describe('test workflow testBuild', () => { }, }; it('executes workflow, withuout getBranchRef', async () => { - const repoPath = mockGithub.repo.getPath('testTestBuildWorkflowRepo') || ''; + const repoPath = mockGithub.repo.getPath('testTestBuildWorkflowRepo') ?? ''; const workflowPath = path.join(repoPath, '.github', 'workflows', 'testBuild.yml'); let act = new eAct.ExtendedAct(repoPath, workflowPath); act = utils.setUpActParams(act, event, eventOptions, secrets, githubToken, {}); @@ -655,7 +659,7 @@ describe('test workflow testBuild', () => { }); describe('actor is not a team member', () => { it('stops the workflow after validation', async () => { - const repoPath = mockGithub.repo.getPath('testTestBuildWorkflowRepo') || ''; + const repoPath = mockGithub.repo.getPath('testTestBuildWorkflowRepo') ?? ''; const workflowPath = path.join(repoPath, '.github', 'workflows', 'testBuild.yml'); let act = new eAct.ExtendedAct(repoPath, workflowPath); act = utils.setUpActParams(act, event, eventOptions, secrets, githubToken, {}); @@ -687,7 +691,7 @@ describe('test workflow testBuild', () => { }); describe('PR does not have READY_TO_BUILD label', () => { it('stops the workflow after validation', async () => { - const repoPath = mockGithub.repo.getPath('testTestBuildWorkflowRepo') || ''; + const repoPath = mockGithub.repo.getPath('testTestBuildWorkflowRepo') ?? ''; const workflowPath = path.join(repoPath, '.github', 'workflows', 'testBuild.yml'); let act = new eAct.ExtendedAct(repoPath, workflowPath); act = utils.setUpActParams(act, event, eventOptions, secrets, githubToken, {}); @@ -719,7 +723,7 @@ describe('test workflow testBuild', () => { }); describe('actor is not a team member and PR does not have READY_TO_BUILD label', () => { it('stops the workflow after validation', async () => { - const repoPath = mockGithub.repo.getPath('testTestBuildWorkflowRepo') || ''; + const repoPath = mockGithub.repo.getPath('testTestBuildWorkflowRepo') ?? ''; const workflowPath = path.join(repoPath, '.github', 'workflows', 'testBuild.yml'); let act = new eAct.ExtendedAct(repoPath, workflowPath); act = utils.setUpActParams(act, event, eventOptions, secrets, githubToken, {}); From d6aefab946dd40cd7576b7d23be9c939bf477792 Mon Sep 17 00:00:00 2001 From: Jakub Szymczak Date: Mon, 18 Mar 2024 17:36:45 +0100 Subject: [PATCH 254/500] migrate validateGithubActions --- ....test.js => validateGithubActions.test.ts} | 20 ++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) rename workflow_tests/{validateGithubActions.test.js => validateGithubActions.test.ts} (87%) diff --git a/workflow_tests/validateGithubActions.test.js b/workflow_tests/validateGithubActions.test.ts similarity index 87% rename from workflow_tests/validateGithubActions.test.js rename to workflow_tests/validateGithubActions.test.ts index dfa5e9362ce7..850f21a54163 100644 --- a/workflow_tests/validateGithubActions.test.js +++ b/workflow_tests/validateGithubActions.test.ts @@ -1,12 +1,14 @@ -const path = require('path'); -const kieMockGithub = require('@kie/mock-github'); -const utils = require('./utils/utils'); -const assertions = require('./assertions/validateGithubActionsAssertions'); -const mocks = require('./mocks/validateGithubActionsMocks'); -const eAct = require('./utils/ExtendedAct'); +/* eslint-disable @typescript-eslint/require-await */ +import type {MockGithub} from '@kie/mock-github'; +import kieMockGithub from '@kie/mock-github'; +import path from 'path'; +import assertions from './assertions/validateGithubActionsAssertions'; +import mocks from './mocks/validateGithubActionsMocks'; +import * as eAct from './utils/ExtendedAct'; +import * as utils from './utils/utils'; jest.setTimeout(90 * 1000); -let mockGithub; +let mockGithub: MockGithub; const FILES_TO_COPY_INTO_TEST_REPO = [ ...utils.deepCopy(utils.FILES_TO_COPY_INTO_TEST_REPO), { @@ -47,7 +49,7 @@ describe('test workflow validateGithubActions', () => { action: 'opened', }; it('executes verification', async () => { - const repoPath = mockGithub.repo.getPath('testValidateGithubActionsWorkflowRepo') || ''; + const repoPath = mockGithub.repo.getPath('testValidateGithubActionsWorkflowRepo') ?? ''; const workflowPath = path.join(repoPath, '.github', 'workflows', 'validateGithubActions.yml'); let act = new eAct.ExtendedAct(repoPath, workflowPath); act = utils.setUpActParams(act, event, eventOptions, {}, githubToken); @@ -70,7 +72,7 @@ describe('test workflow validateGithubActions', () => { action: 'synchronize', }; it('executes verification', async () => { - const repoPath = mockGithub.repo.getPath('testValidateGithubActionsWorkflowRepo') || ''; + const repoPath = mockGithub.repo.getPath('testValidateGithubActionsWorkflowRepo') ?? ''; const workflowPath = path.join(repoPath, '.github', 'workflows', 'validateGithubActions.yml'); let act = new eAct.ExtendedAct(repoPath, workflowPath); act = utils.setUpActParams(act, event, eventOptions, {}, githubToken); From 9c18645db6ba2b9602d459bdf21cd44880cbabde Mon Sep 17 00:00:00 2001 From: Jakub Szymczak Date: Mon, 18 Mar 2024 17:37:05 +0100 Subject: [PATCH 255/500] migrate verifyPodfile --- ...yPodfile.test.js => verifyPodfile.test.ts} | 25 +++++++++++-------- 1 file changed, 14 insertions(+), 11 deletions(-) rename workflow_tests/{verifyPodfile.test.js => verifyPodfile.test.ts} (91%) diff --git a/workflow_tests/verifyPodfile.test.js b/workflow_tests/verifyPodfile.test.ts similarity index 91% rename from workflow_tests/verifyPodfile.test.js rename to workflow_tests/verifyPodfile.test.ts index de062af2a2c2..b5ccfc46c336 100644 --- a/workflow_tests/verifyPodfile.test.js +++ b/workflow_tests/verifyPodfile.test.ts @@ -1,12 +1,15 @@ -const path = require('path'); -const kieMockGithub = require('@kie/mock-github'); -const utils = require('./utils/utils'); -const assertions = require('./assertions/verifyPodfileAssertions'); -const mocks = require('./mocks/verifyPodfileMocks'); -const eAct = require('./utils/ExtendedAct'); +/* eslint-disable @typescript-eslint/require-await */ +import kieMockGithub from '@kie/mock-github'; +import type {MockGithub} from '@kie/mock-github'; +import path from 'path'; +import assertions from './assertions/verifyPodfileAssertions'; +import mocks from './mocks/verifyPodfileMocks'; +import * as eAct from './utils/ExtendedAct'; +import * as utils from './utils/utils'; jest.setTimeout(90 * 1000); -let mockGithub; +let mockGithub: MockGithub; + const FILES_TO_COPY_INTO_TEST_REPO = [ ...utils.deepCopy(utils.FILES_TO_COPY_INTO_TEST_REPO), { @@ -48,7 +51,7 @@ describe('test workflow verifyPodfile', () => { action: 'opened', }; it('executes workflow', async () => { - const repoPath = mockGithub.repo.getPath('testVerifyPodfileWorkflowRepo') || ''; + const repoPath = mockGithub.repo.getPath('testVerifyPodfileWorkflowRepo') ?? ''; const workflowPath = path.join(repoPath, '.github', 'workflows', 'verifyPodfile.yml'); let act = new eAct.ExtendedAct(repoPath, workflowPath); act = utils.setUpActParams(act, event, eventOptions, {}, githubToken); @@ -67,7 +70,7 @@ describe('test workflow verifyPodfile', () => { }); describe('actor is OSBotify', () => { it('does not execute workflow', async () => { - const repoPath = mockGithub.repo.getPath('testVerifyPodfileWorkflowRepo') || ''; + const repoPath = mockGithub.repo.getPath('testVerifyPodfileWorkflowRepo') ?? ''; const workflowPath = path.join(repoPath, '.github', 'workflows', 'verifyPodfile.yml'); let act = new eAct.ExtendedAct(repoPath, workflowPath); act = utils.setUpActParams(act, event, eventOptions, {}, githubToken); @@ -92,7 +95,7 @@ describe('test workflow verifyPodfile', () => { action: 'synchronize', }; it('executes workflow', async () => { - const repoPath = mockGithub.repo.getPath('testVerifyPodfileWorkflowRepo') || ''; + const repoPath = mockGithub.repo.getPath('testVerifyPodfileWorkflowRepo') ?? ''; const workflowPath = path.join(repoPath, '.github', 'workflows', 'verifyPodfile.yml'); let act = new eAct.ExtendedAct(repoPath, workflowPath); act = utils.setUpActParams(act, event, eventOptions, {}, githubToken); @@ -111,7 +114,7 @@ describe('test workflow verifyPodfile', () => { }); describe('actor is OSBotify', () => { it('does not execute workflow', async () => { - const repoPath = mockGithub.repo.getPath('testVerifyPodfileWorkflowRepo') || ''; + const repoPath = mockGithub.repo.getPath('testVerifyPodfileWorkflowRepo') ?? ''; const workflowPath = path.join(repoPath, '.github', 'workflows', 'verifyPodfile.yml'); let act = new eAct.ExtendedAct(repoPath, workflowPath); act = utils.setUpActParams(act, event, eventOptions, {}, githubToken); From fe4547d7ea539a7460539a9fc869375281ebcb08 Mon Sep 17 00:00:00 2001 From: Jakub Szymczak Date: Mon, 18 Mar 2024 17:37:31 +0100 Subject: [PATCH 256/500] migrate verifySignedCommits --- workflow_tests/scripts/runWorkflowTests.sh | 2 +- ...ts.test.js => verifySignedCommits.test.ts} | 21 +++++++++++-------- 2 files changed, 13 insertions(+), 10 deletions(-) rename workflow_tests/{verifySignedCommits.test.js => verifySignedCommits.test.ts} (87%) diff --git a/workflow_tests/scripts/runWorkflowTests.sh b/workflow_tests/scripts/runWorkflowTests.sh index 71ddcdceffb5..c8ee88e33e99 100755 --- a/workflow_tests/scripts/runWorkflowTests.sh +++ b/workflow_tests/scripts/runWorkflowTests.sh @@ -60,4 +60,4 @@ info 'ACT_BINARY environment variable set to an Act executable' success 'Environment setup properly - running tests' # Run tests -npm test -- --config=workflow_tests/jest.config.js --runInBand "$@" +npm test -- --config=workflow_tests/jest.config.ts --runInBand "$@" diff --git a/workflow_tests/verifySignedCommits.test.js b/workflow_tests/verifySignedCommits.test.ts similarity index 87% rename from workflow_tests/verifySignedCommits.test.js rename to workflow_tests/verifySignedCommits.test.ts index 911208e91f4a..9319829118e6 100644 --- a/workflow_tests/verifySignedCommits.test.js +++ b/workflow_tests/verifySignedCommits.test.ts @@ -1,12 +1,15 @@ -const path = require('path'); -const kieMockGithub = require('@kie/mock-github'); -const utils = require('./utils/utils'); -const assertions = require('./assertions/verifySignedCommitsAssertions'); -const mocks = require('./mocks/verifySignedCommitsMocks'); -const eAct = require('./utils/ExtendedAct'); +/* eslint-disable @typescript-eslint/require-await */ +import type {MockGithub} from '@kie/mock-github'; +import kieMockGithub from '@kie/mock-github'; +import path from 'path'; +import assertions from './assertions/verifySignedCommitsAssertions'; +import mocks from './mocks/verifySignedCommitsMocks'; +import * as eAct from './utils/ExtendedAct'; +import * as utils from './utils/utils'; jest.setTimeout(90 * 1000); -let mockGithub; +let mockGithub: MockGithub; + const FILES_TO_COPY_INTO_TEST_REPO = [ ...utils.deepCopy(utils.FILES_TO_COPY_INTO_TEST_REPO), { @@ -47,7 +50,7 @@ describe('test workflow verifySignedCommits', () => { action: 'opened', }; it('test stub', async () => { - const repoPath = mockGithub.repo.getPath('testVerifySignedCommitsWorkflowRepo') || ''; + const repoPath = mockGithub.repo.getPath('testVerifySignedCommitsWorkflowRepo') ?? ''; const workflowPath = path.join(repoPath, '.github', 'workflows', 'verifySignedCommits.yml'); let act = new eAct.ExtendedAct(repoPath, workflowPath); act = utils.setUpActParams(act, event, eventOptions, {}, githubToken); @@ -70,7 +73,7 @@ describe('test workflow verifySignedCommits', () => { action: 'synchronize', }; it('test stub', async () => { - const repoPath = mockGithub.repo.getPath('testVerifySignedCommitsWorkflowRepo') || ''; + const repoPath = mockGithub.repo.getPath('testVerifySignedCommitsWorkflowRepo') ?? ''; const workflowPath = path.join(repoPath, '.github', 'workflows', 'verifySignedCommits.yml'); let act = new eAct.ExtendedAct(repoPath, workflowPath); act = utils.setUpActParams(act, event, eventOptions, {}, githubToken); From 6876ae7384f4917a4657553606460fad4ae75e14 Mon Sep 17 00:00:00 2001 From: Tomasz Misiukiewicz Date: Tue, 19 Mar 2024 16:21:56 +0100 Subject: [PATCH 257/500] update reassure option list utils tests --- src/libs/OptionsListUtils.ts | 5 ++++- tests/perf-test/OptionsListUtils.perf-test.ts | 12 +++++++----- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/src/libs/OptionsListUtils.ts b/src/libs/OptionsListUtils.ts index 5ee58ab861bd..454ac92c201c 100644 --- a/src/libs/OptionsListUtils.ts +++ b/src/libs/OptionsListUtils.ts @@ -550,7 +550,10 @@ function getAlternateTextOption( if (option.isTaskReport) { return showChatPreviewLine && option.lastMessageText ? option.lastMessageText : Localize.translate(preferredLocale, 'report.noActivityYet'); } - return showChatPreviewLine && option.lastMessageText ? option.lastMessageText : LocalePhoneNumber.formatPhoneNumber((option.participantsList ?? [])[0].login ?? ''); + + return showChatPreviewLine && option.lastMessageText + ? option.lastMessageText + : LocalePhoneNumber.formatPhoneNumber(option.participantsList && option.participantsList.length > 0 ? option.participantsList[0].login ?? '' : ''); } /** diff --git a/tests/perf-test/OptionsListUtils.perf-test.ts b/tests/perf-test/OptionsListUtils.perf-test.ts index d81be3165919..5041e919e7c1 100644 --- a/tests/perf-test/OptionsListUtils.perf-test.ts +++ b/tests/perf-test/OptionsListUtils.perf-test.ts @@ -44,35 +44,37 @@ jest.mock('@react-navigation/native', () => { } as typeof NativeNavigation; }); +const options = OptionsListUtils.createOptionList(personalDetails, reports); + /* GetOption is the private function and is never called directly, we are testing the functions which call getOption with different params */ describe('OptionsListUtils', () => { /* Testing getSearchOptions */ test('[OptionsListUtils] getSearchOptions with search value', async () => { await waitForBatchedUpdates(); - await measureFunction(() => OptionsListUtils.getSearchOptions(reports, personalDetails, SEARCH_VALUE, mockedBetas)); + await measureFunction(() => OptionsListUtils.getSearchOptions(options, SEARCH_VALUE, mockedBetas)); }); /* Testing getShareLogOptions */ test('[OptionsListUtils] getShareLogOptions with search value', async () => { await waitForBatchedUpdates(); - await measureFunction(() => OptionsListUtils.getShareLogOptions(reports, personalDetails, SEARCH_VALUE, mockedBetas)); + await measureFunction(() => OptionsListUtils.getShareLogOptions(options, SEARCH_VALUE, mockedBetas)); }); /* Testing getFilteredOptions */ test('[OptionsListUtils] getFilteredOptions with search value', async () => { await waitForBatchedUpdates(); - await measureFunction(() => OptionsListUtils.getFilteredOptions(reports, personalDetails, mockedBetas, SEARCH_VALUE)); + await measureFunction(() => OptionsListUtils.getFilteredOptions(options.reports, options.personalDetails, mockedBetas, SEARCH_VALUE)); }); /* Testing getShareDestinationOptions */ test('[OptionsListUtils] getShareDestinationOptions with search value', async () => { await waitForBatchedUpdates(); - await measureFunction(() => OptionsListUtils.getShareDestinationOptions(reports, personalDetails, mockedBetas, SEARCH_VALUE)); + await measureFunction(() => OptionsListUtils.getShareDestinationOptions(options.reports, options.personalDetails, mockedBetas, SEARCH_VALUE)); }); /* Testing getMemberInviteOptions */ test('[OptionsListUtils] getMemberInviteOptions with search value', async () => { await waitForBatchedUpdates(); - await measureFunction(() => OptionsListUtils.getMemberInviteOptions(personalDetails, mockedBetas, SEARCH_VALUE)); + await measureFunction(() => OptionsListUtils.getMemberInviteOptions(options.personalDetails, mockedBetas, SEARCH_VALUE)); }); }); From c6f9af1d8e456f73e80cd1a2d109f8710820f361 Mon Sep 17 00:00:00 2001 From: Jakub Szymczak Date: Tue, 19 Mar 2024 17:05:47 +0100 Subject: [PATCH 258/500] fix babel not formatting test files --- workflow_tests/jest.config.ts | 2 +- workflow_tests/testBuild.test.ts | 5 ++--- workflow_tests/validateGithubActions.test.ts | 11 +++++------ workflow_tests/verifyPodfile.test.ts | 5 ++--- workflow_tests/verifySignedCommits.test.ts | 5 ++--- 5 files changed, 12 insertions(+), 16 deletions(-) diff --git a/workflow_tests/jest.config.ts b/workflow_tests/jest.config.ts index da9dd2e15794..8156e449039f 100644 --- a/workflow_tests/jest.config.ts +++ b/workflow_tests/jest.config.ts @@ -4,7 +4,7 @@ import type {Config} from 'jest'; const config: Config = { verbose: true, transform: { - '^.+\\.jsx?$': 'babel-jest', + '^.+\\.(js|jsx|ts|tsx)$': 'ts-jest', }, clearMocks: true, resetMocks: true, diff --git a/workflow_tests/testBuild.test.ts b/workflow_tests/testBuild.test.ts index 2c676a63adfe..5f09b851a5b5 100644 --- a/workflow_tests/testBuild.test.ts +++ b/workflow_tests/testBuild.test.ts @@ -1,8 +1,7 @@ /* eslint-disable @typescript-eslint/naming-convention */ /* eslint-disable @typescript-eslint/require-await */ -import type {MockGithub} from '@kie/mock-github'; -import kieMockGithub from '@kie/mock-github'; +import {MockGithub} from '@kie/mock-github'; import path from 'path'; import assertions from './assertions/testBuildAssertions'; import mocks from './mocks/testBuildMocks'; @@ -44,7 +43,7 @@ describe('test workflow testBuild', () => { beforeEach(async () => { // create a local repository and copy required files - mockGithub = new kieMockGithub.MockGithub({ + mockGithub = new MockGithub({ repo: { testTestBuildWorkflowRepo: { files: FILES_TO_COPY_INTO_TEST_REPO, diff --git a/workflow_tests/validateGithubActions.test.ts b/workflow_tests/validateGithubActions.test.ts index 850f21a54163..1fcfd95834b6 100644 --- a/workflow_tests/validateGithubActions.test.ts +++ b/workflow_tests/validateGithubActions.test.ts @@ -1,10 +1,9 @@ /* eslint-disable @typescript-eslint/require-await */ -import type {MockGithub} from '@kie/mock-github'; -import kieMockGithub from '@kie/mock-github'; +import {MockGithub} from '@kie/mock-github'; import path from 'path'; import assertions from './assertions/validateGithubActionsAssertions'; import mocks from './mocks/validateGithubActionsMocks'; -import * as eAct from './utils/ExtendedAct'; +import {ExtendedAct} from './utils/ExtendedAct'; import * as utils from './utils/utils'; jest.setTimeout(90 * 1000); @@ -29,7 +28,7 @@ describe('test workflow validateGithubActions', () => { beforeEach(async () => { // create a local repository and copy required files - mockGithub = new kieMockGithub.MockGithub({ + mockGithub = new MockGithub({ repo: { testValidateGithubActionsWorkflowRepo: { files: FILES_TO_COPY_INTO_TEST_REPO, @@ -51,7 +50,7 @@ describe('test workflow validateGithubActions', () => { it('executes verification', async () => { const repoPath = mockGithub.repo.getPath('testValidateGithubActionsWorkflowRepo') ?? ''; const workflowPath = path.join(repoPath, '.github', 'workflows', 'validateGithubActions.yml'); - let act = new eAct.ExtendedAct(repoPath, workflowPath); + let act = new ExtendedAct(repoPath, workflowPath); act = utils.setUpActParams(act, event, eventOptions, {}, githubToken); const testMockSteps = { verify: mocks.VALIDATEGITHUBACTIONS__VERIFY__STEP_MOCKS, @@ -74,7 +73,7 @@ describe('test workflow validateGithubActions', () => { it('executes verification', async () => { const repoPath = mockGithub.repo.getPath('testValidateGithubActionsWorkflowRepo') ?? ''; const workflowPath = path.join(repoPath, '.github', 'workflows', 'validateGithubActions.yml'); - let act = new eAct.ExtendedAct(repoPath, workflowPath); + let act = new ExtendedAct(repoPath, workflowPath); act = utils.setUpActParams(act, event, eventOptions, {}, githubToken); const testMockSteps = { verify: mocks.VALIDATEGITHUBACTIONS__VERIFY__STEP_MOCKS, diff --git a/workflow_tests/verifyPodfile.test.ts b/workflow_tests/verifyPodfile.test.ts index b5ccfc46c336..2748eae25542 100644 --- a/workflow_tests/verifyPodfile.test.ts +++ b/workflow_tests/verifyPodfile.test.ts @@ -1,6 +1,5 @@ /* eslint-disable @typescript-eslint/require-await */ -import kieMockGithub from '@kie/mock-github'; -import type {MockGithub} from '@kie/mock-github'; +import {MockGithub} from '@kie/mock-github'; import path from 'path'; import assertions from './assertions/verifyPodfileAssertions'; import mocks from './mocks/verifyPodfileMocks'; @@ -31,7 +30,7 @@ describe('test workflow verifyPodfile', () => { beforeEach(async () => { // create a local repository and copy required files - mockGithub = new kieMockGithub.MockGithub({ + mockGithub = new MockGithub({ repo: { testVerifyPodfileWorkflowRepo: { files: FILES_TO_COPY_INTO_TEST_REPO, diff --git a/workflow_tests/verifySignedCommits.test.ts b/workflow_tests/verifySignedCommits.test.ts index 9319829118e6..c861597368c3 100644 --- a/workflow_tests/verifySignedCommits.test.ts +++ b/workflow_tests/verifySignedCommits.test.ts @@ -1,6 +1,5 @@ /* eslint-disable @typescript-eslint/require-await */ -import type {MockGithub} from '@kie/mock-github'; -import kieMockGithub from '@kie/mock-github'; +import {MockGithub} from '@kie/mock-github'; import path from 'path'; import assertions from './assertions/verifySignedCommitsAssertions'; import mocks from './mocks/verifySignedCommitsMocks'; @@ -30,7 +29,7 @@ describe('test workflow verifySignedCommits', () => { beforeEach(async () => { // create a local repository and copy required files - mockGithub = new kieMockGithub.MockGithub({ + mockGithub = new MockGithub({ repo: { testVerifySignedCommitsWorkflowRepo: { files: FILES_TO_COPY_INTO_TEST_REPO, From 245ae01c5aad8fd4881da3d55b89259b54c1d244 Mon Sep 17 00:00:00 2001 From: dukenv0307 Date: Wed, 20 Mar 2024 10:51:10 +0700 Subject: [PATCH 259/500] Fix get parentReportAction --- src/pages/home/HeaderView.tsx | 3 +- src/pages/home/ReportScreen.tsx | 78 ++++++++++++++++++--------------- 2 files changed, 45 insertions(+), 36 deletions(-) diff --git a/src/pages/home/HeaderView.tsx b/src/pages/home/HeaderView.tsx index 8e0aefc39257..6b7d8d3c4586 100644 --- a/src/pages/home/HeaderView.tsx +++ b/src/pages/home/HeaderView.tsx @@ -102,6 +102,7 @@ function HeaderView({report, personalDetails, parentReport, parentReportAction, const policyName = ReportUtils.getPolicyName(report, true); const policyDescription = ReportUtils.getPolicyDescriptionText(policy); const isPersonalExpenseChat = isPolicyExpenseChat && ReportUtils.isCurrentUserSubmitter(report.reportID); + console.log(parentReportAction); const shouldShowSubtitle = () => { if (!subtitle) { return false; @@ -334,7 +335,7 @@ function HeaderView({report, personalDetails, parentReport, parentReportAction, )} - {isTaskReport && !isSmallScreenWidth && ReportUtils.isOpenTaskReport(report) && } + {isTaskReport && !isSmallScreenWidth && ReportUtils.isOpenTaskReport(report, parentReportAction) && } {canJoin && !isSmallScreenWidth && joinButton} {shouldShowThreeDotsButton && ( ; @@ -77,10 +77,7 @@ type ReportScreenOnyxProps = { /** The report metadata loading states */ reportMetadata: OnyxEntry; - - /** The report's parentReportAction */ - parentReportAction: OnyxEntry; -}; +} type OnyxHOCProps = { /** Onyx function that marks the component ready for hydration */ @@ -89,7 +86,15 @@ type OnyxHOCProps = { type ReportScreenNavigationProps = StackScreenProps; -type ReportScreenProps = OnyxHOCProps & ViewportOffsetTopProps & CurrentReportIDContextValue & ReportScreenOnyxProps & ReportScreenNavigationProps; +type ReportScreenPropsWithoutParentReportAction = OnyxHOCProps & CurrentReportIDContextValue & ViewportOffsetTopProps & ReportScreenOnyxPropsWithoutParentReportAction & ReportScreenNavigationProps; + +type ReportScreenParentReportActionOnyxProps = { + /** The report's parentReportAction */ + parentReportAction: OnyxEntry; +} + +type ReportScreenProps = ReportScreenPropsWithoutParentReportAction & ReportScreenParentReportActionOnyxProps + /** Get the currently viewed report ID as number */ function getReportID(route: ReportScreenNavigationProps['route']): string { @@ -122,7 +127,7 @@ function ReportScreen({ isLoadingNewerReportActions: false, }, reportActions = [], - parentReportAction, + parentReportAction: parentReportActions, accountManagerReportID, markReadyForHydration, policies = {}, @@ -226,6 +231,13 @@ function ReportScreen({ ], ); + const parentReportAction = useMemo(() => { + if (!parentReportActions || !report.parentReportActionID) { + return null; + } + return parentReportActions[report.parentReportActionID ?? '0']; + }, [parentReportActions, report.parentReportActionID]) + const prevReport = usePrevious(report); const prevUserLeavingStatus = usePrevious(userLeavingStatus); const [isBannerVisible, setIsBannerVisible] = useState(true); @@ -582,7 +594,7 @@ ReportScreen.displayName = 'ReportScreen'; export default withViewportOffsetTop( withCurrentReportID( - withOnyx( + withOnyx( { isSidebarLoaded: { key: ONYXKEYS.IS_SIDEBAR_LOADED, @@ -623,37 +635,33 @@ export default withViewportOffsetTop( userLeavingStatus: { key: ({route}) => `${ONYXKEYS.COLLECTION.REPORT_USER_IS_LEAVING_ROOM}${getReportID(route)}`, initialValue: false, - }, + } + }, + true, + )( + withOnyx({ parentReportAction: { key: ({report}) => `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${report ? report.parentReportID : 0}`, - selector: (parentReportActions: OnyxEntry, props: WithOnyxInstanceState): OnyxEntry => { - const parentReportActionID = props?.report?.parentReportActionID; - if (!parentReportActionID) { - return null; - } - return parentReportActions?.[parentReportActionID] ?? null; - }, canEvict: false, }, - }, - true, - )( - memo( - ReportScreen, - (prevProps, nextProps) => - prevProps.isSidebarLoaded === nextProps.isSidebarLoaded && - lodashIsEqual(prevProps.reportActions, nextProps.reportActions) && - lodashIsEqual(prevProps.reportMetadata, nextProps.reportMetadata) && - prevProps.isComposerFullSize === nextProps.isComposerFullSize && - lodashIsEqual(prevProps.betas, nextProps.betas) && - lodashIsEqual(prevProps.policies, nextProps.policies) && - prevProps.accountManagerReportID === nextProps.accountManagerReportID && - prevProps.userLeavingStatus === nextProps.userLeavingStatus && - prevProps.currentReportID === nextProps.currentReportID && - prevProps.viewportOffsetTop === nextProps.viewportOffsetTop && - lodashIsEqual(prevProps.parentReportAction, nextProps.parentReportAction) && - lodashIsEqual(prevProps.report, nextProps.report), + })( + memo( + ReportScreen, + (prevProps, nextProps) => + prevProps.isSidebarLoaded === nextProps.isSidebarLoaded && + lodashIsEqual(prevProps.reportActions, nextProps.reportActions) && + lodashIsEqual(prevProps.reportMetadata, nextProps.reportMetadata) && + prevProps.isComposerFullSize === nextProps.isComposerFullSize && + lodashIsEqual(prevProps.betas, nextProps.betas) && + lodashIsEqual(prevProps.policies, nextProps.policies) && + prevProps.accountManagerReportID === nextProps.accountManagerReportID && + prevProps.userLeavingStatus === nextProps.userLeavingStatus && + prevProps.currentReportID === nextProps.currentReportID && + prevProps.viewportOffsetTop === nextProps.viewportOffsetTop && + lodashIsEqual(prevProps.parentReportAction, nextProps.parentReportAction) && + lodashIsEqual(prevProps.report, nextProps.report), + ), ), - ), + ) ), ); From b10eb48614466d94b0bed878b37baad4ab841a2a Mon Sep 17 00:00:00 2001 From: dukenv0307 Date: Wed, 20 Mar 2024 11:00:38 +0700 Subject: [PATCH 260/500] resolve another conflict --- .../SidebarScreen/FloatingActionButtonAndPopover.js | 9 --------- 1 file changed, 9 deletions(-) diff --git a/src/pages/home/sidebar/SidebarScreen/FloatingActionButtonAndPopover.js b/src/pages/home/sidebar/SidebarScreen/FloatingActionButtonAndPopover.js index 0673f22a7261..ec27112ab4b7 100644 --- a/src/pages/home/sidebar/SidebarScreen/FloatingActionButtonAndPopover.js +++ b/src/pages/home/sidebar/SidebarScreen/FloatingActionButtonAndPopover.js @@ -170,16 +170,7 @@ function FloatingActionButtonAndPopover(props) { CONST.IOU.TYPE.REQUEST, // When starting to create a money request from the global FAB, there is not an existing report yet. A random optimistic reportID is generated and used // for all of the routes in the creation flow. -<<<<<<< HEAD - ROUTES.MONEY_REQUEST_CREATE.getRoute( - CONST.IOU.ACTION.CREATE, - CONST.IOU.TYPE.REQUEST, - CONST.IOU.OPTIMISTIC_TRANSACTION_ID, - ReportUtils.generateReportID(), - ), -======= ReportUtils.generateReportID(), ->>>>>>> main ), ), }, From 15d6a41e79d5688c1789d82b659c567b62311319 Mon Sep 17 00:00:00 2001 From: dukenv0307 Date: Wed, 20 Mar 2024 11:15:47 +0700 Subject: [PATCH 261/500] fix ts check --- src/components/MoneyRequestConfirmationList.tsx | 6 ++---- src/libs/actions/IOU.ts | 2 +- src/pages/iou/request/step/IOURequestStepAmount.js | 4 +++- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/components/MoneyRequestConfirmationList.tsx b/src/components/MoneyRequestConfirmationList.tsx index d54636c56c6e..d3ff5f974e6b 100755 --- a/src/components/MoneyRequestConfirmationList.tsx +++ b/src/components/MoneyRequestConfirmationList.tsx @@ -645,10 +645,8 @@ function MoneyRequestConfirmationList({ if (isDistanceRequest) { return; } - const action = props.isEditingSplitBill ? CONST.IOU.ACTION.EDIT : CONST.IOU.ACTION.CREATE; - Navigation.navigate( - ROUTES.MONEY_REQUEST_STEP_AMOUNT.getRoute(action, props.iouType, props.transaction.transactionID, props.reportID, Navigation.getActiveRouteWithoutParams()), - ); + const action = isEditingSplitBill ? CONST.IOU.ACTION.EDIT : CONST.IOU.ACTION.CREATE; + Navigation.navigate(ROUTES.MONEY_REQUEST_STEP_AMOUNT.getRoute(action, iouType, transaction?.transactionID ?? '', reportID, Navigation.getActiveRouteWithoutParams())); }} style={[styles.moneyRequestMenuItem, styles.mt2]} titleStyle={styles.moneyRequestConfirmationAmount} diff --git a/src/libs/actions/IOU.ts b/src/libs/actions/IOU.ts index 91a74c593926..7a9e9989652e 100644 --- a/src/libs/actions/IOU.ts +++ b/src/libs/actions/IOU.ts @@ -312,7 +312,7 @@ function updateMoneyRequestTypeParams(routes: StackNavigationState, reportID: string) { clearMoneyRequest(CONST.IOU.OPTIMISTIC_TRANSACTION_ID); - Navigation.navigate(ROUTES.MONEY_REQUEST_CREATE.getRoute(iouType, CONST.IOU.OPTIMISTIC_TRANSACTION_ID, reportID)); + Navigation.navigate(ROUTES.MONEY_REQUEST_CREATE.getRoute(CONST.IOU.ACTION.CREATE, iouType, CONST.IOU.OPTIMISTIC_TRANSACTION_ID, reportID)); } // eslint-disable-next-line @typescript-eslint/naming-convention diff --git a/src/pages/iou/request/step/IOURequestStepAmount.js b/src/pages/iou/request/step/IOURequestStepAmount.js index 6fc6b433d2fb..44944d41e2c9 100644 --- a/src/pages/iou/request/step/IOURequestStepAmount.js +++ b/src/pages/iou/request/step/IOURequestStepAmount.js @@ -1,5 +1,6 @@ import {useFocusEffect} from '@react-navigation/native'; import lodashGet from 'lodash/get'; +import lodashIsEmpty from 'lodash/isEmpty'; import PropTypes from 'prop-types'; import React, {useCallback, useEffect, useRef} from 'react'; import {withOnyx} from 'react-native-onyx'; @@ -86,7 +87,8 @@ function IOURequestStepAmount({ const iouRequestType = getRequestType(transaction); const isEditing = action === CONST.IOU.ACTION.EDIT; const isSplitBill = iouType === CONST.IOU.TYPE.SPLIT; - const {amount: transactionAmount} = ReportUtils.getTransactionDetails(isEditing && isSplitBill ? splitDraftTransaction : transaction); + const isEditingSplitBill = isEditing && isSplitBill; + const {amount: transactionAmount} = ReportUtils.getTransactionDetails(isEditingSplitBill && !lodashIsEmpty(splitDraftTransaction) ? splitDraftTransaction : transaction); const taxRates = lodashGet(policy, 'taxRates', {}); const isPolicyExpenseChat = ReportUtils.isPolicyExpenseChat(ReportUtils.getRootParentReport(report)); From e9fae4e94994cec2aac8b003e56f1d78b9647771 Mon Sep 17 00:00:00 2001 From: Tomasz Misiukiewicz Date: Wed, 20 Mar 2024 09:28:43 +0100 Subject: [PATCH 262/500] remove redundant function calls --- src/libs/OptionsListUtils.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/libs/OptionsListUtils.ts b/src/libs/OptionsListUtils.ts index 454ac92c201c..dd0dc2ed8224 100644 --- a/src/libs/OptionsListUtils.ts +++ b/src/libs/OptionsListUtils.ts @@ -1528,7 +1528,7 @@ function getOptions( // - All archived reports should remain at the bottom const orderedReportOptions = lodashSortBy(filteredReportOptions, (option) => { const report = option.item; - if (ReportUtils.isArchivedRoom(report)) { + if (option.isArchivedRoom) { return CONST.DATE.UNIX_EPOCH; } @@ -1543,11 +1543,11 @@ function getOptions( return; } - const isThread = ReportUtils.isChatThread(report); - const isTaskReport = ReportUtils.isTaskReport(report); - const isPolicyExpenseChat = ReportUtils.isPolicyExpenseChat(report); - const isMoneyRequestReport = ReportUtils.isMoneyRequestReport(report); - const isSelfDM = ReportUtils.isSelfDM(report); + const isThread = option.isThread; + const isTaskReport = option.isTaskReport; + const isPolicyExpenseChat = option.isPolicyExpenseChat; + const isMoneyRequestReport = option.isMoneyRequestReport; + const isSelfDM = option.isSelfDM; // Currently, currentUser is not included in visibleChatMemberAccountIDs, so for selfDM we need to add the currentUser as participants. const accountIDs = isSelfDM ? [currentUserAccountID ?? 0] : report.visibleChatMemberAccountIDs ?? []; From 46a4fccbcf01bc68e6ce51825eb81452519f0f63 Mon Sep 17 00:00:00 2001 From: Tomasz Misiukiewicz Date: Wed, 20 Mar 2024 11:31:14 +0100 Subject: [PATCH 263/500] speed up setting options --- src/components/OptionListContextProvider.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/OptionListContextProvider.tsx b/src/components/OptionListContextProvider.tsx index feabc0464913..4bbd36c9df27 100644 --- a/src/components/OptionListContextProvider.tsx +++ b/src/components/OptionListContextProvider.tsx @@ -55,7 +55,7 @@ function OptionsListContextProvider({reports, children}: OptionsListProviderProp } setOptions((prevOptions) => { - const newOptions = {...prevOptions}; + const newOptions = prevOptions; newOptions.reports[replaceIndex] = newOption; return newOptions; }); From 58d92f15b129ea7580164e60ef0e970c432f89e5 Mon Sep 17 00:00:00 2001 From: Yogesh Bhatt Date: Wed, 20 Mar 2024 18:39:39 +0530 Subject: [PATCH 264/500] StateSelector bugfix - for issue causing url to not update in browser url bar --- src/components/StateSelector.tsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/components/StateSelector.tsx b/src/components/StateSelector.tsx index 5e1e8591498d..97c8cffc3c48 100644 --- a/src/components/StateSelector.tsx +++ b/src/components/StateSelector.tsx @@ -69,7 +69,9 @@ function StateSelector( // This will cause the form to revalidate and remove any error related to state name onInputChange(stateCode); } - Navigation.setParams({state: stateCode}); + + // This enforces the state selector to use the state from address instead of the state from URL + Navigation.setParams({state: undefined}); setStateToDisplay(stateCode); // eslint-disable-next-line react-hooks/exhaustive-deps From 1075405a20179d457ed544f11fe5480bd85d0629 Mon Sep 17 00:00:00 2001 From: Abdullah Alsigar Date: Wed, 20 Mar 2024 17:27:30 +0300 Subject: [PATCH 265/500] cleanup --- .../iou/request/step/IOURequestStepScan/index.native.js | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/src/pages/iou/request/step/IOURequestStepScan/index.native.js b/src/pages/iou/request/step/IOURequestStepScan/index.native.js index 4503cdf10c61..d02d4874b67d 100644 --- a/src/pages/iou/request/step/IOURequestStepScan/index.native.js +++ b/src/pages/iou/request/step/IOURequestStepScan/index.native.js @@ -67,13 +67,7 @@ function IOURequestStepScan({ const [flash, setFlash] = useState(false); const [cameraPermissionStatus, setCameraPermissionStatus] = useState(undefined); const askedForPermission = useRef(false); - const [didCapturePhoto, setDidCapturePhoto] = useState(false); - useFocusEffect( - useCallback(() => { - setDidCapturePhoto(false); - }, []), - ); const {translate} = useLocalize(); @@ -130,6 +124,8 @@ function IOURequestStepScan({ useFocusEffect( useCallback(() => { + setDidCapturePhoto(false); + const refreshCameraPermissionStatus = (shouldAskForPermission = false) => { CameraPermission.getCameraPermissionStatus() .then((res) => { From b28bd1307c1d81a1b668ff329a31fc23c06bdee5 Mon Sep 17 00:00:00 2001 From: Tomasz Misiukiewicz Date: Wed, 20 Mar 2024 16:07:24 +0100 Subject: [PATCH 266/500] update money request participants selector --- ...raryForRefactorRequestParticipantsSelector.js | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/src/pages/iou/request/MoneyTemporaryForRefactorRequestParticipantsSelector.js b/src/pages/iou/request/MoneyTemporaryForRefactorRequestParticipantsSelector.js index cdc3e72f98f4..927ee855c6eb 100644 --- a/src/pages/iou/request/MoneyTemporaryForRefactorRequestParticipantsSelector.js +++ b/src/pages/iou/request/MoneyTemporaryForRefactorRequestParticipantsSelector.js @@ -20,9 +20,9 @@ import useThemeStyles from '@hooks/useThemeStyles'; import * as DeviceCapabilities from '@libs/DeviceCapabilities'; import * as OptionsListUtils from '@libs/OptionsListUtils'; import * as ReportUtils from '@libs/ReportUtils'; -import reportPropTypes from '@pages/reportPropTypes'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; +import { useOptionsList } from '@components/OptionListContextProvider'; const propTypes = { /** Beta features list */ @@ -48,9 +48,6 @@ const propTypes = { }), ), - /** All reports shared with the user */ - reports: PropTypes.objectOf(reportPropTypes), - /** Padding bottom style of safe area */ safeAreaPaddingBottomStyle: PropTypes.oneOfType([PropTypes.arrayOf(PropTypes.object), PropTypes.object]), @@ -67,7 +64,6 @@ const propTypes = { const defaultProps = { participants: [], safeAreaPaddingBottomStyle: {}, - reports: {}, betas: [], dismissedReferralBanners: {}, didScreenTransitionEnd: false, @@ -76,7 +72,6 @@ const defaultProps = { function MoneyTemporaryForRefactorRequestParticipantsSelector({ betas, participants, - reports, onFinish, onParticipantsAdded, safeAreaPaddingBottomStyle, @@ -92,6 +87,9 @@ function MoneyTemporaryForRefactorRequestParticipantsSelector({ const {isOffline} = useNetwork(); const personalDetails = usePersonalDetails(); const {canUseP2PDistanceRequests} = usePermissions(); + const {options} = useOptionsList({ + shouldInitialize: didScreenTransitionEnd, + }); const offlineMessage = isOffline ? [`${translate('common.youAppearToBeOffline')} ${translate('search.resultsAreLimited')}`, {isTranslated: true}] : ''; @@ -111,8 +109,8 @@ function MoneyTemporaryForRefactorRequestParticipantsSelector({ let indexOffset = 0; const chatOptions = OptionsListUtils.getFilteredOptions( - reports, - personalDetails, + options.reports, + options.personalDetails, betas, searchTerm, participants, @@ -180,7 +178,7 @@ function MoneyTemporaryForRefactorRequestParticipantsSelector({ } return [newSections, chatOptions]; - }, [didScreenTransitionEnd, reports, personalDetails, betas, searchTerm, participants, iouType, iouRequestType, maxParticipantsReached, canUseP2PDistanceRequests, translate]); + }, [didScreenTransitionEnd, options.reports, options.personalDetails, betas, searchTerm, participants, iouType, canUseP2PDistanceRequests, iouRequestType, maxParticipantsReached, personalDetails, translate]); /** * Adds a single participant to the request From 5692a8d60209763e383ba0e9760f91011920187f Mon Sep 17 00:00:00 2001 From: Tomasz Misiukiewicz Date: Wed, 20 Mar 2024 16:57:34 +0100 Subject: [PATCH 267/500] lint code --- ...aryForRefactorRequestParticipantsSelector.js | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/src/pages/iou/request/MoneyTemporaryForRefactorRequestParticipantsSelector.js b/src/pages/iou/request/MoneyTemporaryForRefactorRequestParticipantsSelector.js index 927ee855c6eb..444cfeb2171d 100644 --- a/src/pages/iou/request/MoneyTemporaryForRefactorRequestParticipantsSelector.js +++ b/src/pages/iou/request/MoneyTemporaryForRefactorRequestParticipantsSelector.js @@ -7,6 +7,7 @@ import _ from 'underscore'; import Button from '@components/Button'; import FormHelpMessage from '@components/FormHelpMessage'; import {usePersonalDetails} from '@components/OnyxProvider'; +import {useOptionsList} from '@components/OptionListContextProvider'; import {PressableWithFeedback} from '@components/Pressable'; import ReferralProgramCTA from '@components/ReferralProgramCTA'; import SelectCircle from '@components/SelectCircle'; @@ -22,7 +23,6 @@ import * as OptionsListUtils from '@libs/OptionsListUtils'; import * as ReportUtils from '@libs/ReportUtils'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; -import { useOptionsList } from '@components/OptionListContextProvider'; const propTypes = { /** Beta features list */ @@ -178,7 +178,20 @@ function MoneyTemporaryForRefactorRequestParticipantsSelector({ } return [newSections, chatOptions]; - }, [didScreenTransitionEnd, options.reports, options.personalDetails, betas, searchTerm, participants, iouType, canUseP2PDistanceRequests, iouRequestType, maxParticipantsReached, personalDetails, translate]); + }, [ + didScreenTransitionEnd, + options.reports, + options.personalDetails, + betas, + searchTerm, + participants, + iouType, + canUseP2PDistanceRequests, + iouRequestType, + maxParticipantsReached, + personalDetails, + translate, + ]); /** * Adds a single participant to the request From ca2c10a24615923956fafb6a75442785cfe70520 Mon Sep 17 00:00:00 2001 From: Tomasz Misiukiewicz Date: Wed, 20 Mar 2024 17:07:15 +0100 Subject: [PATCH 268/500] add comments for option list context --- src/components/OptionListContextProvider.tsx | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/components/OptionListContextProvider.tsx b/src/components/OptionListContextProvider.tsx index 4bbd36c9df27..4cabe9db6108 100644 --- a/src/components/OptionListContextProvider.tsx +++ b/src/components/OptionListContextProvider.tsx @@ -9,12 +9,16 @@ import type {Report} from '@src/types/onyx'; import {usePersonalDetails} from './OnyxProvider'; type OptionsListContextProps = { + /** List of options for reports and personal details */ options: OptionList; + /** Function to initialize the options */ initializeOptions: () => void; + /** Flag to check if the options are initialized */ areOptionsInitialized: boolean; }; type OptionsListProviderOnyxProps = { + /** Collection of reports */ reports: OnyxCollection; }; From ec3547e59d731e181a990056e0ba1117963126b0 Mon Sep 17 00:00:00 2001 From: Tomasz Misiukiewicz Date: Wed, 20 Mar 2024 17:53:35 +0100 Subject: [PATCH 269/500] add test for cached options in search page --- src/components/OptionListContextProvider.tsx | 2 +- src/pages/SearchPage/index.tsx | 6 +-- tests/perf-test/SearchPage.perf-test.tsx | 45 +++++++++++++++++++- 3 files changed, 47 insertions(+), 6 deletions(-) diff --git a/src/components/OptionListContextProvider.tsx b/src/components/OptionListContextProvider.tsx index 4cabe9db6108..bad72c52488e 100644 --- a/src/components/OptionListContextProvider.tsx +++ b/src/components/OptionListContextProvider.tsx @@ -134,4 +134,4 @@ export default withOnyx( }, })(OptionsListContextProvider); -export {useOptionsListContext, useOptionsList}; +export {useOptionsListContext, useOptionsList, OptionsListContext}; diff --git a/src/pages/SearchPage/index.tsx b/src/pages/SearchPage/index.tsx index d08d68c37cdc..c25dae65ab5c 100644 --- a/src/pages/SearchPage/index.tsx +++ b/src/pages/SearchPage/index.tsx @@ -150,7 +150,7 @@ function SearchPage({betas, isSearchingForReports, navigation}: SearchPageProps) shouldEnableMaxHeight navigation={navigation} > - {({didScreenTransitionEnd, safeAreaPaddingBottomStyle}) => ( + {({safeAreaPaddingBottomStyle}) => ( <> - sections={(!areOptionsInitialized && didScreenTransitionEnd) || areOptionsInitialized ? sections : CONST.EMPTY_ARRAY} + sections={areOptionsInitialized ? sections : CONST.EMPTY_ARRAY} ListItem={UserListItem} textInputValue={searchValue} textInputLabel={translate('optionsSelector.nameEmailOrPhoneNumber')} @@ -168,7 +168,7 @@ function SearchPage({betas, isSearchingForReports, navigation}: SearchPageProps) onLayout={setPerformanceTimersEnd} autoFocus onSelectRow={selectReport} - showLoadingPlaceholder={areOptionsInitialized && debouncedSearchValue.trim() === '' ? sections.length === 0 : !didScreenTransitionEnd} + showLoadingPlaceholder={!areOptionsInitialized} footerContent={SearchPageFooterInstance} isLoadingNewOptions={isSearchingForReports ?? undefined} /> diff --git a/tests/perf-test/SearchPage.perf-test.tsx b/tests/perf-test/SearchPage.perf-test.tsx index 7bc127886076..b8588253e0c6 100644 --- a/tests/perf-test/SearchPage.perf-test.tsx +++ b/tests/perf-test/SearchPage.perf-test.tsx @@ -2,15 +2,16 @@ import type * as NativeNavigation from '@react-navigation/native'; import type {StackScreenProps} from '@react-navigation/stack'; import {fireEvent, screen, waitFor} from '@testing-library/react-native'; import type {TextMatch} from '@testing-library/react-native/build/matches'; -import React from 'react'; +import React, {useMemo} from 'react'; import type {ComponentType} from 'react'; import Onyx from 'react-native-onyx'; import type {OnyxCollection, OnyxEntry} from 'react-native-onyx'; import {measurePerformance} from 'reassure'; import {LocaleContextProvider} from '@components/LocaleContextProvider'; -import OptionListContextProvider from '@components/OptionListContextProvider'; +import OptionListContextProvider, {OptionsListContext} from '@components/OptionListContextProvider'; import type {WithNavigationFocusProps} from '@components/withNavigationFocus'; import type {RootStackParamList} from '@libs/Navigation/types'; +import {createOptionList} from '@libs/OptionsListUtils'; import SearchPage from '@pages/SearchPage'; import ComposeProviders from '@src/components/ComposeProviders'; import OnyxProvider from '@src/components/OnyxProvider'; @@ -95,6 +96,7 @@ const getMockedPersonalDetails = (length = 100) => const mockedReports = getMockedReports(600); const mockedBetas = Object.values(CONST.BETAS); const mockedPersonalDetails = getMockedPersonalDetails(100); +const mockedOptions = createOptionList(mockedPersonalDetails, mockedReports); beforeAll(() => Onyx.init({ @@ -135,6 +137,45 @@ function SearchPageWrapper(args: SearchPageProps) { ); } +function SearchPageWithCachedOptions(args: SearchPageProps) { + return ( + + ({options: mockedOptions, initializeOptions: () => {}, areOptionsInitialized: true}), [])}> + + + + ); +} + +test('[Search Page] should render list with cached options', async () => { + // @ts-expect-error TODO: Remove this once TestHelper (https://github.com/Expensify/App/issues/25318) is migrated to TypeScript. + const {addListener} = TestHelper.createAddListenerMock(); + + const scenario = async () => { + await screen.findByTestId('SearchPage'); + }; + + const navigation = {addListener}; + + return ( + waitForBatchedUpdates() + .then(() => + Onyx.multiSet({ + ...mockedReports, + [ONYXKEYS.PERSONAL_DETAILS_LIST]: mockedPersonalDetails, + [ONYXKEYS.BETAS]: mockedBetas, + [ONYXKEYS.IS_SEARCHING_FOR_REPORTS]: true, + }), + ) + // @ts-expect-error TODO: Remove this once TestHelper (https://github.com/Expensify/App/issues/25318) is migrated to TypeScript. + .then(() => measurePerformance(, {scenario})) + ); +}); + test('[Search Page] should interact when text input changes', async () => { // @ts-expect-error TODO: Remove this once TestHelper (https://github.com/Expensify/App/issues/25318) is migrated to TypeScript. const {addListener} = TestHelper.createAddListenerMock(); From af6c3c5dd094bdd4657eb5df943822e7e1a67d64 Mon Sep 17 00:00:00 2001 From: Yogesh Bhatt Date: Wed, 20 Mar 2024 23:36:41 +0530 Subject: [PATCH 270/500] useGeographicalStateFromRoute - check for state before accessing stateISO --- src/hooks/useGeographicalStateFromRoute.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/hooks/useGeographicalStateFromRoute.ts b/src/hooks/useGeographicalStateFromRoute.ts index 3e85bfc2a368..13936ee78f5b 100644 --- a/src/hooks/useGeographicalStateFromRoute.ts +++ b/src/hooks/useGeographicalStateFromRoute.ts @@ -19,5 +19,5 @@ export default function useGeographicalStateFromRoute(stateParamName = 'state'): if (!stateFromUrlTemp) { return; } - return COMMON_CONST.STATES[stateFromUrlTemp as State].stateISO; + return COMMON_CONST.STATES[stateFromUrlTemp as State]?.stateISO; } From 24e7de9f508acfba7516482d7899149d14e66bd3 Mon Sep 17 00:00:00 2001 From: Shubham Agrawal Date: Thu, 21 Mar 2024 10:30:18 +0530 Subject: [PATCH 271/500] Fixes after merging --- src/components/SelectionList/TableListItem.tsx | 2 +- src/components/SelectionList/UserListItem.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/SelectionList/TableListItem.tsx b/src/components/SelectionList/TableListItem.tsx index d8ded2ada697..fa741bccbbae 100644 --- a/src/components/SelectionList/TableListItem.tsx +++ b/src/components/SelectionList/TableListItem.tsx @@ -65,7 +65,7 @@ function TableListItem({ // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing disabled={isDisabled || item.isDisabledCheckbox} onPress={handleCheckboxPress} - style={[styles.cursorUnset, StyleUtils.getCheckboxPressableStyle(), item.isDisabledCheckbox && styles.cursorDisabled]} + style={[styles.cursorUnset, StyleUtils.getCheckboxPressableStyle(), item.isDisabledCheckbox && styles.cursorDisabled, styles.mr3]} > {item.isSelected && ( diff --git a/src/components/SelectionList/UserListItem.tsx b/src/components/SelectionList/UserListItem.tsx index 081d49d27d68..226c93bf2cf8 100644 --- a/src/components/SelectionList/UserListItem.tsx +++ b/src/components/SelectionList/UserListItem.tsx @@ -79,7 +79,7 @@ function UserListItem({ // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing disabled={isDisabled || item.isDisabledCheckbox} onPress={handleCheckboxPress} - style={[styles.cursorUnset, StyleUtils.getCheckboxPressableStyle(), item.isDisabledCheckbox && styles.cursorDisabled]} + style={[styles.cursorUnset, StyleUtils.getCheckboxPressableStyle(), item.isDisabledCheckbox && styles.cursorDisabled, styles.mr3]} > {item.isSelected && ( From eef4d629e4ceb8c6bb571249b83b19c1f86bca50 Mon Sep 17 00:00:00 2001 From: Tomasz Misiukiewicz Date: Thu, 21 Mar 2024 08:55:56 +0100 Subject: [PATCH 272/500] update displaying lists --- src/pages/RoomInvitePage.tsx | 26 ++++------------- .../MoneyRequestParticipantsSelector.js | 28 ++++--------------- 2 files changed, 11 insertions(+), 43 deletions(-) diff --git a/src/pages/RoomInvitePage.tsx b/src/pages/RoomInvitePage.tsx index df57a4fee892..3f95d11027ba 100644 --- a/src/pages/RoomInvitePage.tsx +++ b/src/pages/RoomInvitePage.tsx @@ -1,5 +1,3 @@ -import {useNavigation} from '@react-navigation/native'; -import type {StackNavigationProp} from '@react-navigation/stack'; import Str from 'expensify-common/lib/str'; import React, {useCallback, useEffect, useMemo, useState} from 'react'; import type {SectionListData} from 'react-native'; @@ -19,7 +17,6 @@ import useThemeStyles from '@hooks/useThemeStyles'; import * as DeviceCapabilities from '@libs/DeviceCapabilities'; import * as LoginUtils from '@libs/LoginUtils'; import Navigation from '@libs/Navigation/Navigation'; -import type {RootStackParamList} from '@libs/Navigation/types'; import * as OptionsListUtils from '@libs/OptionsListUtils'; import * as PersonalDetailsUtils from '@libs/PersonalDetailsUtils'; import * as PhoneNumber from '@libs/PhoneNumber'; @@ -51,8 +48,6 @@ function RoomInvitePage({betas, personalDetails, report, policies}: RoomInvitePa const [selectedOptions, setSelectedOptions] = useState([]); const [invitePersonalDetails, setInvitePersonalDetails] = useState([]); const [userToInvite, setUserToInvite] = useState(null); - const [didScreenTransitionEnd, setDidScreenTransitionEnd] = useState(false); - const navigation: StackNavigationProp = useNavigation(); const {options, areOptionsInitialized} = useOptionsList(); useEffect(() => { @@ -87,26 +82,14 @@ function RoomInvitePage({betas, personalDetails, report, policies}: RoomInvitePa setUserToInvite(inviteOptions.userToInvite); setInvitePersonalDetails(inviteOptions.personalDetails); setSelectedOptions(newSelectedOptions); - // eslint-disable-next-line react-hooks/exhaustive-deps -- we don't want to recalculate when selectedOptions change - }, [personalDetails, betas, searchTerm, excludedUsers]); - - useEffect(() => { - const unsubscribeTransitionEnd = navigation.addListener('transitionEnd', () => { - setDidScreenTransitionEnd(true); - }); - - return () => { - unsubscribeTransitionEnd(); - }; - // Rule disabled because this effect is only for component did mount & will component unmount lifecycle event // eslint-disable-next-line react-hooks/exhaustive-deps - }, []); + }, [personalDetails, betas, searchTerm, excludedUsers, options.personalDetails]); const sections = useMemo(() => { const sectionsArr: Sections = []; let indexOffset = 0; - if (!didScreenTransitionEnd) { + if (!areOptionsInitialized) { return []; } @@ -153,7 +136,7 @@ function RoomInvitePage({betas, personalDetails, report, policies}: RoomInvitePa } return sectionsArr; - }, [invitePersonalDetails, searchTerm, selectedOptions, translate, userToInvite, didScreenTransitionEnd]); + }, [areOptionsInitialized, selectedOptions, searchTerm, invitePersonalDetails, userToInvite, translate]); const toggleOption = useCallback( (option: OptionsListUtils.MemberForList) => { @@ -216,6 +199,7 @@ function RoomInvitePage({betas, personalDetails, report, policies}: RoomInvitePa } return OptionsListUtils.getHeaderMessage(invitePersonalDetails.length !== 0, Boolean(userToInvite), searchValue); }, [searchTerm, userToInvite, excludedUsers, invitePersonalDetails, translate, reportName]); + return ( { const chatOptions = OptionsListUtils.getFilteredOptions( - reports, - personalDetails, + options.reports, + options.personalDetails, betas, searchTerm, participants, @@ -132,7 +123,7 @@ function MoneyRequestParticipantsSelector({ personalDetails: chatOptions.personalDetails, userToInvite: chatOptions.userToInvite, }; - }, [betas, reports, participants, personalDetails, searchTerm, iouType, isDistanceRequest, canUseP2PDistanceRequests]); + }, [options.reports, options.personalDetails, betas, searchTerm, participants, iouType, canUseP2PDistanceRequests, isDistanceRequest]); /** * Returns the sections needed for the OptionsSelector @@ -360,7 +351,7 @@ function MoneyRequestParticipantsSelector({ onSelectRow={addSingleParticipant} footerContent={footerContent} headerMessage={headerMessage} - showLoadingPlaceholder={isSearchingForReports} + showLoadingPlaceholder={!areOptionsInitialized} rightHandSideComponent={itemRightSideComponent} /> @@ -376,14 +367,7 @@ export default withOnyx({ key: ONYXKEYS.ACCOUNT, selector: (data) => data.dismissedReferralBanners || {}, }, - reports: { - key: ONYXKEYS.COLLECTION.REPORT, - }, betas: { key: ONYXKEYS.BETAS, }, - isSearchingForReports: { - key: ONYXKEYS.IS_SEARCHING_FOR_REPORTS, - initWithStoredValues: false, - }, })(MoneyRequestParticipantsSelector); From fe2e60c9fd70f8de3b27a6192f5f2a5d2d05260c Mon Sep 17 00:00:00 2001 From: dukenv0307 Date: Thu, 21 Mar 2024 17:06:54 +0700 Subject: [PATCH 273/500] remove unnecessary code --- src/ROUTES.ts | 4 - src/SCREENS.ts | 1 - .../MoneyRequestConfirmationList.tsx | 5 +- .../AppNavigator/ModalStackNavigators.tsx | 1 - src/libs/Navigation/linkingConfig/config.ts | 1 - src/libs/Navigation/types.ts | 1 - src/pages/EditRequestPage.js | 37 +--- src/pages/EditSplitBillPage.tsx | 28 +-- src/pages/iou/steps/NewRequestAmountPage.js | 195 ------------------ 9 files changed, 6 insertions(+), 267 deletions(-) delete mode 100644 src/pages/iou/steps/NewRequestAmountPage.js diff --git a/src/ROUTES.ts b/src/ROUTES.ts index 6868773a8290..7a7cb8d3e3ef 100644 --- a/src/ROUTES.ts +++ b/src/ROUTES.ts @@ -276,10 +276,6 @@ const ROUTES = { route: 'r/:reportID/invite', getRoute: (reportID: string) => `r/${reportID}/invite` as const, }, - MONEY_REQUEST_AMOUNT: { - route: ':iouType/new/amount/:reportID?', - getRoute: (iouType: string, reportID = '') => `${iouType}/new/amount/${reportID}` as const, - }, MONEY_REQUEST_PARTICIPANTS: { route: ':iouType/new/participants/:reportID?', getRoute: (iouType: string, reportID = '') => `${iouType}/new/participants/${reportID}` as const, diff --git a/src/SCREENS.ts b/src/SCREENS.ts index 4d4e9ea327c6..84739d2da010 100644 --- a/src/SCREENS.ts +++ b/src/SCREENS.ts @@ -149,7 +149,6 @@ const SCREENS = { STEP_WAYPOINT: 'Money_Request_Step_Waypoint', STEP_TAX_AMOUNT: 'Money_Request_Step_Tax_Amount', STEP_TAX_RATE: 'Money_Request_Step_Tax_Rate', - AMOUNT: 'Money_Request_Amount', PARTICIPANTS: 'Money_Request_Participants', CONFIRMATION: 'Money_Request_Confirmation', CURRENCY: 'Money_Request_Currency', diff --git a/src/components/MoneyRequestConfirmationList.tsx b/src/components/MoneyRequestConfirmationList.tsx index 63ca7c016bfc..7d5a0527fc10 100755 --- a/src/components/MoneyRequestConfirmationList.tsx +++ b/src/components/MoneyRequestConfirmationList.tsx @@ -645,8 +645,9 @@ function MoneyRequestConfirmationList({ if (isDistanceRequest) { return; } - const action = isEditingSplitBill ? CONST.IOU.ACTION.EDIT : CONST.IOU.ACTION.CREATE; - Navigation.navigate(ROUTES.MONEY_REQUEST_STEP_AMOUNT.getRoute(action, iouType, transaction?.transactionID ?? '', reportID, Navigation.getActiveRouteWithoutParams())); + Navigation.navigate( + ROUTES.MONEY_REQUEST_STEP_AMOUNT.getRoute(CONST.IOU.ACTION.EDIT, iouType, transaction?.transactionID ?? '', reportID, Navigation.getActiveRouteWithoutParams()), + ); }} style={[styles.moneyRequestMenuItem, styles.mt2]} titleStyle={styles.moneyRequestConfirmationAmount} diff --git a/src/libs/Navigation/AppNavigator/ModalStackNavigators.tsx b/src/libs/Navigation/AppNavigator/ModalStackNavigators.tsx index bd5bfc46134a..22f06089ab05 100644 --- a/src/libs/Navigation/AppNavigator/ModalStackNavigators.tsx +++ b/src/libs/Navigation/AppNavigator/ModalStackNavigators.tsx @@ -94,7 +94,6 @@ const MoneyRequestModalStackNavigator = createModalStackNavigator require('../../../pages/iou/request/step/IOURequestStepScan').default as React.ComponentType, [SCREENS.MONEY_REQUEST.STEP_TAG]: () => require('../../../pages/iou/request/step/IOURequestStepTag').default as React.ComponentType, [SCREENS.MONEY_REQUEST.STEP_WAYPOINT]: () => require('../../../pages/iou/request/step/IOURequestStepWaypoint').default as React.ComponentType, - [SCREENS.MONEY_REQUEST.AMOUNT]: () => require('../../../pages/iou/steps/NewRequestAmountPage').default as React.ComponentType, [SCREENS.MONEY_REQUEST.PARTICIPANTS]: () => require('../../../pages/iou/steps/MoneyRequstParticipantsPage/MoneyRequestParticipantsPage').default as React.ComponentType, [SCREENS.MONEY_REQUEST.CONFIRMATION]: () => require('../../../pages/iou/steps/MoneyRequestConfirmPage').default as React.ComponentType, [SCREENS.MONEY_REQUEST.CURRENCY]: () => require('../../../pages/iou/IOUCurrencySelection').default as React.ComponentType, diff --git a/src/libs/Navigation/linkingConfig/config.ts b/src/libs/Navigation/linkingConfig/config.ts index 130fdf23732f..bc9143dfacc3 100644 --- a/src/libs/Navigation/linkingConfig/config.ts +++ b/src/libs/Navigation/linkingConfig/config.ts @@ -503,7 +503,6 @@ const config: LinkingOptions['config'] = { [SCREENS.MONEY_REQUEST.STEP_SCAN]: ROUTES.MONEY_REQUEST_STEP_SCAN.route, [SCREENS.MONEY_REQUEST.STEP_TAG]: ROUTES.MONEY_REQUEST_STEP_TAG.route, [SCREENS.MONEY_REQUEST.STEP_WAYPOINT]: ROUTES.MONEY_REQUEST_STEP_WAYPOINT.route, - [SCREENS.MONEY_REQUEST.AMOUNT]: ROUTES.MONEY_REQUEST_AMOUNT.route, [SCREENS.MONEY_REQUEST.STEP_TAX_AMOUNT]: ROUTES.MONEY_REQUEST_STEP_TAX_AMOUNT.route, [SCREENS.MONEY_REQUEST.STEP_TAX_RATE]: ROUTES.MONEY_REQUEST_STEP_TAX_RATE.route, [SCREENS.MONEY_REQUEST.PARTICIPANTS]: ROUTES.MONEY_REQUEST_PARTICIPANTS.route, diff --git a/src/libs/Navigation/types.ts b/src/libs/Navigation/types.ts index 9b0d9ce4decc..5a12b2168d9e 100644 --- a/src/libs/Navigation/types.ts +++ b/src/libs/Navigation/types.ts @@ -315,7 +315,6 @@ type RoomInviteNavigatorParamList = { }; type MoneyRequestNavigatorParamList = { - [SCREENS.MONEY_REQUEST.AMOUNT]: undefined; [SCREENS.MONEY_REQUEST.PARTICIPANTS]: { iouType: string; reportID: string; diff --git a/src/pages/EditRequestPage.js b/src/pages/EditRequestPage.js index de17d16a7c38..ab0fd6b1a609 100644 --- a/src/pages/EditRequestPage.js +++ b/src/pages/EditRequestPage.js @@ -8,7 +8,6 @@ import ScreenWrapper from '@components/ScreenWrapper'; import tagPropTypes from '@components/tagPropTypes'; import transactionPropTypes from '@components/transactionPropTypes'; import compose from '@libs/compose'; -import * as CurrencyUtils from '@libs/CurrencyUtils'; import * as IOUUtils from '@libs/IOUUtils'; import Navigation from '@libs/Navigation/Navigation'; import * as OptionsListUtils from '@libs/OptionsListUtils'; @@ -18,8 +17,6 @@ import * as TransactionUtils from '@libs/TransactionUtils'; import * as IOU from '@userActions/IOU'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; -import ROUTES from '@src/ROUTES'; -import EditRequestAmountPage from './EditRequestAmountPage'; import EditRequestDistancePage from './EditRequestDistancePage'; import EditRequestReceiptPage from './EditRequestReceiptPage'; import EditRequestTagPage from './EditRequestTagPage'; @@ -75,9 +72,8 @@ const defaultProps = { function EditRequestPage({report, route, policy, policyCategories, policyTags, parentReportActions, transaction}) { const parentReportActionID = lodashGet(report, 'parentReportActionID', '0'); const parentReportAction = lodashGet(parentReportActions, parentReportActionID, {}); - const {amount: transactionAmount, currency: transactionCurrency, tag: transactionTag} = ReportUtils.getTransactionDetails(transaction); + const {tag: transactionTag} = ReportUtils.getTransactionDetails(transaction); - const defaultCurrency = lodashGet(route, 'params.currency', '') || transactionCurrency; const fieldToEdit = lodashGet(route, ['params', 'field'], ''); const tagIndex = Number(lodashGet(route, ['params', 'tagIndex'], undefined)); @@ -104,22 +100,6 @@ function EditRequestPage({report, route, policy, policyCategories, policyTags, p }); }, [parentReportAction, fieldToEdit]); - const saveAmountAndCurrency = useCallback( - ({amount, currency: newCurrency}) => { - const newAmount = CurrencyUtils.convertToBackendAmount(Number.parseFloat(amount)); - - // If the value hasn't changed, don't request to save changes on the server and just close the modal - if (newAmount === TransactionUtils.getAmount(transaction) && newCurrency === TransactionUtils.getCurrency(transaction)) { - Navigation.dismissModal(); - return; - } - - IOU.updateMoneyRequestAmountAndCurrency(transaction.transactionID, report.reportID, newCurrency, newAmount, policy, policyTags, policyCategories); - Navigation.dismissModal(); - }, - [transaction, report, policy, policyTags, policyCategories], - ); - const saveTag = useCallback( ({tag: newTag}) => { let updatedTag = newTag; @@ -140,21 +120,6 @@ function EditRequestPage({report, route, policy, policyCategories, policyTags, p [tag, transaction.transactionID, report.reportID, transactionTag, tagIndex, policy, policyTags, policyCategories], ); - if (fieldToEdit === CONST.EDIT_REQUEST_FIELD.AMOUNT) { - return ( - { - const activeRoute = encodeURIComponent(Navigation.getActiveRouteWithoutParams()); - Navigation.navigate(ROUTES.EDIT_CURRENCY_REQUEST.getRoute(report.reportID, defaultCurrency, activeRoute)); - }} - /> - ); - } - if (fieldToEdit === CONST.EDIT_REQUEST_FIELD.TAG && shouldShowTags) { return ( ; function EditSplitBillPage({route, transaction, draftTransaction, report}: EditSplitBillProps) { - const {field: fieldToEdit, reportID, reportActionID, currency, tagIndex} = route.params; + const {field: fieldToEdit, reportID, reportActionID, tagIndex} = route.params; - const {amount: transactionAmount, currency: transactionCurrency, tag: transactionTag} = ReportUtils.getTransactionDetails(draftTransaction ?? transaction) ?? {}; + const {tag: transactionTag} = ReportUtils.getTransactionDetails(draftTransaction ?? transaction) ?? {}; - const defaultCurrency = currency ?? transactionCurrency; function navigateBackToSplitDetails() { Navigation.navigate(ROUTES.SPLIT_BILL_DETAILS.getRoute(reportID, reportActionID)); } @@ -53,27 +50,6 @@ function EditSplitBillPage({route, transaction, draftTransaction, report}: EditS navigateBackToSplitDetails(); }; - if (fieldToEdit === CONST.EDIT_REQUEST_FIELD.AMOUNT) { - return ( - { - const amount = CurrencyUtils.convertToBackendAmount(Number.parseFloat(transactionChanges.amount)); - - setDraftSplitTransaction({ - amount, - currency: transactionChanges.currency, - }); - }} - onNavigateToCurrency={() => { - const activeRoute = encodeURIComponent(Navigation.getActiveRouteWithoutParams()); - Navigation.navigate(ROUTES.EDIT_SPLIT_BILL_CURRENCY.getRoute(reportID, reportActionID, defaultCurrency, activeRoute)); - }} - /> - ); - } - if (fieldToEdit === CONST.EDIT_REQUEST_FIELD.TAG) { return ( { - focusTimeoutRef.current = setTimeout(() => textInput.current && textInput.current.focus(), CONST.ANIMATED_TRANSITION); - return () => { - if (!focusTimeoutRef.current) { - return; - } - clearTimeout(focusTimeoutRef.current); - }; - }, []), - ); - - // Because we use Onyx to store IOU info, when we try to make two different money requests from different tabs, - // it can result in an IOU sent with improper values. In such cases we want to reset the flow and redirect the user to the first step of the IOU. - useEffect(() => { - if (isEditing) { - // ID in Onyx could change by initiating a new request in a separate browser tab or completing a request - if (prevMoneyRequestID.current !== iou.id) { - // The ID is cleared on completing a request. In that case, we will do nothing. - if (!iou.id) { - return; - } - Navigation.goBack(ROUTES.MONEY_REQUEST.getRoute(iouType, reportID), true); - return; - } - const moneyRequestID = `${iouType}${reportID}`; - const shouldReset = iou.id !== moneyRequestID; - if (shouldReset) { - IOU.resetMoneyRequestInfo(moneyRequestID); - } - - if (!isDistanceRequestTab && (_.isEmpty(iou.participants) || iou.amount === 0 || shouldReset)) { - Navigation.goBack(ROUTES.MONEY_REQUEST.getRoute(iouType, reportID), true); - } - } - - return () => { - prevMoneyRequestID.current = iou.id; - }; - }, [iou.participants, iou.amount, iou.id, isEditing, iouType, reportID, isDistanceRequestTab]); - - const navigateBack = () => { - Navigation.goBack(isEditing ? ROUTES.MONEY_REQUEST_CONFIRMATION.getRoute(iouType, reportID) : ROUTES.HOME); - }; - - const navigateToCurrencySelectionPage = () => { - // If the money request being created is a distance request, don't allow the user to choose the currency. - // Only USD is allowed for distance requests. - if (isDistanceRequestTab) { - return; - } - - // Remove query from the route and encode it. - const activeRoute = encodeURIComponent(Navigation.getActiveRouteWithoutParams()); - Navigation.navigate(ROUTES.MONEY_REQUEST_CURRENCY.getRoute(iouType, reportID, currency, activeRoute)); - }; - - const navigateToNextPage = ({amount}) => { - const amountInSmallestCurrencyUnits = CurrencyUtils.convertToBackendAmount(Number.parseFloat(amount)); - IOU.setMoneyRequestAmount(amountInSmallestCurrencyUnits); - IOU.setMoneyRequestCurrency(currency); - - if (isEditing) { - Navigation.goBack(ROUTES.MONEY_REQUEST_CONFIRMATION.getRoute(iouType, reportID)); - return; - } - - IOU.navigateToNextPage(iou, iouType, report); - }; - - const content = ( - (textInput.current = e)} - onCurrencyButtonPress={navigateToCurrencySelectionPage} - onSubmitButtonPress={navigateToNextPage} - selectedTab={selectedTab} - /> - ); - - // ScreenWrapper is only needed in edit mode because we have a dedicated route for the edit amount page (MoneyRequestEditAmountPage). - // The rest of the cases this component is rendered through which has it's own ScreenWrapper - if (!isEditing) { - return content; - } - - return ( - - {({safeAreaPaddingBottomStyle}) => ( - - - - {content} - - - )} - - ); -} - -NewRequestAmountPage.propTypes = propTypes; -NewRequestAmountPage.defaultProps = defaultProps; -NewRequestAmountPage.displayName = 'NewRequestAmountPage'; - -export default withOnyx({ - iou: {key: ONYXKEYS.IOU}, - report: { - key: ({route}) => `${ONYXKEYS.COLLECTION.REPORT}${lodashGet(route, 'params.reportID', '')}`, - }, - selectedTab: { - key: `${ONYXKEYS.COLLECTION.SELECTED_TAB}${CONST.TAB.RECEIPT_TAB_ID}`, - }, -})(NewRequestAmountPage); From 125f18120c3dbc336c7af0a44d4a20ca923b7879 Mon Sep 17 00:00:00 2001 From: Tomasz Misiukiewicz Date: Thu, 21 Mar 2024 16:00:09 +0100 Subject: [PATCH 274/500] add client side logging tool to hidden menu --- .../ClientSideLoggingToolMenu/index.tsx | 50 +++++++++++++++++++ src/components/TestToolsModal.tsx | 2 + 2 files changed, 52 insertions(+) create mode 100644 src/components/ClientSideLoggingToolMenu/index.tsx diff --git a/src/components/ClientSideLoggingToolMenu/index.tsx b/src/components/ClientSideLoggingToolMenu/index.tsx new file mode 100644 index 000000000000..94baed584e1f --- /dev/null +++ b/src/components/ClientSideLoggingToolMenu/index.tsx @@ -0,0 +1,50 @@ +import React from 'react'; +import {withOnyx} from 'react-native-onyx'; +import type {OnyxEntry} from 'react-native-onyx'; +import Switch from '@components/Switch'; +import TestToolRow from '@components/TestToolRow'; +import * as Console from '@libs/actions/Console'; +import type {Log} from '@libs/Console'; +import ONYXKEYS from '@src/ONYXKEYS'; + +type CapturedLogs = Record; + +type ClientSideLoggingToolMenuOnyxProps = { + /** Logs captured on the current device */ + capturedLogs: OnyxEntry; + + /** Whether or not logs should be stored */ + shouldStoreLogs: OnyxEntry; +}; + +type ClientSideLoggingToolProps = ClientSideLoggingToolMenuOnyxProps; + +function ClientSideLoggingToolMenu({shouldStoreLogs, capturedLogs}: ClientSideLoggingToolProps) { + const onToggle = () => { + if (!shouldStoreLogs) { + Console.setShouldStoreLogs(true); + } else { + console.log({capturedLogs}); + Console.disableLoggingAndFlushLogs(); + } + }; + + return ( + + + + ); +} + +export default withOnyx({ + capturedLogs: { + key: ONYXKEYS.LOGS, + }, + shouldStoreLogs: { + key: ONYXKEYS.SHOULD_STORE_LOGS, + }, +})(ClientSideLoggingToolMenu); diff --git a/src/components/TestToolsModal.tsx b/src/components/TestToolsModal.tsx index 824162e63c51..4c6777a6b7d1 100644 --- a/src/components/TestToolsModal.tsx +++ b/src/components/TestToolsModal.tsx @@ -8,6 +8,7 @@ import useWindowDimensions from '@hooks/useWindowDimensions'; import toggleTestToolsModal from '@userActions/TestTool'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; +import ClientSideLoggingToolMenu from './ClientSideLoggingToolMenu'; import Modal from './Modal'; import ProfilingToolMenu from './ProfilingToolMenu'; import TestToolMenu from './TestToolMenu'; @@ -32,6 +33,7 @@ function TestToolsModal({isTestToolsModalOpen = false}: TestToolsModalProps) { > {isDevelopment && } + From 635c74bc3427ba5d0aa67fe8ce77d7248ba97339 Mon Sep 17 00:00:00 2001 From: Etotaziba Olei Date: Thu, 21 Mar 2024 18:27:59 +0100 Subject: [PATCH 275/500] No taxRates props needed for taxPicker --- .../workspace/taxes/WorkspaceTaxesSettingsForeignCurrency.tsx | 1 - .../workspace/taxes/WorkspaceTaxesSettingsWorkspaceCurrency.tsx | 1 - 2 files changed, 2 deletions(-) diff --git a/src/pages/workspace/taxes/WorkspaceTaxesSettingsForeignCurrency.tsx b/src/pages/workspace/taxes/WorkspaceTaxesSettingsForeignCurrency.tsx index 91d543b51b09..ccd9de846b75 100644 --- a/src/pages/workspace/taxes/WorkspaceTaxesSettingsForeignCurrency.tsx +++ b/src/pages/workspace/taxes/WorkspaceTaxesSettingsForeignCurrency.tsx @@ -56,7 +56,6 @@ function WorkspaceTaxesSettingsForeignCurrency({ diff --git a/src/pages/workspace/taxes/WorkspaceTaxesSettingsWorkspaceCurrency.tsx b/src/pages/workspace/taxes/WorkspaceTaxesSettingsWorkspaceCurrency.tsx index 2fe2985daa22..0536cc8e1999 100644 --- a/src/pages/workspace/taxes/WorkspaceTaxesSettingsWorkspaceCurrency.tsx +++ b/src/pages/workspace/taxes/WorkspaceTaxesSettingsWorkspaceCurrency.tsx @@ -56,7 +56,6 @@ function WorkspaceTaxesSettingsWorkspaceCurrency({ From d11cf7364741cfe6fb8e60a0097fc1a002749557 Mon Sep 17 00:00:00 2001 From: dukenv0307 Date: Fri, 22 Mar 2024 16:27:01 +0700 Subject: [PATCH 276/500] add type for step amount page --- src/libs/Navigation/types.ts | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/libs/Navigation/types.ts b/src/libs/Navigation/types.ts index 210c1fd006b4..80021e92a0fd 100644 --- a/src/libs/Navigation/types.ts +++ b/src/libs/Navigation/types.ts @@ -323,6 +323,13 @@ type RoomInviteNavigatorParamList = { }; type MoneyRequestNavigatorParamList = { + [SCREENS.MONEY_REQUEST.STEP_AMOUNT]: { + action: ValueOf; + iouType: ValueOf; + transactionID: string; + reportID: string; + backTo: string; + }; [SCREENS.MONEY_REQUEST.PARTICIPANTS]: { iouType: string; reportID: string; From 576786c9c3d483b25660a834b443f306813673da Mon Sep 17 00:00:00 2001 From: Tomasz Misiukiewicz Date: Fri, 22 Mar 2024 10:31:27 +0100 Subject: [PATCH 277/500] save and share logs from hidden menu --- .../ClientSideLoggingToolMenu/index.tsx | 54 +++++++++++++++---- .../ProfilingToolMenu/index.native.tsx | 25 ++++----- src/components/TestToolsModal.tsx | 11 +++- src/libs/Console/index.ts | 27 +++++++++- src/pages/settings/AboutPage/ConsolePage.tsx | 27 +--------- 5 files changed, 92 insertions(+), 52 deletions(-) diff --git a/src/components/ClientSideLoggingToolMenu/index.tsx b/src/components/ClientSideLoggingToolMenu/index.tsx index 94baed584e1f..43f895c8068d 100644 --- a/src/components/ClientSideLoggingToolMenu/index.tsx +++ b/src/components/ClientSideLoggingToolMenu/index.tsx @@ -1,10 +1,16 @@ -import React from 'react'; +import React, {useState} from 'react'; +import {Share} from 'react-native'; import {withOnyx} from 'react-native-onyx'; import type {OnyxEntry} from 'react-native-onyx'; +import Button from '@components/Button'; import Switch from '@components/Switch'; import TestToolRow from '@components/TestToolRow'; +import Text from '@components/Text'; +import useThemeStyles from '@hooks/useThemeStyles'; import * as Console from '@libs/actions/Console'; +import {parseStringifyMessages} from '@libs/Console'; import type {Log} from '@libs/Console'; +import localFileCreate from '@libs/localFileCreate'; import ONYXKEYS from '@src/ONYXKEYS'; type CapturedLogs = Record; @@ -20,23 +26,53 @@ type ClientSideLoggingToolMenuOnyxProps = { type ClientSideLoggingToolProps = ClientSideLoggingToolMenuOnyxProps; function ClientSideLoggingToolMenu({shouldStoreLogs, capturedLogs}: ClientSideLoggingToolProps) { + const [file, setFile] = useState<{path: string; newFileName: string; size: number}>(); + const styles = useThemeStyles(); + const onToggle = () => { if (!shouldStoreLogs) { + setFile(undefined); Console.setShouldStoreLogs(true); } else { - console.log({capturedLogs}); + const logs = Object.values(capturedLogs as Log[]); + const logsWithParsedMessages = parseStringifyMessages(logs); + localFileCreate('logs', JSON.stringify(logsWithParsedMessages, null, 2)).then((localFile) => { + setFile(localFile); + }); Console.disableLoggingAndFlushLogs(); } }; + const shareLogs = () => { + if (!file) { + return; + } + + Share.share({url: file.path, title: file.newFileName}); + }; + return ( - - - + <> + + + + {!!file && ( + <> + {`path: ${file.path}`} + +