diff --git a/.github/ISSUE_TEMPLATE/Accessibility.md b/.github/ISSUE_TEMPLATE/Accessibility.md
index 1323e2c17e78..97fc17d28a94 100644
--- a/.github/ISSUE_TEMPLATE/Accessibility.md
+++ b/.github/ISSUE_TEMPLATE/Accessibility.md
@@ -35,12 +35,12 @@ What can we do to fix the issue?
Check off any platforms that are affected by this issue
--->
Which of our officially supported platforms is this issue occurring on? Please only tick the box if you have provided a screen-recording in the thread for each platform:
-- [ ] Android / native
-- [ ] Android / Chrome
-- [ ] iOS / native
-- [ ] iOS / Safari
-- [ ] MacOS / Chrome / Safari
-- [ ] MacOS / Desktop
+- [ ] Android: Native
+- [ ] Android: mWeb Chrome
+- [ ] iOS: Native
+- [ ] iOS: mWeb Safari
+- [ ] MacOS: Chrome / Safari
+- [ ] MacOS: Desktop
**Version Number:**
**Reproducible in staging?:**
diff --git a/.github/ISSUE_TEMPLATE/Performance.md b/.github/ISSUE_TEMPLATE/Performance.md
index 67b2e6971874..bbb729e8af31 100644
--- a/.github/ISSUE_TEMPLATE/Performance.md
+++ b/.github/ISSUE_TEMPLATE/Performance.md
@@ -28,12 +28,12 @@ Note: These should be the same as the benchmarks collected before any changes.
Check off any platforms that are affected by this issue
--->
Which of our officially supported platforms is this issue occurring on?
-- [ ] Android / native
-- [ ] Android / Chrome
-- [ ] iOS / native
-- [ ] iOS / Safari
-- [ ] MacOS / Chrome / Safari
-- [ ] MacOS / Desktop
+- [ ] Android: Native
+- [ ] Android: mWeb Chrome
+- [ ] iOS: Native
+- [ ] iOS: mWeb Safari
+- [ ] MacOS: Chrome / Safari
+- [ ] MacOS: Desktop
**Version Number:**
**Reproducible in staging?:**
diff --git a/.github/ISSUE_TEMPLATE/Standard.md b/.github/ISSUE_TEMPLATE/Standard.md
index 39d1c38fa56f..5e0e3633f3bc 100644
--- a/.github/ISSUE_TEMPLATE/Standard.md
+++ b/.github/ISSUE_TEMPLATE/Standard.md
@@ -7,6 +7,16 @@ labels: Bug, Daily
If you haven’t already, check out our [contributing guidelines](https://github.com/Expensify/ReactNativeChat/blob/main/contributingGuides/CONTRIBUTING.md) for onboarding and email contributors@expensify.com to request to join our Slack channel!
___
+**Version Number:**
+**Reproducible in staging?:**
+**Reproducible in production?:**
+**If this was caught during regression testing, add the test name, ID and link from TestRail:**
+**Email or phone of affected tester (no customers):**
+**Logs:** https://stackoverflow.com/c/expensify/questions/4856
+**Expensify/Expensify Issue URL:**
+**Issue reported by:**
+**Slack conversation:**
+
## Action Performed:
Break down in numbered steps
@@ -24,22 +34,54 @@ Can the user still use Expensify without this being fixed? Have you informed the
Check off any platforms that are affected by this issue
--->
Which of our officially supported platforms is this issue occurring on?
-- [ ] Android / native
-- [ ] Android / Chrome
-- [ ] iOS / native
-- [ ] iOS / Safari
-- [ ] MacOS / Chrome / Safari
-- [ ] MacOS / Desktop
+- [ ] Android: Native
+- [ ] Android: mWeb Chrome
+- [ ] iOS: Native
+- [ ] iOS: mWeb Safari
+- [ ] MacOS: Chrome / Safari
+- [ ] MacOS: Desktop
-**Version Number:**
-**Reproducible in staging?:**
-**Reproducible in production?:**
-**If this was caught during regression testing, add the test name, ID and link from TestRail:**
-**Email or phone of affected tester (no customers):**
-**Logs:** https://stackoverflow.com/c/expensify/questions/4856
-**Notes/Photos/Videos:** Any additional supporting documentation
-**Expensify/Expensify Issue URL:**
-**Issue reported by:**
-**Slack conversation:**
+## Screenshots/Videos
+
+Android: Native
+
+
+
+
+
+
+Android: mWeb Chrome
+
+
+
+
+
+
+iOS: Native
+
+
+
+
+
+
+iOS: mWeb Safari
+
+
+
+
+
+
+MacOS: Chrome / Safari
+
+
+
+
+
+
+MacOS: Desktop
+
+
+
+
[View all open jobs on GitHub](https://github.com/Expensify/App/issues?q=is%3Aopen+is%3Aissue+label%3A%22Help+Wanted%22)
diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md
index 800888580518..0396a7543b50 100644
--- a/.github/PULL_REQUEST_TEMPLATE.md
+++ b/.github/PULL_REQUEST_TEMPLATE.md
@@ -70,12 +70,12 @@ This is a checklist for PR authors. Please make sure to complete all tasks and c
- [ ] I tested this PR with a [High Traffic account](https://github.com/Expensify/App/blob/main/contributingGuides/CONTRIBUTING.md#high-traffic-accounts) against the staging or production API to ensure there are no regressions (e.g. long loading states that impact usability).
- [ ] I included screenshots or videos for tests on [all platforms](https://github.com/Expensify/App/blob/main/contributingGuides/CONTRIBUTING.md#make-sure-you-can-test-on-all-platforms)
- [ ] I ran the tests on **all platforms** & verified they passed on:
- - [ ] Android / native
- - [ ] Android / Chrome
- - [ ] iOS / native
- - [ ] iOS / Safari
- - [ ] MacOS / Chrome / Safari
- - [ ] MacOS / Desktop
+ - [ ] Android: Native
+ - [ ] Android: mWeb Chrome
+ - [ ] iOS: Native
+ - [ ] iOS: mWeb Safari
+ - [ ] MacOS: Chrome / Safari
+ - [ ] MacOS: Desktop
- [ ] I verified there are no console errors (if there's a console error not related to the PR, report it or open an issue for it to be fixed)
- [ ] I followed proper code patterns (see [Reviewing the code](https://github.com/Expensify/App/blob/main/contributingGuides/PR_REVIEW_GUIDELINES.md#reviewing-the-code))
- [ ] I verified that any callback methods that were added or modified are named for what the method does and never what callback they handle (i.e. `toggleReport` and not `onIconClick`)
@@ -120,42 +120,42 @@ This is a checklist for PR authors. Please make sure to complete all tasks and c
### Screenshots/Videos
-Web
+Android: Native
-Mobile Web - Chrome
+Android: mWeb Chrome
-Mobile Web - Safari
+iOS: Native
-Desktop
+iOS: mWeb Safari
-iOS
+MacOS: Chrome / Safari
-Android
+MacOS: Desktop
diff --git a/contributingGuides/REVIEWER_CHECKLIST.md b/contributingGuides/REVIEWER_CHECKLIST.md
index 6a5dffc8b073..d52d80a818bb 100644
--- a/contributingGuides/REVIEWER_CHECKLIST.md
+++ b/contributingGuides/REVIEWER_CHECKLIST.md
@@ -10,12 +10,12 @@
- [ ] I checked that screenshots or videos are included for tests on [all platforms](https://github.com/Expensify/App/blob/main/contributingGuides/CONTRIBUTING.md#make-sure-you-can-test-on-all-platforms)
- [ ] I included screenshots or videos for tests on [all platforms](https://github.com/Expensify/App/blob/main/contributingGuides/CONTRIBUTING.md#make-sure-you-can-test-on-all-platforms)
- [ ] I verified tests pass on **all platforms** & I tested again on:
- - [ ] Android / native
- - [ ] Android / Chrome
- - [ ] iOS / native
- - [ ] iOS / Safari
- - [ ] MacOS / Chrome / Safari
- - [ ] MacOS / Desktop
+ - [ ] Android: Native
+ - [ ] Android: mWeb Chrome
+ - [ ] iOS: Native
+ - [ ] iOS: mWeb Safari
+ - [ ] MacOS: Chrome / Safari
+ - [ ] MacOS: Desktop
- [ ] If there are any errors in the console that are unrelated to this PR, I either fixed them (preferred) or linked to where I reported them in Slack
- [ ] I verified proper code patterns were followed (see [Reviewing the code](https://github.com/Expensify/App/blob/main/contributingGuides/PR_REVIEW_GUIDELINES.md#reviewing-the-code))
- [ ] I verified that any callback methods that were added or modified are named for what the method does and never what callback they handle (i.e. `toggleReport` and not `onIconClick`).
diff --git a/src/CONST.ts b/src/CONST.ts
index 1ffa64efe28c..652a38200ab1 100755
--- a/src/CONST.ts
+++ b/src/CONST.ts
@@ -931,6 +931,7 @@ const CONST = {
RECEIPTS: 'receipts@expensify.com',
STUDENT_AMBASSADOR: 'studentambassadors@expensify.com',
SVFG: 'svfg@expensify.com',
+ EXPENSIFY_EMAIL_DOMAIN: '@expensify.com',
},
ACCOUNT_ID: {
@@ -2726,6 +2727,8 @@ const CONST = {
SPEND: 'spend',
WORKSPACES: 'workspaces',
},
+
+ MISSING_TRANSLATION: 'MISSING TRANSLATION',
} as const;
export default CONST;
diff --git a/src/components/MoneyRequestConfirmationList.js b/src/components/MoneyRequestConfirmationList.js
index b71047e3ca36..a427ba0b0718 100755
--- a/src/components/MoneyRequestConfirmationList.js
+++ b/src/components/MoneyRequestConfirmationList.js
@@ -141,6 +141,9 @@ const propTypes = {
/** Whether the money request is a distance request */
isDistanceRequest: PropTypes.bool,
+ /** A flag for verifying that the current report is a sub-report of a workspace chat */
+ isPolicyExpenseChat: PropTypes.bool,
+
/* Onyx Props */
/** Collection of categories attached to a policy */
policyCategories: PropTypes.objectOf(categoryPropTypes),
@@ -176,6 +179,7 @@ const defaultProps = {
transaction: {},
mileageRate: {unit: CONST.CUSTOM_UNITS.DISTANCE_UNIT_MILES, rate: 0, currency: 'USD'},
isDistanceRequest: false,
+ isPolicyExpenseChat: false,
};
function MoneyRequestConfirmationList(props) {
@@ -193,11 +197,8 @@ function MoneyRequestConfirmationList(props) {
const distance = lodashGet(transaction, 'routes.route0.distance', 0);
const shouldCalculateDistanceAmount = props.isDistanceRequest && props.iouAmount === 0;
- // A flag for verifying that the current report is a sub-report of a workspace chat
- const isPolicyExpenseChat = useMemo(() => ReportUtils.isPolicyExpenseChat(ReportUtils.getRootParentReport(ReportUtils.getReport(props.reportID))), [props.reportID]);
-
// A flag for showing the categories field
- const shouldShowCategories = isPolicyExpenseChat && Permissions.canUseCategories(props.betas) && OptionsListUtils.hasEnabledOptions(_.values(props.policyCategories));
+ const shouldShowCategories = props.isPolicyExpenseChat && Permissions.canUseCategories(props.betas) && OptionsListUtils.hasEnabledOptions(_.values(props.policyCategories));
// Fetches the first tag list of the policy
const policyTag = PolicyUtils.getTag(props.policyTags);
@@ -205,7 +206,7 @@ function MoneyRequestConfirmationList(props) {
const policyTagListName = lodashGet(policyTag, 'name', translate('common.tag'));
const canUseTags = Permissions.canUseTags(props.betas);
// A flag for showing the tags field
- const shouldShowTags = isPolicyExpenseChat && canUseTags && OptionsListUtils.hasEnabledOptions(_.values(policyTagList));
+ const shouldShowTags = props.isPolicyExpenseChat && canUseTags && OptionsListUtils.hasEnabledOptions(_.values(policyTagList));
// A flag for showing the billable field
const shouldShowBillable = canUseTags && !lodashGet(props.policy, 'disabledFields.defaultBillable', true);
diff --git a/src/components/OptionRow.js b/src/components/OptionRow.js
index 70415ab03a13..47ab4fe45db1 100644
--- a/src/components/OptionRow.js
+++ b/src/components/OptionRow.js
@@ -282,6 +282,7 @@ class OptionRow extends Component {
) : (
this.props.onSelectedStatePressed(this.props.option)}
+ disabled={this.state.isDisabled}
accessibilityRole={CONST.ACCESSIBILITY_ROLE.CHECKBOX}
accessibilityLabel={CONST.ACCESSIBILITY_ROLE.CHECKBOX}
>
diff --git a/src/libs/Localize/index.js b/src/libs/Localize/index.js
index db371301f43f..a26c7d4ebc10 100644
--- a/src/libs/Localize/index.js
+++ b/src/libs/Localize/index.js
@@ -2,12 +2,27 @@ import _ from 'underscore';
import lodashGet from 'lodash/get';
import Str from 'expensify-common/lib/str';
import * as RNLocalize from 'react-native-localize';
+import Onyx from 'react-native-onyx';
import Log from '../Log';
import Config from '../../CONFIG';
import translations from '../../languages/translations';
import CONST from '../../CONST';
import LocaleListener from './LocaleListener';
import BaseLocaleListener from './LocaleListener/BaseLocaleListener';
+import ONYXKEYS from '../../ONYXKEYS';
+
+// Current user mail is needed for handling missing translations
+let userEmail = '';
+Onyx.connect({
+ key: ONYXKEYS.SESSION,
+ waitForCollectionCallback: true,
+ callback: (val) => {
+ if (!val) {
+ return;
+ }
+ userEmail = val.email;
+ },
+});
// Listener when an update in Onyx happens so we use the updated locale when translating/localizing items.
LocaleListener.connect();
@@ -70,11 +85,14 @@ function translate(desiredLanguage = CONST.LOCALES.DEFAULT, phraseKey, phrasePar
return Str.result(translatedPhrase, phraseParameters);
}
- // Phrase is not found in default language, on production log an alert to server
+ // Phrase is not found in default language, on production and staging log an alert to server
// on development throw an error
- if (Config.IS_IN_PRODUCTION) {
+ if (Config.IS_IN_PRODUCTION || Config.IS_IN_STAGING) {
const phraseString = _.isArray(phraseKey) ? phraseKey.join('.') : phraseKey;
Log.alert(`${phraseString} was not found in the en locale`);
+ if (userEmail.includes(CONST.EMAIL.EXPENSIFY_EMAIL_DOMAIN)) {
+ return CONST.MISSING_TRANSLATION;
+ }
return phraseString;
}
throw new Error(`${phraseKey} was not found in the default language`);
diff --git a/src/libs/actions/IOU.js b/src/libs/actions/IOU.js
index 44eba0cef5a5..c99c8d17175a 100644
--- a/src/libs/actions/IOU.js
+++ b/src/libs/actions/IOU.js
@@ -22,6 +22,7 @@ import * as Report from './Report';
import * as NumberUtils from '../NumberUtils';
import ReceiptGeneric from '../../../assets/images/receipt-generic.png';
import * as LocalePhoneNumber from '../LocalePhoneNumber';
+import * as Policy from './Policy';
let allReports;
Onyx.connect({
@@ -44,13 +45,6 @@ Onyx.connect({
},
});
-let allRecentlyUsedCategories = {};
-Onyx.connect({
- key: ONYXKEYS.COLLECTION.POLICY_RECENTLY_USED_CATEGORIES,
- waitForCollectionCallback: true,
- callback: (val) => (allRecentlyUsedCategories = val),
-});
-
let allRecentlyUsedTags = {};
Onyx.connect({
key: ONYXKEYS.COLLECTION.POLICY_RECENTLY_USED_TAGS,
@@ -137,7 +131,7 @@ function buildOnyxDataForMoneyRequest(
iouAction,
optimisticPersonalDetailListAction,
reportPreviewAction,
- optimisticRecentlyUsedCategories,
+ optimisticPolicyRecentlyUsedCategories,
optimisticPolicyRecentlyUsedTags,
isNewChatReport,
isNewIOUReport,
@@ -189,11 +183,11 @@ function buildOnyxDataForMoneyRequest(
},
];
- if (!_.isEmpty(optimisticRecentlyUsedCategories)) {
+ if (!_.isEmpty(optimisticPolicyRecentlyUsedCategories)) {
optimisticData.push({
onyxMethod: Onyx.METHOD.SET,
key: `${ONYXKEYS.COLLECTION.POLICY_RECENTLY_USED_CATEGORIES}${iouReport.policyID}`,
- value: optimisticRecentlyUsedCategories,
+ value: optimisticPolicyRecentlyUsedCategories,
});
}
@@ -478,13 +472,7 @@ function getMoneyRequestInformation(
billable,
);
- const uniquePolicyRecentlyUsedCategories = allRecentlyUsedCategories
- ? _.filter(
- allRecentlyUsedCategories[`${ONYXKEYS.COLLECTION.POLICY_RECENTLY_USED_CATEGORIES}${iouReport.policyID}`],
- (recentlyUsedPolicyCategory) => recentlyUsedPolicyCategory !== category,
- )
- : [];
- const optimisticPolicyRecentlyUsedCategories = [category, ...uniquePolicyRecentlyUsedCategories];
+ const optimisticPolicyRecentlyUsedCategories = Policy.buildOptimisticPolicyRecentlyUsedCategories(iouReport.policyID, category);
const optimisticPolicyRecentlyUsedTags = {};
const policyTags = allPolicyTags[`${ONYXKEYS.COLLECTION.POLICY_TAGS}${iouReport.policyID}`];
@@ -882,11 +870,12 @@ function requestMoney(
* @param {Number} amount - always in the smallest unit of the currency
* @param {String} comment
* @param {String} currency
+ * @param {String} category
* @param {String} existingSplitChatReportID - the report ID where the split bill happens, could be a group chat or a workspace chat
*
* @return {Object}
*/
-function createSplitsAndOnyxData(participants, currentUserLogin, currentUserAccountID, amount, comment, currency, existingSplitChatReportID = '') {
+function createSplitsAndOnyxData(participants, currentUserLogin, currentUserAccountID, amount, comment, currency, category, existingSplitChatReportID = '') {
const currentUserEmailForIOUSplit = OptionsListUtils.addSMSDomainIfPhoneNumber(currentUserLogin);
const participantAccountIDs = _.map(participants, (participant) => Number(participant.accountID));
const existingSplitChatReport =
@@ -910,6 +899,10 @@ function createSplitsAndOnyxData(participants, currentUserLogin, currentUserAcco
'',
'',
`${Localize.translateLocal('iou.splitBill')} ${Localize.translateLocal('common.with')} ${formattedParticipants} [${DateUtils.getDBTime().slice(0, 10)}]`,
+ undefined,
+ undefined,
+ undefined,
+ category,
);
// Note: The created action must be optimistically generated before the IOU action so there's no chance that the created action appears after the IOU action in the chat
@@ -1036,9 +1029,12 @@ function createSplitsAndOnyxData(participants, currentUserLogin, currentUserAcco
const hasMultipleParticipants = participants.length > 1;
_.each(participants, (participant) => {
- // In case the participant is a worskapce, email & accountID should remain undefined and won't be used in the rest of this code
- const email = isOwnPolicyExpenseChat ? '' : OptionsListUtils.addSMSDomainIfPhoneNumber(participant.login).toLowerCase();
- const accountID = isOwnPolicyExpenseChat ? 0 : Number(participant.accountID);
+ // In a case when a participant is a workspace, even when a current user is not an owner of the workspace
+ const isPolicyExpenseChat = ReportUtils.isPolicyExpenseChat(participant);
+
+ // In case the participant is a workspace, email & accountID should remain undefined and won't be used in the rest of this code
+ const email = isOwnPolicyExpenseChat || isPolicyExpenseChat ? '' : OptionsListUtils.addSMSDomainIfPhoneNumber(participant.login).toLowerCase();
+ const accountID = isOwnPolicyExpenseChat || isPolicyExpenseChat ? 0 : Number(participant.accountID);
if (email === currentUserEmailForIOUSplit) {
return;
}
@@ -1090,6 +1086,11 @@ function createSplitsAndOnyxData(participants, currentUserLogin, currentUserAcco
'',
CONST.IOU.MONEY_REQUEST_TYPE.SPLIT,
splitTransaction.transactionID,
+ undefined,
+ undefined,
+ undefined,
+ undefined,
+ category,
);
// STEP 4: Build optimistic reportActions. We need:
@@ -1131,6 +1132,12 @@ function createSplitsAndOnyxData(participants, currentUserLogin, currentUserAcco
oneOnOneReportPreviewAction = ReportUtils.buildOptimisticReportPreview(oneOnOneChatReport, oneOnOneIOUReport);
}
+ // Add category to optimistic policy recently used categories when a participant is a workspace
+ let optimisticPolicyRecentlyUsedCategories = [];
+ if (isPolicyExpenseChat) {
+ optimisticPolicyRecentlyUsedCategories = Policy.buildOptimisticPolicyRecentlyUsedCategories(participant.policyID, category);
+ }
+
// STEP 5: Build Onyx Data
const [oneOnOneOptimisticData, oneOnOneSuccessData, oneOnOneFailureData] = buildOnyxDataForMoneyRequest(
oneOnOneChatReport,
@@ -1141,7 +1148,7 @@ function createSplitsAndOnyxData(participants, currentUserLogin, currentUserAcco
oneOnOneIOUAction,
oneOnOnePersonalDetailListAction,
oneOnOneReportPreviewAction,
- [],
+ optimisticPolicyRecentlyUsedCategories,
{},
isNewOneOnOneChatReport,
shouldCreateNewOneOnOneIOUReport,
@@ -1191,11 +1198,11 @@ function createSplitsAndOnyxData(participants, currentUserLogin, currentUserAcco
* @param {Number} amount - always in smallest currency unit
* @param {String} comment
* @param {String} currency
+ * @param {String} category
* @param {String} existingSplitChatReportID - Either a group DM or a workspace chat
*/
-function splitBill(participants, currentUserLogin, currentUserAccountID, amount, comment, currency, existingSplitChatReportID = '') {
- const {splitData, splits, onyxData} = createSplitsAndOnyxData(participants, currentUserLogin, currentUserAccountID, amount, comment, currency, existingSplitChatReportID);
-
+function splitBill(participants, currentUserLogin, currentUserAccountID, amount, comment, currency, category, existingSplitChatReportID = '') {
+ const {splitData, splits, onyxData} = createSplitsAndOnyxData(participants, currentUserLogin, currentUserAccountID, amount, comment, currency, category, existingSplitChatReportID);
API.write(
'SplitBill',
{
@@ -1204,6 +1211,7 @@ function splitBill(participants, currentUserLogin, currentUserAccountID, amount,
splits: JSON.stringify(splits),
currency,
comment,
+ category,
transactionID: splitData.transactionID,
reportActionID: splitData.reportActionID,
createdReportActionID: splitData.createdReportActionID,
@@ -1224,9 +1232,10 @@ function splitBill(participants, currentUserLogin, currentUserAccountID, amount,
* @param {Number} amount - always in smallest currency unit
* @param {String} comment
* @param {String} currency
+ * @param {String} category
*/
-function splitBillAndOpenReport(participants, currentUserLogin, currentUserAccountID, amount, comment, currency) {
- const {splitData, splits, onyxData} = createSplitsAndOnyxData(participants, currentUserLogin, currentUserAccountID, amount, comment, currency);
+function splitBillAndOpenReport(participants, currentUserLogin, currentUserAccountID, amount, comment, currency, category) {
+ const {splitData, splits, onyxData} = createSplitsAndOnyxData(participants, currentUserLogin, currentUserAccountID, amount, comment, currency, category);
API.write(
'SplitBillAndOpenReport',
@@ -1236,6 +1245,7 @@ function splitBillAndOpenReport(participants, currentUserLogin, currentUserAccou
splits: JSON.stringify(splits),
currency,
comment,
+ category,
transactionID: splitData.transactionID,
reportActionID: splitData.reportActionID,
createdReportActionID: splitData.createdReportActionID,
@@ -2376,6 +2386,21 @@ function navigateToNextPage(iou, iouType, report, path = '') {
Navigation.navigate(ROUTES.MONEY_REQUEST_PARTICIPANTS.getRoute(iouType));
}
+/**
+ * When the money request or split bill creation flow is initialized via FAB, the reportID is not passed as a navigation
+ * parameter.
+ * Gets a report id from the first participant of the IOU object stored in Onyx.
+ * @param {Object} iou
+ * @param {Array} iou.participants
+ * @param {Object} route
+ * @param {Object} route.params
+ * @param {String} [route.params.reportID]
+ * @returns {String}
+ */
+function getIOUReportID(iou, route) {
+ return lodashGet(route, 'params.reportID') || lodashGet(iou, 'participants.0.reportID', '');
+}
+
export {
createDistanceRequest,
editMoneyRequest,
@@ -2407,4 +2432,5 @@ export {
navigateToNextPage,
updateDistanceRequest,
replaceReceipt,
+ getIOUReportID,
};
diff --git a/src/libs/actions/Policy.js b/src/libs/actions/Policy.js
index fcce909c5582..3e5e91a74488 100644
--- a/src/libs/actions/Policy.js
+++ b/src/libs/actions/Policy.js
@@ -1,6 +1,7 @@
import _ from 'underscore';
import Onyx from 'react-native-onyx';
import lodashGet from 'lodash/get';
+import lodashUnion from 'lodash/union';
import {PUBLIC_DOMAINS} from 'expensify-common/lib/CONST';
import Str from 'expensify-common/lib/str';
import {escapeRegExp} from 'lodash';
@@ -65,6 +66,13 @@ Onyx.connect({
callback: (val) => (allPersonalDetails = val),
});
+let allRecentlyUsedCategories = {};
+Onyx.connect({
+ key: ONYXKEYS.COLLECTION.POLICY_RECENTLY_USED_CATEGORIES,
+ waitForCollectionCallback: true,
+ callback: (val) => (allRecentlyUsedCategories = val),
+});
+
/**
* Stores in Onyx the policy ID of the last workspace that was accessed by the user
* @param {String|null} policyID
@@ -1168,6 +1176,21 @@ function clearErrors(policyID) {
hideWorkspaceAlertMessage(policyID);
}
+/**
+ * @param {String} policyID
+ * @param {String} category
+ * @returns {Object}
+ */
+function buildOptimisticPolicyRecentlyUsedCategories(policyID, category) {
+ if (!policyID || !category) {
+ return [];
+ }
+
+ const policyRecentlyUsedCategories = lodashGet(allRecentlyUsedCategories, `${ONYXKEYS.COLLECTION.POLICY_RECENTLY_USED_CATEGORIES}${policyID}`, []);
+
+ return lodashUnion([category], policyRecentlyUsedCategories);
+}
+
export {
removeMembers,
addMembersToWorkspace,
@@ -1197,4 +1220,5 @@ export {
setWorkspaceInviteMembersDraft,
clearErrors,
openDraftWorkspaceRequest,
+ buildOptimisticPolicyRecentlyUsedCategories,
};
diff --git a/src/pages/iou/MoneyRequestCategoryPage.js b/src/pages/iou/MoneyRequestCategoryPage.js
index 5284b785304f..5055c9f90f8a 100644
--- a/src/pages/iou/MoneyRequestCategoryPage.js
+++ b/src/pages/iou/MoneyRequestCategoryPage.js
@@ -2,6 +2,7 @@ import React from 'react';
import PropTypes from 'prop-types';
import lodashGet from 'lodash/get';
import {withOnyx} from 'react-native-onyx';
+import compose from '../../libs/compose';
import ROUTES from '../../ROUTES';
import Navigation from '../../libs/Navigation/Navigation';
import useLocalize from '../../hooks/useLocalize';
@@ -83,11 +84,20 @@ MoneyRequestCategoryPage.displayName = 'MoneyRequestCategoryPage';
MoneyRequestCategoryPage.propTypes = propTypes;
MoneyRequestCategoryPage.defaultProps = defaultProps;
-export default withOnyx({
- report: {
- key: ({route}) => `${ONYXKEYS.COLLECTION.REPORT}${lodashGet(route, 'params.reportID', '')}`,
- },
- iou: {
- key: ONYXKEYS.IOU,
- },
-})(MoneyRequestCategoryPage);
+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/MoneyRequestTagPage.js b/src/pages/iou/MoneyRequestTagPage.js
index 43c0cd9d1480..2fc37d2c4271 100644
--- a/src/pages/iou/MoneyRequestTagPage.js
+++ b/src/pages/iou/MoneyRequestTagPage.js
@@ -98,14 +98,21 @@ MoneyRequestTagPage.defaultProps = defaultProps;
export default compose(
withOnyx({
- report: {
- key: ({route}) => `${ONYXKEYS.COLLECTION.REPORT}${lodashGet(route, 'params.reportID')}`,
- },
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'}`,
diff --git a/src/pages/iou/steps/MoneyRequestConfirmPage.js b/src/pages/iou/steps/MoneyRequestConfirmPage.js
index 3881221d5c52..907869c0e3a4 100644
--- a/src/pages/iou/steps/MoneyRequestConfirmPage.js
+++ b/src/pages/iou/steps/MoneyRequestConfirmPage.js
@@ -75,8 +75,14 @@ function MoneyRequestConfirmPage(props) {
}),
[props.iou.participants, props.personalDetails],
);
+ const isPolicyExpenseChat = useMemo(() => ReportUtils.isPolicyExpenseChat(ReportUtils.getRootParentReport(props.report)), [props.report]);
const isManualRequestDM = props.selectedTab === CONST.TAB.MANUAL && iouType.current === CONST.IOU.MONEY_REQUEST_TYPE.REQUEST;
+ useEffect(() => {
+ IOU.resetMoneyRequestCategory();
+ IOU.resetMoneyRequestTag();
+ }, []);
+
useEffect(() => {
const policyExpenseChat = _.find(participants, (participant) => participant.isPolicyExpenseChat);
if (policyExpenseChat) {
@@ -198,6 +204,7 @@ function MoneyRequestConfirmPage(props) {
props.iou.amount,
trimmedComment,
props.iou.currency,
+ props.iou.category,
reportID.current,
);
return;
@@ -212,6 +219,7 @@ function MoneyRequestConfirmPage(props) {
props.iou.amount,
trimmedComment,
props.iou.currency,
+ props.iou.category,
);
return;
}
@@ -238,6 +246,7 @@ function MoneyRequestConfirmPage(props) {
props.currentUserPersonalDetails.login,
props.currentUserPersonalDetails.accountID,
props.iou.currency,
+ props.iou.category,
props.iou.receiptPath,
props.iou.receiptSource,
isDistanceRequest,
@@ -338,6 +347,7 @@ function MoneyRequestConfirmPage(props) {
receiptSource={props.iou.receiptSource}
iouType={iouType.current}
reportID={reportID.current}
+ 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
@@ -375,12 +385,8 @@ export default compose(
withOnyx({
report: {
key: ({route, iou}) => {
- let reportID = lodashGet(route, 'params.reportID', '');
- if (!reportID) {
- // When the money request creation flow is initialized on Global Create, the reportID is not passed as a navigation parameter.
- // Get the report id from the participants list on the IOU object stored in Onyx.
- reportID = lodashGet(iou, 'participants.0.reportID', '');
- }
+ const reportID = IOU.getIOUReportID(iou, route);
+
return `${ONYXKEYS.COLLECTION.REPORT}${reportID}`;
},
},
diff --git a/src/pages/iou/steps/MoneyRequstParticipantsPage/MoneyRequestParticipantsPage.js b/src/pages/iou/steps/MoneyRequstParticipantsPage/MoneyRequestParticipantsPage.js
index 8d745903eb40..89c18efc4e76 100644
--- a/src/pages/iou/steps/MoneyRequstParticipantsPage/MoneyRequestParticipantsPage.js
+++ b/src/pages/iou/steps/MoneyRequstParticipantsPage/MoneyRequestParticipantsPage.js
@@ -45,7 +45,6 @@ const defaultProps = {
function MoneyRequestParticipantsPage({iou, selectedTab, route}) {
const {translate} = useLocalize();
const prevMoneyRequestId = useRef(iou.id);
- const isNewReportIDSelectedLocally = useRef(false);
const optionsSelectorRef = useRef();
const iouType = useRef(lodashGet(route, 'params.iouType', ''));
const reportID = useRef(lodashGet(route, 'params.reportID', ''));
@@ -63,19 +62,7 @@ function MoneyRequestParticipantsPage({iou, selectedTab, route}) {
setHeaderTitle(_.isEmpty(iou.participants) ? translate('tabSelector.manual') : translate('iou.split'));
}, [iou.participants, isDistanceRequest, translate]);
- const navigateToRequestStep = (moneyRequestType, option) => {
- if (option.reportID) {
- isNewReportIDSelectedLocally.current = true;
- IOU.setMoneyRequestId(`${moneyRequestType}${option.reportID}`);
- Navigation.navigate(ROUTES.MONEY_REQUEST_CONFIRMATION.getRoute(moneyRequestType, option.reportID));
- return;
- }
-
- IOU.setMoneyRequestId(moneyRequestType);
- Navigation.navigate(ROUTES.MONEY_REQUEST_CONFIRMATION.getRoute(moneyRequestType, reportID.current));
- };
-
- const navigateToSplitStep = (moneyRequestType) => {
+ const navigateToConfirmationStep = (moneyRequestType) => {
IOU.setMoneyRequestId(moneyRequestType);
Navigation.navigate(ROUTES.MONEY_REQUEST_CONFIRMATION.getRoute(moneyRequestType, reportID.current));
};
@@ -88,7 +75,7 @@ function MoneyRequestParticipantsPage({iou, selectedTab, route}) {
// 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 && !isDistanceRequest && !isSplitRequest && !isNewReportIDSelectedLocally.current) {
+ if (iou.id && !isDistanceRequest && !isSplitRequest) {
navigateBack(true);
}
return;
@@ -96,7 +83,7 @@ function MoneyRequestParticipantsPage({iou, selectedTab, route}) {
// Reset the money request Onyx if the ID in Onyx does not match the ID from params
const moneyRequestId = `${iouType.current}${reportID.current}`;
- const shouldReset = iou.id !== moneyRequestId && !isNewReportIDSelectedLocally.current;
+ const shouldReset = iou.id !== moneyRequestId;
if (shouldReset) {
IOU.resetMoneyRequestInfo(moneyRequestId);
}
@@ -126,8 +113,8 @@ function MoneyRequestParticipantsPage({iou, selectedTab, route}) {
ref={(el) => (optionsSelectorRef.current = el)}
participants={iou.participants}
onAddParticipants={IOU.setMoneyRequestParticipants}
- navigateToRequest={(option) => navigateToRequestStep(CONST.IOU.MONEY_REQUEST_TYPE.REQUEST, option)}
- navigateToSplit={() => navigateToSplitStep(CONST.IOU.MONEY_REQUEST_TYPE.SPLIT)}
+ navigateToRequest={() => navigateToConfirmationStep(CONST.IOU.MONEY_REQUEST_TYPE.REQUEST)}
+ navigateToSplit={() => navigateToConfirmationStep(CONST.IOU.MONEY_REQUEST_TYPE.SPLIT)}
safeAreaPaddingBottomStyle={safeAreaPaddingBottomStyle}
iouType={iouType.current}
isDistanceRequest={isDistanceRequest}
diff --git a/src/pages/iou/steps/MoneyRequstParticipantsPage/MoneyRequestParticipantsSelector.js b/src/pages/iou/steps/MoneyRequstParticipantsPage/MoneyRequestParticipantsSelector.js
index 05b206ce4147..ac30bcf55787 100755
--- a/src/pages/iou/steps/MoneyRequstParticipantsPage/MoneyRequestParticipantsSelector.js
+++ b/src/pages/iou/steps/MoneyRequstParticipantsPage/MoneyRequestParticipantsSelector.js
@@ -164,7 +164,7 @@ function MoneyRequestParticipantsSelector({
onAddParticipants([
{accountID: option.accountID, login: option.login, isPolicyExpenseChat: option.isPolicyExpenseChat, reportID: option.reportID, selected: true, searchText: option.searchText},
]);
- navigateToRequest(option);
+ navigateToRequest();
};
/**