diff --git a/android/app/build.gradle b/android/app/build.gradle
index f367beb199ba..25acb6f86df5 100644
--- a/android/app/build.gradle
+++ b/android/app/build.gradle
@@ -98,8 +98,8 @@ android {
minSdkVersion rootProject.ext.minSdkVersion
targetSdkVersion rootProject.ext.targetSdkVersion
multiDexEnabled rootProject.ext.multiDexEnabled
- versionCode 1001042901
- versionName "1.4.29-1"
+ versionCode 1001043000
+ versionName "1.4.30-0"
}
flavorDimensions "default"
diff --git a/ios/NewExpensify/Info.plist b/ios/NewExpensify/Info.plist
index d4078ea25068..124077702be2 100644
--- a/ios/NewExpensify/Info.plist
+++ b/ios/NewExpensify/Info.plist
@@ -19,7 +19,7 @@
CFBundlePackageType
APPL
CFBundleShortVersionString
- 1.4.29
+ 1.4.30
CFBundleSignature
????
CFBundleURLTypes
@@ -40,7 +40,7 @@
CFBundleVersion
- 1.4.29.1
+ 1.4.30.0
ITSAppUsesNonExemptEncryption
LSApplicationQueriesSchemes
diff --git a/ios/NewExpensifyTests/Info.plist b/ios/NewExpensifyTests/Info.plist
index 2d52c398bbf5..2554f5dce3f2 100644
--- a/ios/NewExpensifyTests/Info.plist
+++ b/ios/NewExpensifyTests/Info.plist
@@ -15,10 +15,10 @@
CFBundlePackageType
BNDL
CFBundleShortVersionString
- 1.4.29
+ 1.4.30
CFBundleSignature
????
CFBundleVersion
- 1.4.29.1
+ 1.4.30.0
diff --git a/ios/NotificationServiceExtension/Info.plist b/ios/NotificationServiceExtension/Info.plist
index f1b09af842f4..1069a5847a02 100644
--- a/ios/NotificationServiceExtension/Info.plist
+++ b/ios/NotificationServiceExtension/Info.plist
@@ -3,9 +3,9 @@
CFBundleShortVersionString
- 1.4.29
+ 1.4.30
CFBundleVersion
- 1.4.29.1
+ 1.4.30.0
NSExtension
NSExtensionPointIdentifier
diff --git a/package-lock.json b/package-lock.json
index 9979a58f53fa..3e5ae6e8d58c 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,12 +1,12 @@
{
"name": "new.expensify",
- "version": "1.4.29-1",
+ "version": "1.4.30-0",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "new.expensify",
- "version": "1.4.29-1",
+ "version": "1.4.30-0",
"hasInstallScript": true,
"license": "MIT",
"dependencies": {
diff --git a/package.json b/package.json
index bd01bd26a106..f1c077102930 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "new.expensify",
- "version": "1.4.29-1",
+ "version": "1.4.30-0",
"author": "Expensify, Inc.",
"homepage": "https://new.expensify.com",
"description": "New Expensify is the next generation of Expensify: a reimagination of payments based atop a foundation of chat.",
diff --git a/src/CONST.ts b/src/CONST.ts
index accce6e92555..813ef3e6b0cc 100755
--- a/src/CONST.ts
+++ b/src/CONST.ts
@@ -3133,4 +3133,8 @@ const CONST = {
MINI_CONTEXT_MENU_MAX_ITEMS: 4,
} as const;
+type Country = keyof typeof CONST.ALL_COUNTRIES;
+
+export type {Country};
+
export default CONST;
diff --git a/src/components/CountrySelector.js b/src/components/CountrySelector.tsx
similarity index 62%
rename from src/components/CountrySelector.js
rename to src/components/CountrySelector.tsx
index 68a6486bce48..589530cd7879 100644
--- a/src/components/CountrySelector.js
+++ b/src/components/CountrySelector.tsx
@@ -1,39 +1,30 @@
-import PropTypes from 'prop-types';
-import React, {useEffect} from 'react';
+import React, {forwardRef, useEffect} from 'react';
+import type {ForwardedRef} from 'react';
import {View} from 'react-native';
import useLocalize from '@hooks/useLocalize';
import useThemeStyles from '@hooks/useThemeStyles';
import Navigation from '@libs/Navigation/Navigation';
+import type {Country} from '@src/CONST';
import ROUTES from '@src/ROUTES';
import FormHelpMessage from './FormHelpMessage';
import MenuItemWithTopDescription from './MenuItemWithTopDescription';
-import refPropTypes from './refPropTypes';
-const propTypes = {
+type CountrySelectorProps = {
/** Form error text. e.g when no country is selected */
- errorText: PropTypes.string,
+ errorText?: string;
/** Callback called when the country changes. */
- onInputChange: PropTypes.func.isRequired,
+ onInputChange: (value?: string) => void;
/** Current selected country */
- value: PropTypes.string,
+ value?: Country;
/** 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: refPropTypes,
-};
-
-const defaultProps = {
- errorText: '',
- value: undefined,
- forwardedRef: () => {},
+ inputID: string;
};
-function CountrySelector({errorText, value: countryCode, onInputChange, forwardedRef}) {
+function CountrySelector({errorText = '', value: countryCode, onInputChange}: CountrySelectorProps, ref: ForwardedRef) {
const styles = useThemeStyles();
const {translate} = useLocalize();
@@ -51,12 +42,12 @@ function CountrySelector({errorText, value: countryCode, onInputChange, forwarde
{
const activeRoute = Navigation.getActiveRouteWithoutParams();
- Navigation.navigate(ROUTES.SETTINGS_PERSONAL_DETAILS_ADDRESS_COUNTRY.getRoute(countryCode, activeRoute));
+ Navigation.navigate(ROUTES.SETTINGS_PERSONAL_DETAILS_ADDRESS_COUNTRY.getRoute(countryCode ?? '', activeRoute));
}}
/>
@@ -66,18 +57,6 @@ function CountrySelector({errorText, value: countryCode, onInputChange, forwarde
);
}
-CountrySelector.propTypes = propTypes;
-CountrySelector.defaultProps = defaultProps;
CountrySelector.displayName = 'CountrySelector';
-const CountrySelectorWithRef = React.forwardRef((props, ref) => (
-
-));
-
-CountrySelectorWithRef.displayName = 'CountrySelectorWithRef';
-
-export default CountrySelectorWithRef;
+export default forwardRef(CountrySelector);
diff --git a/src/languages/en.ts b/src/languages/en.ts
index b6da38df21a0..0a754a883d07 100755
--- a/src/languages/en.ts
+++ b/src/languages/en.ts
@@ -1,6 +1,7 @@
import {CONST as COMMON_CONST} from 'expensify-common/lib/CONST';
import Str from 'expensify-common/lib/str';
import CONST from '@src/CONST';
+import type {Country} from '@src/CONST';
import type {
AddressLineParams,
AlreadySignedInParams,
@@ -106,7 +107,7 @@ type StateValue = {
type States = Record;
-type AllCountries = Record;
+type AllCountries = Record;
/* eslint-disable max-len */
export default {
diff --git a/src/libs/actions/IOU.js b/src/libs/actions/IOU.js
index 2b49fb8b7dce..d258b5419103 100644
--- a/src/libs/actions/IOU.js
+++ b/src/libs/actions/IOU.js
@@ -1183,6 +1183,21 @@ function updateMoneyRequestTag(transactionID, transactionThreadReportID, tag) {
API.write('UpdateMoneyRequestTag', params, onyxData);
}
+/**
+ * Updates the category of a money request
+ *
+ * @param {String} transactionID
+ * @param {Number} transactionThreadReportID
+ * @param {String} category
+ */
+function updateMoneyRequestCategory(transactionID, transactionThreadReportID, category) {
+ const transactionChanges = {
+ category,
+ };
+ const {params, onyxData} = getUpdateMoneyRequestParams(transactionID, transactionThreadReportID, transactionChanges, true);
+ API.write('UpdateMoneyRequestCategory', params, onyxData);
+}
+
/**
* Updates the description of a money request
*
@@ -1957,6 +1972,32 @@ function startSplitBill(participants, currentUserLogin, currentUserAccountID, co
});
});
+ _.each(participants, (participant) => {
+ const isPolicyExpenseChat = ReportUtils.isPolicyExpenseChat(participant);
+ if (!isPolicyExpenseChat) {
+ return;
+ }
+
+ const optimisticPolicyRecentlyUsedCategories = Policy.buildOptimisticPolicyRecentlyUsedCategories(participant.policyID, category);
+ const optimisticPolicyRecentlyUsedTags = Policy.buildOptimisticPolicyRecentlyUsedTags(participant.policyID, tag);
+
+ if (!_.isEmpty(optimisticPolicyRecentlyUsedCategories)) {
+ optimisticData.push({
+ onyxMethod: Onyx.METHOD.SET,
+ key: `${ONYXKEYS.COLLECTION.POLICY_RECENTLY_USED_CATEGORIES}${participant.policyID}`,
+ value: optimisticPolicyRecentlyUsedCategories,
+ });
+ }
+
+ if (!_.isEmpty(optimisticPolicyRecentlyUsedTags)) {
+ optimisticData.push({
+ onyxMethod: Onyx.METHOD.MERGE,
+ key: `${ONYXKEYS.COLLECTION.POLICY_RECENTLY_USED_TAGS}${participant.policyID}`,
+ value: optimisticPolicyRecentlyUsedTags,
+ });
+ }
+ });
+
// Save the new splits array into the transaction's comment in case the user calls CompleteSplitBill while offline
optimisticData.push({
onyxMethod: Onyx.METHOD.MERGE,
@@ -3693,6 +3734,7 @@ export {
updateMoneyRequestBillable,
updateMoneyRequestMerchant,
updateMoneyRequestTag,
+ updateMoneyRequestCategory,
updateMoneyRequestAmountAndCurrency,
updateMoneyRequestDescription,
replaceReceipt,
diff --git a/src/pages/EditRequestPage.js b/src/pages/EditRequestPage.js
index 606d3da1ddb9..3eb9d88f1120 100644
--- a/src/pages/EditRequestPage.js
+++ b/src/pages/EditRequestPage.js
@@ -110,12 +110,6 @@ function EditRequestPage({report, route, policyCategories, policyTags, parentRep
});
}, [parentReportAction, fieldToEdit]);
- // Update the transaction object and close the modal
- function editMoneyRequest(transactionChanges) {
- IOU.editMoneyRequest(transaction, report.reportID, transactionChanges);
- Navigation.dismissModal(report.reportID);
- }
-
const saveAmountAndCurrency = useCallback(
({amount, currency: newCurrency}) => {
const newAmount = CurrencyUtils.convertToBackendAmount(Number.parseFloat(amount));
@@ -176,6 +170,16 @@ function EditRequestPage({report, route, policyCategories, policyTags, parentRep
[transactionTag, transaction.transactionID, report.reportID],
);
+ const saveCategory = useCallback(
+ ({category: newCategory}) => {
+ // In case the same category has been selected, reset the category.
+ const updatedCategory = newCategory === transactionCategory ? '' : newCategory;
+ IOU.updateMoneyRequestCategory(transaction.transactionID, report.reportID, updatedCategory);
+ Navigation.dismissModal();
+ },
+ [transactionCategory, transaction.transactionID, report.reportID],
+ );
+
const saveComment = useCallback(
({comment: newComment}) => {
// Only update comment if it has changed
@@ -235,14 +239,7 @@ function EditRequestPage({report, route, policyCategories, policyTags, parentRep
{
- let updatedCategory = transactionChanges.category;
- // In case the same category has been selected, do reset of the category.
- if (transactionCategory === updatedCategory) {
- updatedCategory = '';
- }
- editMoneyRequest({category: updatedCategory});
- }}
+ onSubmit={saveCategory}
/>
);
}