diff --git a/.well-known/apple-app-site-association b/.well-known/apple-app-site-association
index b3adf0f59b9c..a2c7365f7de8 100644
--- a/.well-known/apple-app-site-association
+++ b/.well-known/apple-app-site-association
@@ -32,10 +32,6 @@
"/": "/iou/*",
"comment": "I Owe You reports"
},
- {
- "/": "/request/*",
- "comment": "Money request"
- },
{
"/": "/enable-payments/*",
"comment": "Payments setup"
@@ -54,11 +50,11 @@
},
{
"/": "/split/*",
- "comment": "Split Bill"
+ "comment": "Split Expense"
},
{
"/": "/request/*",
- "comment": "Request Money"
+ "comment": "Submit Expense"
},
{
"/": "/new/*",
@@ -82,7 +78,7 @@
},
{
"/": "/send/*",
- "comment": "Send money"
+ "comment": "Pay someone"
},
{
"/": "/money2020/*",
diff --git a/android/app/build.gradle b/android/app/build.gradle
index 32dfb496daa8..237c284700ed 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 1001046213
- versionName "1.4.62-13"
+ versionCode 1001046214
+ versionName "1.4.62-14"
// Supported language variants must be declared here to avoid from being removed during the compilation.
// This also helps us to not include unnecessary language variants in the APK.
resConfigs "en", "es"
diff --git a/docs/redirects.csv b/docs/redirects.csv
index 51c8c7515e10..af595ecc5f83 100644
--- a/docs/redirects.csv
+++ b/docs/redirects.csv
@@ -152,7 +152,6 @@ https://help.expensify.com/articles/expensify-classic/manage-employees-and-repor
https://help.expensify.com/articles/expensify-classic/manage-employees-and-report-approvals/Invite-Members,https://help.expensify.com/articles/expensify-classic/copilots-and-delegates/Invite-Members
https://help.expensify.com/articles/expensify-classic/manage-employees-and-report-approvals/Removing-Members,https://help.expensify.com/articles/expensify-classic/copilots-and-delegates/Removing-Members
https://help.expensify.com/articles/expensify-classic/manage-employees-and-report-approvals/User-Roles,https://help.expensify.com/expensify-classic/hubs/copilots-and-delegates/
-https://help.expensify.com/articles/expensify-classic/reports/Currency,https://help.expensify.com/articles/expensify-classic/workspaces/Currency
https://help.expensify.com/articles/expensify-classic/send-payments/Reimbursing-Reports,https://help.expensify.com/articles/expensify-classic/bank-accounts-and-payments/Reimbursing-Reports
https://help.expensify.com/articles/expensify-classic/workspace-and-domain-settings/SAML-SSO,https://help.expensify.com/articles/expensify-classic/settings/Enable-two-factor-authentication
https://help.expensify.com/articles/expensify-classic/workspaces/Budgets,https://help.expensify.com/articles/expensify-classic/workspaces/Set-budgets
diff --git a/ios/NewExpensify/Info.plist b/ios/NewExpensify/Info.plist
index 0b59d7e8316d..fa153e5ec674 100644
--- a/ios/NewExpensify/Info.plist
+++ b/ios/NewExpensify/Info.plist
@@ -40,7 +40,7 @@
CFBundleVersion
- 1.4.62.13
+ 1.4.62.14ITSAppUsesNonExemptEncryptionLSApplicationQueriesSchemes
diff --git a/ios/NewExpensifyTests/Info.plist b/ios/NewExpensifyTests/Info.plist
index 2a6db0634f2f..eed52462a526 100644
--- a/ios/NewExpensifyTests/Info.plist
+++ b/ios/NewExpensifyTests/Info.plist
@@ -19,6 +19,6 @@
CFBundleSignature????CFBundleVersion
- 1.4.62.13
+ 1.4.62.14
diff --git a/ios/NotificationServiceExtension/Info.plist b/ios/NotificationServiceExtension/Info.plist
index 50171f3eb050..35dbb3c0b6cd 100644
--- a/ios/NotificationServiceExtension/Info.plist
+++ b/ios/NotificationServiceExtension/Info.plist
@@ -13,7 +13,7 @@
CFBundleShortVersionString1.4.62CFBundleVersion
- 1.4.62.13
+ 1.4.62.14NSExtensionNSExtensionPointIdentifier
diff --git a/ios/Podfile.lock b/ios/Podfile.lock
index ac5f3fdd397e..5a8c67088f16 100644
--- a/ios/Podfile.lock
+++ b/ios/Podfile.lock
@@ -1818,7 +1818,7 @@ PODS:
- RNGoogleSignin (10.0.1):
- GoogleSignIn (~> 7.0)
- React-Core
- - RNLiveMarkdown (0.1.47):
+ - RNLiveMarkdown (0.1.62):
- glog
- hermes-engine
- RCT-Folly (= 2022.05.16.00)
@@ -1836,9 +1836,9 @@ PODS:
- React-utils
- ReactCommon/turbomodule/bridging
- ReactCommon/turbomodule/core
- - RNLiveMarkdown/common (= 0.1.47)
+ - RNLiveMarkdown/common (= 0.1.62)
- Yoga
- - RNLiveMarkdown/common (0.1.47):
+ - RNLiveMarkdown/common (0.1.62):
- glog
- hermes-engine
- RCT-Folly (= 2022.05.16.00)
@@ -2547,7 +2547,7 @@ SPEC CHECKSUMS:
RNFS: 4ac0f0ea233904cb798630b3c077808c06931688
RNGestureHandler: 1190c218cdaaf029ee1437076a3fbbc3297d89fb
RNGoogleSignin: ccaa4a81582cf713eea562c5dd9dc1961a715fd0
- RNLiveMarkdown: f172c7199283dc9d21bccf7e21ea10741fd19e1d
+ RNLiveMarkdown: 47dfb50244f9ba1caefbc0efc6404ba41bf6620a
RNLocalize: d4b8af4e442d4bcca54e68fc687a2129b4d71a81
rnmapbox-maps: 3e273e0e867a079ec33df9ee33bb0482434b897d
RNPermissions: 8990fc2c10da3640938e6db1647cb6416095b729
@@ -2564,7 +2564,7 @@ SPEC CHECKSUMS:
SocketRocket: f32cd54efbe0f095c4d7594881e52619cfe80b17
Turf: 13d1a92d969ca0311bbc26e8356cca178ce95da2
VisionCamera: 3033e0dd5272d46e97bcb406adea4ae0e6907abf
- Yoga: 64cd2a583ead952b0315d5135bf39e053ae9be70
+ Yoga: 1b901a6d6eeba4e8a2e8f308f708691cdb5db312
PODFILE CHECKSUM: a25a81f2b50270f0c0bd0aff2e2ebe4d0b4ec06d
diff --git a/package-lock.json b/package-lock.json
index 478ae3c12b3c..64dd4fb0c885 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,19 +1,19 @@
{
"name": "new.expensify",
- "version": "1.4.62-13",
+ "version": "1.4.62-14",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "new.expensify",
- "version": "1.4.62-13",
+ "version": "1.4.62-14",
"hasInstallScript": true,
"license": "MIT",
"dependencies": {
"@babel/plugin-proposal-private-methods": "^7.18.6",
"@babel/plugin-proposal-private-property-in-object": "^7.21.11",
"@dotlottie/react-player": "^1.6.3",
- "@expensify/react-native-live-markdown": "^0.1.49",
+ "@expensify/react-native-live-markdown": "0.1.62",
"@expo/metro-runtime": "~3.1.1",
"@formatjs/intl-datetimeformat": "^6.10.0",
"@formatjs/intl-listformat": "^7.2.2",
@@ -57,7 +57,7 @@
"date-fns-tz": "^2.0.0",
"dom-serializer": "^0.2.2",
"domhandler": "^4.3.0",
- "expensify-common": "git+ssh://git@github.com/Expensify/expensify-common.git#13de5b0606662df33fa1392ad82cc11daadff52e",
+ "expensify-common": "git+ssh://git@github.com/Expensify/expensify-common.git#c0f7f3b6558fbeda0527c80d68460d418afef219",
"expo": "^50.0.3",
"expo-av": "~13.10.4",
"expo-image": "1.11.0",
@@ -3570,9 +3570,9 @@
}
},
"node_modules/@expensify/react-native-live-markdown": {
- "version": "0.1.49",
- "resolved": "https://registry.npmjs.org/@expensify/react-native-live-markdown/-/react-native-live-markdown-0.1.49.tgz",
- "integrity": "sha512-5l+/NtUTuSxWkdsT2JOlhKD5NW1hZ+nQcmgrCSz5e/TNIcfkYjJNiW/nEf8qmBV54afiTmTTwKYrh2DwM/BQ0g==",
+ "version": "0.1.62",
+ "resolved": "https://registry.npmjs.org/@expensify/react-native-live-markdown/-/react-native-live-markdown-0.1.62.tgz",
+ "integrity": "sha512-o70/tFIGZJ1U8U8aqTQu1HAZed6nt5LYWk74mrceRxQHOqsKhZgn2q5EuEy8EMIcnCGKjwxuDyZJbuRexgHx/A==",
"engines": {
"node": ">= 18.0.0"
},
@@ -16462,10 +16462,8 @@
},
"node_modules/classnames": {
"version": "2.5.0",
- "license": "MIT",
- "workspaces": [
- "benchmarks"
- ]
+ "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.5.0.tgz",
+ "integrity": "sha512-FQuRlyKinxrb5gwJlfVASbSrDlikDJ07426TrfPsdGLvtochowmkbnSFdQGJ2aoXrSetq5KqGV9emvWpy+91xA=="
},
"node_modules/clean-css": {
"version": "5.3.2",
@@ -16551,7 +16549,8 @@
},
"node_modules/clipboard": {
"version": "2.0.11",
- "license": "MIT",
+ "resolved": "https://registry.npmjs.org/clipboard/-/clipboard-2.0.11.tgz",
+ "integrity": "sha512-C+0bbOqkezLIsmWSvlsXS0Q0bmkugu7jcfMIACB+RDEntIzQIkdr148we28AfSloQLRdZlYL/QYyrq05j/3Faw==",
"dependencies": {
"good-listener": "^1.2.2",
"select": "^1.1.2",
@@ -18058,7 +18057,8 @@
},
"node_modules/delegate": {
"version": "3.2.0",
- "license": "MIT"
+ "resolved": "https://registry.npmjs.org/delegate/-/delegate-3.2.0.tgz",
+ "integrity": "sha512-IofjkYBZaZivn0V8nnsMJGBr4jVLxHDheKSW88PyxS5QC4Vo9ZbZVvhzlSxY87fVq3STR6r+4cGepyHkcWOQSw=="
},
"node_modules/delegates": {
"version": "1.0.0",
@@ -20212,8 +20212,8 @@
},
"node_modules/expensify-common": {
"version": "1.0.0",
- "resolved": "git+ssh://git@github.com/Expensify/expensify-common.git#13de5b0606662df33fa1392ad82cc11daadff52e",
- "integrity": "sha512-/NAZoAXqeqFWHvC61dueqq9VjRugF69urUtDdDhsfvu1sQE2PCnBoM7a+ACoAEWRYrnP82cyHHhdSA8e7fPuAg==",
+ "resolved": "git+ssh://git@github.com/Expensify/expensify-common.git#c0f7f3b6558fbeda0527c80d68460d418afef219",
+ "integrity": "sha512-zz0/y0apISP1orxXEQOgn+Uod45O4wVypwwtaqcDPV4dH1tC3i4L98NoLSZvLn7Y17EcceSkfN6QCEsscgFTDQ==",
"license": "MIT",
"dependencies": {
"classnames": "2.5.0",
@@ -20266,6 +20266,8 @@
},
"node_modules/expensify-common/node_modules/ua-parser-js": {
"version": "1.0.37",
+ "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-1.0.37.tgz",
+ "integrity": "sha512-bhTyI94tZofjo+Dn8SN6Zv8nBDvyXTymAdM3LDI/0IboIUwTu1rEhW7v2TfiVsoYWgkQ4kOVqnI8APUFbIQIFQ==",
"funding": [
{
"type": "opencollective",
@@ -20280,7 +20282,6 @@
"url": "https://github.com/sponsors/faisalman"
}
],
- "license": "MIT",
"engines": {
"node": "*"
}
@@ -21728,7 +21729,8 @@
},
"node_modules/good-listener": {
"version": "1.2.2",
- "license": "MIT",
+ "resolved": "https://registry.npmjs.org/good-listener/-/good-listener-1.2.2.tgz",
+ "integrity": "sha512-goW1b+d9q/HIwbVYZzZ6SsTr4IgE+WA44A0GmPIQstuOrgsFcT7VEJ48nmr9GaRtNu0XTKacFLGnBPAM6Afouw==",
"dependencies": {
"delegate": "^3.1.2"
}
@@ -22779,7 +22781,8 @@
},
"node_modules/immediate": {
"version": "3.0.6",
- "license": "MIT"
+ "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz",
+ "integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ=="
},
"node_modules/import-fresh": {
"version": "3.3.0",
@@ -26838,7 +26841,8 @@
},
"node_modules/lie": {
"version": "3.1.1",
- "license": "MIT",
+ "resolved": "https://registry.npmjs.org/lie/-/lie-3.1.1.tgz",
+ "integrity": "sha512-RiNhHysUjhrDQntfYSfY4MU24coXXdEOgw9WGcKHNeEwffDYbF//u87M1EWaMGzuFoSbqW0C9C6lEEhDOAswfw==",
"dependencies": {
"immediate": "~3.0.5"
}
@@ -26981,7 +26985,8 @@
},
"node_modules/localforage": {
"version": "1.10.0",
- "license": "Apache-2.0",
+ "resolved": "https://registry.npmjs.org/localforage/-/localforage-1.10.0.tgz",
+ "integrity": "sha512-14/H1aX7hzBBmmh7sGPd+AOMkkIrHM3Z1PAyGgZigA1H1p5O5ANnMyWzvpAETtG68/dC4pC0ncy3+PPGzXZHPg==",
"dependencies": {
"lie": "3.1.1"
}
@@ -33311,7 +33316,8 @@
},
"node_modules/select": {
"version": "1.1.2",
- "license": "MIT"
+ "resolved": "https://registry.npmjs.org/select/-/select-1.1.2.tgz",
+ "integrity": "sha512-OwpTSOfy6xSs1+pwcNrv0RBMOzI39Lp3qQKUTPVVPRjCdNa5JH/oPRiqsesIskK8TVgmRiHwO4KXlV2Li9dANA=="
},
"node_modules/select-hose": {
"version": "2.0.0",
diff --git a/package.json b/package.json
index e5092e132eae..78e1a3a13e2c 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "new.expensify",
- "version": "1.4.62-13",
+ "version": "1.4.62-14",
"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.",
@@ -64,7 +64,7 @@
"@babel/plugin-proposal-private-methods": "^7.18.6",
"@babel/plugin-proposal-private-property-in-object": "^7.21.11",
"@dotlottie/react-player": "^1.6.3",
- "@expensify/react-native-live-markdown": "0.1.49",
+ "@expensify/react-native-live-markdown": "0.1.62",
"@expo/metro-runtime": "~3.1.1",
"@formatjs/intl-datetimeformat": "^6.10.0",
"@formatjs/intl-listformat": "^7.2.2",
@@ -108,7 +108,7 @@
"date-fns-tz": "^2.0.0",
"dom-serializer": "^0.2.2",
"domhandler": "^4.3.0",
- "expensify-common": "git+ssh://git@github.com/Expensify/expensify-common.git#13de5b0606662df33fa1392ad82cc11daadff52e",
+ "expensify-common": "git+ssh://git@github.com/Expensify/expensify-common.git#c0f7f3b6558fbeda0527c80d68460d418afef219",
"expo": "^50.0.3",
"expo-av": "~13.10.4",
"expo-image": "1.11.0",
diff --git a/src/CONST.ts b/src/CONST.ts
index 556a161876f4..a6df33987c8d 100755
--- a/src/CONST.ts
+++ b/src/CONST.ts
@@ -879,7 +879,7 @@ const CONST = {
},
TIMING: {
CALCULATE_MOST_RECENT_LAST_MODIFIED_ACTION: 'calc_most_recent_last_modified_action',
- SEARCH_RENDER: 'search_render',
+ CHAT_FINDER_RENDER: 'search_render',
CHAT_RENDER: 'chat_render',
OPEN_REPORT: 'open_report',
HOMEPAGE_INITIAL_RENDER: 'homepage_initial_render',
@@ -1363,7 +1363,7 @@ const CONST = {
},
KYC_WALL_SOURCE: {
- REPORT: 'REPORT', // The user attempted to pay a money request
+ REPORT: 'REPORT', // The user attempted to pay an expense
ENABLE_WALLET: 'ENABLE_WALLET', // The user clicked on the `Enable wallet` button on the Wallet page
TRANSFER_BALANCE: 'TRANSFER_BALANCE', // The user attempted to transfer their wallet balance to their bank account or debit card
},
@@ -1399,7 +1399,7 @@ const CONST = {
},
IOU: {
- // This is the transactionID used when going through the create money request flow so that it mimics a real transaction (like the edit flow)
+ // This is the transactionID used when going through the create expense flow so that it mimics a real transaction (like the edit flow)
OPTIMISTIC_TRANSACTION_ID: '1',
// Note: These payment types are used when building IOU reportAction message values in the server and should
// not be changed.
@@ -3549,12 +3549,11 @@ const CONST = {
ONBOARDING_CONCIERGE: {
[onboardingChoices.TRACK]:
- "# Welcome to Expensify, let's start tracking your expenses!\n" +
- "Hi there, I'm Concierge. Chat with me here for anything you need.\n" +
+ "# Let's start tracking your expenses!\n" +
'\n' +
"To track your expenses, create a workspace to keep everything in one place. Here's how:\n" +
'1. From the home screen, click the green + button > New Workspace\n' +
- '2. Give your workspace a name (e.g. "My business expenses”).\n' +
+ '2. Give your workspace a name (e.g. "My business expenses").\n' +
'\n' +
'Then, add expenses to your workspace:\n' +
'1. Find your workspace using the search field.\n' +
@@ -3563,8 +3562,7 @@ const CONST = {
'\n' +
"We'll store all expenses in your new workspace for easy access. Let me know if you have any questions!",
[onboardingChoices.EMPLOYER]:
- '# Welcome to Expensify, the fastest way to get paid back!\n' +
- "Hi there, I'm Concierge. Chat with me here for anything you need.\n" +
+ '# Expensify is the fastest way to get paid back!\n' +
'\n' +
'To submit expenses for reimbursement:\n' +
'1. From the home screen, click the green + button > Request money.\n' +
@@ -3572,21 +3570,19 @@ const CONST = {
'\n' +
"That'll send a request to get you paid back. Let me know if you have any questions!",
[onboardingChoices.MANAGE_TEAM]:
- "# Welcome to Expensify, let's start managing your team's expenses!\n" +
- "Hi there, I'm Concierge. Chat with me here for anything you need.\n" +
+ "# Let's start managing your team's expenses!\n" +
'\n' +
"To manage your team's expenses, create a workspace to keep everything in one place. Here's how:\n" +
'1. From the home screen, click the green + button > New Workspace\n' +
- '2. Give your workspace a name (e.g. “Sales team expenses”).\n' +
+ '2. Give your workspace a name (e.g. "Sales team expenses").\n' +
'\n' +
- 'Then, invite your team to your workspace via the Members pane and connect a business bank account to reimburse them. Let me know if you have any questions!',
+ 'Then, invite your team to your workspace via the Members pane and [connect a business bank account](https://help.expensify.com/articles/new-expensify/bank-accounts/Connect-a-Bank-Account) to reimburse them. Let me know if you have any questions!',
[onboardingChoices.PERSONAL_SPEND]:
- "# Welcome to Expensify, let's start tracking your expenses!\n" +
- "Hi there, I'm Concierge. Chat with me here for anything you need.\n" +
+ "# Let's start tracking your expenses! \n" +
'\n' +
"To track your expenses, create a workspace to keep everything in one place. Here's how:\n" +
'1. From the home screen, click the green + button > New Workspace\n' +
- '2. Give your workspace a name (e.g. "My expenses”).\n' +
+ '2. Give your workspace a name (e.g. "My expenses").\n' +
'\n' +
'Then, add expenses to your workspace:\n' +
'1. Find your workspace using the search field.\n' +
@@ -3595,19 +3591,13 @@ const CONST = {
'\n' +
"We'll store all expenses in your new workspace for easy access. Let me know if you have any questions!",
[onboardingChoices.CHAT_SPLIT]:
- '# Welcome to Expensify, where splitting the bill is an easy conversation!\n' +
- "Hi there, I'm Concierge. Chat with me here for anything you need.\n" +
+ '# Splitting the bill is as easy as a conversation!\n' +
'\n' +
'To split an expense:\n' +
'1. From the home screen, click the green + button > Request money.\n' +
'2. Enter an amount or scan a receipt, then choose who you want to split it with.\n' +
'\n' +
"We'll send a request to each person so they can pay you back. Let me know if you have any questions!",
- [onboardingChoices.LOOKING_AROUND]:
- '# Welcome to Expensify!\n' +
- "Hi there, I'm Concierge. Chat with me here for anything you need.\n" +
- '\n' +
- "Expensify is best known for expense and corporate card management, but we do a lot more than that. Let me know what you're interested in and I'll help get you started.",
},
REPORT_FIELD_TITLE_FIELD_ID: 'text_title',
diff --git a/src/ONYXKEYS.ts b/src/ONYXKEYS.ts
index 89114700e0d4..7c9247bcdbd7 100755
--- a/src/ONYXKEYS.ts
+++ b/src/ONYXKEYS.ts
@@ -126,7 +126,7 @@ const ONYXKEYS = {
/** The NVP with the last payment method used per policy */
NVP_LAST_PAYMENT_METHOD: 'nvp_private_lastPaymentMethod',
- /** This NVP holds to most recent waypoints that a person has used when creating a distance request */
+ /** This NVP holds to most recent waypoints that a person has used when creating a distance expense */
NVP_RECENT_WAYPOINTS: 'expensify_recentWaypoints',
/** This NVP will be `true` if the user has ever dismissed the engagement modal on either OldDot or NewDot. If it becomes true it should stay true forever. */
@@ -318,6 +318,11 @@ const ONYXKEYS = {
POLICY_RECENTLY_USED_CATEGORIES: 'policyRecentlyUsedCategories_',
POLICY_TAGS: 'policyTags_',
POLICY_RECENTLY_USED_TAGS: 'nvp_recentlyUsedTags_',
+ // Whether the policy's connection data was attempted to be fetched in
+ // the current user session. As this state only exists client-side, it
+ // should not be included as part of the policy object. The policy
+ // object should mirror the data as it's stored in the database.
+ POLICY_HAS_CONNECTIONS_DATA_BEEN_FETCHED: 'policyHasConnectionsDataBeenFetched_',
OLD_POLICY_RECENTLY_USED_TAGS: 'policyRecentlyUsedTags_',
POLICY_CONNECTION_SYNC_PROGRESS: 'policyConnectionSyncProgress_',
WORKSPACE_INVITE_MEMBERS_DRAFT: 'workspaceInviteMembersDraft_',
@@ -346,7 +351,7 @@ const ONYXKEYS = {
PRIVATE_NOTES_DRAFT: 'privateNotesDraft_',
NEXT_STEP: 'reportNextStep_',
- // Manual request tab selector
+ // Manual expense tab selector
SELECTED_TAB: 'selectedTab_',
/** This is deprecated, but needed for a migration, so we still need to include it here so that it will be initialized in Onyx.init */
@@ -524,7 +529,7 @@ type OnyxCollectionValuesMapping = {
[ONYXKEYS.COLLECTION.POLICY_CATEGORIES]: OnyxTypes.PolicyCategories;
[ONYXKEYS.COLLECTION.POLICY_TAGS]: OnyxTypes.PolicyTagList;
[ONYXKEYS.COLLECTION.POLICY_RECENTLY_USED_CATEGORIES]: OnyxTypes.RecentlyUsedCategories;
- [ONYXKEYS.COLLECTION.POLICY_CONNECTION_SYNC_PROGRESS]: OnyxTypes.PolicyConnectionSyncProgress;
+ [ONYXKEYS.COLLECTION.POLICY_HAS_CONNECTIONS_DATA_BEEN_FETCHED]: boolean;
[ONYXKEYS.COLLECTION.DEPRECATED_POLICY_MEMBER_LIST]: OnyxTypes.PolicyEmployeeList;
[ONYXKEYS.COLLECTION.WORKSPACE_INVITE_MEMBERS_DRAFT]: OnyxTypes.InvitedEmailsToAccountIDs;
[ONYXKEYS.COLLECTION.WORKSPACE_INVITE_MESSAGE_DRAFT]: string;
diff --git a/src/ROUTES.ts b/src/ROUTES.ts
index 3b745512bcc2..7d73d8e55503 100644
--- a/src/ROUTES.ts
+++ b/src/ROUTES.ts
@@ -28,7 +28,7 @@ const ROUTES = {
route: 'flag/:reportID/:reportActionID',
getRoute: (reportID: string, reportActionID: string) => `flag/${reportID}/${reportActionID}` as const,
},
- SEARCH: 'search',
+ CHAT_FINDER: 'chat-finder',
DETAILS: {
route: 'details',
getRoute: (login: string) => `details?login=${encodeURIComponent(login)}` as const,
diff --git a/src/SCREENS.ts b/src/SCREENS.ts
index 27d8331b5473..d474945d332e 100644
--- a/src/SCREENS.ts
+++ b/src/SCREENS.ts
@@ -95,7 +95,7 @@ const SCREENS = {
ROOT: 'SaveTheWorld_Root',
},
LEFT_MODAL: {
- SEARCH: 'Search',
+ CHAT_FINDER: 'ChatFinder',
WORKSPACE_SWITCHER: 'WorkspaceSwitcher',
},
WORKSPACE_SWITCHER: {
@@ -321,7 +321,7 @@ const SCREENS = {
},
ROOM_MEMBERS_ROOT: 'RoomMembers_Root',
ROOM_INVITE_ROOT: 'RoomInvite_Root',
- SEARCH_ROOT: 'Search_Root',
+ CHAT_FINDER_ROOT: 'ChatFinder_Root',
FLAG_COMMENT_ROOT: 'FlagComment_Root',
REIMBURSEMENT_ACCOUNT: 'ReimbursementAccount',
GET_ASSISTANCE: 'GetAssistance',
diff --git a/src/components/ConfirmedRoute.tsx b/src/components/ConfirmedRoute.tsx
index 17c5097b8154..351129a5ee3e 100644
--- a/src/components/ConfirmedRoute.tsx
+++ b/src/components/ConfirmedRoute.tsx
@@ -24,7 +24,7 @@ type ConfirmedRoutePropsOnyxProps = {
};
type ConfirmedRouteProps = ConfirmedRoutePropsOnyxProps & {
- /** Transaction that stores the distance request data */
+ /** Transaction that stores the distance expense data */
transaction: OnyxEntry;
};
diff --git a/src/components/DatePicker/CalendarPicker/YearPickerModal.tsx b/src/components/DatePicker/CalendarPicker/YearPickerModal.tsx
index c7816b710692..6170b81073a2 100644
--- a/src/components/DatePicker/CalendarPicker/YearPickerModal.tsx
+++ b/src/components/DatePicker/CalendarPicker/YearPickerModal.tsx
@@ -34,7 +34,7 @@ function YearPickerModal({isVisible, years, currentYear = new Date().getFullYear
const yearsList = searchText === '' ? years : years.filter((year) => year.text?.includes(searchText));
return {
headerMessage: !yearsList.length ? translate('common.noResultsFound') : '',
- sections: [{data: yearsList.sort((a, b) => b.value - a.value)}],
+ sections: [{data: yearsList.sort((a, b) => b.value - a.value), indexOffset: 0}],
};
}, [years, searchText, translate]);
diff --git a/src/components/DistanceEReceipt.tsx b/src/components/DistanceEReceipt.tsx
index 20b927913bfb..f6adac9b6034 100644
--- a/src/components/DistanceEReceipt.tsx
+++ b/src/components/DistanceEReceipt.tsx
@@ -20,7 +20,7 @@ import ScrollView from './ScrollView';
import Text from './Text';
type DistanceEReceiptProps = {
- /** The transaction for the distance request */
+ /** The transaction for the distance expense */
transaction: Transaction;
};
diff --git a/src/components/DistanceRequest/DistanceRequestFooter.tsx b/src/components/DistanceRequest/DistanceRequestFooter.tsx
index 624ea940888b..e21bdc1c9e56 100644
--- a/src/components/DistanceRequest/DistanceRequestFooter.tsx
+++ b/src/components/DistanceRequest/DistanceRequestFooter.tsx
@@ -27,7 +27,7 @@ type DistanceRequestFooterOnyxProps = {
};
type DistanceRequestFooterProps = DistanceRequestFooterOnyxProps & {
- /** The waypoints for the distance request */
+ /** The waypoints for the distance expense */
waypoints?: WaypointCollection;
/** Function to call when the user wants to add a new waypoint */
diff --git a/src/components/DistanceRequest/DistanceRequestRenderItem.tsx b/src/components/DistanceRequest/DistanceRequestRenderItem.tsx
index f4b11c159326..fa143192eafc 100644
--- a/src/components/DistanceRequest/DistanceRequestRenderItem.tsx
+++ b/src/components/DistanceRequest/DistanceRequestRenderItem.tsx
@@ -7,7 +7,7 @@ import type {TranslationPaths} from '@src/languages/types';
import type {WaypointCollection} from '@src/types/onyx/Transaction';
type DistanceRequestProps = {
- /** The waypoints for the distance request */
+ /** The waypoints for the distance expense */
waypoints?: WaypointCollection;
/** The index of the item */
diff --git a/src/components/HTMLEngineProvider/BaseHTMLEngineProvider.tsx b/src/components/HTMLEngineProvider/BaseHTMLEngineProvider.tsx
index 9551e9d8f7c8..51f9981f1524 100755
--- a/src/components/HTMLEngineProvider/BaseHTMLEngineProvider.tsx
+++ b/src/components/HTMLEngineProvider/BaseHTMLEngineProvider.tsx
@@ -92,6 +92,9 @@ function BaseHTMLEngineProvider({textSelectable = false, children, enableExperim
tagsStyles={styles.webViewStyles.tagStyles}
enableCSSInlineProcessing={false}
systemFonts={Object.values(FontUtils.fontFamily.single)}
+ htmlParserOptions={{
+ recognizeSelfClosing: true,
+ }}
domVisitors={{
// eslint-disable-next-line no-param-reassign
onText: (text) => (text.data = convertToLTR(text.data)),
diff --git a/src/components/HoldBanner.tsx b/src/components/HoldBanner.tsx
index af77d9076629..d760d4efbe96 100644
--- a/src/components/HoldBanner.tsx
+++ b/src/components/HoldBanner.tsx
@@ -12,7 +12,7 @@ function HoldBanner() {
return (
{translate('iou.hold')}
- {translate('iou.requestOnHold')}
+ {translate('iou.expenseOnHold')}
);
}
diff --git a/src/components/MoneyReportHeader.tsx b/src/components/MoneyReportHeader.tsx
index 14227d6a2051..56dc6bf0075d 100644
--- a/src/components/MoneyReportHeader.tsx
+++ b/src/components/MoneyReportHeader.tsx
@@ -43,7 +43,7 @@ type MoneyReportHeaderProps = MoneyReportHeaderOnyxProps & {
/** The report currently being looked at */
report: OnyxTypes.Report;
- /** The policy tied to the money request report */
+ /** The policy tied to the expense report */
policy: OnyxEntry;
/** Array of report actions for the report */
@@ -288,7 +288,7 @@ function MoneyReportHeader({session, policy, chatReport, nextStep, report: money
danger
/>
setIsDeleteRequestModalVisible(false)}
diff --git a/src/components/MoneyRequestConfirmationList.tsx b/src/components/MoneyRequestConfirmationList.tsx
index aa6c75edbf5d..4b3e4096484f 100755
--- a/src/components/MoneyRequestConfirmationList.tsx
+++ b/src/components/MoneyRequestConfirmationList.tsx
@@ -342,12 +342,12 @@ function MoneyRequestConfirmationList({
if (isSplitBill && iouAmount === 0) {
text = translate('iou.split');
} else if ((!!receiptPath && isTypeRequest) || isDistanceRequestWithPendingRoute) {
- text = translate('iou.request');
+ text = translate('iou.expense');
if (iouAmount !== 0) {
- text = translate('iou.requestAmount', {amount: formattedAmount});
+ text = translate('iou.submitAmount', {amount: formattedAmount});
}
} else {
- const translationKey = isSplitBill ? 'iou.splitAmount' : 'iou.requestAmount';
+ const translationKey = isSplitBill ? 'iou.splitAmount' : 'iou.submitAmount';
text = translate(translationKey, {amount: formattedAmount});
}
return [
diff --git a/src/components/MoneyRequestHeader.tsx b/src/components/MoneyRequestHeader.tsx
index f451f5f15581..f7825ef2f622 100644
--- a/src/components/MoneyRequestHeader.tsx
+++ b/src/components/MoneyRequestHeader.tsx
@@ -63,7 +63,7 @@ function MoneyRequestHeader({session, parentReport, report, parentReportAction,
const isOnHold = TransactionUtils.isOnHold(transaction);
const {isSmallScreenWidth, windowWidth} = useWindowDimensions();
- // Only the requestor can take delete the request, admins can only edit it.
+ // Only the requestor can take delete the expense, admins can only edit it.
const isActionOwner = typeof parentReportAction?.actorAccountID === 'number' && typeof session?.accountID === 'number' && parentReportAction.actorAccountID === session?.accountID;
const isPolicyAdmin = policy?.role === CONST.POLICY.ROLE.ADMIN;
const isApprover = ReportUtils.isMoneyRequestReport(moneyRequestReport) && (session?.accountID ?? null) === moneyRequestReport?.managerID;
@@ -118,14 +118,14 @@ function MoneyRequestHeader({session, parentReport, report, parentReportAction,
if (isOnHold && (isHoldCreator || (!isRequestIOU && canModifyStatus))) {
threeDotsMenuItems.push({
icon: Expensicons.Stopwatch,
- text: translate('iou.unholdRequest'),
+ text: translate('iou.unholdExpense'),
onSelected: () => changeMoneyRequestStatus(),
});
}
if (!isOnHold && (isRequestIOU || canModifyStatus)) {
threeDotsMenuItems.push({
icon: Expensicons.Stopwatch,
- text: translate('iou.holdRequest'),
+ text: translate('iou.holdExpense'),
onSelected: () => changeMoneyRequestStatus(),
});
}
@@ -196,7 +196,7 @@ function MoneyRequestHeader({session, parentReport, report, parentReportAction,
{isOnHold && }
setIsDeleteModalVisible(false)}
diff --git a/src/components/MoneyRequestSkeletonView.tsx b/src/components/MoneyRequestSkeletonView.tsx
index e11e7bcecc07..400e3c9534d7 100644
--- a/src/components/MoneyRequestSkeletonView.tsx
+++ b/src/components/MoneyRequestSkeletonView.tsx
@@ -13,8 +13,8 @@ function MoneyRequestSkeletonView() {
animate
width={styles.w100.width}
height={variables.moneyRequestSkeletonHeight}
- backgroundColor={theme.borderLighter}
- foregroundColor={theme.border}
+ backgroundColor={theme.skeletonLHNIn}
+ foregroundColor={theme.skeletonLHNOut}
>
;
- /** Unit and rate used for if the money request is a distance request */
+ /** Unit and rate used for if the expense is a distance expense */
mileageRate: OnyxEntry;
};
@@ -73,7 +73,7 @@ type MoneyRequestConfirmationListProps = MoneyRequestConfirmationListOnyxProps &
/** Callback to inform parent modal of success */
onConfirm?: (selectedParticipants: Participant[]) => void;
- /** Callback to parent modal to send money */
+ /** Callback to parent modal to pay someone */
onSendMoney?: (paymentMethod: PaymentMethodType | undefined) => void;
/** Callback to inform a participant is selected */
@@ -112,7 +112,7 @@ type MoneyRequestConfirmationListProps = MoneyRequestConfirmationListOnyxProps &
/** Selected participants from MoneyRequestModal with login / accountID */
selectedParticipants: Participant[];
- /** Payee of the money request with login */
+ /** Payee of the expense with login */
payeePersonalDetails?: OnyxTypes.PersonalDetails;
/** Can the participants be modified or not */
@@ -139,16 +139,16 @@ type MoneyRequestConfirmationListProps = MoneyRequestConfirmationListOnyxProps &
/** List styles for OptionsSelector */
listStyles?: StyleProp;
- /** Transaction that represents the money request */
+ /** Transaction that represents the expense */
transaction?: OnyxEntry;
- /** Whether the money request is a distance request */
+ /** Whether the expense is a distance expense */
isDistanceRequest?: boolean;
- /** Whether the money request is a scan request */
+ /** Whether the expense is a scan expense */
isScanRequest?: boolean;
- /** Whether we're editing a split bill */
+ /** Whether we're editing a split expense */
isEditingSplitBill?: boolean;
/** Whether we should show the amount, date, and merchant fields. */
@@ -236,7 +236,7 @@ function MoneyTemporaryForRefactorRequestConfirmationList({
// A flag and a toggler for showing the rest of the form fields
const [shouldExpandFields, toggleShouldExpandFields] = useReducer((state) => !state, false);
- // Do not hide fields in case of send money request
+ // Do not hide fields in case of paying someone
const shouldShowAllFields = isDistanceRequest || shouldExpandFields || !shouldShowSmartScanFields || isTypeSend || isEditingSplitBill;
const shouldShowDate = (shouldShowSmartScanFields || isDistanceRequest) && !isTypeSend;
@@ -354,7 +354,7 @@ function MoneyTemporaryForRefactorRequestConfirmationList({
[iouAmount, iouCurrencyCode],
);
- // If completing a split bill fails, set didConfirm to false to allow the user to edit the fields again
+ // If completing a split expense fails, set didConfirm to false to allow the user to edit the fields again
if (isEditingSplitBill && didConfirm) {
setDidConfirm(false);
}
@@ -364,14 +364,14 @@ function MoneyTemporaryForRefactorRequestConfirmationList({
if (isTypeTrackExpense) {
text = translate('iou.trackExpense');
} else if (isTypeSplit && iouAmount === 0) {
- text = translate('iou.split');
+ text = translate('iou.splitExpense');
} else if ((receiptPath && isTypeRequest) || isDistanceRequestWithPendingRoute) {
- text = translate('iou.request');
+ text = translate('iou.submitExpense');
if (iouAmount !== 0) {
- text = translate('iou.requestAmount', {amount: formattedAmount});
+ text = translate('iou.submitAmount', {amount: formattedAmount});
}
} else {
- const translationKey = isTypeSplit ? 'iou.splitAmount' : 'iou.requestAmount';
+ const translationKey = isTypeSplit ? 'iou.splitAmount' : 'iou.submitAmount';
text = translate(translationKey, {amount: formattedAmount});
}
return [
@@ -552,7 +552,7 @@ function MoneyTemporaryForRefactorRequestConfirmationList({
Log.info(`[IOU] Sending money via: ${paymentMethod}`);
onSendMoney?.(paymentMethod);
} else {
- // validate the amount for distance requests
+ // validate the amount for distance expenses
const decimals = CurrencyUtils.getCurrencyDecimals(iouCurrencyCode);
if (isDistanceRequest && !isDistanceRequestWithPendingRoute && !MoneyRequestUtils.validateAmount(String(iouAmount), decimals)) {
setFormError('common.error.invalidAmount');
@@ -907,7 +907,7 @@ function MoneyTemporaryForRefactorRequestConfirmationList({
// eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
source={resolvedThumbnail || resolvedReceiptImage || ''}
// AuthToken is required when retrieving the image from the server
- // but we don't need it to load the blob:// or file:// image when starting a money request / split bill
+ // but we don't need it to load the blob:// or file:// image when starting an expense/split
// So if we have a thumbnail, it means we're retrieving the image from the server
isAuthTokenRequired={!!receiptThumbnail}
fileExtension={fileExtension}
diff --git a/src/components/OptionsList/BaseOptionsList.tsx b/src/components/OptionsList/BaseOptionsList.tsx
index 436f4c147931..7bbd3e344c3f 100644
--- a/src/components/OptionsList/BaseOptionsList.tsx
+++ b/src/components/OptionsList/BaseOptionsList.tsx
@@ -184,7 +184,7 @@ function BaseOptionsList(
option={item}
showTitleTooltip={showTitleTooltip}
hoverStyle={optionHoveredStyle}
- optionIsFocused={!disableFocusOptions && !isItemDisabled && focusedIndex === index + section.indexOffset}
+ optionIsFocused={!disableFocusOptions && !isItemDisabled && focusedIndex === index + (section.indexOffset ?? 0)}
onSelectRow={onSelectRow}
isSelected={isSelected}
showSelectedState={canSelectMultipleOptions}
diff --git a/src/components/OptionsList/types.ts b/src/components/OptionsList/types.ts
index b7180e6281b4..7f23da965f39 100644
--- a/src/components/OptionsList/types.ts
+++ b/src/components/OptionsList/types.ts
@@ -22,7 +22,7 @@ type Section = {
type SectionWithIndexOffset = Section & {
/** The initial index of this section given the total number of options in each section's data array */
- indexOffset: number;
+ indexOffset?: number;
};
type OptionsListProps = {
diff --git a/src/components/ParentNavigationSubtitle.tsx b/src/components/ParentNavigationSubtitle.tsx
index 0ad32f18659b..cbc9e1352f21 100644
--- a/src/components/ParentNavigationSubtitle.tsx
+++ b/src/components/ParentNavigationSubtitle.tsx
@@ -35,7 +35,12 @@ function ParentNavigationSubtitle({parentNavigationSubtitleData, parentReportAct
onPress={() => {
const parentAction = ReportActionsUtils.getReportAction(parentReportID, parentReportActionID ?? '');
const isVisibleAction = ReportActionsUtils.shouldReportActionBeVisible(parentAction, parentAction?.reportActionID ?? '');
- Navigation.navigate(ROUTES.REPORT_WITH_ID.getRoute(parentReportID, isVisibleAction && !isOffline ? parentReportActionID : undefined));
+ // Pop the thread report screen before navigating to the chat report.
+ Navigation.goBack(ROUTES.REPORT_WITH_ID.getRoute(parentReportID));
+ if (isVisibleAction && !isOffline) {
+ // Pop the chat report screen before navigating to the linked report action.
+ Navigation.goBack(ROUTES.REPORT_WITH_ID.getRoute(parentReportID, parentReportActionID));
+ }
}}
accessibilityLabel={translate('threads.parentNavigationSummary', {reportName, workspaceName})}
role={CONST.ROLE.LINK}
diff --git a/src/components/ReportActionItem/MoneyRequestAction.tsx b/src/components/ReportActionItem/MoneyRequestAction.tsx
index 7d9ba2697c7a..4f91b2084b45 100644
--- a/src/components/ReportActionItem/MoneyRequestAction.tsx
+++ b/src/components/ReportActionItem/MoneyRequestAction.tsx
@@ -37,7 +37,7 @@ type MoneyRequestActionProps = MoneyRequestActionOnyxProps & {
/** The ID of the associated chatReport */
chatReportID: string;
- /** The ID of the associated request report */
+ /** The ID of the associated expense report */
requestReportID: string;
/** The ID of the current report */
@@ -114,10 +114,8 @@ function MoneyRequestAction({
let message: TranslationPaths;
if (isReversedTransaction) {
message = 'parentReportAction.reversedTransaction';
- } else if (isTrackExpenseAction) {
- message = 'parentReportAction.deletedExpense';
} else {
- message = 'parentReportAction.deletedRequest';
+ message = 'parentReportAction.deletedExpense';
}
return ${translate(message)}`} />;
}
diff --git a/src/components/ReportActionItem/MoneyRequestPreview/MoneyRequestPreviewContent.tsx b/src/components/ReportActionItem/MoneyRequestPreview/MoneyRequestPreviewContent.tsx
index 97287e64b829..8994d456904a 100644
--- a/src/components/ReportActionItem/MoneyRequestPreview/MoneyRequestPreviewContent.tsx
+++ b/src/components/ReportActionItem/MoneyRequestPreview/MoneyRequestPreviewContent.tsx
@@ -101,7 +101,7 @@ function MoneyRequestPreviewContent({
/*
Show the merchant for IOUs and expenses only if:
- the merchant is not empty, is custom, or is not related to scanning smartscan;
- - the request is not a distance request with a pending route and amount = 0 - in this case,
+ - the expense is not a distance expense with a pending route and amount = 0 - in this case,
the merchant says: "Route pending...", which is already shown in the amount field;
*/
const shouldShowMerchant =
diff --git a/src/components/ReportActionItem/MoneyRequestPreview/types.ts b/src/components/ReportActionItem/MoneyRequestPreview/types.ts
index 3b3eda4ec30a..0e3eb37ce6e3 100644
--- a/src/components/ReportActionItem/MoneyRequestPreview/types.ts
+++ b/src/components/ReportActionItem/MoneyRequestPreview/types.ts
@@ -53,7 +53,7 @@ type MoneyRequestPreviewProps = MoneyRequestPreviewOnyxProps & {
/** Extra styles to pass to View wrapper */
containerStyles?: StyleProp;
- /** True if this is this IOU is a split instead of a 1:1 request */
+ /** True if this IOU has a type of split */
isBillSplit: boolean;
/** Whether this IOU is a track expense */
@@ -62,7 +62,7 @@ type MoneyRequestPreviewProps = MoneyRequestPreviewOnyxProps & {
/** True if the IOU Preview card is hovered */
isHovered?: boolean;
- /** Whether or not an IOU report contains money requests in a different currency
+ /** Whether or not an IOU report contains expenses in a different currency
* that are either created or cancelled offline, and thus haven't been converted to the report's currency yet
*/
shouldShowPendingConversionMessage?: boolean;
diff --git a/src/components/ReportActionItem/MoneyRequestView.tsx b/src/components/ReportActionItem/MoneyRequestView.tsx
index 73fc7e9bae6e..c5cad0eccdeb 100644
--- a/src/components/ReportActionItem/MoneyRequestView.tsx
+++ b/src/components/ReportActionItem/MoneyRequestView.tsx
@@ -132,7 +132,7 @@ function MoneyRequestView({
? transaction && TransactionUtils.getDefaultTaxName(taxRates, transaction)
: transactionTaxCode && TransactionUtils.getTaxName(taxRates?.taxes, transactionTaxCode));
- // Flags for allowing or disallowing editing a money request
+ // Flags for allowing or disallowing editing an expense
const isSettled = ReportUtils.isSettled(moneyRequestReport?.reportID);
const isCancelled = moneyRequestReport && moneyRequestReport.isCancelledIOU;
@@ -220,7 +220,7 @@ function MoneyRequestView({
const getErrorForField = useCallback(
(field: ViolationField, data?: OnyxTypes.TransactionViolation['data']) => {
- // Checks applied when creating a new money request
+ // Checks applied when creating a new expense
// NOTE: receipt field can return multiple violations, so we need to handle it separately
const fieldChecks: Partial> = {
amount: {
diff --git a/src/components/ReportActionItem/ReportActionItemImages.tsx b/src/components/ReportActionItem/ReportActionItemImages.tsx
index ee8cb0849ca0..e2bcce9b9f1b 100644
--- a/src/components/ReportActionItem/ReportActionItemImages.tsx
+++ b/src/components/ReportActionItem/ReportActionItemImages.tsx
@@ -30,7 +30,7 @@ type ReportActionItemImagesProps = {
/**
* This component displays a row of images in a report action item like a card, such
- * as report previews or money request previews which contain receipt images. The maximum of images
+ * as report previews or expense previews which contain receipt images. The maximum of images
* shown in this row is dictated by the size prop, which, if not passed, is just the number of images.
* Otherwise, if size is passed and the number of images is over size, we show a small overlay on the
* last image of how many additional images there are. If passed, total prop can be used to change how this
diff --git a/src/components/ReportActionItem/ReportPreview.tsx b/src/components/ReportActionItem/ReportPreview.tsx
index 190343e48abd..d14d2df1bb43 100644
--- a/src/components/ReportActionItem/ReportPreview.tsx
+++ b/src/components/ReportActionItem/ReportPreview.tsx
@@ -36,7 +36,7 @@ import type {PaymentMethodType} from '@src/types/onyx/OriginalMessage';
import ReportActionItemImages from './ReportActionItemImages';
type ReportPreviewOnyxProps = {
- /** The policy tied to the money request report */
+ /** The policy tied to the expense report */
policy: OnyxEntry;
/** ChatReport associated with iouReport */
@@ -216,11 +216,11 @@ function ReportPreview({
const shouldShowRBR = !iouSettled && hasErrors;
/*
- Show subtitle if at least one of the money requests is not being smart scanned, and either:
- - There is more than one money request – in this case, the "X requests, Y scanning" subtitle is shown;
- - There is only one money request, it has a receipt and is not being smart scanned – in this case, the request merchant or description is shown;
+ Show subtitle if at least one of the expenses is not being smart scanned, and either:
+ - There is more than one expense – in this case, the "X expenses, Y scanning" subtitle is shown;
+ - There is only one expense, it has a receipt and is not being smart scanned – in this case, the expense merchant or description is shown;
- * There is an edge case when there is only one distance request with a pending route and amount = 0.
+ * There is an edge case when there is only one distance expense with a pending route and amount = 0.
In this case, we don't want to show the merchant or description because it says: "Pending route...", which is already displayed in the amount field.
*/
const shouldShowSingleRequestMerchantOrDescription =
@@ -237,7 +237,7 @@ function ReportPreview({
}
return {
isSupportTextHtml: false,
- supportText: translate('iou.requestCount', {
+ supportText: translate('iou.expenseCount', {
count: numberOfRequests - numberOfScanningReceipts - numberOfPendingRequests,
scanningReceipts: numberOfScanningReceipts,
pendingReceipts: numberOfPendingRequests,
diff --git a/src/components/ReportActionItem/TaskAction.tsx b/src/components/ReportActionItem/TaskAction.tsx
index e85a2e708feb..e1b36713592f 100644
--- a/src/components/ReportActionItem/TaskAction.tsx
+++ b/src/components/ReportActionItem/TaskAction.tsx
@@ -1,3 +1,4 @@
+import ExpensiMark from 'expensify-common/lib/ExpensiMark';
import React from 'react';
import {View} from 'react-native';
import type {OnyxEntry} from 'react-native-onyx';
@@ -15,10 +16,15 @@ type TaskActionProps = {
function TaskAction({action}: TaskActionProps) {
const styles = useThemeStyles();
const message = TaskUtils.getTaskReportActionMessage(action);
+ const parser = new ExpensiMark();
return (
- {message.html ? ${message.html}`} /> : {message.text}}
+ {message.html ? (
+ ${parser.replace(message.html)}`} />
+ ) : (
+ {message.text}
+ )}
);
}
diff --git a/src/components/SelectionList/BaseSelectionList.tsx b/src/components/SelectionList/BaseSelectionList.tsx
index 62f098e76228..a5bdf46450ae 100644
--- a/src/components/SelectionList/BaseSelectionList.tsx
+++ b/src/components/SelectionList/BaseSelectionList.tsx
@@ -2,7 +2,7 @@ import {useFocusEffect, useIsFocused} from '@react-navigation/native';
import isEmpty from 'lodash/isEmpty';
import type {ForwardedRef} from 'react';
import React, {forwardRef, useCallback, useEffect, useImperativeHandle, useMemo, useRef, useState} from 'react';
-import type {LayoutChangeEvent, SectionList as RNSectionList, TextInput as RNTextInput, SectionListRenderItemInfo} from 'react-native';
+import type {LayoutChangeEvent, SectionList as RNSectionList, TextInput as RNTextInput, SectionListData, SectionListRenderItemInfo} from 'react-native';
import {View} from 'react-native';
import Button from '@components/Button';
import Checkbox from '@components/Checkbox';
@@ -52,6 +52,7 @@ function BaseSelectionList(
onConfirm,
headerContent,
footerContent,
+ listFooterContent,
showScrollIndicator = true,
showLoadingPlaceholder = false,
showConfirmButton = false,
@@ -294,7 +295,7 @@ function BaseSelectionList(
*
* [{header}, {sectionHeader}, {item}, {item}, {sectionHeader}, {item}, {item}, {footer}]
*/
- const getItemLayout = (data: Array> | null, flatDataArrayIndex: number) => {
+ const getItemLayout = (data: Array>> | null, flatDataArrayIndex: number) => {
const targetItem = flattenedSections.itemLayouts[flatDataArrayIndex];
if (!targetItem) {
@@ -313,6 +314,10 @@ function BaseSelectionList(
};
const renderSectionHeader = ({section}: {section: SectionListDataType}) => {
+ if (section.CustomSectionHeader) {
+ return ;
+ }
+
if (!section.title || isEmptyObject(section.data)) {
return null;
}
@@ -329,7 +334,7 @@ function BaseSelectionList(
};
const renderItem = ({item, index, section}: SectionListRenderItemInfo>) => {
- const normalizedIndex = index + section.indexOffset;
+ const normalizedIndex = index + (section?.indexOffset ?? 0);
const isDisabled = !!section.isDisabled || item.isDisabled;
const isItemFocused = !isDisabled && (focusedIndex === normalizedIndex || itemsToHighlight?.has(item.keyForList ?? ''));
// We only create tooltips for the first 10 users or so since some reports have hundreds of users, causing performance to degrade.
@@ -603,7 +608,7 @@ function BaseSelectionList(
testID="selection-list"
onLayout={onSectionListLayout}
style={(!maxToRenderPerBatch || (shouldHideListOnInitialRender && isInitialSectionListRender)) && styles.opacity0}
- ListFooterComponent={ShowMoreButtonInstance}
+ ListFooterComponent={listFooterContent ?? ShowMoreButtonInstance}
/>
{children}
>
diff --git a/src/components/SelectionList/types.ts b/src/components/SelectionList/types.ts
index 119634c61fd8..9d1d9e15ca0b 100644
--- a/src/components/SelectionList/types.ts
+++ b/src/components/SelectionList/types.ts
@@ -12,6 +12,11 @@ import type RadioListItem from './RadioListItem';
import type TableListItem from './TableListItem';
import type UserListItem from './UserListItem';
+type TRightHandSideComponent = {
+ /** Component to display on the right side */
+ rightHandSideComponent?: ((item: TItem) => ReactElement | null | undefined) | ReactElement | null;
+};
+
type CommonListItemProps = {
/** Whether this item is focused (for arrow key controls) */
isFocused?: boolean;
@@ -34,9 +39,6 @@ type CommonListItemProps = {
/** Callback to fire when an error is dismissed */
onDismissError?: (item: TItem) => void;
- /** Component to display on the right side */
- rightHandSideComponent?: ((item: TItem) => ReactElement | null) | ReactElement | null;
-
/** Styles for the pressable component */
pressableStyle?: StyleProp;
@@ -54,7 +56,7 @@ type CommonListItemProps = {
/** Handles what to do when the item is focused */
onFocus?: () => void;
-};
+} & TRightHandSideComponent;
type ListItem = {
/** Text to display */
@@ -185,12 +187,12 @@ type Section = {
type SectionWithIndexOffset = Section & {
/** The initial index of this section given the total number of options in each section's data array */
- indexOffset: number;
+ indexOffset?: number;
};
type BaseSelectionListProps = Partial & {
/** Sections for the section list */
- sections: Array>> | typeof CONST.EMPTY_ARRAY;
+ sections: Array> | typeof CONST.EMPTY_ARRAY;
/** Default renderer for every item in the list */
ListItem: ValidListItem;
@@ -282,6 +284,9 @@ type BaseSelectionListProps = Partial & {
/** Custom content to display in the footer */
footerContent?: ReactNode;
+ /** Custom content to display in the footer of list component. If present ShowMore button won't be displayed */
+ listFooterContent?: React.JSX.Element | null;
+
/** Whether to use dynamic maxToRenderPerBatch depending on the visible number of elements */
shouldUseDynamicMaxToRenderPerBatch?: boolean;
@@ -294,9 +299,6 @@ type BaseSelectionListProps = Partial & {
/** Whether focus event should be delayed */
shouldDelayFocus?: boolean;
- /** Component to display on the right side of each child */
- rightHandSideComponent?: ((item: TItem) => ReactElement | null) | ReactElement | null;
-
/** Whether to show the loading indicator for new options */
isLoadingNewOptions?: boolean;
@@ -323,7 +325,7 @@ type BaseSelectionListProps = Partial & {
* When false, the list will render immediately and scroll to the bottom which works great for small lists.
*/
shouldHideListOnInitialRender?: boolean;
-};
+} & TRightHandSideComponent;
type SelectionListHandle = {
scrollAndHighlightItem?: (items: string[], timeout: number) => void;
@@ -344,7 +346,11 @@ type FlattenedSectionsReturn = {
type ButtonOrCheckBoxRoles = 'button' | 'checkbox';
-type SectionListDataType = SectionListData>;
+type ExtendedSectionListData> = SectionListData & {
+ CustomSectionHeader?: ({section}: {section: TSection}) => ReactElement;
+};
+
+type SectionListDataType = ExtendedSectionListData>;
export type {
BaseSelectionListProps,
diff --git a/src/components/SettlementButton.tsx b/src/components/SettlementButton.tsx
index 84c57ae381e3..f56c4dd1a863 100644
--- a/src/components/SettlementButton.tsx
+++ b/src/components/SettlementButton.tsx
@@ -180,13 +180,13 @@ function SettlementButton({
};
const canUseWallet = !isExpenseReport && currency === CONST.CURRENCY.USD;
- // Only show the Approve button if the user cannot pay the request
+ // Only show the Approve button if the user cannot pay the expense
if (shouldHidePaymentOptions && shouldShowApproveButton) {
return [approveButtonOption];
}
// To achieve the one tap pay experience we need to choose the correct payment type as default.
- // If the user has previously chosen a specific payment option or paid for some request or expense,
+ // If the user has previously chosen a specific payment option or paid for some expense,
// let's use the last payment method or use default.
const paymentMethod = nvpLastPaymentMethod?.[policyID] ?? '';
if (canUseWallet) {
diff --git a/src/components/TagPicker/index.tsx b/src/components/TagPicker/index.tsx
index f968af4f6030..97cd9aa5c691 100644
--- a/src/components/TagPicker/index.tsx
+++ b/src/components/TagPicker/index.tsx
@@ -32,7 +32,7 @@ type TagPickerProps = TagPickerOnyxProps & {
// eslint-disable-next-line react/no-unused-prop-types
policyID: string;
- /** The selected tag of the money request */
+ /** The selected tag of the expense */
selectedTag: string;
/** The name of tag list we are getting tags for */
diff --git a/src/components/transactionPropTypes.js b/src/components/transactionPropTypes.js
index 7eb1b776358c..f951837503f3 100644
--- a/src/components/transactionPropTypes.js
+++ b/src/components/transactionPropTypes.js
@@ -39,7 +39,7 @@ export default PropTypes.shape({
/** The text of the comment */
comment: PropTypes.string,
- /** The waypoints defining the distance request */
+ /** The waypoints defining the distance expense */
waypoints: PropTypes.shape({
/** The latitude of the waypoint */
lat: PropTypes.number,
diff --git a/src/hooks/useMarkdownStyle.ts b/src/hooks/useMarkdownStyle.ts
index 21c8d02e9194..b1f430e232e4 100644
--- a/src/hooks/useMarkdownStyle.ts
+++ b/src/hooks/useMarkdownStyle.ts
@@ -49,6 +49,10 @@ function useMarkdownStyle(message: string | null = null): MarkdownStyle {
color: theme.mentionText,
backgroundColor: theme.mentionBG,
},
+ mentionReport: {
+ color: theme.mentionText,
+ backgroundColor: theme.mentionBG,
+ },
}),
[theme, emojiFontSize],
);
diff --git a/src/languages/en.ts b/src/languages/en.ts
index 0ae49fee7398..dd09a0c470fd 100755
--- a/src/languages/en.ts
+++ b/src/languages/en.ts
@@ -50,6 +50,7 @@ import type {
PayerPaidAmountParams,
PayerPaidParams,
PayerSettledParams,
+ PaySomeoneParams,
RemovedTheRequestParams,
RenamedRoomActionParams,
ReportArchiveReasonsClosedParams,
@@ -418,7 +419,7 @@ export default {
},
login: {
hero: {
- header: 'Split bills, request payments, and chat with friends.',
+ header: 'Manage spend, split expenses, and chat with your team.',
body: 'Welcome to the future of Expensify, your new go-to place for financial collaboration with friends and teammates alike.',
},
},
@@ -470,14 +471,9 @@ export default {
copyEmailToClipboard: 'Copy email to clipboard',
markAsUnread: 'Mark as unread',
markAsRead: 'Mark as read',
- editAction: ({action}: EditActionParams) =>
- `Edit ${action?.actionName === CONST.REPORT.ACTIONS.TYPE.IOU ? `${action?.originalMessage.type === CONST.IOU.REPORT_ACTION_TYPE.TRACK ? 'expense' : 'request'}` : 'comment'}`,
- deleteAction: ({action}: DeleteActionParams) =>
- `Delete ${action?.actionName === CONST.REPORT.ACTIONS.TYPE.IOU ? `${action?.originalMessage.type === CONST.IOU.REPORT_ACTION_TYPE.TRACK ? 'expense' : 'request'}` : 'comment'}`,
- deleteConfirmation: ({action}: DeleteConfirmationParams) =>
- `Are you sure you want to delete this ${
- action?.actionName === CONST.REPORT.ACTIONS.TYPE.IOU ? `${action?.originalMessage.type === CONST.IOU.REPORT_ACTION_TYPE.TRACK ? 'expense' : 'request'}` : 'comment'
- }?`,
+ editAction: ({action}: EditActionParams) => `Edit ${action?.actionName === CONST.REPORT.ACTIONS.TYPE.IOU ? 'expense' : 'comment'}`,
+ deleteAction: ({action}: DeleteActionParams) => `Delete ${action?.actionName === CONST.REPORT.ACTIONS.TYPE.IOU ? 'expense' : 'comment'}`,
+ deleteConfirmation: ({action}: DeleteConfirmationParams) => `Are you sure you want to delete this ${action?.actionName === CONST.REPORT.ACTIONS.TYPE.IOU ? 'expense' : 'comment'}?`,
onlyVisible: 'Only visible to',
replyInThread: 'Reply in thread',
joinThread: 'Join thread',
@@ -505,7 +501,7 @@ export default {
beginningOfChatHistory: 'This is the beginning of your chat with ',
beginningOfChatHistoryPolicyExpenseChatPartOne: 'Collaboration between ',
beginningOfChatHistoryPolicyExpenseChatPartTwo: ' and ',
- beginningOfChatHistoryPolicyExpenseChatPartThree: ' starts here! 🎉 This is the place to chat, request money and settle up.',
+ beginningOfChatHistoryPolicyExpenseChatPartThree: ' starts here! 🎉 This is the place to chat, submit expenses and settle up.',
beginningOfChatHistorySelfDM: 'This is your personal space. Use it for notes, tasks, drafts, and reminders.',
chatWithAccountManager: 'Chat with your account manager here',
sayHello: 'Say hello!',
@@ -513,9 +509,9 @@ export default {
welcomeToRoom: ({roomName}: WelcomeToRoomParams) => `Welcome to ${roomName}!`,
usePlusButton: ({additionalText}: UsePlusButtonParams) => `\nYou can also use the + button to ${additionalText}, or assign a task!`,
iouTypes: {
- send: 'send money',
- split: 'split a bill',
- request: 'request money',
+ send: 'pay expenses',
+ split: 'split an expense',
+ request: 'submit an expense',
// eslint-disable-next-line @typescript-eslint/naming-convention
'track-expense': 'track an expense',
},
@@ -595,15 +591,15 @@ export default {
quickAction: {
scanReceipt: 'Scan receipt',
recordDistance: 'Record distance',
- requestMoney: 'Request money',
- splitBill: 'Split bill',
+ requestMoney: 'Submit expense',
+ splitBill: 'Split expense',
splitScan: 'Split receipt',
splitDistance: 'Split distance',
- sendMoney: 'Send money',
+ sendMoney: 'Pay someone',
assignTask: 'Assign task',
header: 'Quick action',
- trackManual: 'Track manual',
- trackScan: 'Track scan',
+ trackManual: 'Track expense',
+ trackScan: 'Track receipt',
trackDistance: 'Track distance',
},
iou: {
@@ -616,14 +612,13 @@ export default {
card: 'Card',
original: 'Original',
split: 'Split',
- addToSplit: 'Add to split',
- splitBill: 'Split bill',
- request: 'Request',
+ splitExpense: 'Split expense',
+ paySomeone: ({name}: PaySomeoneParams) => `Pay ${name ?? 'someone'}`,
+ expense: 'Expense',
categorize: 'Categorize',
share: 'Share',
participants: 'Participants',
- requestMoney: 'Request money',
- sendMoney: 'Send money',
+ submitExpense: 'Submit expense',
trackExpense: 'Track expense',
pay: 'Pay',
cancelPayment: 'Cancel payment',
@@ -642,20 +637,20 @@ export default {
receiptStatusText: "Only you can see this receipt when it's scanning. Check back later or enter the details now.",
receiptScanningFailed: 'Receipt scanning failed. Enter the details manually.',
transactionPendingText: 'It takes a few days from the date the card was used for the transaction to post.',
- requestCount: ({count, scanningReceipts = 0, pendingReceipts = 0}: RequestCountParams) =>
- `${count} ${Str.pluralize('request', 'requests', count)}${scanningReceipts > 0 ? `, ${scanningReceipts} scanning` : ''}${
+ expenseCount: ({count, scanningReceipts = 0, pendingReceipts = 0}: RequestCountParams) =>
+ `${count} ${Str.pluralize('expense', 'expenses', count)}${scanningReceipts > 0 ? `, ${scanningReceipts} scanning` : ''}${
pendingReceipts > 0 ? `, ${pendingReceipts} pending` : ''
}`,
- deleteRequest: 'Delete request',
- deleteConfirmation: 'Are you sure that you want to delete this request?',
+ deleteExpense: 'Delete expense',
+ deleteConfirmation: 'Are you sure that you want to delete this expense?',
settledExpensify: 'Paid',
settledElsewhere: 'Paid elsewhere',
settleExpensify: ({formattedAmount}: SettleExpensifyCardParams) => (formattedAmount ? `Pay ${formattedAmount} with Expensify` : `Pay with Expensify`),
payElsewhere: ({formattedAmount}: SettleExpensifyCardParams) => (formattedAmount ? `Pay ${formattedAmount} elsewhere` : `Pay elsewhere`),
nextStep: 'Next Steps',
finished: 'Finished',
- requestAmount: ({amount}: RequestAmountParams) => `request ${amount}`,
- requestedAmount: ({formattedAmount, comment}: RequestedAmountMessageParams) => `requested ${formattedAmount}${comment ? ` for ${comment}` : ''}`,
+ submitAmount: ({amount}: RequestAmountParams) => `submit ${amount}`,
+ submittedAmount: ({formattedAmount, comment}: RequestedAmountMessageParams) => `submitted ${formattedAmount}${comment ? ` for ${comment}` : ''}`,
trackedAmount: ({formattedAmount, comment}: RequestedAmountMessageParams) => `tracking ${formattedAmount}${comment ? ` for ${comment}` : ''}`,
splitAmount: ({amount}: SplitAmountParams) => `split ${amount}`,
didSplitAmount: ({formattedAmount, comment}: DidSplitAmountMessageParams) => `split ${formattedAmount}${comment ? ` for ${comment}` : ''}`,
@@ -680,16 +675,16 @@ export default {
paidWithExpensifyWithAmount: ({payer, amount}: PaidWithExpensifyWithAmountParams) => `${payer ? `${payer} ` : ''}paid ${amount} using Expensify`,
noReimbursableExpenses: 'This report has an invalid amount',
pendingConversionMessage: "Total will update when you're back online",
- changedTheRequest: 'changed the request',
+ changedTheExpense: 'changed the expense',
setTheRequest: ({valueName, newValueToDisplay}: SetTheRequestParams) => `the ${valueName} to ${newValueToDisplay}`,
setTheDistance: ({newDistanceToDisplay, newAmountToDisplay}: SetTheDistanceParams) => `set the distance to ${newDistanceToDisplay}, which set the amount to ${newAmountToDisplay}`,
removedTheRequest: ({valueName, oldValueToDisplay}: RemovedTheRequestParams) => `the ${valueName} (previously ${oldValueToDisplay})`,
updatedTheRequest: ({valueName, newValueToDisplay, oldValueToDisplay}: UpdatedTheRequestParams) => `the ${valueName} to ${newValueToDisplay} (previously ${oldValueToDisplay})`,
updatedTheDistance: ({newDistanceToDisplay, oldDistanceToDisplay, newAmountToDisplay, oldAmountToDisplay}: UpdatedTheDistanceParams) =>
`changed the distance to ${newDistanceToDisplay} (previously ${oldDistanceToDisplay}), which updated the amount to ${newAmountToDisplay} (previously ${oldAmountToDisplay})`,
- threadRequestReportName: ({formattedAmount, comment}: ThreadRequestReportNameParams) => `${formattedAmount} ${comment ? `for ${comment}` : 'request'}`,
+ threadExpenseReportName: ({formattedAmount, comment}: ThreadRequestReportNameParams) => `${formattedAmount} ${comment ? `for ${comment}` : 'expense'}`,
threadTrackReportName: ({formattedAmount, comment}: ThreadRequestReportNameParams) => `Tracking ${formattedAmount} ${comment ? `for ${comment}` : ''}`,
- threadSentMoneyReportName: ({formattedAmount, comment}: ThreadSentMoneyReportNameParams) => `${formattedAmount} sent${comment ? ` for ${comment}` : ''}`,
+ threadPaySomeoneReportName: ({formattedAmount, comment}: ThreadSentMoneyReportNameParams) => `${formattedAmount} sent${comment ? ` for ${comment}` : ''}`,
tagSelection: 'Select a tag to better organize your spend.',
categorySelection: 'Select a category to better organize your spend.',
error: {
@@ -698,36 +693,36 @@ export default {
invalidTaxAmount: ({amount}: RequestAmountParams) => `Maximum tax amount is ${amount}`,
invalidSplit: 'Split amounts do not equal total amount',
other: 'Unexpected error, please try again later',
- genericCreateFailureMessage: 'Unexpected error requesting money, please try again later',
+ genericCreateFailureMessage: 'Unexpected error submitting this expense. Please try again later.',
receiptFailureMessage: "The receipt didn't upload. ",
saveFileMessage: 'Download the file ',
loseFileMessage: 'or dismiss this error and lose it',
- genericDeleteFailureMessage: 'Unexpected error deleting the money request, please try again later',
- genericEditFailureMessage: 'Unexpected error editing the money request, please try again later',
+ genericDeleteFailureMessage: 'Unexpected error deleting this expense, please try again later',
+ genericEditFailureMessage: 'Unexpected error editing this expense, please try again later',
genericSmartscanFailureMessage: 'Transaction is missing fields',
duplicateWaypointsErrorMessage: 'Please remove duplicate waypoints',
atLeastTwoDifferentWaypoints: 'Please enter at least two different addresses',
- splitBillMultipleParticipantsErrorMessage: 'Split bill is only allowed between a single workspace or individual users. Please update your selection.',
+ splitExpenseMultipleParticipantsErrorMessage: 'An expense cannot be split between a workspace and other members. Please update your selection.',
invalidMerchant: 'Please enter a correct merchant.',
},
waitingOnEnabledWallet: ({submitterDisplayName}: WaitingOnBankAccountParams) => `Started settling up, payment is held until ${submitterDisplayName} enables their Wallet`,
enableWallet: 'Enable Wallet',
hold: 'Hold',
- holdRequest: 'Hold request',
- unholdRequest: 'Unhold request',
- heldRequest: 'held this request',
- unheldRequest: 'unheld this request',
- explainHold: "Explain why you're holding this request.",
+ holdExpense: 'Hold expense',
+ unholdExpense: 'Unhold expense',
+ heldExpense: 'held this expense',
+ unheldExpense: 'unheld this expense',
+ explainHold: "Explain why you're holding this expense.",
reason: 'Reason',
holdReasonRequired: 'A reason is required when holding.',
- requestOnHold: 'This request was put on hold. Review the comments for next steps.',
+ expenseOnHold: 'This expense was put on hold. Review the comments for next steps.',
confirmApprove: 'Confirm what to approve',
confirmApprovalAmount: 'Approve the entire report total or only the amount not on hold.',
confirmPay: 'Confirm what to pay',
confirmPayAmount: 'Pay all out-of-pocket spend or only the amount not on hold.',
payOnly: 'Pay only',
approveOnly: 'Approve only',
- holdEducationalTitle: 'This request is on',
+ holdEducationalTitle: 'This expense is on',
whatIsHoldTitle: 'What is hold?',
whatIsHoldExplain: 'Hold is our way of streamlining financial collaboration. "Reject" is so harsh!',
holdIsTemporaryTitle: 'Hold is usually temporary',
@@ -938,8 +933,7 @@ export default {
reasonForLeavingPrompt: 'We’d hate to see you go! Would you kindly tell us why, so we can improve?',
enterMessageHere: 'Enter message here',
closeAccountWarning: 'Closing your account cannot be undone.',
- closeAccountPermanentlyDeleteData:
- 'This will permanently delete all of your unsubmitted expense data and will cancel and decline any outstanding money requests. Are you sure you want to delete the account?',
+ closeAccountPermanentlyDeleteData: 'Are you sure you want to delete your account? This will permanently delete any outstanding expenses.',
enterDefaultContactToConfirm: 'Please type your default contact method to confirm you wish to close your account. Your default contact method is:',
enterDefaultContact: 'Enter your default contact method',
defaultContact: 'Default contact method:',
@@ -1267,19 +1261,6 @@ export default {
},
chooseThemeBelowOrSync: 'Choose a theme below, or sync with your device settings.',
},
- signInPage: {
- expensifyDotCash: 'New Expensify',
- theCode: 'the code',
- openJobs: 'open jobs',
- heroHeading: 'Split bills\nand chat with friends.',
- heroDescription: {
- phrase1: "Money talks. And now that chat and payments are in one place, it's also easy. Your payments get to you as fast as you can get your point across.",
- phrase2: 'The New Expensify is open source. View',
- phrase3: 'the code',
- phrase4: 'View',
- phrase5: 'open jobs',
- },
- },
termsOfUse: {
phrase1: 'By logging in, you agree to the',
phrase2: 'Terms of Service',
@@ -1345,7 +1326,7 @@ export default {
[CONST.ONBOARDING_CHOICES.EMPLOYER]: 'Get paid back by my employer',
[CONST.ONBOARDING_CHOICES.MANAGE_TEAM]: "Manage my team's expenses",
[CONST.ONBOARDING_CHOICES.PERSONAL_SPEND]: 'Track and budget personal spend',
- [CONST.ONBOARDING_CHOICES.CHAT_SPLIT]: 'Chat and split bills with friends',
+ [CONST.ONBOARDING_CHOICES.CHAT_SPLIT]: 'Chat and split expenses with friends',
[CONST.ONBOARDING_CHOICES.LOOKING_AROUND]: "I'm just looking around",
},
error: {
@@ -2540,7 +2521,6 @@ export default {
parentReportAction: {
deletedReport: '[Deleted report]',
deletedMessage: '[Deleted message]',
- deletedRequest: '[Deleted request]',
deletedExpense: '[Deleted expense]',
reversedTransaction: '[Reversed transaction]',
deletedTask: '[Deleted task]',
@@ -2589,7 +2569,7 @@ export default {
decline: 'Decline',
},
actionableMentionTrackExpense: {
- request: 'Request someone to pay it',
+ submit: 'Submit it to someone',
categorize: 'Categorize it',
share: 'Share it with my accountant',
nothing: 'Nothing for now',
@@ -2668,27 +2648,27 @@ export default {
body: `Get paid to talk to your friends! Start a chat with a new Expensify account and get $${CONST.REFERRAL_PROGRAM.REVENUE} when they become a customer.`,
},
[CONST.REFERRAL_PROGRAM.CONTENT_TYPES.MONEY_REQUEST]: {
- buttonText1: 'Request money, ',
+ buttonText1: 'Submit expense, ',
buttonText2: `get $${CONST.REFERRAL_PROGRAM.REVENUE}.`,
- header: `Request money, get $${CONST.REFERRAL_PROGRAM.REVENUE}`,
- body: `It pays to get paid! Request money from a new Expensify account and get $${CONST.REFERRAL_PROGRAM.REVENUE} when they become a customer.`,
+ header: `Submit an expense, get $${CONST.REFERRAL_PROGRAM.REVENUE}`,
+ body: `It pays to get paid! Submit an expense to a new Expensify account and get $${CONST.REFERRAL_PROGRAM.REVENUE} when they become a customer.`,
},
[CONST.REFERRAL_PROGRAM.CONTENT_TYPES.SEND_MONEY]: {
- buttonText1: 'Send money, ',
+ buttonText1: 'Pay Someone, ',
buttonText2: `get $${CONST.REFERRAL_PROGRAM.REVENUE}.`,
- header: `Send money, get $${CONST.REFERRAL_PROGRAM.REVENUE}`,
- body: `You gotta send money to make money! Send money to a new Expensify account and get $${CONST.REFERRAL_PROGRAM.REVENUE} when they become a customer.`,
+ header: `Pay Someone, get $${CONST.REFERRAL_PROGRAM.REVENUE}`,
+ body: `You gotta spend money to make money! Pay someone with Expensify and get $${CONST.REFERRAL_PROGRAM.REVENUE} when they become a customer.`,
},
[CONST.REFERRAL_PROGRAM.CONTENT_TYPES.REFER_FRIEND]: {
buttonText1: 'Invite a friend, ',
buttonText2: `get $${CONST.REFERRAL_PROGRAM.REVENUE}.`,
header: `Get $${CONST.REFERRAL_PROGRAM.REVENUE}`,
- body: `Be the first to chat, send or request money, split a bill, or share your invite link with a friend, and you'll get $${CONST.REFERRAL_PROGRAM.REVENUE} when they become a customer. You can post your invite link on social media, too!`,
+ body: `Chat, pay, submit, or split an expense with a friend and get $${CONST.REFERRAL_PROGRAM.REVENUE} when they become a customer. Otherwise, just share your invite link!`,
},
[CONST.REFERRAL_PROGRAM.CONTENT_TYPES.SHARE_CODE]: {
buttonText1: `Get $${CONST.REFERRAL_PROGRAM.REVENUE}`,
header: `Get $${CONST.REFERRAL_PROGRAM.REVENUE}`,
- body: `Be the first to chat, send or request money, split a bill, or share your invite link with a friend, and you'll get $${CONST.REFERRAL_PROGRAM.REVENUE} when they become a customer. You can post your invite link on social media, too!`,
+ body: `Chat, pay, submit, or split an expense with a friend and get $${CONST.REFERRAL_PROGRAM.REVENUE} when they become a customer. Otherwise, just share your invite link!`,
},
copyReferralLink: 'Copy invite link',
},
@@ -2696,7 +2676,7 @@ export default {
[CONST.INTRO_CHOICES.TRACK]: 'Track business spend for taxes',
[CONST.INTRO_CHOICES.SUBMIT]: 'Get paid back by my employer',
[CONST.INTRO_CHOICES.MANAGE_TEAM]: "Manage my team's expenses",
- [CONST.INTRO_CHOICES.CHAT_SPLIT]: 'Chat and split bills with friends',
+ [CONST.INTRO_CHOICES.CHAT_SPLIT]: 'Chat and split expenses with friends',
welcomeMessage: 'Welcome to Expensify',
welcomeSubtitle: 'What would you like to do?',
},
diff --git a/src/languages/es.ts b/src/languages/es.ts
index 8955841559fe..b526a2785495 100644
--- a/src/languages/es.ts
+++ b/src/languages/es.ts
@@ -49,6 +49,7 @@ import type {
PayerPaidAmountParams,
PayerPaidParams,
PayerSettledParams,
+ PaySomeoneParams,
RemovedTheRequestParams,
RenamedRoomActionParams,
ReportArchiveReasonsClosedParams,
@@ -409,7 +410,7 @@ export default {
},
login: {
hero: {
- header: 'Divida las facturas, solicite pagos y chatee con sus amigos.',
+ header: 'Gestiona, divide gastos y chatea con tu equipo.',
body: 'Bienvenido al futuro de Expensify, tu nuevo lugar de referencia para la colaboración financiera con amigos y compañeros de equipo por igual.',
},
},
@@ -461,18 +462,10 @@ export default {
copyEmailToClipboard: 'Copiar email al portapapeles',
markAsUnread: 'Marcar como no leído',
markAsRead: 'Marcar como leído',
- editAction: ({action}: EditActionParams) =>
- `Editar ${
- action?.actionName === CONST.REPORT.ACTIONS.TYPE.IOU ? `${action?.originalMessage.type === CONST.IOU.REPORT_ACTION_TYPE.TRACK ? 'gastos' : 'solicitud'}` : 'comentario'
- }`,
- deleteAction: ({action}: DeleteActionParams) =>
- `Eliminar ${
- action?.actionName === CONST.REPORT.ACTIONS.TYPE.IOU ? `${action?.originalMessage.type === CONST.IOU.REPORT_ACTION_TYPE.TRACK ? 'gastos' : 'solicitud'}` : 'comentario'
- }`,
+ editAction: ({action}: EditActionParams) => `Editar ${action?.actionName === CONST.REPORT.ACTIONS.TYPE.IOU ? 'gastos' : 'comentario'}`,
+ deleteAction: ({action}: DeleteActionParams) => `Eliminar ${action?.actionName === CONST.REPORT.ACTIONS.TYPE.IOU ? 'gastos' : 'comentario'}`,
deleteConfirmation: ({action}: DeleteConfirmationParams) =>
- `¿Estás seguro de que quieres eliminar esta ${
- action?.actionName === CONST.REPORT.ACTIONS.TYPE.IOU ? `${action?.originalMessage.type === CONST.IOU.REPORT_ACTION_TYPE.TRACK ? 'gastos' : 'solicitud'}` : 'comentario'
- }`,
+ `¿Estás seguro de que quieres eliminar este ${action?.actionName === CONST.REPORT.ACTIONS.TYPE.IOU ? 'gasto' : 'comentario'}`,
onlyVisible: 'Visible sólo para',
replyInThread: 'Responder en el hilo',
joinThread: 'Unirse al hilo',
@@ -501,7 +494,7 @@ export default {
beginningOfChatHistory: 'Aquí comienzan tus conversaciones con ',
beginningOfChatHistoryPolicyExpenseChatPartOne: '¡La colaboración entre ',
beginningOfChatHistoryPolicyExpenseChatPartTwo: ' y ',
- beginningOfChatHistoryPolicyExpenseChatPartThree: ' empieza aquí! 🎉 Este es el lugar donde chatear, pedir dinero y pagar.',
+ beginningOfChatHistoryPolicyExpenseChatPartThree: ' empieza aquí! 🎉 Este es el lugar donde chatear y presentar o pagar gastos.',
beginningOfChatHistorySelfDM: 'Este es tu espacio personal. Úsalo para notas, tareas, borradores y recordatorios.',
chatWithAccountManager: 'Chatea con tu gestor de cuenta aquí',
sayHello: '¡Saluda!',
@@ -509,9 +502,9 @@ export default {
welcomeToRoom: ({roomName}: WelcomeToRoomParams) => `¡Bienvenido a ${roomName}!`,
usePlusButton: ({additionalText}: UsePlusButtonParams) => `\n¡También puedes usar el botón + de abajo para ${additionalText}, o asignar una tarea!`,
iouTypes: {
- send: 'enviar dinero',
- split: 'dividir una factura',
- request: 'pedir dinero',
+ send: 'pagar gastos',
+ split: 'dividir un gasto',
+ request: 'presentar un gasto',
// eslint-disable-next-line @typescript-eslint/naming-convention
'track-expense': 'rastrear un gasto',
},
@@ -591,11 +584,11 @@ export default {
quickAction: {
scanReceipt: 'Escanear recibo',
recordDistance: 'Grabar distancia',
- requestMoney: 'Solicitar dinero',
- splitBill: 'Dividir cuenta',
+ requestMoney: 'Presentar gasto',
+ splitBill: 'Dividir gasto',
splitScan: 'Dividir recibo',
splitDistance: 'Dividir distancia',
- sendMoney: 'Enviar dinero',
+ sendMoney: 'Pagar a alguien',
assignTask: 'Assignar tarea',
header: 'Acción rápida',
trackManual: 'Crear gasto',
@@ -612,14 +605,13 @@ export default {
card: 'Tarjeta',
original: 'Original',
split: 'Dividir',
- addToSplit: 'Añadir para dividir',
- splitBill: 'Dividir factura',
- request: 'Solicitar',
+ splitExpense: 'Dividir gasto',
+ expense: 'Gasto',
categorize: 'Categorizar',
share: 'Compartir',
participants: 'Participantes',
- requestMoney: 'Pedir dinero',
- sendMoney: 'Enviar dinero',
+ submitExpense: 'Presentar gasto',
+ paySomeone: ({name}: PaySomeoneParams) => `Pagar a ${name ?? 'alguien'}`,
trackExpense: 'Seguimiento de gastos',
pay: 'Pagar',
cancelPayment: 'Cancelar el pago',
@@ -638,11 +630,11 @@ export default {
receiptStatusText: 'Solo tú puedes ver este recibo cuando se está escaneando. Vuelve más tarde o introduce los detalles ahora.',
receiptScanningFailed: 'El escaneo de recibo ha fallado. Introduce los detalles manualmente.',
transactionPendingText: 'La transacción tarda unos días en contabilizarse desde la fecha en que se utilizó la tarjeta.',
- requestCount: ({count, scanningReceipts = 0, pendingReceipts = 0}: RequestCountParams) =>
- `${count} ${Str.pluralize('solicitude', 'solicitudes', count)}${scanningReceipts > 0 ? `, ${scanningReceipts} escaneando` : ''}${
+ expenseCount: ({count, scanningReceipts = 0, pendingReceipts = 0}: RequestCountParams) =>
+ `${count} ${Str.pluralize('gasto', 'gastos', count)}${scanningReceipts > 0 ? `, ${scanningReceipts} escaneando` : ''}${
pendingReceipts > 0 ? `, ${pendingReceipts} pendiente` : ''
}`,
- deleteRequest: 'Eliminar solicitud',
+ deleteExpense: 'Eliminar gasto',
deleteConfirmation: '¿Estás seguro de que quieres eliminar esta solicitud?',
settledExpensify: 'Pagado',
settledElsewhere: 'Pagado de otra forma',
@@ -650,9 +642,9 @@ export default {
payElsewhere: ({formattedAmount}: SettleExpensifyCardParams) => (formattedAmount ? `Pagar ${formattedAmount} de otra forma` : `Pagar de otra forma`),
nextStep: 'Pasos Siguientes',
finished: 'Finalizado',
- requestAmount: ({amount}: RequestAmountParams) => `solicitar ${amount}`,
- requestedAmount: ({formattedAmount, comment}: RequestedAmountMessageParams) => `solicité ${formattedAmount}${comment ? ` para ${comment}` : ''}`,
- trackedAmount: ({formattedAmount, comment}: RequestedAmountMessageParams) => `seguimiento ${formattedAmount}${comment ? ` para ${comment}` : ''}`,
+ submitAmount: ({amount}: RequestAmountParams) => `solicitar ${amount}`,
+ submittedAmount: ({formattedAmount, comment}: RequestedAmountMessageParams) => `solicitó ${formattedAmount}${comment ? ` para ${comment}` : ''}`,
+ trackedAmount: ({formattedAmount, comment}: RequestedAmountMessageParams) => `realizó un seguimiento de ${formattedAmount}${comment ? ` para ${comment}` : ''}`,
splitAmount: ({amount}: SplitAmountParams) => `dividir ${amount}`,
didSplitAmount: ({formattedAmount, comment}: DidSplitAmountMessageParams) => `dividió ${formattedAmount}${comment ? ` para ${comment}` : ''}`,
amountEach: ({amount}: AmountEachParams) => `${amount} cada uno`,
@@ -676,7 +668,7 @@ export default {
paidWithExpensifyWithAmount: ({payer, amount}: PaidWithExpensifyWithAmountParams) => `${payer ? `${payer} ` : ''}pagó ${amount} con Expensify`,
noReimbursableExpenses: 'El importe de este informe no es válido',
pendingConversionMessage: 'El total se actualizará cuando estés online',
- changedTheRequest: 'cambió la solicitud',
+ changedTheExpense: 'cambió el gasto',
setTheRequest: ({valueName, newValueToDisplay}: SetTheRequestParams) => `${valueName === 'comerciante' ? 'el' : 'la'} ${valueName} a ${newValueToDisplay}`,
setTheDistance: ({newDistanceToDisplay, newAmountToDisplay}: SetTheDistanceParams) =>
`estableció la distancia a ${newDistanceToDisplay}, lo que estableció el importe a ${newAmountToDisplay}`,
@@ -685,9 +677,9 @@ export default {
`${valueName === 'comerciante' ? 'el' : 'la'} ${valueName} a ${newValueToDisplay} (previamente ${oldValueToDisplay})`,
updatedTheDistance: ({newDistanceToDisplay, oldDistanceToDisplay, newAmountToDisplay, oldAmountToDisplay}: UpdatedTheDistanceParams) =>
`cambió la distancia a ${newDistanceToDisplay} (previamente ${oldDistanceToDisplay}), lo que cambió el importe a ${newAmountToDisplay} (previamente ${oldAmountToDisplay})`,
- threadRequestReportName: ({formattedAmount, comment}: ThreadRequestReportNameParams) => `${comment ? `${formattedAmount} para ${comment}` : `Solicitud de ${formattedAmount}`}`,
+ threadExpenseReportName: ({formattedAmount, comment}: ThreadRequestReportNameParams) => `${comment ? `${formattedAmount} para ${comment}` : `Gasto de ${formattedAmount}`}`,
threadTrackReportName: ({formattedAmount, comment}: ThreadRequestReportNameParams) => `Seguimiento ${formattedAmount} ${comment ? `para ${comment}` : ''}`,
- threadSentMoneyReportName: ({formattedAmount, comment}: ThreadSentMoneyReportNameParams) => `${formattedAmount} enviado${comment ? ` para ${comment}` : ''}`,
+ threadPaySomeoneReportName: ({formattedAmount, comment}: ThreadSentMoneyReportNameParams) => `${formattedAmount} enviado${comment ? ` para ${comment}` : ''}`,
tagSelection: 'Selecciona una etiqueta para organizar mejor tu dinero.',
categorySelection: 'Seleccione una categoría para organizar mejor tu dinero.',
error: {
@@ -696,28 +688,28 @@ export default {
invalidTaxAmount: ({amount}: RequestAmountParams) => `El importe máximo del impuesto es ${amount}`,
invalidSplit: 'La suma de las partes no equivale al importe total',
other: 'Error inesperado, por favor inténtalo más tarde',
- genericCreateFailureMessage: 'Error inesperado solicitando dinero. Por favor, inténtalo más tarde',
+ genericCreateFailureMessage: 'Error inesperado al enviar este gasto. Por favor, inténtalo más tarde.',
receiptFailureMessage: 'El recibo no se subió. ',
saveFileMessage: 'Guarda el archivo ',
loseFileMessage: 'o descarta este error y piérdelo',
- genericDeleteFailureMessage: 'Error inesperado eliminando la solicitud de dinero. Por favor, inténtalo más tarde',
- genericEditFailureMessage: 'Error inesperado al guardar la solicitud de dinero. Por favor, inténtalo más tarde',
+ genericDeleteFailureMessage: 'Error inesperado al eliminar este gasto. Por favor, inténtalo más tarde',
+ genericEditFailureMessage: 'Error inesperado al editar este gasto. Por favor, inténtalo más tarde',
genericSmartscanFailureMessage: 'La transacción tiene campos vacíos',
duplicateWaypointsErrorMessage: 'Por favor, elimina los puntos de ruta duplicados',
atLeastTwoDifferentWaypoints: 'Por favor, introduce al menos dos direcciones diferentes',
- splitBillMultipleParticipantsErrorMessage: 'Solo puedes dividir una cuenta entre un único espacio de trabajo o con usuarios individuales. Por favor, actualiza tu selección.',
+ splitExpenseMultipleParticipantsErrorMessage: 'Solo puedes dividir un gasto entre un único espacio de trabajo o con usuarios individuales. Por favor, actualiza tu selección.',
invalidMerchant: 'Por favor, introduce un comerciante correcto.',
},
waitingOnEnabledWallet: ({submitterDisplayName}: WaitingOnBankAccountParams) => `Inició el pago, pero no se procesará hasta que ${submitterDisplayName} active su Billetera`,
enableWallet: 'Habilitar Billetera',
- holdRequest: 'Bloquear solicitud',
- unholdRequest: 'Desbloquear solicitud',
- heldRequest: 'bloqueó esta solicitud',
- unheldRequest: 'desbloqueó esta solicitud',
+ holdExpense: 'Bloquear gasto',
+ unholdExpense: 'Desbloquear gasto',
+ heldExpense: 'bloqueó este gasto',
+ unheldExpense: 'desbloqueó este gasto',
explainHold: 'Explica la razón para bloquear esta solicitud.',
reason: 'Razón',
holdReasonRequired: 'Se requiere una razón para bloquear.',
- requestOnHold: 'Este solicitud está bloqueada. Revisa los comentarios para saber como proceder.',
+ expenseOnHold: 'Este gasto está bloqueado. Revisa los comentarios para saber como proceder.',
confirmApprove: 'Confirma que quieres aprobar',
confirmApprovalAmount: 'Aprobar el total o solo la parte no bloqueada.',
confirmPay: 'Confirma que quieres pagar',
@@ -725,7 +717,7 @@ export default {
payOnly: 'Solo pagar',
approveOnly: 'Solo aprobar',
hold: 'Bloqueada',
- holdEducationalTitle: 'Esta solicitud está',
+ holdEducationalTitle: 'Este gasto está',
whatIsHoldTitle: '¿Qué es Bloquear?',
whatIsHoldExplain: 'Bloquear es nuestra forma de agilizar la colaboración financiera. ¡"Rechazar" es tan duro!',
holdIsTemporaryTitle: 'Bloquear suele ser temporal',
@@ -936,8 +928,7 @@ export default {
reasonForLeavingPrompt: '¡Lamentamos verte partir! ¿Serías tan amable de decirnos por qué, para que podamos mejorar?',
enterMessageHere: 'Escribe aquí tu mensaje',
closeAccountWarning: 'Una vez cerrada tu cuenta no se puede revertir.',
- closeAccountPermanentlyDeleteData:
- 'Esta acción eliminará permanentemente toda la información de tus gastos no enviados y cancelará o rechazará cualquier solicitud de dinero pendiente. ¿Estás seguro de que quieres eliminar tu cuenta?',
+ closeAccountPermanentlyDeleteData: '¿Estás seguro de que quieres eliminar tu cuenta? Esta acción eliminará permanentemente toda la información de cualquier gasto pendiente.',
enterDefaultContactToConfirm: 'Por favor, escribe tu método de contacto predeterminado para confirmar que deseas eliminar tu cuenta. Tu método de contacto predeterminado es:',
enterDefaultContact: 'Tu método de contacto predeterminado',
defaultContact: 'Método de contacto predeterminado:',
@@ -1269,19 +1260,6 @@ export default {
},
chooseThemeBelowOrSync: 'Elige un tema a continuación o sincronízalo con los ajustes de tu dispositivo.',
},
- signInPage: {
- expensifyDotCash: 'Nuevo Expensify',
- theCode: 'el código',
- openJobs: 'trabajos disponibles',
- heroHeading: 'Dividir cuentas\ny chatear con amigos.',
- heroDescription: {
- phrase1: 'El dinero habla. Y ahora que el chat y los pagos están en un solo lugar, también es fácil. Tus pagos te llegan tan rápido como puedes hacer llegar tu mensaje',
- phrase2: 'Nuevo Expensify es de código abierto. Vista',
- phrase3: 'el código',
- phrase4: 'Vista',
- phrase5: 'vacantes',
- },
- },
termsOfUse: {
phrase1: 'Al iniciar sesión, estás accediendo a los',
phrase2: 'Términos de Servicio',
@@ -1347,7 +1325,7 @@ export default {
[CONST.ONBOARDING_CHOICES.EMPLOYER]: 'Cobrar de mi empresa',
[CONST.ONBOARDING_CHOICES.MANAGE_TEAM]: 'Gestionar los gastos de mi equipo',
[CONST.ONBOARDING_CHOICES.PERSONAL_SPEND]: 'Controlar y presupuestar los gastos personales',
- [CONST.ONBOARDING_CHOICES.CHAT_SPLIT]: 'Chatea y divide cuentas con tus amigos',
+ [CONST.ONBOARDING_CHOICES.CHAT_SPLIT]: 'Chatea y divide gastos con tus amigos',
[CONST.ONBOARDING_CHOICES.LOOKING_AROUND]: 'Sólo estoy mirando',
},
error: {
@@ -3032,7 +3010,6 @@ export default {
parentReportAction: {
deletedReport: '[Informe eliminado]',
deletedMessage: '[Mensaje eliminado]',
- deletedRequest: '[Solicitud eliminada]',
deletedExpense: '[Gasto eliminado]',
reversedTransaction: '[Transacción anulada]',
deletedTask: '[Tarea eliminada]',
@@ -3059,7 +3036,7 @@ export default {
decline: 'Rechazar',
},
actionableMentionTrackExpense: {
- request: 'Pedirle a alguien que lo pague',
+ submit: 'Pedirle a alguien que lo pague',
categorize: 'Categorizarlo',
share: 'Compartirlo con mi contador',
nothing: 'Por ahora, nada',
@@ -3161,27 +3138,27 @@ export default {
body: `¡Gana dinero por hablar con tus amigos! Inicia un chat con una cuenta nueva de Expensify y recibe $${CONST.REFERRAL_PROGRAM.REVENUE} cuando se conviertan en clientes.`,
},
[CONST.REFERRAL_PROGRAM.CONTENT_TYPES.MONEY_REQUEST]: {
- buttonText1: 'Pide dinero, ',
+ buttonText1: 'Presentar gasto, ',
buttonText2: `recibe $${CONST.REFERRAL_PROGRAM.REVENUE}`,
- header: `Pide dinero y recibe $${CONST.REFERRAL_PROGRAM.REVENUE}`,
- body: `¡Vale la pena cobrar! Pide dinero a una cuenta nueva de Expensify y recibe $${CONST.REFERRAL_PROGRAM.REVENUE} cuando se conviertan en clientes.`,
+ header: `Presenta un gasto y consigue $${CONST.REFERRAL_PROGRAM.REVENUE}`,
+ body: `¡Vale la pena cobrar! Envia un gasto a una cuenta nueva de Expensify y recibe $${CONST.REFERRAL_PROGRAM.REVENUE} cuando se conviertan en clientes.`,
},
[CONST.REFERRAL_PROGRAM.CONTENT_TYPES.SEND_MONEY]: {
- buttonText1: 'Envía dinero, ',
+ buttonText1: 'Pagar a alguien, ',
buttonText2: `recibe $${CONST.REFERRAL_PROGRAM.REVENUE}`,
- header: `Envía dinero y recibe $${CONST.REFERRAL_PROGRAM.REVENUE}`,
- body: `¡Hay que enviar dinero para ganar dinero! Envía dinero a una cuenta nueva de Expensify y recibe $${CONST.REFERRAL_PROGRAM.REVENUE} cuando se conviertan en clientes.`,
+ header: `Paga a alguien y recibe $${CONST.REFERRAL_PROGRAM.REVENUE}`,
+ body: `¡Hay que gastar dinero para ganar dinero! Paga a alguien con Expensify y recibe $${CONST.REFERRAL_PROGRAM.REVENUE} cuando se conviertan en clientes.`,
},
[CONST.REFERRAL_PROGRAM.CONTENT_TYPES.REFER_FRIEND]: {
buttonText1: 'Invita a un amigo y ',
buttonText2: `recibe $${CONST.REFERRAL_PROGRAM.REVENUE}`,
header: `Recibe $${CONST.REFERRAL_PROGRAM.REVENUE}`,
- body: `Sé el primero en chatear, enviar o pedir dinero, dividir una factura o compartir tu enlace de invitación con un amigo, y recibirás $${CONST.REFERRAL_PROGRAM.REVENUE} cuando se convierta en cliente. También puedes publicar tu enlace de invitación en las redes sociales.`,
+ body: `Chatea, paga, presenta y divide gastos con un amigo y recibirás $${CONST.REFERRAL_PROGRAM.REVENUE} cuando se convierta en cliente. También puedes publicar tu enlace de invitación en las redes sociales.`,
},
[CONST.REFERRAL_PROGRAM.CONTENT_TYPES.SHARE_CODE]: {
buttonText1: `Recibe $${CONST.REFERRAL_PROGRAM.REVENUE}`,
header: `Recibe $${CONST.REFERRAL_PROGRAM.REVENUE}`,
- body: `Sé el primero en chatear, enviar o pedir dinero, dividir una factura o compartir tu enlace de invitación con un amigo, y recibirás $${CONST.REFERRAL_PROGRAM.REVENUE} cuando se convierta en cliente. También puedes publicar tu enlace de invitación en las redes sociales.`,
+ body: `Chatea, paga, presenta y divide gastos con un amigo y recibirás $${CONST.REFERRAL_PROGRAM.REVENUE} cuando se convierta en cliente. También puedes publicar tu enlace de invitación en las redes sociales.`,
},
copyReferralLink: 'Copiar enlace de invitación',
},
diff --git a/src/languages/types.ts b/src/languages/types.ts
index c365363f84af..30b7f842db4c 100644
--- a/src/languages/types.ts
+++ b/src/languages/types.ts
@@ -247,6 +247,8 @@ type ViolationsTagOutOfPolicyParams = {tagName?: string};
type ViolationsTaxOutOfPolicyParams = {taxName?: string};
+type PaySomeoneParams = {name?: string};
+
type TaskCreatedActionParams = {title: string};
/* Translation Object types */
@@ -400,4 +402,5 @@ export type {
ZipCodeExampleFormatParams,
LogSizeParams,
HeldRequestParams,
+ PaySomeoneParams,
};
diff --git a/src/libs/API/parameters/OpenPolicyAccountingPageParams.ts b/src/libs/API/parameters/OpenPolicyAccountingPageParams.ts
new file mode 100644
index 000000000000..0ebaa58ef0d1
--- /dev/null
+++ b/src/libs/API/parameters/OpenPolicyAccountingPageParams.ts
@@ -0,0 +1,5 @@
+type OpenPolicyAccountingPageParams = {
+ policyID: string;
+};
+
+export default OpenPolicyAccountingPageParams;
diff --git a/src/libs/API/parameters/index.ts b/src/libs/API/parameters/index.ts
index cc3ec2767490..bfa89b5d3bd3 100644
--- a/src/libs/API/parameters/index.ts
+++ b/src/libs/API/parameters/index.ts
@@ -212,3 +212,4 @@ export type {default as ConvertTrackedExpenseToRequestParams} from './ConvertTra
export type {default as ShareTrackedExpenseParams} from './ShareTrackedExpenseParams';
export type {default as CategorizeTrackedExpenseParams} from './CategorizeTrackedExpenseParams';
export type {default as LeavePolicyParams} from './LeavePolicyParams';
+export type {default as OpenPolicyAccountingPageParams} from './OpenPolicyAccountingPageParams';
diff --git a/src/libs/API/types.ts b/src/libs/API/types.ts
index 3d32d27084c3..f91b694548ba 100644
--- a/src/libs/API/types.ts
+++ b/src/libs/API/types.ts
@@ -461,6 +461,7 @@ const READ_COMMANDS = {
OPEN_POLICY_WORKFLOWS_PAGE: 'OpenPolicyWorkflowsPage',
OPEN_POLICY_DISTANCE_RATES_PAGE: 'OpenPolicyDistanceRatesPage',
OPEN_POLICY_MORE_FEATURES_PAGE: 'OpenPolicyMoreFeaturesPage',
+ OPEN_POLICY_ACCOUNTING_PAGE: 'OpenPolicyAccountingPage',
} as const;
type ReadCommand = ValueOf;
@@ -502,6 +503,7 @@ type ReadCommandParameters = {
[READ_COMMANDS.OPEN_POLICY_WORKFLOWS_PAGE]: Parameters.OpenPolicyWorkflowsPageParams;
[READ_COMMANDS.OPEN_POLICY_DISTANCE_RATES_PAGE]: Parameters.OpenPolicyDistanceRatesPageParams;
[READ_COMMANDS.OPEN_POLICY_MORE_FEATURES_PAGE]: Parameters.OpenPolicyMoreFeaturesPageParams;
+ [READ_COMMANDS.OPEN_POLICY_ACCOUNTING_PAGE]: Parameters.OpenPolicyAccountingPageParams;
};
const SIDE_EFFECT_REQUEST_COMMANDS = {
diff --git a/src/libs/DistanceRequestUtils.ts b/src/libs/DistanceRequestUtils.ts
index 9a7e0a568627..12a240ae9041 100644
--- a/src/libs/DistanceRequestUtils.ts
+++ b/src/libs/DistanceRequestUtils.ts
@@ -79,7 +79,7 @@ function getRoundedDistanceInUnits(distanceInMeters: number, unit: Unit): string
}
/**
- * @param hasRoute Whether the route exists for the distance request
+ * @param hasRoute Whether the route exists for the distance expense
* @param distanceInMeters Distance traveled
* @param unit Unit that should be used to display the distance
* @param rate Expensable amount allowed per unit
@@ -100,7 +100,7 @@ function getDistanceForDisplay(hasRoute: boolean, distanceInMeters: number, unit
}
/**
- * @param hasRoute Whether the route exists for the distance request
+ * @param hasRoute Whether the route exists for the distance expense
* @param distanceInMeters Distance traveled
* @param unit Unit that should be used to display the distance
* @param rate Expensable amount allowed per unit
@@ -133,12 +133,12 @@ function getDistanceMerchant(
}
/**
- * Calculates the request amount based on distance, unit, and rate.
+ * Calculates the expense amount based on distance, unit, and rate.
*
* @param distance - The distance traveled in meters
* @param unit - The unit of measurement for the distance
- * @param rate - Rate used for calculating the request amount
- * @returns The computed request amount (rounded) in "cents".
+ * @param rate - Rate used for calculating the expense amount
+ * @returns The computed expense amount (rounded) in "cents".
*/
function getDistanceRequestAmount(distance: number, unit: Unit, rate: number): number {
const convertedDistance = convertDistanceUnit(distance, unit);
diff --git a/src/libs/E2E/reactNativeLaunchingTest.ts b/src/libs/E2E/reactNativeLaunchingTest.ts
index 776e3de74f06..9d5b0be0d2e7 100644
--- a/src/libs/E2E/reactNativeLaunchingTest.ts
+++ b/src/libs/E2E/reactNativeLaunchingTest.ts
@@ -35,7 +35,7 @@ if (!appInstanceId) {
// import your test here, define its name and config first in e2e/config.js
const tests: Tests = {
[E2EConfig.TEST_NAMES.AppStartTime]: require('./tests/appStartTimeTest.e2e').default,
- [E2EConfig.TEST_NAMES.OpenSearchPage]: require('./tests/openSearchPageTest.e2e').default,
+ [E2EConfig.TEST_NAMES.OpenChatFinderPage]: require('./tests/openChatFinderPageTest.e2e').default,
[E2EConfig.TEST_NAMES.ChatOpening]: require('./tests/chatOpeningTest.e2e').default,
[E2EConfig.TEST_NAMES.ReportTyping]: require('./tests/reportTypingTest.e2e').default,
[E2EConfig.TEST_NAMES.Linking]: require('./tests/linkingTest.e2e').default,
diff --git a/src/libs/E2E/tests/openSearchPageTest.e2e.ts b/src/libs/E2E/tests/openChatFinderPageTest.e2e.ts
similarity index 82%
rename from src/libs/E2E/tests/openSearchPageTest.e2e.ts
rename to src/libs/E2E/tests/openChatFinderPageTest.e2e.ts
index 86da851396f6..9d2b117a7044 100644
--- a/src/libs/E2E/tests/openSearchPageTest.e2e.ts
+++ b/src/libs/E2E/tests/openChatFinderPageTest.e2e.ts
@@ -9,7 +9,7 @@ import ROUTES from '@src/ROUTES';
const test = () => {
// check for login (if already logged in the action will simply resolve)
- console.debug('[E2E] Logging in for search');
+ console.debug('[E2E] Logging in for chat finder');
E2ELogin().then((neededLogin: boolean): Promise | undefined => {
if (neededLogin) {
@@ -19,24 +19,24 @@ const test = () => {
);
}
- console.debug('[E2E] Logged in, getting search metrics and submitting them…');
+ console.debug('[E2E] Logged in, getting chat finder metrics and submitting them…');
Performance.subscribeToMeasurements((entry) => {
if (entry.name === CONST.TIMING.SIDEBAR_LOADED) {
- console.debug(`[E2E] Sidebar loaded, navigating to search route…`);
- Navigation.navigate(ROUTES.SEARCH);
+ console.debug(`[E2E] Sidebar loaded, navigating to chat finder route…`);
+ Navigation.navigate(ROUTES.CHAT_FINDER);
return;
}
console.debug(`[E2E] Entry: ${JSON.stringify(entry)}`);
- if (entry.name !== CONST.TIMING.SEARCH_RENDER) {
+ if (entry.name !== CONST.TIMING.CHAT_FINDER_RENDER) {
return;
}
console.debug(`[E2E] Submitting!`);
E2EClient.submitTestResults({
branch: Config.E2E_BRANCH,
- name: 'Open Search Page TTI',
+ name: 'Open Chat Finder Page TTI',
duration: entry.duration,
})
.then(() => {
diff --git a/src/libs/IOUUtils.ts b/src/libs/IOUUtils.ts
index 27af031d19a8..27eff132ef40 100644
--- a/src/libs/IOUUtils.ts
+++ b/src/libs/IOUUtils.ts
@@ -63,8 +63,8 @@ function calculateAmount(numberOfParticipants: number, total: number, currency:
* For example: if user1 owes user2 $10, then we have: {ownerAccountID: user2, managerID: user1, total: $10 (a positive amount, owed to user2)}
* If user1 requests $17 from user2, then we have: {ownerAccountID: user1, managerID: user2, total: $7 (still a positive amount, but now owed to user1)}
*
- * @param isDeleting - whether the user is deleting the request
- * @param isUpdating - whether the user is updating the request
+ * @param isDeleting - whether the user is deleting the expense
+ * @param isUpdating - whether the user is updating the expense
*/
function updateIOUOwnerAndTotal>(
iouReport: TReport,
@@ -102,7 +102,7 @@ function updateIOUOwnerAndTotal>(
}
/**
- * Returns whether or not an IOU report contains money requests in a different currency
+ * Returns whether or not an IOU report contains expenses in a different currency
* that are either created or cancelled offline, and thus haven't been converted to the report's currency yet
*/
function isIOUReportPendingCurrencyConversion(iouReport: Report): boolean {
diff --git a/src/libs/ModifiedExpenseMessage.ts b/src/libs/ModifiedExpenseMessage.ts
index 0d961ea27115..d73771734636 100644
--- a/src/libs/ModifiedExpenseMessage.ts
+++ b/src/libs/ModifiedExpenseMessage.ts
@@ -253,7 +253,7 @@ function getForReportAction(reportID: string | undefined, reportAction: OnyxEntr
buildMessageFragmentForValue(
reportActionOriginalMessage?.billable ?? '',
reportActionOriginalMessage?.oldBillable ?? '',
- Localize.translateLocal('iou.request'),
+ Localize.translateLocal('iou.expense'),
true,
setFragments,
removalFragments,
@@ -266,7 +266,7 @@ function getForReportAction(reportID: string | undefined, reportAction: OnyxEntr
getMessageLine(`\n${Localize.translateLocal('iou.set')}`, setFragments) +
getMessageLine(`\n${Localize.translateLocal('iou.removed')}`, removalFragments);
if (message === '') {
- return Localize.translateLocal('iou.changedTheRequest');
+ return Localize.translateLocal('iou.changedTheExpense');
}
return `${message.substring(1, message.length)}`;
}
diff --git a/src/libs/MoneyRequestUtils.ts b/src/libs/MoneyRequestUtils.ts
index 1a573ce74628..ec934cb87888 100644
--- a/src/libs/MoneyRequestUtils.ts
+++ b/src/libs/MoneyRequestUtils.ts
@@ -78,14 +78,14 @@ function replaceAllDigits(text: string, convertFn: (char: string) => string): st
}
/**
- * Check if distance request or not
+ * Check if distance expense or not
*/
function isDistanceRequest(iouType: ValueOf, selectedTab: OnyxEntry): boolean {
return iouType === CONST.IOU.TYPE.REQUEST && selectedTab === CONST.TAB_REQUEST.DISTANCE;
}
/**
- * Check if scan request or not
+ * Check if scan expense or not
*/
function isScanRequest(selectedTab: SelectedTabRequest): boolean {
return selectedTab === CONST.TAB_REQUEST.SCAN;
diff --git a/src/libs/Navigation/AppNavigator/AuthScreens.tsx b/src/libs/Navigation/AppNavigator/AuthScreens.tsx
index fde0202d3d2f..9157d7486c9e 100644
--- a/src/libs/Navigation/AppNavigator/AuthScreens.tsx
+++ b/src/libs/Navigation/AppNavigator/AuthScreens.tsx
@@ -238,7 +238,7 @@ function AuthScreens({session, lastOpenedPublicRoomID, initialLastUpdateIDApplie
const unsubscribeSearchShortcut = KeyboardShortcut.subscribe(
searchShortcutConfig.shortcutKey,
() => {
- Modal.close(Session.checkIfActionIsAllowed(() => Navigation.navigate(ROUTES.SEARCH)));
+ Modal.close(Session.checkIfActionIsAllowed(() => Navigation.navigate(ROUTES.CHAT_FINDER)));
},
shortcutsOverviewShortcutConfig.descriptionKey,
shortcutsOverviewShortcutConfig.modifiers,
diff --git a/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx b/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx
index 470d06037c6d..a596acf0a3ac 100644
--- a/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx
+++ b/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx
@@ -4,6 +4,7 @@ import {createStackNavigator} from '@react-navigation/stack';
import React from 'react';
import type {
AddPersonalBankAccountNavigatorParamList,
+ ChatFinderNavigatorParamList,
DetailsNavigatorParamList,
EditRequestNavigatorParamList,
EnablePaymentsNavigatorParamList,
@@ -22,7 +23,6 @@ import type {
ReportSettingsNavigatorParamList,
RoomInviteNavigatorParamList,
RoomMembersNavigatorParamList,
- SearchNavigatorParamList,
SettingsNavigatorParamList,
SignInNavigatorParamList,
SplitDetailsNavigatorParamList,
@@ -147,8 +147,8 @@ const RoomInviteModalStackNavigator = createModalStackNavigator require('../../../../pages/RoomInvitePage').default as React.ComponentType,
});
-const SearchModalStackNavigator = createModalStackNavigator({
- [SCREENS.SEARCH_ROOT]: () => require('../../../../pages/SearchPage').default as React.ComponentType,
+const ChatFinderModalStackNavigator = createModalStackNavigator({
+ [SCREENS.CHAT_FINDER_ROOT]: () => require('../../../../pages/ChatFinderPage').default as React.ComponentType,
});
const NewChatModalStackNavigator = createModalStackNavigator({
@@ -343,7 +343,7 @@ export {
ReportDescriptionModalStackNavigator,
RoomInviteModalStackNavigator,
RoomMembersModalStackNavigator,
- SearchModalStackNavigator,
+ ChatFinderModalStackNavigator,
SettingsModalStackNavigator,
SignInModalStackNavigator,
SplitDetailsModalStackNavigator,
diff --git a/src/libs/Navigation/AppNavigator/Navigators/LeftModalNavigator.tsx b/src/libs/Navigation/AppNavigator/Navigators/LeftModalNavigator.tsx
index 8f76d8fbdd7b..159430a66a43 100644
--- a/src/libs/Navigation/AppNavigator/Navigators/LeftModalNavigator.tsx
+++ b/src/libs/Navigation/AppNavigator/Navigators/LeftModalNavigator.tsx
@@ -32,8 +32,8 @@ function LeftModalNavigator({navigation}: LeftModalNavigatorProps) {
Navigation.navigate(ROUTES.SEARCH))}
+ onPress={Session.checkIfActionIsAllowed(() => Navigation.navigate(ROUTES.CHAT_FINDER))}
>
| undefined): NavigationPartialRoute | undefined {
- const bottomTabNavigatorRoute = state?.routes[0];
+ const bottomTabNavigatorRoute = state?.routes.findLast((route) => route.name === NAVIGATORS.BOTTOM_TAB_NAVIGATOR);
// The bottomTabNavigatorRoute state may be empty if we just logged in.
- if (!bottomTabNavigatorRoute || bottomTabNavigatorRoute.name !== 'BottomTabNavigator' || bottomTabNavigatorRoute.state === undefined) {
+ if (!bottomTabNavigatorRoute || bottomTabNavigatorRoute.name !== NAVIGATORS.BOTTOM_TAB_NAVIGATOR || bottomTabNavigatorRoute.state === undefined) {
return undefined;
}
diff --git a/src/libs/Navigation/linkingConfig/config.ts b/src/libs/Navigation/linkingConfig/config.ts
index 85f0759a5f92..67ffcf43dece 100644
--- a/src/libs/Navigation/linkingConfig/config.ts
+++ b/src/libs/Navigation/linkingConfig/config.ts
@@ -71,9 +71,9 @@ const config: LinkingOptions['config'] = {
[SCREENS.NOT_FOUND]: '*',
[NAVIGATORS.LEFT_MODAL_NAVIGATOR]: {
screens: {
- [SCREENS.LEFT_MODAL.SEARCH]: {
+ [SCREENS.LEFT_MODAL.CHAT_FINDER]: {
screens: {
- [SCREENS.SEARCH_ROOT]: ROUTES.SEARCH,
+ [SCREENS.CHAT_FINDER_ROOT]: ROUTES.CHAT_FINDER,
},
},
[SCREENS.LEFT_MODAL.WORKSPACE_SWITCHER]: {
diff --git a/src/libs/Navigation/types.ts b/src/libs/Navigation/types.ts
index ebc324945146..7dd2f274aa9e 100644
--- a/src/libs/Navigation/types.ts
+++ b/src/libs/Navigation/types.ts
@@ -300,8 +300,8 @@ type NewChatNavigatorParamList = {
};
};
-type SearchNavigatorParamList = {
- [SCREENS.SEARCH_ROOT]: undefined;
+type ChatFinderNavigatorParamList = {
+ [SCREENS.CHAT_FINDER_ROOT]: undefined;
};
type DetailsNavigatorParamList = {
@@ -614,7 +614,7 @@ type PrivateNotesNavigatorParamList = {
};
type LeftModalNavigatorParamList = {
- [SCREENS.LEFT_MODAL.SEARCH]: NavigatorScreenParams;
+ [SCREENS.LEFT_MODAL.CHAT_FINDER]: NavigatorScreenParams;
[SCREENS.LEFT_MODAL.WORKSPACE_SWITCHER]: NavigatorScreenParams;
};
@@ -790,7 +790,7 @@ type AuthScreensParamList = SharedScreensParamList & {
};
};
-type RootStackParamList = PublicScreensParamList & AuthScreensParamList & SearchNavigatorParamList;
+type RootStackParamList = PublicScreensParamList & AuthScreensParamList & ChatFinderNavigatorParamList;
type BottomTabName = keyof BottomTabNavigatorParamList;
@@ -834,7 +834,7 @@ export type {
ParticipantsNavigatorParamList,
RoomMembersNavigatorParamList,
RoomInviteNavigatorParamList,
- SearchNavigatorParamList,
+ ChatFinderNavigatorParamList,
NewChatNavigatorParamList,
NewTaskNavigatorParamList,
TeachersUniteNavigatorParamList,
diff --git a/src/libs/OptionsListUtils.ts b/src/libs/OptionsListUtils.ts
index aa16d7b2dc5a..c11a1499a88f 100644
--- a/src/libs/OptionsListUtils.ts
+++ b/src/libs/OptionsListUtils.ts
@@ -117,6 +117,7 @@ type TaxSection = {
type CategoryTreeSection = CategorySectionBase & {
data: OptionTree[];
+ indexOffset?: number;
};
type Category = {
@@ -1023,11 +1024,13 @@ function getCategoryListSections(
const numberOfEnabledCategories = enabledCategories.length;
if (numberOfEnabledCategories === 0 && selectedOptions.length > 0) {
+ const data = getCategoryOptionTree(selectedOptions, true);
categorySections.push({
// "Selected" section
title: '',
shouldShow: false,
- data: getCategoryOptionTree(selectedOptions, true),
+ data,
+ indexOffset: data.length,
});
return categorySections;
@@ -1046,22 +1049,26 @@ function getCategoryListSections(
});
});
+ const data = getCategoryOptionTree(searchCategories, true);
categorySections.push({
// "Search" section
title: '',
shouldShow: true,
- data: getCategoryOptionTree(searchCategories, true),
+ data,
+ indexOffset: data.length,
});
return categorySections;
}
if (selectedOptions.length > 0) {
+ const data = getCategoryOptionTree(selectedOptions, true);
categorySections.push({
// "Selected" section
title: '',
shouldShow: false,
- data: getCategoryOptionTree(selectedOptions, true),
+ data,
+ indexOffset: data.length,
});
}
@@ -1069,11 +1076,13 @@ function getCategoryListSections(
const filteredCategories = enabledCategories.filter((category) => !selectedOptionNames.includes(category.name));
if (numberOfEnabledCategories < CONST.CATEGORY_LIST_THRESHOLD) {
+ const data = getCategoryOptionTree(filteredCategories, false, selectedOptionNames);
categorySections.push({
// "All" section when items amount less than the threshold
title: '',
shouldShow: false,
- data: getCategoryOptionTree(filteredCategories, false, selectedOptionNames),
+ data,
+ indexOffset: data.length,
});
return categorySections;
@@ -1089,19 +1098,23 @@ function getCategoryListSections(
if (filteredRecentlyUsedCategories.length > 0) {
const cutRecentlyUsedCategories = filteredRecentlyUsedCategories.slice(0, maxRecentReportsToShow);
+ const data = getCategoryOptionTree(cutRecentlyUsedCategories, true);
categorySections.push({
// "Recent" section
title: Localize.translateLocal('common.recent'),
shouldShow: true,
- data: getCategoryOptionTree(cutRecentlyUsedCategories, true),
+ data,
+ indexOffset: data.length,
});
}
+ const data = getCategoryOptionTree(filteredCategories, false, selectedOptionNames);
categorySections.push({
// "All" section when items amount more than the threshold
title: Localize.translateLocal('common.all'),
shouldShow: true,
- data: getCategoryOptionTree(filteredCategories, false, selectedOptionNames),
+ data,
+ indexOffset: data.length,
});
return categorySections;
@@ -1707,7 +1720,7 @@ function getOptions(
return;
}
- // In case user needs to add credit bank account, don't allow them to request more money from the workspace.
+ // In case user needs to add credit bank account, don't allow them to submit an expense from the workspace.
if (includeOwnedWorkspaceChats && ReportUtils.hasIOUWaitingOnCurrentUserBankAccount(report)) {
return;
}
@@ -2356,4 +2369,4 @@ export {
getFirstKeyForList,
};
-export type {MemberForList, CategorySection, CategoryTreeSection, Options, OptionList, SearchOption, PayeePersonalDetails, Category, TaxRatesOption, Option};
+export type {MemberForList, CategorySection, CategoryTreeSection, Options, OptionList, SearchOption, PayeePersonalDetails, Category, TaxRatesOption, Option, OptionTree};
diff --git a/src/libs/ReportActionsUtils.ts b/src/libs/ReportActionsUtils.ts
index 32972a81bcb5..d4a2afafb420 100644
--- a/src/libs/ReportActionsUtils.ts
+++ b/src/libs/ReportActionsUtils.ts
@@ -20,7 +20,6 @@ import type {Message, ReportActionBase, ReportActions} from '@src/types/onyx/Rep
import type ReportAction from '@src/types/onyx/ReportAction';
import type {EmptyObject} from '@src/types/utils/EmptyObject';
import {isEmptyObject} from '@src/types/utils/EmptyObject';
-import * as CollectionUtils from './CollectionUtils';
import * as Environment from './Environment/Environment';
import isReportMessageAttachment from './isReportMessageAttachment';
import * as Localize from './Localize';
@@ -59,16 +58,16 @@ Onyx.connect({
},
});
-const allReportActions: OnyxCollection = {};
+let allReportActions: OnyxCollection;
Onyx.connect({
key: ONYXKEYS.COLLECTION.REPORT_ACTIONS,
- callback: (actions, key) => {
- if (!key || !actions) {
+ waitForCollectionCallback: true,
+ callback: (actions) => {
+ if (!actions) {
return;
}
- const reportID = CollectionUtils.extractCollectionItemID(key);
- allReportActions[reportID] = actions;
+ allReportActions = actions;
},
});
@@ -195,7 +194,7 @@ function getParentReportAction(report: OnyxEntry | EmptyObject): ReportA
if (!report?.parentReportID || !report.parentReportActionID) {
return {};
}
- return allReportActions?.[report.parentReportID]?.[report.parentReportActionID] ?? {};
+ return allReportActions?.[`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${report.parentReportID}`]?.[report.parentReportActionID] ?? {};
}
/**
@@ -225,7 +224,7 @@ function isTransactionThread(parentReportAction: OnyxEntry | Empty
/**
* Returns the reportID for the transaction thread associated with a report by iterating over the reportActions and identifying the IOU report actions with a childReportID. Returns a reportID if there is exactly one transaction thread for the report, and null otherwise.
*/
-function getOneTransactionThreadReportID(reportID: string, reportActions: OnyxEntry | ReportAction[]): string | null {
+function getOneTransactionThreadReportID(reportID: string, reportActions: OnyxEntry | ReportAction[], isOffline: boolean | undefined = undefined): string | null {
// If the report is not an IOU or Expense report, it shouldn't be treated as one-transaction report.
const report = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${reportID}`];
if (report?.type !== CONST.REPORT.TYPE.IOU && report?.type !== CONST.REPORT.TYPE.EXPENSE) {
@@ -250,7 +249,7 @@ function getOneTransactionThreadReportID(reportID: string, reportActions: OnyxEn
action.actionName === CONST.REPORT.ACTIONS.TYPE.IOU &&
(iouRequestTypes.includes(action.originalMessage.type) ?? []) &&
action.childReportID &&
- action.originalMessage.IOUTransactionID,
+ (Boolean(action.originalMessage.IOUTransactionID) || (action.pendingAction === CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE && (isOffline ?? isNetworkOffline))),
);
// If we don't have any IOU request actions, or we have more than one IOU request actions, this isn't a oneTransaction report
@@ -518,7 +517,7 @@ function shouldReportActionBeVisible(reportAction: OnyxEntry, key:
return false;
}
- // Ignore markedAsReimbursed action here since we're already display message that explains the request was paid
+ // Ignore markedAsReimbursed action here since we're already display message that explains the expense was paid
// elsewhere in the IOU reportAction
if (reportAction.actionName === CONST.REPORT.ACTIONS.TYPE.MARKEDREIMBURSED) {
return false;
@@ -593,7 +592,7 @@ function replaceBaseURLInPolicyChangeLogAction(reportAction: ReportAction): Repo
}
function getLastVisibleAction(reportID: string, actionsToMerge: OnyxCollection = {}): OnyxEntry {
- const reportActions = Object.values(fastMerge(allReportActions?.[reportID] ?? {}, actionsToMerge ?? {}, true));
+ const reportActions = Object.values(fastMerge(allReportActions?.[`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${reportID}`] ?? {}, actionsToMerge ?? {}, true));
const visibleReportActions = Object.values(reportActions ?? {}).filter((action): action is ReportAction => shouldReportActionBeVisibleAsLastAction(action));
const sortedReportActions = getSortedReportActions(visibleReportActions, true);
if (sortedReportActions.length === 0) {
@@ -714,7 +713,7 @@ function getLatestReportActionFromOnyxData(onyxData: OnyxUpdate[] | null): OnyxE
* Find the transaction associated with this reportAction, if one exists.
*/
function getLinkedTransactionID(reportActionOrID: string | OnyxEntry, reportID?: string): string | null {
- const reportAction = typeof reportActionOrID === 'string' ? allReportActions?.[reportID ?? '']?.[reportActionOrID] : reportActionOrID;
+ const reportAction = typeof reportActionOrID === 'string' ? allReportActions?.[`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${reportID}`]?.[reportActionOrID] : reportActionOrID;
if (!reportAction || reportAction.actionName !== CONST.REPORT.ACTIONS.TYPE.IOU) {
return null;
}
@@ -722,7 +721,7 @@ function getLinkedTransactionID(reportActionOrID: string | OnyxEntry {
- return allReportActions?.[reportID]?.[reportActionID] ?? null;
+ return allReportActions?.[`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${reportID}`]?.[reportActionID] ?? null;
}
function getMostRecentReportActionLastModified(): string {
@@ -769,7 +768,7 @@ function getMostRecentReportActionLastModified(): string {
*/
function getReportPreviewAction(chatReportID: string, iouReportID: string): OnyxEntry {
return (
- Object.values(allReportActions?.[chatReportID] ?? {}).find(
+ Object.values(allReportActions?.[`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${chatReportID}`] ?? {}).find(
(reportAction) => reportAction && reportAction.actionName === CONST.REPORT.ACTIONS.TYPE.REPORTPREVIEW && reportAction.originalMessage.linkedReportID === iouReportID,
) ?? null
);
@@ -794,7 +793,7 @@ function isMessageDeleted(reportAction: OnyxEntry): boolean {
}
/**
- * Returns the number of money requests associated with a report preview
+ * Returns the number of expenses associated with a report preview
*/
function getNumberOfMoneyRequests(reportPreviewAction: OnyxEntry): number {
return reportPreviewAction?.childMoneyRequestCount ?? 0;
@@ -823,7 +822,7 @@ function isTaskAction(reportAction: OnyxEntry): boolean {
* If there are no visible actions left (including system messages), we can hide the report from view entirely
*/
function doesReportHaveVisibleActions(reportID: string, actionsToMerge: ReportActions = {}): boolean {
- const reportActions = Object.values(fastMerge(allReportActions?.[reportID] ?? {}, actionsToMerge, true));
+ const reportActions = Object.values(fastMerge(allReportActions?.[`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${reportID}`] ?? {}, actionsToMerge, true));
const visibleReportActions = Object.values(reportActions ?? {}).filter((action) => shouldReportActionBeVisibleAsLastAction(action));
// Exclude the task system message and the created message
@@ -832,7 +831,7 @@ function doesReportHaveVisibleActions(reportID: string, actionsToMerge: ReportAc
}
function getAllReportActions(reportID: string): ReportActions {
- return allReportActions?.[reportID] ?? {};
+ return allReportActions?.[`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${reportID}`] ?? {};
}
/**
@@ -997,7 +996,7 @@ function getMemberChangeMessagePlainText(reportAction: OnyxEntry):
}
/**
- * Helper method to determine if the provided accountID has made a request on the specified report.
+ * Helper method to determine if the provided accountID has submitted an expense on the specified report.
*
* @param reportID
* @param currentAccountID
diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts
index f31b4a780c5a..e1c7d8a3f287 100644
--- a/src/libs/ReportUtils.ts
+++ b/src/libs/ReportUtils.ts
@@ -55,7 +55,6 @@ import {isEmptyObject} from '@src/types/utils/EmptyObject';
import type IconAsset from '@src/types/utils/IconAsset';
import * as IOU from './actions/IOU';
import * as store from './actions/ReimbursementAccount/store';
-import * as CollectionUtils from './CollectionUtils';
import * as CurrencyUtils from './CurrencyUtils';
import DateUtils from './DateUtils';
import {hasValidDraftComment} from './DraftCommentUtils';
@@ -537,16 +536,15 @@ Onyx.connect({
},
});
-const reportActionsByReport: OnyxCollection = {};
+let allReportActions: OnyxCollection;
Onyx.connect({
key: ONYXKEYS.COLLECTION.REPORT_ACTIONS,
- callback: (actions, key) => {
- if (!key || !actions) {
+ waitForCollectionCallback: true,
+ callback: (actions) => {
+ if (!actions) {
return;
}
-
- const reportID = CollectionUtils.extractCollectionItemID(key);
- reportActionsByReport[reportID] = actions;
+ allReportActions = actions;
},
});
@@ -1172,6 +1170,15 @@ function isJoinRequestInAdminRoom(report: OnyxEntry): boolean {
if (!report) {
return false;
}
+ // If this policy isn't owned by Expensify,
+ // Account manager/guide should not have the workspace join request pinned to their LHN,
+ // since they are not a part of the company, and should not action it on their behalf.
+ if (report.policyID) {
+ const policy = getPolicy(report.policyID);
+ if (!PolicyUtils.isExpensifyTeam(policy.owner) && PolicyUtils.isExpensifyTeam(currentUserPersonalDetails?.login)) {
+ return false;
+ }
+ }
return ReportActionsUtils.isActionableJoinRequestPending(report.reportID);
}
@@ -1320,7 +1327,7 @@ function isMoneyRequestReport(reportOrID: OnyxEntry | EmptyObject | stri
* Checks if a report has only one transaction associated with it
*/
function isOneTransactionReport(reportID: string): boolean {
- const reportActions = reportActionsByReport?.[reportID] ?? ([] as ReportAction[]);
+ const reportActions = allReportActions?.[`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${reportID}`] ?? ([] as ReportAction[]);
return ReportActionsUtils.getOneTransactionThreadReportID(reportID, reportActions) !== null;
}
@@ -1328,7 +1335,7 @@ function isOneTransactionReport(reportID: string): boolean {
* Checks if a report is a transaction thread associated with a report that has only one transaction
*/
function isOneTransactionThread(reportID: string, parentReportID: string): boolean {
- const parentReportActions = reportActionsByReport?.[`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${parentReportID}`] ?? ([] as ReportAction[]);
+ const parentReportActions = allReportActions?.[`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${parentReportID}`] ?? ([] as ReportAction[]);
const transactionThreadReportID = ReportActionsUtils.getOneTransactionThreadReportID(parentReportID, parentReportActions);
return reportID === transactionThreadReportID;
}
@@ -1352,7 +1359,7 @@ function isOneOnOneChat(report: OnyxEntry): boolean {
}
/**
- * Checks if the current user is a payer of the request
+ * Checks if the current user is a payer of the expense
*/
function isPayer(session: OnyxEntry, iouReport: OnyxEntry) {
@@ -1442,9 +1449,10 @@ function canDeleteReportAction(reportAction: OnyxEntry, reportID:
return false;
}
+ const linkedReport = isThreadFirstChat(reportAction, reportID) ? getReport(report?.parentReportID) : report;
if (isActionOwner) {
- if (!isEmptyObject(report) && isMoneyRequestReport(report)) {
- return canAddOrDeleteTransactions(report);
+ if (!isEmptyObject(linkedReport) && isMoneyRequestReport(linkedReport)) {
+ return canAddOrDeleteTransactions(linkedReport);
}
return true;
}
@@ -2130,7 +2138,7 @@ function isUnreadWithMention(reportOrOption: OnyxEntry | OptionData): bo
* Determines if the option requires action from the current user. This can happen when it:
* - is unread and the user was mentioned in one of the unread comments
* - is for an outstanding task waiting on the user
- * - has an outstanding child money request that is waiting for an action from the current user (e.g. pay, approve, add bank account)
+ * - has an outstanding child expense that is waiting for an action from the current user (e.g. pay, approve, add bank account)
*
* @param option (report or optionItem)
* @param parentReportAction (the report action the current report is a thread of)
@@ -2390,7 +2398,7 @@ function getMoneyRequestReportName(report: OnyxEntry, policy: OnyxEntry<
}
/**
- * Gets transaction created, amount, currency, comment, and waypoints (for distance request)
+ * Gets transaction created, amount, currency, comment, and waypoints (for distance expense)
* into a flat object. Used for displaying transactions and sending them in API commands
*/
@@ -2478,11 +2486,11 @@ function canEditMoneyRequest(reportAction: OnyxEntry): boolean {
}
/**
- * Checks if the current user can edit the provided property of a money request
+ * Checks if the current user can edit the provided property of an expense
*
*/
function canEditFieldOfMoneyRequest(reportAction: OnyxEntry, fieldToEdit: ValueOf): boolean {
- // A list of fields that cannot be edited by anyone, once a money request has been settled
+ // A list of fields that cannot be edited by anyone, once an expense has been settled
const restrictedFields: string[] = [
CONST.EDIT_REQUEST_FIELD.AMOUNT,
CONST.EDIT_REQUEST_FIELD.CURRENCY,
@@ -2536,7 +2544,7 @@ function canEditFieldOfMoneyRequest(reportAction: OnyxEntry, field
*
* - It was written by the current user
* - It's an ADDCOMMENT that is not an attachment
- * - It's money request where conditions for editability are defined in canEditMoneyRequest method
+ * - It's an expense where conditions for editability are defined in canEditMoneyRequest method
* - It's not pending deletion
*/
function canEditReportAction(reportAction: OnyxEntry): boolean {
@@ -2566,7 +2574,7 @@ function getTransactionsWithReceipts(iouReportID: string | undefined): Transacti
* instead of the report total only when we have no report total ready to show. This is the case when
* all requests are receipts that are being SmartScanned. As soon as we have a non-receipt request,
* or as soon as one receipt request is done scanning, we have at least one
- * "ready" money request, and we remove this indicator to show the partial report total.
+ * "ready" expense, and we remove this indicator to show the partial report total.
*/
function areAllRequestsBeingSmartScanned(iouReportID: string, reportPreviewAction: OnyxEntry): boolean {
const transactionsWithReceipts = getTransactionsWithReceipts(iouReportID);
@@ -2610,10 +2618,7 @@ function getTransactionReportName(reportAction: OnyxEntry, policy: OnyxEntry = nu
return participantsWithoutCurrentUser.map((accountID) => getDisplayNameForParticipant(accountID, isMultipleParticipantReport)).join(', ');
}
+/**
+ * Get the payee name given a report.
+ */
+function getPayeeName(report: OnyxEntry): string | undefined {
+ if (isEmptyObject(report)) {
+ return undefined;
+ }
+
+ const participantAccountIDs = report?.participantAccountIDs ?? [];
+ const participantsWithoutCurrentUser = participantAccountIDs.filter((accountID) => accountID !== currentUserAccountID);
+ if (participantsWithoutCurrentUser.length === 0) {
+ return undefined;
+ }
+ return getDisplayNameForParticipant(participantsWithoutCurrentUser[0], true);
+}
+
/**
* Get either the policyName or domainName the chat is tied to
*/
@@ -3122,6 +3143,27 @@ function hasReportNameError(report: OnyxEntry): boolean {
return !isEmptyObject(report?.errorFields?.reportName);
}
+/**
+ * Adds a domain to a short mention, converting it into a full mention with email or SMS domain.
+ * @param mention The user mention to be converted.
+ * @returns The converted mention as a full mention string or undefined if conversion is not applicable.
+ */
+function addDomainToShortMention(mention: string): string | undefined {
+ if (!Str.isValidEmail(mention) && currentUserPrivateDomain) {
+ const mentionWithEmailDomain = `${mention}@${currentUserPrivateDomain}`;
+ if (allPersonalDetailLogins.includes(mentionWithEmailDomain)) {
+ return mentionWithEmailDomain;
+ }
+ }
+ if (Str.isValidE164Phone(mention)) {
+ const mentionWithSmsDomain = PhoneNumber.addSMSDomainIfPhoneNumber(mention);
+ if (allPersonalDetailLogins.includes(mentionWithSmsDomain)) {
+ return mentionWithSmsDomain;
+ }
+ }
+ return undefined;
+}
+
/**
* For comments shorter than or equal to 10k chars, convert the comment from MD into HTML because that's how it is stored in the database
* For longer comments, skip parsing, but still escape the text, and display plaintext for performance reasons. It takes over 40s to parse a 100k long string!!
@@ -3130,21 +3172,8 @@ function getParsedComment(text: string): string {
const parser = new ExpensiMark();
const textWithMention = text.replace(CONST.REGEX.SHORT_MENTION, (match) => {
const mention = match.substring(1);
-
- if (!Str.isValidEmail(mention) && currentUserPrivateDomain) {
- const mentionWithEmailDomain = `${mention}@${currentUserPrivateDomain}`;
- if (allPersonalDetailLogins.includes(mentionWithEmailDomain)) {
- return `@${mentionWithEmailDomain}`;
- }
- }
- if (Str.isValidE164Phone(mention)) {
- const mentionWithSmsDomain = PhoneNumber.addSMSDomainIfPhoneNumber(mention);
- if (allPersonalDetailLogins.includes(mentionWithSmsDomain)) {
- return `@${mentionWithSmsDomain}`;
- }
- }
-
- return match;
+ const mentionWithDomain = addDomainToShortMention(mention);
+ return mentionWithDomain ? `@${mentionWithDomain}` : match;
});
return text.length <= CONST.MAX_MARKUP_LENGTH ? parser.replace(textWithMention, {shouldEscapeText: !shouldAllowRawHTMLMessages()}) : lodashEscape(text);
@@ -3307,7 +3336,7 @@ function buildOptimisticTaskCommentReportAction(taskReportID: string, taskTitle:
* @param total - IOU amount in the smallest unit of the currency.
* @param chatReportID - Report ID of the chat where the IOU is.
* @param currency - IOU currency.
- * @param isSendingMoney - If we send money the IOU should be created as settled
+ * @param isSendingMoney - If we pay someone the IOU should be created as settled
*/
function buildOptimisticIOUReport(payeeAccountID: number, payerAccountID: number, total: number, chatReportID: string, currency: string, isSendingMoney = false): OptimisticIOUReport {
@@ -3460,7 +3489,7 @@ function getIOUReportActionMessage(iouReportID: string, type: string, total: num
iouMessage = `submitted ${amount}`;
break;
case CONST.IOU.REPORT_ACTION_TYPE.CREATE:
- iouMessage = `requested ${amount}${comment && ` for ${comment}`}`;
+ iouMessage = `submitted ${amount}${comment && ` for ${comment}`}`;
break;
case CONST.IOU.REPORT_ACTION_TYPE.TRACK:
iouMessage = `tracking ${amount}${comment && ` for ${comment}`}`;
@@ -3469,7 +3498,7 @@ function getIOUReportActionMessage(iouReportID: string, type: string, total: num
iouMessage = `split ${amount}${comment && ` for ${comment}`}`;
break;
case CONST.IOU.REPORT_ACTION_TYPE.DELETE:
- iouMessage = `deleted the ${amount} request${comment && ` for ${comment}`}`;
+ iouMessage = `deleted the ${amount} expense${comment && ` for ${comment}`}`;
break;
case CONST.IOU.REPORT_ACTION_TYPE.PAY:
iouMessage = isSettlingUp ? `paid ${amount}${paymentMethodMessage}` : `sent ${amount}${comment && ` for ${comment}`}${paymentMethodMessage}`;
@@ -3500,7 +3529,7 @@ function getIOUReportActionMessage(iouReportID: string, type: string, total: num
* @param [paymentType] - Only required if the IOUReportAction type is 'pay'. Can be oneOf(elsewhere, Expensify).
* @param [iouReportID] - Only required if the IOUReportActions type is oneOf(decline, cancel, pay). Generates a randomID as default.
* @param [isSettlingUp] - Whether we are settling up an IOU.
- * @param [isSendMoneyFlow] - Whether this is send money flow
+ * @param [isSendMoneyFlow] - Whether this is pay someone flow
* @param [receipt]
* @param [isOwnPolicyExpenseChat] - Whether this is an expense report create from the current user's policy expense chat
*/
@@ -3532,7 +3561,7 @@ function buildOptimisticIOUReportAction(
};
if (type === CONST.IOU.REPORT_ACTION_TYPE.PAY) {
- // In send money flow, we store amount, comment, currency in IOUDetails when type = pay
+ // In pay someone flow, we store amount, comment, currency in IOUDetails when type = pay
if (isSendMoneyFlow) {
const keys = ['amount', 'comment', 'currency'] as const;
keys.forEach((key) => {
@@ -3541,7 +3570,7 @@ function buildOptimisticIOUReportAction(
originalMessage.IOUDetails = {amount, comment, currency};
originalMessage.paymentType = paymentType;
} else {
- // In case of pay money request action, we dont store the comment
+ // In case of pay someone action, we dont store the comment
// and there is no single transctionID to link the action to.
delete originalMessage.IOUTransactionID;
delete originalMessage.comment;
@@ -3552,7 +3581,7 @@ function buildOptimisticIOUReportAction(
// IOUs of type split only exist in group DMs and those don't have an iouReport so we need to delete the IOUReportID key
if (type === CONST.IOU.REPORT_ACTION_TYPE.SPLIT) {
delete originalMessage.IOUReportID;
- // Split bill made from a policy expense chat only have the payee's accountID as the participant because the payer could be any policy admin
+ // Split expense made from a policy expense chat only have the payee's accountID as the participant because the payer could be any policy admin
if (isOwnPolicyExpenseChat) {
originalMessage.participantAccountIDs = currentUserAccountID ? [currentUserAccountID] : [];
} else {
@@ -3855,7 +3884,7 @@ function updateReportPreview(iouReport: OnyxEntry, reportPreviewAction:
...previousTransactions,
}
: recentReceiptTransactions,
- // As soon as we add a transaction without a receipt to the report, it will have ready money requests,
+ // As soon as we add a transaction without a receipt to the report, it will have ready expenses,
// so we remove the whisper
whisperedToAccountIDs: hasReceipt ? reportPreviewAction?.whisperedToAccountIDs : [],
};
@@ -4073,7 +4102,7 @@ function buildOptimisticHoldReportAction(created = DateUtils.getDBTime()): Optim
{
type: CONST.REPORT.MESSAGE.TYPE.TEXT,
style: 'normal',
- text: Localize.translateLocal('iou.heldRequest'),
+ text: Localize.translateLocal('iou.heldExpense'),
},
],
person: [
@@ -4135,7 +4164,7 @@ function buildOptimisticUnHoldReportAction(created = DateUtils.getDBTime()): Opt
{
type: CONST.REPORT.MESSAGE.TYPE.TEXT,
style: 'normal',
- text: Localize.translateLocal('iou.unheldRequest'),
+ text: Localize.translateLocal('iou.unheldExpense'),
},
],
person: [
@@ -4411,7 +4440,7 @@ function buildTransactionThread(
}
/**
- * Build optimistic money request entities:
+ * Build optimistic expense entities:
*
* 1. CREATED action for the chatReport
* 2. CREATED action for the iouReport
@@ -4554,13 +4583,13 @@ function canAccessReport(report: OnyxEntry, policies: OnyxCollection, currentReportId: string): boolean {
const currentReport = getReport(currentReportId);
const parentReport = getParentReport(!isEmptyObject(currentReport) ? currentReport : null);
- const reportActions = reportActionsByReport?.[report?.reportID ?? ''] ?? {};
+ const reportActions = allReportActions?.[`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${report?.reportID}`] ?? {};
const isChildReportHasComment = Object.values(reportActions ?? {})?.some((reportAction) => (reportAction?.childVisibleActionCount ?? 0) > 0);
return parentReport?.reportID !== report?.reportID && !isChildReportHasComment;
}
/**
- * Checks to see if a report's parentAction is a money request that contains a violation
+ * Checks to see if a report's parentAction is an expense that contains a violation
*/
function doesTransactionThreadHaveViolations(report: OnyxEntry, transactionViolations: OnyxCollection, parentReportAction: OnyxEntry): boolean {
if (parentReportAction?.actionName !== CONST.REPORT.ACTIONS.TYPE.IOU) {
@@ -4580,7 +4609,7 @@ function doesTransactionThreadHaveViolations(report: OnyxEntry, transact
}
/**
- * Checks if we should display violation - we display violations when the money request has violation and it is not settled
+ * Checks if we should display violation - we display violations when the expense has violation and it is not settled
*/
function shouldDisplayTransactionThreadViolations(
report: OnyxEntry,
@@ -4728,7 +4757,7 @@ function shouldReportBeInOptionList({
}
/**
- * Attempts to find a report in onyx with the provided list of participants. Does not include threads, task, money request, room, and policy expense chat.
+ * Attempts to find a report in onyx with the provided list of participants. Does not include threads, task, expense, room, and policy expense chat.
*/
function getChatByParticipants(newParticipantList: number[], reports: OnyxCollection = allReports): OnyxEntry {
const sortedNewParticipantList = newParticipantList.sort();
@@ -4949,20 +4978,20 @@ function hasIOUWaitingOnCurrentUserBankAccount(chatReport: OnyxEntry): b
}
/**
- * Users can request money:
+ * Users can submit an expense:
* - in policy expense chats only if they are in a role of a member in the chat (in other words, if it's their policy expense chat)
* - in an open or submitted expense report tied to a policy expense chat the user owns
- * - employee can request money in submitted expense report only if the policy has Instant Submit settings turned on
+ * - employee can submit expenses in a submitted expense report only if the policy has Instant Submit settings turned on
* - in an IOU report, which is not settled yet
* - in a 1:1 DM chat
*/
function canRequestMoney(report: OnyxEntry, policy: OnyxEntry, otherParticipants: number[]): boolean {
- // User cannot request money in chat thread or in task report or in chat room
+ // User cannot submit expenses in a chat thread, task report or in a chat room
if (isChatThread(report) || isTaskReport(report) || isChatRoom(report) || isSelfDM(report) || isGroupChat(report)) {
return false;
}
- // Users can only request money in DMs if they are a 1:1 DM
+ // Users can only submit expenses in DMs if they are a 1:1 DM
if (isDM(report)) {
return otherParticipants.length === 1;
}
@@ -4977,19 +5006,19 @@ function canRequestMoney(report: OnyxEntry, policy: OnyxEntry, o
isOwnPolicyExpenseChat = Boolean(getParentReport(report)?.isOwnPolicyExpenseChat);
}
- // In case there are no other participants than the current user and it's not user's own policy expense chat, they can't request money from such report
+ // In case there are no other participants than the current user and it's not user's own policy expense chat, they can't submit expenses from such report
if (otherParticipants.length === 0 && !isOwnPolicyExpenseChat) {
return false;
}
- // User can request money in any IOU report, unless paid, but user can only request money in an expense report
+ // User can submit expenses in any IOU report, unless paid, but the user can only submit expenses in an expense report
// which is tied to their workspace chat.
if (isMoneyRequestReport(report)) {
const canAddTransactions = canAddOrDeleteTransactions(report);
return isGroupPolicy(report) ? isOwnPolicyExpenseChat && canAddTransactions : canAddTransactions;
}
- // In case of policy expense chat, users can only request money from their own policy expense chat
+ // In the case of policy expense chat, users can only submit expenses from their own policy expense chat
return !isPolicyExpenseChat(report) || isOwnPolicyExpenseChat;
}
@@ -5004,19 +5033,19 @@ function isGroupChatAdmin(report: OnyxEntry, accountID: number) {
}
/**
- * Helper method to define what money request options we want to show for particular method.
- * There are 4 money request options: Request, Split, Send and Track expense:
- * - Request option should show for:
+ * Helper method to define what expense options we want to show for particular method.
+ * There are 4 expense options: Submit, Split, Pay and Track expense:
+ * - Submit option should show for:
* - DMs
* - own policy expense chats
* - open and processing expense reports tied to own policy expense chat
* - unsettled IOU reports
- * - Send option should show for:
+ * - Pay option should show for:
* - DMs
* - Split options should show for:
* - DMs
* - chat/policy rooms with more than 1 participant
- * - groups chats with 3 and more participants
+ * - groups chats with 2 and more participants
* - corporate workspace chats
* - Track expense option should show for:
* - Self DMs
@@ -5027,7 +5056,7 @@ function isGroupChatAdmin(report: OnyxEntry, accountID: number) {
* as a participant of the report.
*/
function getMoneyRequestOptions(report: OnyxEntry, policy: OnyxEntry, reportParticipants: number[], canUseTrackExpense = true): Array> {
- // In any thread or task report, we do not allow any new money requests yet
+ // In any thread or task report, we do not allow any new expenses yet
if (isChatThread(report) || isTaskReport(report) || (!canUseTrackExpense && isSelfDM(report))) {
return [];
}
@@ -5047,10 +5076,10 @@ function getMoneyRequestOptions(report: OnyxEntry, policy: OnyxEntry 0) ||
(isDM(report) && otherParticipants.length > 0) ||
@@ -5069,7 +5098,7 @@ function getMoneyRequestOptions(report: OnyxEntry, policy: OnyxEntry): Error
}
/**
- * Return true if the Money Request report is marked for deletion.
+ * Return true if the expense report is marked for deletion.
*/
function isMoneyRequestReportPendingDeletion(report: OnyxEntry | EmptyObject): boolean {
if (!isMoneyRequestReport(report)) {
@@ -5200,7 +5229,7 @@ function isMoneyRequestReportPendingDeletion(report: OnyxEntry | EmptyOb
function canUserPerformWriteAction(report: OnyxEntry) {
const reportErrors = getAddWorkspaceRoomOrChatReportErrors(report);
- // If the Money Request report is marked for deletion, let us prevent any further write action.
+ // If the expense report is marked for deletion, let us prevent any further write action.
if (isMoneyRequestReportPendingDeletion(report)) {
return false;
}
@@ -5212,7 +5241,7 @@ function canUserPerformWriteAction(report: OnyxEntry) {
* Returns ID of the original report from which the given reportAction is first created.
*/
function getOriginalReportID(reportID: string, reportAction: OnyxEntry): string | undefined {
- const reportActions = reportActionsByReport?.[reportID];
+ const reportActions = allReportActions?.[`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${reportID}`];
const currentReportAction = reportActions?.[reportAction?.reportActionID ?? ''] ?? null;
const transactionThreadReportID = ReportActionsUtils.getOneTransactionThreadReportID(reportID, reportActions ?? ([] as ReportAction[]));
if (transactionThreadReportID !== null) {
@@ -5228,7 +5257,7 @@ function getOriginalReportID(reportID: string, reportAction: OnyxEntry): Repo
}
/**
- * Check if the report can create the request with type is iouType
+ * Check if the report can create the expense with type is iouType
*/
function canCreateRequest(report: OnyxEntry, policy: OnyxEntry, iouType: (typeof CONST.IOU.TYPE)[keyof typeof CONST.IOU.TYPE]): boolean {
const participantAccountIDs = report?.participantAccountIDs ?? [];
@@ -5436,7 +5465,7 @@ function getIOUReportActionDisplayMessage(reportAction: OnyxEntry,
if (originalMessage.type === CONST.IOU.REPORT_ACTION_TYPE.PAY) {
// The `REPORT_ACTION_TYPE.PAY` action type is used for both fulfilling existing requests and sending money. To
// differentiate between these two scenarios, we check if the `originalMessage` contains the `IOUDetails`
- // property. If it does, it indicates that this is a 'Send money' action.
+ // property. If it does, it indicates that this is a 'Pay someone' action.
const {amount, currency} = originalMessage.IOUDetails ?? originalMessage;
const formattedAmount = CurrencyUtils.convertToDisplayString(Math.abs(amount), currency) ?? '';
@@ -5483,7 +5512,7 @@ function getIOUReportActionDisplayMessage(reportAction: OnyxEntry,
} else if (ReportActionsUtils.isTrackExpenseAction(reportAction)) {
translationKey = 'iou.trackedAmount';
} else {
- translationKey = 'iou.requestedAmount';
+ translationKey = 'iou.submittedAmount';
}
return Localize.translateLocal(translationKey, {
formattedAmount,
@@ -5497,7 +5526,7 @@ function getIOUReportActionDisplayMessage(reportAction: OnyxEntry,
* A report is a group chat if it meets the following conditions:
* - Not a chat thread.
* - Not a task report.
- * - Not a money request / IOU report.
+ * - Not an expense / IOU report.
* - Not an archived room.
* - Not a public / admin / announce chat room (chat type doesn't match any of the specified types).
* - More than 2 participants.
@@ -5701,7 +5730,7 @@ function getNonHeldAndFullAmount(iouReport: OnyxEntry, policy: OnyxEntry
* Disable reply in thread action if:
*
* - The action is listed in the thread-disabled list
- * - The action is a split bill action
+ * - The action is a split expense action
* - The action is deleted and is not threaded
* - The report is archived and the action is not threaded
* - The action is a whisper action and it's neither a report preview nor IOU action
@@ -5714,9 +5743,10 @@ function shouldDisableThread(reportAction: OnyxEntry, reportID: st
const isIOUAction = ReportActionsUtils.isMoneyRequestAction(reportAction);
const isWhisperAction = ReportActionsUtils.isWhisperAction(reportAction) || ReportActionsUtils.isActionableTrackExpense(reportAction);
const isArchivedReport = isArchivedRoom(getReport(reportID));
+ const isActionDisabled = CONST.REPORT.ACTIONS.THREAD_DISABLED.some((action: string) => action === reportAction?.actionName);
return (
- CONST.REPORT.ACTIONS.THREAD_DISABLED.some((action: string) => action === reportAction?.actionName) ||
+ isActionDisabled ||
isSplitBillAction ||
(isDeletedAction && !reportAction?.childVisibleActionCount) ||
(isArchivedReport && !reportAction?.childVisibleActionCount) ||
@@ -5889,12 +5919,12 @@ function getIndicatedMissingPaymentMethod(userWallet: OnyxEntry, rep
* Checks if report chat contains missing payment method
*/
function hasMissingPaymentMethod(userWallet: OnyxEntry, iouReportID: string): boolean {
- const reportActions = reportActionsByReport?.[iouReportID] ?? {};
+ const reportActions = allReportActions?.[`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${iouReportID}`] ?? {};
return Object.values(reportActions).some((action) => getIndicatedMissingPaymentMethod(userWallet, iouReportID, action) !== undefined);
}
/**
- * Used from money request actions to decide if we need to build an optimistic money request report.
+ * Used from expense actions to decide if we need to build an optimistic expense report.
Create a new report if:
- we don't have an iouReport set in the chatReport
- we have one, but it's waiting on the payee adding a bank account
@@ -5908,7 +5938,7 @@ function shouldCreateNewMoneyRequestReport(existingIOUReport: OnyxEntry
* Checks if report contains actions with errors
*/
function hasActionsWithErrors(reportID: string): boolean {
- const reportActions = reportActionsByReport?.[reportID ?? ''] ?? {};
+ const reportActions = allReportActions?.[`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${reportID}`] ?? {};
return Object.values(reportActions).some((action) => !isEmptyObject(action.errors));
}
@@ -5931,7 +5961,7 @@ function getReportActionActorAccountID(reportAction: OnyxEntry, io
function createDraftTransactionAndNavigateToParticipantSelector(transactionID: string, reportID: string, actionName: ValueOf, reportActionID: string): void {
const transaction = allTransactions?.[`${ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`] ?? ({} as Transaction);
- const reportActions = reportActionsByReport?.[reportID] ?? ([] as ReportAction[]);
+ const reportActions = allReportActions?.[`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${reportID}`] ?? ([] as ReportAction[]);
if (!transaction || !reportActions) {
return;
@@ -6109,6 +6139,7 @@ export {
getDefaultWorkspaceAvatarTestID,
getCommentLength,
getParsedComment,
+ addDomainToShortMention,
getMoneyRequestOptions,
canCreateRequest,
hasIOUWaitingOnCurrentUserBankAccount,
@@ -6227,6 +6258,7 @@ export {
buildParticipantsFromAccountIDs,
canReportBeMentionedWithinPolicy,
getAllHeldTransactions,
+ getPayeeName,
};
export type {
diff --git a/src/libs/SidebarUtils.ts b/src/libs/SidebarUtils.ts
index 3f1b354d70fc..9fc83d50f1e1 100644
--- a/src/libs/SidebarUtils.ts
+++ b/src/libs/SidebarUtils.ts
@@ -23,18 +23,14 @@ import * as ReportUtils from './ReportUtils';
import * as TaskUtils from './TaskUtils';
import * as UserUtils from './UserUtils';
-const reportActionsByReport: OnyxCollection = {};
const visibleReportActionItems: ReportActions = {};
-
Onyx.connect({
key: ONYXKEYS.COLLECTION.REPORT_ACTIONS,
callback: (actions, key) => {
- if (!key || !actions) {
+ if (!actions || !key) {
return;
}
-
const reportID = CollectionUtils.extractCollectionItemID(key);
- reportActionsByReport[reportID] = actions;
const actionsArray: ReportAction[] = ReportActionsUtils.getSortedReportActions(Object.values(actions));
@@ -47,6 +43,7 @@ Onyx.connect({
reportAction.actionName !== CONST.REPORT.ACTIONS.TYPE.CREATED &&
reportAction.pendingAction !== CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE,
);
+
visibleReportActionItems[reportID] = reportActionsForDisplay[reportActionsForDisplay.length - 1];
},
});
@@ -87,7 +84,7 @@ function getOrderedReportIDs(
const parentReportActionsKey = `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${report.parentReportID}`;
const parentReportActions = allReportActions?.[parentReportActionsKey];
- const reportActions = reportActionsByReport?.[report.reportID] ?? {};
+ const reportActions = allReportActions?.[`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${report.reportID}`] ?? {};
const parentReportAction = parentReportActions?.find((action) => action && action?.reportActionID === report.parentReportActionID);
const doesReportHaveViolations = !!(
betas?.includes(CONST.BETAS.VIOLATIONS) &&
@@ -398,7 +395,7 @@ function getOptionData({
result.isIOUReportOwner = ReportUtils.isIOUOwnedByCurrentUser(result as Report);
- if (ReportActionsUtils.isActionableJoinRequestPending(report.reportID)) {
+ if (ReportUtils.isJoinRequestInAdminRoom(report)) {
result.isPinned = true;
result.isUnread = true;
result.brickRoadIndicator = CONST.BRICK_ROAD_INDICATOR_STATUS.INFO;
diff --git a/src/libs/TransactionUtils.ts b/src/libs/TransactionUtils.ts
index 89b89ff7b584..a5b85b87e37e 100644
--- a/src/libs/TransactionUtils.ts
+++ b/src/libs/TransactionUtils.ts
@@ -36,7 +36,7 @@ Onyx.connect({
});
function isDistanceRequest(transaction: OnyxEntry): boolean {
- // This is used during the request creation flow before the transaction has been saved to the server
+ // This is used during the expense creation flow before the transaction has been saved to the server
if (lodashHas(transaction, 'iouRequestType')) {
return transaction?.iouRequestType === CONST.IOU.REQUEST_TYPE.DISTANCE;
}
@@ -48,7 +48,7 @@ function isDistanceRequest(transaction: OnyxEntry): boolean {
}
function isScanRequest(transaction: OnyxEntry): boolean {
- // This is used during the request creation flow before the transaction has been saved to the server
+ // This is used during the expense creation flow before the transaction has been saved to the server
if (lodashHas(transaction, 'iouRequestType')) {
return transaction?.iouRequestType === CONST.IOU.REQUEST_TYPE.SCAN;
}
@@ -68,7 +68,7 @@ function getRequestType(transaction: OnyxEntry): IOURequestType {
}
function isManualRequest(transaction: Transaction): boolean {
- // This is used during the request creation flow before the transaction has been saved to the server
+ // This is used during the expense creation flow before the transaction has been saved to the server
if (lodashHas(transaction, 'iouRequestType')) {
return transaction.iouRequestType === CONST.IOU.REQUEST_TYPE.MANUAL;
}
@@ -80,7 +80,7 @@ function isManualRequest(transaction: Transaction): boolean {
* Optimistically generate a transaction.
*
* @param amount – in cents
- * @param [existingTransactionID] When creating a distance request, an empty transaction has already been created with a transactionID. In that case, the transaction here needs to have
+ * @param [existingTransactionID] When creating a distance expense, an empty transaction has already been created with a transactionID. In that case, the transaction here needs to have
* it's transactionID match what was already generated.
*/
function buildOptimisticTransaction(
@@ -176,7 +176,7 @@ function areRequiredFieldsEmpty(transaction: OnyxEntry): boolean {
}
/**
- * Given the edit made to the money request, return an updated transaction object.
+ * Given the edit made to the expnse, return an updated transaction object.
*/
function getUpdatedTransaction(transaction: Transaction, transactionChanges: TransactionChanges, isFromExpenseReport: boolean, shouldUpdateReceiptState = true): Transaction {
// Only changing the first level fields so no need for deep clone now
@@ -215,12 +215,10 @@ function getUpdatedTransaction(transaction: Transaction, transactionChanges: Tra
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') {
diff --git a/src/libs/WorkspacesSettingsUtils.ts b/src/libs/WorkspacesSettingsUtils.ts
index db41bb18aeef..8205a9473e1b 100644
--- a/src/libs/WorkspacesSettingsUtils.ts
+++ b/src/libs/WorkspacesSettingsUtils.ts
@@ -6,7 +6,6 @@ import type {TranslationPaths} from '@src/languages/types';
import ONYXKEYS from '@src/ONYXKEYS';
import type {Policy, ReimbursementAccount, Report, ReportActions} from '@src/types/onyx';
import type {Unit} from '@src/types/onyx/Policy';
-import * as CollectionUtils from './CollectionUtils';
import * as CurrencyUtils from './CurrencyUtils';
import type {Phrase, PhraseParameters} from './Localize';
import * as OptionsListUtils from './OptionsListUtils';
@@ -42,16 +41,15 @@ Onyx.connect({
},
});
-const reportActionsByReport: OnyxCollection = {};
+let allReportActions: OnyxCollection;
Onyx.connect({
key: ONYXKEYS.COLLECTION.REPORT_ACTIONS,
- callback: (actions, key) => {
- if (!key || !actions) {
+ waitForCollectionCallback: true,
+ callback: (actions) => {
+ if (!actions) {
return;
}
-
- const reportID = CollectionUtils.extractCollectionItemID(key);
- reportActionsByReport[reportID] = actions;
+ allReportActions = actions;
},
});
@@ -60,7 +58,7 @@ Onyx.connect({
* @returns BrickRoad for the policy passed as a param
*/
const getBrickRoadForPolicy = (report: Report): BrickRoad => {
- const reportActions = reportActionsByReport?.[report.reportID] ?? {};
+ const reportActions = allReportActions?.[`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${report.reportID}`] ?? {};
const reportErrors = OptionsListUtils.getAllReportErrors(report, reportActions);
const doesReportContainErrors = Object.keys(reportErrors ?? {}).length !== 0 ? CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR : undefined;
if (doesReportContainErrors) {
@@ -70,7 +68,7 @@ const getBrickRoadForPolicy = (report: Report): BrickRoad => {
// To determine if the report requires attention from the current user, we need to load the parent report action
let itemParentReportAction = {};
if (report.parentReportID) {
- const itemParentReportActions = reportActionsByReport[report.parentReportID] ?? {};
+ const itemParentReportActions = allReportActions?.[`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${report.parentReportID}`] ?? {};
itemParentReportAction = report.parentReportActionID ? itemParentReportActions[report.parentReportActionID] : {};
}
const reportOption = {...report, isUnread: ReportUtils.isUnread(report), isUnreadWithMention: ReportUtils.isUnreadWithMention(report)};
diff --git a/src/libs/actions/IOU.ts b/src/libs/actions/IOU.ts
index 896b88988818..83caa65e1d77 100644
--- a/src/libs/actions/IOU.ts
+++ b/src/libs/actions/IOU.ts
@@ -25,7 +25,6 @@ import type {
UpdateMoneyRequestParams,
} from '@libs/API/parameters';
import {WRITE_COMMANDS} from '@libs/API/types';
-import * as CollectionUtils from '@libs/CollectionUtils';
import * as CurrencyUtils from '@libs/CurrencyUtils';
import DateUtils from '@libs/DateUtils';
import DistanceRequestUtils from '@libs/DistanceRequestUtils';
@@ -259,16 +258,15 @@ Onyx.connect({
callback: (value) => (allPolicies = value),
});
-const reportActionsByReport: OnyxCollection = {};
+let allReportActions: OnyxCollection;
Onyx.connect({
key: ONYXKEYS.COLLECTION.REPORT_ACTIONS,
- callback: (actions, key) => {
- if (!key || !actions) {
+ waitForCollectionCallback: true,
+ callback: (actions) => {
+ if (!actions) {
return;
}
-
- const reportID = CollectionUtils.extractCollectionItemID(key);
- reportActionsByReport[reportID] = actions;
+ allReportActions = actions;
},
});
@@ -286,7 +284,7 @@ function getPolicy(policyID: string | undefined): OnyxTypes.Policy | EmptyObject
* Find the report preview action from given chat report and iou report
*/
function getReportPreviewAction(chatReportID: string, iouReportID: string): OnyxEntry {
- const reportActions = reportActionsByReport?.[chatReportID] ?? {};
+ const reportActions = allReportActions?.[`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${chatReportID}`] ?? {};
// Find the report preview action from the chat report
return (
@@ -297,7 +295,7 @@ function getReportPreviewAction(chatReportID: string, iouReportID: string): Onyx
}
/**
- * Initialize money request info
+ * Initialize expense info
* @param reportID to attach the transaction to
* @param policy
* @param isFromGlobalCreate
@@ -311,7 +309,7 @@ function initMoneyRequest(reportID: string, policy: OnyxEntry,
const created = currentDate || format(new Date(), 'yyyy-MM-dd');
const comment: Comment = {};
- // Add initial empty waypoints when starting a distance request
+ // Add initial empty waypoints when starting a distance expense
if (iouRequestType === CONST.IOU.REQUEST_TYPE.DISTANCE) {
comment.waypoints = {
waypoint0: {},
@@ -357,7 +355,7 @@ function clearMoneyRequest(transactionID: string) {
}
/**
- * Update money request-related pages IOU type params
+ * Update money expense-related pages IOU type params
*/
function updateMoneyRequestTypeParams(routes: StackNavigationState['routes'] | NavigationPartialRoute[], newIouType: string, tab?: string) {
routes.forEach((route) => {
@@ -373,7 +371,7 @@ function updateMoneyRequestTypeParams(routes: StackNavigationState,
iouReport: OnyxTypes.Report,
@@ -1170,7 +1168,7 @@ function getDeleteTrackExpenseInformation(
// STEP 2: Decide if we need to:
// 1. Delete the transactionThread - delete if there are no visible comments in the thread and we're not moving the transaction
- // 2. Update the moneyRequestPreview to show [Deleted request] - update if the transactionThread exists AND it isn't being deleted and we're not moving the transaction
+ // 2. Update the moneyRequestPreview to show [Deleted expense] - update if the transactionThread exists AND it isn't being deleted and we're not moving the transaction
const shouldDeleteTransactionThread = !isMovingTransactionFromTrackExpense && (transactionThreadID ? (reportAction?.childVisibleActionCount ?? 0) === 0 : false);
const shouldShowDeletedRequestMessage = !isMovingTransactionFromTrackExpense && !!transactionThreadID && !shouldDeleteTransactionThread;
@@ -1316,7 +1314,7 @@ function getDeleteTrackExpenseInformation(
}
/**
- * Gathers all the data needed to make a money request. It attempts to find existing reports, iouReports, and receipts. If it doesn't find them, then
+ * Gathers all the data needed to submit an expense. It attempts to find existing reports, iouReports, and receipts. If it doesn't find them, then
* it creates optimistic versions of them and uses those instead
*/
function getMoneyRequestInformation(
@@ -1364,8 +1362,8 @@ function getMoneyRequestInformation(
chatReport = ReportUtils.buildOptimisticChatReport([payerAccountID]);
}
- // STEP 2: Get the money request report. If the moneyRequestReportID has been provided, we want to add the transaction to this specific report.
- // If no such reportID has been provided, let's use the chatReport.iouReportID property. In case that is not present, build a new optimistic money request report.
+ // STEP 2: Get the Expense/IOU report. If the moneyRequestReportID has been provided, we want to add the transaction to this specific report.
+ // If no such reportID has been provided, let's use the chatReport.iouReportID property. In case that is not present, build a new optimistic Expense/IOU report.
let iouReport: OnyxEntry = null;
if (moneyRequestReportID) {
iouReport = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${moneyRequestReportID}`] ?? null;
@@ -1568,8 +1566,8 @@ function getTrackExpenseInformation(
return {};
}
- // STEP 2: If not in the self-DM flow, we need to use the money request report.
- // For this, first use the chatReport.iouReportID property. Build a new optimistic money request report if needed.
+ // STEP 2: If not in the self-DM flow, we need to use the expense report.
+ // For this, first use the chatReport.iouReportID property. Build a new optimistic expense report if needed.
const shouldUseMoneyReport = !!isPolicyExpenseChat;
let iouReport: OnyxEntry = null;
@@ -2287,7 +2285,7 @@ function getUpdateTrackExpenseParams(
};
}
-/** Updates the created date of a money request */
+/** Updates the created date of an expense */
function updateMoneyRequestDate(
transactionID: string,
transactionThreadReportID: string,
@@ -2310,7 +2308,7 @@ function updateMoneyRequestDate(
API.write(WRITE_COMMANDS.UPDATE_MONEY_REQUEST_DATE, params, onyxData);
}
-/** Updates the billable field of a money request */
+/** Updates the billable field of an expense */
function updateMoneyRequestBillable(
transactionID: string,
transactionThreadReportID: string,
@@ -2326,7 +2324,7 @@ function updateMoneyRequestBillable(
API.write(WRITE_COMMANDS.UPDATE_MONEY_REQUEST_BILLABLE, params, onyxData);
}
-/** Updates the merchant field of a money request */
+/** Updates the merchant field of an expense */
function updateMoneyRequestMerchant(
transactionID: string,
transactionThreadReportID: string,
@@ -2349,7 +2347,7 @@ function updateMoneyRequestMerchant(
API.write(WRITE_COMMANDS.UPDATE_MONEY_REQUEST_MERCHANT, params, onyxData);
}
-/** Updates the tag of a money request */
+/** Updates the tag of an expense */
function updateMoneyRequestTag(
transactionID: string,
transactionThreadReportID: string,
@@ -2365,7 +2363,7 @@ function updateMoneyRequestTag(
API.write(WRITE_COMMANDS.UPDATE_MONEY_REQUEST_TAG, params, onyxData);
}
-/** Updates the created tax amount of a money request */
+/** Updates the created tax amount of an expense */
function updateMoneyRequestTaxAmount(
transactionID: string,
optimisticReportActionID: string,
@@ -2381,7 +2379,7 @@ function updateMoneyRequestTaxAmount(
API.write('UpdateMoneyRequestTaxAmount', params, onyxData);
}
-/** Updates the created tax rate of a money request */
+/** Updates the created tax rate of an expense */
function updateMoneyRequestTaxRate(
transactionID: string,
optimisticReportActionID: string,
@@ -2406,7 +2404,7 @@ type UpdateMoneyRequestDistanceParams = {
policyCategories?: OnyxEntry;
};
-/** Updates the waypoints of a distance money request */
+/** Updates the waypoints of a distance expense */
function updateMoneyRequestDistance({
transactionID,
transactionThreadReportID,
@@ -2429,7 +2427,7 @@ function updateMoneyRequestDistance({
API.write(WRITE_COMMANDS.UPDATE_MONEY_REQUEST_DISTANCE, params, onyxData);
}
-/** Updates the category of a money request */
+/** Updates the category of an expense */
function updateMoneyRequestCategory(
transactionID: string,
transactionThreadReportID: string,
@@ -2445,7 +2443,7 @@ function updateMoneyRequestCategory(
API.write(WRITE_COMMANDS.UPDATE_MONEY_REQUEST_CATEGORY, params, onyxData);
}
-/** Updates the description of a money request */
+/** Updates the description of an expense */
function updateMoneyRequestDescription(
transactionID: string,
transactionThreadReportID: string,
@@ -2468,7 +2466,7 @@ function updateMoneyRequestDescription(
API.write(WRITE_COMMANDS.UPDATE_MONEY_REQUEST_DESCRIPTION, params, onyxData);
}
-/** Edits an existing distance request */
+/** Edits an existing distance expense */
function updateDistanceRequest(
transactionID: string,
transactionThreadReportID: string,
@@ -2762,7 +2760,7 @@ function shareTrackedExpense(
}
/**
- * Request money from another user
+ * Submit expense to another user
*/
function requestMoney(
report: OnyxEntry,
@@ -3129,7 +3127,7 @@ function getOrCreateOptimisticSplitChatReport(existingSplitChatReportID: string,
* {email: 'user3', amount: 100, iouReportID: '200', chatReportID: '210', transactionID: '220', reportActionID: '230'}
* ]
* @param amount - always in the smallest unit of the currency
- * @param existingSplitChatReportID - the report ID where the split bill happens, could be a group chat or a workspace chat
+ * @param existingSplitChatReportID - the report ID where the split expense happens, could be a group chat or a workspace chat
*/
function createSplitsAndOnyxData(
participants: Participant[],
@@ -3160,7 +3158,7 @@ function createSplitsAndOnyxData(
created,
'',
'',
- merchant || Localize.translateLocal('iou.request'),
+ merchant || Localize.translateLocal('iou.expense'),
undefined,
undefined,
undefined,
@@ -3374,7 +3372,7 @@ function createSplitsAndOnyxData(
created,
CONST.IOU.TYPE.SPLIT,
splitTransaction.transactionID,
- merchant || Localize.translateLocal('iou.request'),
+ merchant || Localize.translateLocal('iou.expense'),
undefined,
undefined,
undefined,
@@ -3641,7 +3639,7 @@ type StartSplitBilActionParams = {
currency: string;
};
-/** Used exclusively for starting a split bill request that contains a receipt, the split request will be completed once the receipt is scanned
+/** Used exclusively for starting a split expense request that contains a receipt, the split request will be completed once the receipt is scanned
* or user enters details manually.
*
* @param existingSplitChatReportID - Either a group DM or a workspace chat
@@ -3929,7 +3927,7 @@ function startSplitBill({
Report.notifyNewAction(splitChatReport.chatReportID ?? '', currentUserAccountID);
}
-/** Used for editing a split bill while it's still scanning or when SmartScan fails, it completes a split bill started by startSplitBill above.
+/** Used for editing a split expense while it's still scanning or when SmartScan fails, it completes a split expense started by startSplitBill above.
*
* @param chatReportID - The group chat or workspace reportID
* @param reportAction - The split action that lives in the chatReport above
@@ -4031,7 +4029,7 @@ function completeSplitBill(chatReportID: string, reportAction: OnyxTypes.ReportA
let oneOnOneChatReport: OnyxTypes.Report | null;
let isNewOneOnOneChatReport = false;
if (isPolicyExpenseChat) {
- // The workspace chat reportID is saved in the splits array when starting a split bill with a workspace
+ // The workspace chat reportID is saved in the splits array when starting a split expense with a workspace
oneOnOneChatReport = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${participant.chatReportID}`] ?? null;
} else {
const existingChatReport = ReportUtils.getChatByParticipants(participant.accountID ? [participant.accountID] : []);
@@ -4456,7 +4454,7 @@ type UpdateMoneyRequestAmountAndCurrencyParams = {
policyCategories?: OnyxEntry;
};
-/** Updates the amount and currency fields of a money request */
+/** Updates the amount and currency fields of an expense */
function updateMoneyRequestAmountAndCurrency({
transactionID,
transactionThreadReportID,
@@ -4498,7 +4496,7 @@ function deleteMoneyRequest(transactionID: string, reportAction: OnyxTypes.Repor
// STEP 2: Decide if we need to:
// 1. Delete the transactionThread - delete if there are no visible comments in the thread
- // 2. Update the moneyRequestPreview to show [Deleted request] - update if the transactionThread exists AND it isn't being deleted
+ // 2. Update the moneyRequestPreview to show [Deleted expense] - update if the transactionThread exists AND it isn't being deleted
const shouldDeleteTransactionThread = transactionThreadID ? (reportAction?.childVisibleActionCount ?? 0) === 0 : false;
const shouldShowDeletedRequestMessage = !!transactionThreadID && !shouldDeleteTransactionThread;
@@ -5346,7 +5344,7 @@ function canIOUBePaid(iouReport: OnyxEntry | EmptyObject, chat
}
function hasIOUToApproveOrPay(chatReport: OnyxEntry | EmptyObject, excludedIOUReportID: string): boolean {
- const chatReportActions = reportActionsByReport?.[chatReport?.reportID ?? ''] ?? {};
+ const chatReportActions = allReportActions?.[`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${chatReport?.reportID}`] ?? {};
return Object.values(chatReportActions).some((action) => {
const iouReport = ReportUtils.getReport(action.childReportID ?? '');
@@ -5801,7 +5799,7 @@ function replaceReceipt(transactionID: string, file: File, source: string) {
* @param report attached to the transaction
*/
function setMoneyRequestParticipantsFromReport(transactionID: string, report: OnyxEntry) {
- // If the report is iou or expense report, we should get the chat report to set participant for request money
+ // If the report is iou or expense report, we should get the chat report to set participant for expense
const chatReport = ReportUtils.isMoneyRequestReport(report) ? ReportUtils.getReport(report?.chatReportID) : report;
const currentUserAccountID = currentUserPersonalDetails.accountID;
const shouldAddAsReport = !isEmptyObject(chatReport) && ReportUtils.isSelfDM(chatReport);
@@ -5846,7 +5844,7 @@ function setShownHoldUseExplanation() {
}
/**
- * Put money request on HOLD
+ * Put expense on HOLD
*/
function putOnHold(transactionID: string, comment: string, reportID: string) {
const currentTime = DateUtils.getDBTime();
@@ -5910,7 +5908,7 @@ function putOnHold(transactionID: string, comment: string, reportID: string) {
}
/**
- * Remove money request from HOLD
+ * Remove expense from HOLD
*/
function unholdRequest(transactionID: string, reportID: string) {
const createdReportAction = ReportUtils.buildOptimisticUnHoldReportAction();
diff --git a/src/libs/actions/PaymentMethods.ts b/src/libs/actions/PaymentMethods.ts
index dc8cefc0c30e..c5a74bdc6ace 100644
--- a/src/libs/actions/PaymentMethods.ts
+++ b/src/libs/actions/PaymentMethods.ts
@@ -10,7 +10,6 @@ import * as CardUtils from '@libs/CardUtils';
import Navigation from '@libs/Navigation/Navigation';
import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
-import ROUTES from '@src/ROUTES';
import type {Route} from '@src/ROUTES';
import INPUT_IDS from '@src/types/form/AddDebitCardForm';
import type {BankAccountList, FundList} from '@src/types/onyx';
@@ -30,7 +29,7 @@ const kycWallRef: MutableRefObject = createRef();
/**
* When we successfully add a payment method or pass the KYC checks we will continue with our setup action if we have one set.
*/
-function continueSetup(fallbackRoute: Route = ROUTES.HOME) {
+function continueSetup(fallbackRoute?: Route) {
if (!kycWallRef.current?.continueAction) {
Navigation.goBack(fallbackRoute);
return;
diff --git a/src/libs/actions/PolicyConnections.ts b/src/libs/actions/PolicyConnections.ts
new file mode 100644
index 000000000000..7ccf9f2506bd
--- /dev/null
+++ b/src/libs/actions/PolicyConnections.ts
@@ -0,0 +1,38 @@
+import Onyx from 'react-native-onyx';
+import type {OnyxUpdate} from 'react-native-onyx';
+import * as API from '@libs/API';
+import type {OpenPolicyAccountingPageParams} from '@libs/API/parameters';
+import {READ_COMMANDS} from '@libs/API/types';
+import ONYXKEYS from '@src/ONYXKEYS';
+
+function openPolicyAccountingPage(policyID: string) {
+ const hasConnectionsDataBeenFetchedKey = `${ONYXKEYS.COLLECTION.POLICY_HAS_CONNECTIONS_DATA_BEEN_FETCHED}${policyID}` as const;
+
+ const optimisticData: OnyxUpdate[] = [
+ {
+ onyxMethod: Onyx.METHOD.MERGE,
+ key: hasConnectionsDataBeenFetchedKey,
+ value: false,
+ },
+ ];
+ const finallyData: OnyxUpdate[] = [
+ {
+ onyxMethod: Onyx.METHOD.MERGE,
+ key: hasConnectionsDataBeenFetchedKey,
+ value: true,
+ },
+ ];
+
+ const parameters: OpenPolicyAccountingPageParams = {
+ policyID,
+ };
+
+ API.read(READ_COMMANDS.OPEN_POLICY_ACCOUNTING_PAGE, parameters, {
+ optimisticData,
+ finallyData,
+ });
+}
+
+// More action functions will be added later
+// eslint-disable-next-line import/prefer-default-export
+export {openPolicyAccountingPage};
diff --git a/src/libs/actions/Report.ts b/src/libs/actions/Report.ts
index f599208ff915..41079d20a982 100644
--- a/src/libs/actions/Report.ts
+++ b/src/libs/actions/Report.ts
@@ -2941,8 +2941,29 @@ function getReportPrivateNote(reportID: string | undefined) {
* - Sets the introSelected NVP to the choice the user made
* - Creates an optimistic report comment from concierge
*/
-function completeEngagementModal(text: string, choice: ValueOf) {
+function completeEngagementModal(choice: ValueOf, text?: string) {
const conciergeAccountID = PersonalDetailsUtils.getAccountIDsByLogins([CONST.EMAIL.CONCIERGE])[0];
+
+ // We do not need to send any message for some choices
+ if (!text) {
+ const parameters: CompleteEngagementModalParams = {
+ reportID: conciergeChatReportID ?? '',
+ engagementChoice: choice,
+ };
+
+ const optimisticData: OnyxUpdate[] = [
+ {
+ onyxMethod: Onyx.METHOD.MERGE,
+ key: ONYXKEYS.NVP_INTRO_SELECTED,
+ value: {choice},
+ },
+ ];
+ API.write(WRITE_COMMANDS.COMPLETE_ENGAGEMENT_MODAL, parameters, {
+ optimisticData,
+ });
+ return;
+ }
+
const reportComment = ReportUtils.buildOptimisticAddCommentReportAction(text, undefined, conciergeAccountID);
const reportCommentAction: OptimisticAddCommentReportAction = reportComment.reportAction;
const lastComment = reportCommentAction?.message?.[0];
@@ -3007,6 +3028,7 @@ function completeEngagementModal(text: string, choice: ValueOf = {};
+let allReportActions: OnyxCollection;
Onyx.connect({
key: ONYXKEYS.COLLECTION.REPORT_ACTIONS,
- callback: (actions, key) => {
- if (!key || !actions) {
+ waitForCollectionCallback: true,
+ callback: (actions) => {
+ if (!actions) {
return;
}
-
- const reportID = CollectionUtils.extractCollectionItemID(key);
- reportActionsByReport[reportID] = actions;
+ allReportActions = actions;
},
});
@@ -94,7 +92,7 @@ function clearAllRelatedReportActionErrors(reportID: string, reportAction: Repor
}
if (reportAction.childReportID && ignore !== 'child') {
- const childActions = reportActionsByReport?.[reportAction.childReportID] ?? {};
+ const childActions = allReportActions?.[`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${reportAction.childReportID}`] ?? {};
Object.values(childActions).forEach((action) => {
const childErrorKeys = Object.keys(action.errors ?? {}).filter((err) => errorKeys.includes(err));
clearAllRelatedReportActionErrors(reportAction.childReportID ?? '', action, 'parent', childErrorKeys);
diff --git a/src/libs/actions/Task.ts b/src/libs/actions/Task.ts
index 9cf7b0b78008..f74b451c29f6 100644
--- a/src/libs/actions/Task.ts
+++ b/src/libs/actions/Task.ts
@@ -5,7 +5,6 @@ import * as Expensicons from '@components/Icon/Expensicons';
import * as API from '@libs/API';
import type {CancelTaskParams, CompleteTaskParams, CreateTaskParams, EditTaskAssigneeParams, EditTaskParams, ReopenTaskParams} from '@libs/API/parameters';
import {WRITE_COMMANDS} from '@libs/API/types';
-import * as CollectionUtils from '@libs/CollectionUtils';
import DateUtils from '@libs/DateUtils';
import * as ErrorUtils from '@libs/ErrorUtils';
import * as LocalePhoneNumber from '@libs/LocalePhoneNumber';
@@ -67,16 +66,16 @@ Onyx.connect({
},
});
-const allReportActions: OnyxCollection = {};
+let allReportActions: OnyxCollection;
Onyx.connect({
key: ONYXKEYS.COLLECTION.REPORT_ACTIONS,
- callback: (actions, key) => {
- if (!key || !actions) {
+ waitForCollectionCallback: true,
+ callback: (actions) => {
+ if (!actions) {
return;
}
- const reportID = CollectionUtils.extractCollectionItemID(key);
- allReportActions[reportID] = actions;
+ allReportActions = actions;
},
});
@@ -795,7 +794,7 @@ function getParentReportAction(report: OnyxEntry): ReportActio
if (!report?.parentReportID || !report.parentReportActionID) {
return {};
}
- return allReportActions?.[report.parentReportID]?.[report.parentReportActionID] ?? {};
+ return allReportActions?.[`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${report.parentReportID}`]?.[report.parentReportActionID] ?? {};
}
/**
diff --git a/src/libs/actions/Transaction.ts b/src/libs/actions/Transaction.ts
index 9ddaca686e95..a95bf9a825f0 100644
--- a/src/libs/actions/Transaction.ts
+++ b/src/libs/actions/Transaction.ts
@@ -67,7 +67,7 @@ function saveWaypoint(transactionID: string, index: string, waypoint: RecentWayp
[`waypoint${index}`]: waypoint,
},
},
- // We want to reset the amount only for draft transactions (when creating the request).
+ // We want to reset the amount only for draft transactions (when creating the expense).
// When modifying an existing transaction, the amount will be updated on the actual IOU update operation.
...(isDraft && {amount: CONST.IOU.DEFAULT_AMOUNT}),
// Empty out errors when we're saving a new waypoint as this indicates the user is updating their input
@@ -141,7 +141,7 @@ function removeWaypoint(transaction: OnyxEntry, currentIndex: strin
...transaction?.comment,
waypoints: reIndexedWaypoints,
},
- // We want to reset the amount only for draft transactions (when creating the request).
+ // We want to reset the amount only for draft transactions (when creating the expense).
// When modifying an existing transaction, the amount will be updated on the actual IOU update operation.
...(isDraft && {amount: CONST.IOU.DEFAULT_AMOUNT}),
};
@@ -239,7 +239,7 @@ function updateWaypoints(transactionID: string, waypoints: WaypointCollection, i
comment: {
waypoints,
},
- // We want to reset the amount only for draft transactions (when creating the request).
+ // We want to reset the amount only for draft transactions (when creating the expense).
// When modifying an existing transaction, the amount will be updated on the actual IOU update operation.
...(isDraft && {amount: CONST.IOU.DEFAULT_AMOUNT}),
// Empty out errors when we're saving new waypoints as this indicates the user is updating their input
diff --git a/src/libs/actions/User.ts b/src/libs/actions/User.ts
index dcd6e025e23b..f347655b6a4d 100644
--- a/src/libs/actions/User.ts
+++ b/src/libs/actions/User.ts
@@ -525,7 +525,7 @@ function playSoundForMessageType(pushJSON: OnyxServerUpdate[]) {
const types = flatten.map((data) => data?.originalMessage).filter(Boolean) as OriginalMessage[];
for (const message of types) {
- // someone sent money
+ // Pay someone flow
if ('IOUDetails' in message) {
return playSound(SOUNDS.SUCCESS);
}
@@ -545,12 +545,12 @@ function playSoundForMessageType(pushJSON: OnyxServerUpdate[]) {
return playSound(SOUNDS.ATTENTION);
}
- // request money
+ // Submit expense flow
if ('IOUTransactionID' in message) {
return playSound(SOUNDS.ATTENTION);
}
- // Someone completes a money request
+ // Someone reimburses an expense
if ('IOUReportID' in message) {
return playSound(SOUNDS.SUCCESS);
}
diff --git a/src/libs/getSectionsWithIndexOffset.ts b/src/libs/getSectionsWithIndexOffset.ts
index 7de78d048a4d..3237651a0385 100644
--- a/src/libs/getSectionsWithIndexOffset.ts
+++ b/src/libs/getSectionsWithIndexOffset.ts
@@ -3,7 +3,7 @@ import type {SectionListData} from 'react-native';
/**
* Returns a list of sections with indexOffset
*/
-export default function getSectionsWithIndexOffset(sections: Array>): Array> {
+export default function getSectionsWithIndexOffset(sections: Array>): Array> {
return sections.map((section, index) => {
const indexOffset = [...sections].splice(0, index).reduce((acc, curr) => acc + (curr.data?.length ?? 0), 0);
return {...section, indexOffset};
diff --git a/src/libs/migrations/TransactionBackupsToCollection.ts b/src/libs/migrations/TransactionBackupsToCollection.ts
index 407bc70e1f38..a7167492007a 100644
--- a/src/libs/migrations/TransactionBackupsToCollection.ts
+++ b/src/libs/migrations/TransactionBackupsToCollection.ts
@@ -8,7 +8,7 @@ import type {Transaction} from '@src/types/onyx';
* This migration moves all the transaction backups stored in the transaction collection, ONYXKEYS.COLLECTION.TRANSACTION, to a reserved collection that only
* stores draft transactions, ONYXKEYS.COLLECTION.TRANSACTION_DRAFT. The purpose of the migration is that there is a possibility that transaction backups are
* not filtered by most functions, e.g, getAllReportTransactions (src/libs/TransactionUtils.ts). One problem that arose from storing transaction backups with
- * the other transactions is that for every distance request which have their waypoints updated offline, we expect the ReportPreview component to display the
+ * the other transactions is that for every distance expense which have their waypoints updated offline, we expect the ReportPreview component to display the
* default image of a pending map. However, due to the presence of the transaction backup, the previous map image will be displayed alongside the pending map.
* The problem was further discussed in this PR. https://github.com/Expensify/App/pull/30232#issuecomment-178110172
*/
diff --git a/src/pages/SearchPage/SearchPageFooter.tsx b/src/pages/ChatFinderPage/ChatFinderPageFooter.tsx
similarity index 64%
rename from src/pages/SearchPage/SearchPageFooter.tsx
rename to src/pages/ChatFinderPage/ChatFinderPageFooter.tsx
index a9369ff9f0b7..4c006abacfc7 100644
--- a/src/pages/SearchPage/SearchPageFooter.tsx
+++ b/src/pages/ChatFinderPage/ChatFinderPageFooter.tsx
@@ -2,10 +2,10 @@ import React from 'react';
import ReferralProgramCTA from '@components/ReferralProgramCTA';
import CONST from '@src/CONST';
-function SearchPageFooter() {
+function ChatFinderPageFooter() {
return ;
}
-SearchPageFooter.displayName = 'SearchPageFooter';
+ChatFinderPageFooter.displayName = 'ChatFinderPageFooter';
-export default SearchPageFooter;
+export default ChatFinderPageFooter;
diff --git a/src/pages/SearchPage/index.tsx b/src/pages/ChatFinderPage/index.tsx
similarity index 85%
rename from src/pages/SearchPage/index.tsx
rename to src/pages/ChatFinderPage/index.tsx
index d79c60ff4f45..f992fa37d8c5 100644
--- a/src/pages/SearchPage/index.tsx
+++ b/src/pages/ChatFinderPage/index.tsx
@@ -25,9 +25,9 @@ import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
import type SCREENS from '@src/SCREENS';
import type * as OnyxTypes from '@src/types/onyx';
-import SearchPageFooter from './SearchPageFooter';
+import ChatFinderPageFooter from './ChatFinderPageFooter';
-type SearchPageOnyxProps = {
+type ChatFinderPageOnyxProps = {
/** Beta features list */
betas: OnyxEntry;
@@ -35,23 +35,23 @@ type SearchPageOnyxProps = {
isSearchingForReports: OnyxEntry;
};
-type SearchPageProps = SearchPageOnyxProps & StackScreenProps;
+type ChatFinderPageProps = ChatFinderPageOnyxProps & StackScreenProps;
-type SearchPageSectionItem = {
+type ChatFinderPageSectionItem = {
data: OptionData[];
shouldShow: boolean;
};
-type SearchPageSectionList = SearchPageSectionItem[];
+type ChatFinderPageSectionList = ChatFinderPageSectionItem[];
const setPerformanceTimersEnd = () => {
- Timing.end(CONST.TIMING.SEARCH_RENDER);
- Performance.markEnd(CONST.TIMING.SEARCH_RENDER);
+ Timing.end(CONST.TIMING.CHAT_FINDER_RENDER);
+ Performance.markEnd(CONST.TIMING.CHAT_FINDER_RENDER);
};
-const SerachPageFooterInstance = ;
+const ChatFinderPageFooterInstance = ;
-function SearchPage({betas, isSearchingForReports, navigation}: SearchPageProps) {
+function ChatFinderPage({betas, isSearchingForReports, navigation}: ChatFinderPageProps) {
const [isScreenTransitionEnd, setIsScreenTransitionEnd] = useState(false);
const themeStyles = useThemeStyles();
const {translate} = useLocalize();
@@ -65,8 +65,8 @@ function SearchPage({betas, isSearchingForReports, navigation}: SearchPageProps)
const [searchValue, debouncedSearchValue, setSearchValue] = useDebouncedState('');
useEffect(() => {
- Timing.start(CONST.TIMING.SEARCH_RENDER);
- Performance.markStart(CONST.TIMING.SEARCH_RENDER);
+ Timing.start(CONST.TIMING.CHAT_FINDER_RENDER);
+ Performance.markStart(CONST.TIMING.CHAT_FINDER_RENDER);
}, []);
useEffect(() => {
@@ -113,8 +113,8 @@ function SearchPage({betas, isSearchingForReports, navigation}: SearchPageProps)
const {recentReports, personalDetails: localPersonalDetails, userToInvite, headerMessage} = debouncedSearchValue.trim() !== '' ? filteredOptions : searchOptions;
- const sections = useMemo((): SearchPageSectionList => {
- const newSections: SearchPageSectionList = [];
+ const sections = useMemo((): ChatFinderPageSectionList => {
+ const newSections: ChatFinderPageSectionList = [];
if (recentReports?.length > 0) {
newSections.push({
@@ -162,12 +162,13 @@ function SearchPage({betas, isSearchingForReports, navigation}: SearchPageProps)
return (
@@ -183,16 +184,16 @@ function SearchPage({betas, isSearchingForReports, navigation}: SearchPageProps)
onLayout={setPerformanceTimersEnd}
onSelectRow={selectReport}
showLoadingPlaceholder={!areOptionsInitialized || !isScreenTransitionEnd}
- footerContent={!isDismissed && SerachPageFooterInstance}
+ footerContent={!isDismissed && ChatFinderPageFooterInstance}
isLoadingNewOptions={isSearchingForReports ?? undefined}
/>
);
}
-SearchPage.displayName = 'SearchPage';
+ChatFinderPage.displayName = 'ChatFinderPage';
-export default withOnyx({
+export default withOnyx({
betas: {
key: ONYXKEYS.BETAS,
},
@@ -200,4 +201,4 @@ export default withOnyx({
key: ONYXKEYS.IS_SEARCHING_FOR_REPORTS,
initWithStoredValues: false,
},
-})(SearchPage);
+})(ChatFinderPage);
diff --git a/src/pages/OnboardEngagement/ManageTeamsExpensesPage.tsx b/src/pages/OnboardEngagement/ManageTeamsExpensesPage.tsx
index fb642c897dbc..c6ec7dc49b9f 100644
--- a/src/pages/OnboardEngagement/ManageTeamsExpensesPage.tsx
+++ b/src/pages/OnboardEngagement/ManageTeamsExpensesPage.tsx
@@ -56,7 +56,7 @@ function ManageTeamsExpensesModal() {
);
const completeEngagement = () => {
- Report.completeEngagementModal(messageCopy, CONST.INTRO_CHOICES.MANAGE_TEAM);
+ Report.completeEngagementModal(CONST.INTRO_CHOICES.MANAGE_TEAM, messageCopy);
Report.navigateToConciergeChat();
};
diff --git a/src/pages/OnboardEngagement/PurposeForUsingExpensifyPage.tsx b/src/pages/OnboardEngagement/PurposeForUsingExpensifyPage.tsx
index 3c7520b850b4..0618a2bcc5b1 100644
--- a/src/pages/OnboardEngagement/PurposeForUsingExpensifyPage.tsx
+++ b/src/pages/OnboardEngagement/PurposeForUsingExpensifyPage.tsx
@@ -32,7 +32,7 @@ const messageCopy = {
'\n' +
'Next, start adding expenses to your workspace:\n' +
'\n' +
- '1. Click the green *+* > *Request money*.\n' +
+ '1. Click the green *+* > *Submit expense*.\n' +
'2. Add an expense or scan a receipt.\n' +
'3. Choose your workspace as the destination.\n' +
'\n' +
@@ -40,7 +40,7 @@ const messageCopy = {
[CONST.INTRO_CHOICES.SUBMIT]:
"Here's how to submit expenses for reimbursement:\n" +
'\n' +
- '1. Click the green *+* > *Request money*.\n' +
+ '1. Click the green *+* > *Submit expense*.\n' +
'2. Add an expense or scan a receipt.\n' +
"3. Enter your reimburser's email or phone number.\n" +
'\n' +
@@ -49,15 +49,15 @@ const messageCopy = {
[CONST.INTRO_CHOICES.CHAT_SPLIT]:
"Here's how to split expenses with friends:\n" +
'\n' +
- '1. Tap the green *+* > *Request money*.\n' +
+ '1. Tap the green *+* > *Split expense*.\n' +
'2. Add an expense or scan a receipt.\n' +
"3. Enter your friend's email or phone number.\n" +
- '4. Tap *Split* next to their contact info.\n' +
+ '4. Select the option.\n' +
'5. Repeat for any additional friends.\n' +
"6. Tap *Add to split* when you're done.\n" +
- '7. Review and tap *Split* to send your request(s).\n' +
+ '7. Review and tap *Split* to split your expense(s).\n' +
'\n' +
- "We'll send a money request to each of your friends and make sure you get paid back. Let me know how it goes!",
+ "We'll submit an expense to each of your friends and make sure you get paid back. Let me know how it goes!",
};
const menuIcons = {
@@ -86,7 +86,7 @@ function PurposeForUsingExpensifyModal() {
return Navigation.navigate(ROUTES.ONBOARD_MANAGE_EXPENSES);
}
- Report.completeEngagementModal(message, choice);
+ Report.completeEngagementModal(choice, message);
Report.navigateToConciergeChat(true);
}, []);
diff --git a/src/pages/OnboardingPurpose/BaseOnboardingPurpose.tsx b/src/pages/OnboardingPurpose/BaseOnboardingPurpose.tsx
index 3b82a79e6c48..82f171e12f14 100644
--- a/src/pages/OnboardingPurpose/BaseOnboardingPurpose.tsx
+++ b/src/pages/OnboardingPurpose/BaseOnboardingPurpose.tsx
@@ -83,7 +83,7 @@ function BaseOnboardingPurpose({shouldUseNativeStyles, shouldEnableMaxHeight, on
return;
}
- Report.completeEngagementModal(CONST.ONBOARDING_CONCIERGE[selectedPurpose], selectedPurpose);
+ Report.completeEngagementModal(selectedPurpose, CONST.ONBOARDING_CONCIERGE[selectedPurpose]);
Navigation.dismissModal();
// Only navigate to concierge chat when central pane is visible
diff --git a/src/pages/WorkspaceSwitcherPage.tsx b/src/pages/WorkspaceSwitcherPage.tsx
deleted file mode 100644
index f1a439548f1b..000000000000
--- a/src/pages/WorkspaceSwitcherPage.tsx
+++ /dev/null
@@ -1,291 +0,0 @@
-import React, {useCallback, useMemo, useState} from 'react';
-import {View} from 'react-native';
-import type {OnyxCollection} from 'react-native-onyx';
-import {withOnyx} from 'react-native-onyx';
-import HeaderWithBackButton from '@components/HeaderWithBackButton';
-import Icon from '@components/Icon';
-import * as Expensicons from '@components/Icon/Expensicons';
-import {MagnifyingGlass} from '@components/Icon/Expensicons';
-import OptionRow from '@components/OptionRow';
-import PressableWithFeedback from '@components/Pressable/PressableWithFeedback';
-import ScreenWrapper from '@components/ScreenWrapper';
-import SelectionList from '@components/SelectionList';
-import UserListItem from '@components/SelectionList/UserListItem';
-import Text from '@components/Text';
-import Tooltip from '@components/Tooltip';
-import useActiveWorkspace from '@hooks/useActiveWorkspace';
-import useAutoFocusInput from '@hooks/useAutoFocusInput';
-import useLocalize from '@hooks/useLocalize';
-import useNetwork from '@hooks/useNetwork';
-import useTheme from '@hooks/useTheme';
-import useThemeStyles from '@hooks/useThemeStyles';
-import interceptAnonymousUser from '@libs/interceptAnonymousUser';
-import Navigation from '@libs/Navigation/Navigation';
-import * as PolicyUtils from '@libs/PolicyUtils';
-import * as ReportUtils from '@libs/ReportUtils';
-import {getWorkspacesBrickRoads, getWorkspacesUnreadStatuses} from '@libs/WorkspacesSettingsUtils';
-import * as App from '@userActions/App';
-import CONST from '@src/CONST';
-import ONYXKEYS from '@src/ONYXKEYS';
-import type {Policy} from '@src/types/onyx';
-import {isEmptyObject} from '@src/types/utils/EmptyObject';
-import WorkspaceCardCreateAWorkspace from './workspace/card/WorkspaceCardCreateAWorkspace';
-
-type SimpleWorkspaceItem = {
- text?: string;
- policyID?: string;
- isPolicyAdmin?: boolean;
-};
-
-const sortWorkspacesBySelected = (workspace1: SimpleWorkspaceItem, workspace2: SimpleWorkspaceItem, selectedWorkspaceID: string | undefined): number => {
- if (workspace1.policyID === selectedWorkspaceID) {
- return -1;
- }
- if (workspace2.policyID === selectedWorkspaceID) {
- return 1;
- }
- return workspace1.text?.toLowerCase().localeCompare(workspace2.text?.toLowerCase() ?? '') ?? 0;
-};
-
-type WorkspaceSwitcherPageOnyxProps = {
- /** The list of this user's policies */
- policies: OnyxCollection;
-};
-
-type WorkspaceSwitcherPageProps = WorkspaceSwitcherPageOnyxProps;
-
-function WorkspaceSwitcherPage({policies}: WorkspaceSwitcherPageProps) {
- const theme = useTheme();
- const styles = useThemeStyles();
- const {isOffline} = useNetwork();
- const [searchTerm, setSearchTerm] = useState('');
- const {inputCallbackRef} = useAutoFocusInput();
- const {translate} = useLocalize();
- const {activeWorkspaceID, setActiveWorkspaceID} = useActiveWorkspace();
-
- const brickRoadsForPolicies = useMemo(() => getWorkspacesBrickRoads(), []);
- const unreadStatusesForPolicies = useMemo(() => getWorkspacesUnreadStatuses(), []);
-
- const getIndicatorTypeForPolicy = useCallback(
- (policyId?: string) => {
- if (policyId && policyId !== activeWorkspaceID) {
- return brickRoadsForPolicies[policyId];
- }
-
- if (Object.values(brickRoadsForPolicies).includes(CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR)) {
- return CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR;
- }
-
- if (Object.values(brickRoadsForPolicies).includes(CONST.BRICK_ROAD_INDICATOR_STATUS.INFO)) {
- return CONST.BRICK_ROAD_INDICATOR_STATUS.INFO;
- }
-
- return undefined;
- },
- [activeWorkspaceID, brickRoadsForPolicies],
- );
-
- const hasUnreadData = useCallback(
- // TO DO: Implement checking if policy has some unread data
- (policyId?: string) => {
- if (policyId) {
- return unreadStatusesForPolicies[policyId];
- }
-
- return Object.values(unreadStatusesForPolicies).some((status) => status);
- },
- [unreadStatusesForPolicies],
- );
-
- const selectPolicy = useCallback(
- (option?: SimpleWorkspaceItem) => {
- if (!option) {
- return;
- }
-
- const {policyID} = option;
-
- setActiveWorkspaceID(policyID);
-
- if (policyID !== activeWorkspaceID) {
- Navigation.navigateWithSwitchPolicyID({policyID});
- } else {
- Navigation.goBack();
- }
- },
- [activeWorkspaceID, setActiveWorkspaceID],
- );
-
- const usersWorkspaces = useMemo(() => {
- if (!policies || isEmptyObject(policies)) {
- return [];
- }
-
- return Object.values(policies)
- .filter((policy) => PolicyUtils.shouldShowPolicy(policy, !!isOffline))
- .map((policy) => ({
- text: policy?.name,
- policyID: policy?.id,
- brickRoadIndicator: getIndicatorTypeForPolicy(policy?.id),
- icons: [
- {
- source: policy?.avatar ? policy.avatar : ReportUtils.getDefaultWorkspaceAvatar(policy?.name),
- fallbackIcon: Expensicons.FallbackWorkspaceAvatar,
- name: policy?.name,
- type: CONST.ICON_TYPE_WORKSPACE,
- },
- ],
- boldStyle: hasUnreadData(policy?.id),
- keyForList: policy?.id,
- isPolicyAdmin: PolicyUtils.isPolicyAdmin(policy),
- isSelected: policy?.id === activeWorkspaceID,
- }));
- }, [policies, getIndicatorTypeForPolicy, hasUnreadData, isOffline, activeWorkspaceID]);
-
- const filteredAndSortedUserWorkspaces = useMemo(
- () =>
- usersWorkspaces
- .filter((policy) => policy.text?.toLowerCase().includes(searchTerm?.toLowerCase() ?? ''))
- .sort((policy1, policy2) => sortWorkspacesBySelected(policy1, policy2, activeWorkspaceID)),
- [searchTerm, usersWorkspaces, activeWorkspaceID],
- );
-
- const usersWorkspacesSectionData = useMemo(
- () => ({
- data: filteredAndSortedUserWorkspaces,
- shouldShow: true,
- }),
- [filteredAndSortedUserWorkspaces],
- );
-
- const everythingSection = useMemo(() => {
- const option = {
- reportID: '',
- text: CONST.WORKSPACE_SWITCHER.NAME,
- icons: [
- {
- source: Expensicons.ExpensifyAppIcon,
- name: CONST.WORKSPACE_SWITCHER.NAME,
- type: CONST.ICON_TYPE_AVATAR,
- },
- ],
- brickRoadIndicator: getIndicatorTypeForPolicy(undefined),
- boldStyle: hasUnreadData(undefined),
- };
-
- return (
- <>
-
-
- {translate('workspace.switcher.everythingSection')}
-
-
-
-
-
- >
- );
- }, [activeWorkspaceID, getIndicatorTypeForPolicy, hasUnreadData, selectPolicy, styles, theme.textSupporting, translate]);
-
- const headerMessage = filteredAndSortedUserWorkspaces.length === 0 ? translate('common.noResultsFound') : '';
-
- const workspacesSection = useMemo(
- () => (
- <>
- 0 ? [styles.mb1] : [styles.mb3])]}>
-
-
- {translate('common.workspaces')}
-
-
-
- {
- Navigation.goBack();
- interceptAnonymousUser(() => App.createWorkspaceWithPolicyDraftAndNavigateToIt());
- }}
- >
- {({hovered}) => (
-
- )}
-
-
-
-
- {usersWorkspaces.length > 0 ? (
- = CONST.WORKSPACE_SWITCHER.MINIMUM_WORKSPACES_TO_SHOW_SEARCH ? MagnifyingGlass : undefined}
- initiallyFocusedOptionKey={activeWorkspaceID}
- textInputAutoFocus={false}
- />
- ) : (
-
- )}
- >
- ),
- [
- inputCallbackRef,
- setSearchTerm,
- searchTerm,
- selectPolicy,
- styles,
- theme.textSupporting,
- translate,
- usersWorkspaces.length,
- usersWorkspacesSectionData,
- activeWorkspaceID,
- theme.icon,
- headerMessage,
- ],
- );
-
- return (
-
-
- {everythingSection}
- {workspacesSection}
-
- );
-}
-
-WorkspaceSwitcherPage.displayName = 'WorkspaceSwitcherPage';
-
-export default withOnyx({
- policies: {
- key: ONYXKEYS.COLLECTION.POLICY,
- },
-})(WorkspaceSwitcherPage);
diff --git a/src/pages/WorkspaceSwitcherPage/WorkspacesSectionHeader.tsx b/src/pages/WorkspaceSwitcherPage/WorkspacesSectionHeader.tsx
new file mode 100644
index 000000000000..85e13ba4c0a5
--- /dev/null
+++ b/src/pages/WorkspaceSwitcherPage/WorkspacesSectionHeader.tsx
@@ -0,0 +1,55 @@
+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';
+import Text from '@components/Text';
+import Tooltip from '@components/Tooltip';
+import useLocalize from '@hooks/useLocalize';
+import useTheme from '@hooks/useTheme';
+import useThemeStyles from '@hooks/useThemeStyles';
+import interceptAnonymousUser from '@libs/interceptAnonymousUser';
+import Navigation from '@libs/Navigation/Navigation';
+import * as App from '@userActions/App';
+import CONST from '@src/CONST';
+
+function WorkspacesSectionHeader() {
+ const theme = useTheme();
+ const styles = useThemeStyles();
+ const {translate} = useLocalize();
+
+ return (
+
+
+
+ {translate('common.workspaces')}
+
+
+
+ {
+ Navigation.goBack();
+ interceptAnonymousUser(() => App.createWorkspaceWithPolicyDraftAndNavigateToIt());
+ }}
+ >
+ {({hovered}) => (
+
+ )}
+
+
+
+ );
+}
+
+export default WorkspacesSectionHeader;
diff --git a/src/pages/WorkspaceSwitcherPage/index.tsx b/src/pages/WorkspaceSwitcherPage/index.tsx
new file mode 100644
index 000000000000..489c9566d6c7
--- /dev/null
+++ b/src/pages/WorkspaceSwitcherPage/index.tsx
@@ -0,0 +1,208 @@
+import React, {useCallback, useMemo} from 'react';
+import type {OnyxCollection} from 'react-native-onyx';
+import {withOnyx} from 'react-native-onyx';
+import HeaderWithBackButton from '@components/HeaderWithBackButton';
+import * as Expensicons from '@components/Icon/Expensicons';
+import ScreenWrapper from '@components/ScreenWrapper';
+import SelectionList from '@components/SelectionList';
+import type {ListItem, SectionListDataType} from '@components/SelectionList/types';
+import UserListItem from '@components/SelectionList/UserListItem';
+import useActiveWorkspace from '@hooks/useActiveWorkspace';
+import useDebouncedState from '@hooks/useDebouncedState';
+import useLocalize from '@hooks/useLocalize';
+import useNetwork from '@hooks/useNetwork';
+import Navigation from '@libs/Navigation/Navigation';
+import * as PolicyUtils from '@libs/PolicyUtils';
+import * as ReportUtils from '@libs/ReportUtils';
+import {getWorkspacesBrickRoads, getWorkspacesUnreadStatuses} from '@libs/WorkspacesSettingsUtils';
+import type {BrickRoad} from '@libs/WorkspacesSettingsUtils';
+import WorkspaceCardCreateAWorkspace from '@pages/workspace/card/WorkspaceCardCreateAWorkspace';
+import CONST from '@src/CONST';
+import ONYXKEYS from '@src/ONYXKEYS';
+import type {Policy} from '@src/types/onyx';
+import {isEmptyObject} from '@src/types/utils/EmptyObject';
+import WorkspacesSectionHeader from './WorkspacesSectionHeader';
+
+type WorkspaceListItem = {
+ text: string;
+ policyID: string;
+ isPolicyAdmin?: boolean;
+ brickRoadIndicator?: BrickRoad;
+} & ListItem;
+
+const sortWorkspacesBySelected = (workspace1: WorkspaceListItem, workspace2: WorkspaceListItem, selectedWorkspaceID: string | undefined): number => {
+ if (workspace1.policyID === selectedWorkspaceID) {
+ return -1;
+ }
+ if (workspace2.policyID === selectedWorkspaceID) {
+ return 1;
+ }
+ return workspace1.text?.toLowerCase().localeCompare(workspace2.text?.toLowerCase() ?? '') ?? 0;
+};
+
+type WorkspaceSwitcherPageOnyxProps = {
+ /** The list of this user's policies */
+ policies: OnyxCollection;
+};
+
+type WorkspaceSwitcherPageProps = WorkspaceSwitcherPageOnyxProps;
+
+const WorkspaceCardCreateAWorkspaceInstance = ;
+
+function WorkspaceSwitcherPage({policies}: WorkspaceSwitcherPageProps) {
+ const {isOffline} = useNetwork();
+ const [searchTerm, debouncedSearchTerm, setSearchTerm] = useDebouncedState('');
+ const {translate} = useLocalize();
+ const {activeWorkspaceID, setActiveWorkspaceID} = useActiveWorkspace();
+
+ const brickRoadsForPolicies = useMemo(() => getWorkspacesBrickRoads(), []);
+ const unreadStatusesForPolicies = useMemo(() => getWorkspacesUnreadStatuses(), []);
+
+ const getIndicatorTypeForPolicy = useCallback(
+ (policyId?: string) => {
+ if (policyId && policyId !== activeWorkspaceID) {
+ return brickRoadsForPolicies[policyId];
+ }
+
+ if (Object.values(brickRoadsForPolicies).includes(CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR)) {
+ return CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR;
+ }
+
+ if (Object.values(brickRoadsForPolicies).includes(CONST.BRICK_ROAD_INDICATOR_STATUS.INFO)) {
+ return CONST.BRICK_ROAD_INDICATOR_STATUS.INFO;
+ }
+
+ return undefined;
+ },
+ [activeWorkspaceID, brickRoadsForPolicies],
+ );
+
+ const hasUnreadData = useCallback(
+ // TO DO: Implement checking if policy has some unread data
+ (policyId?: string) => {
+ if (policyId) {
+ return unreadStatusesForPolicies[policyId];
+ }
+
+ return Object.values(unreadStatusesForPolicies).some((status) => status);
+ },
+ [unreadStatusesForPolicies],
+ );
+
+ const selectPolicy = useCallback(
+ (option?: WorkspaceListItem) => {
+ if (!option) {
+ return;
+ }
+
+ const {policyID} = option;
+
+ setActiveWorkspaceID(policyID);
+ Navigation.goBack();
+ if (policyID !== activeWorkspaceID) {
+ Navigation.navigateWithSwitchPolicyID({policyID});
+ }
+ },
+ [activeWorkspaceID, setActiveWorkspaceID],
+ );
+
+ const usersWorkspaces = useMemo(() => {
+ if (!policies || isEmptyObject(policies)) {
+ return [];
+ }
+
+ return Object.values(policies)
+ .filter((policy) => PolicyUtils.shouldShowPolicy(policy, !!isOffline))
+ .map((policy) => ({
+ text: policy?.name ?? '',
+ policyID: policy?.id ?? '',
+ brickRoadIndicator: getIndicatorTypeForPolicy(policy?.id),
+ icons: [
+ {
+ source: policy?.avatar ? policy.avatar : ReportUtils.getDefaultWorkspaceAvatar(policy?.name),
+ fallbackIcon: Expensicons.FallbackWorkspaceAvatar,
+ name: policy?.name,
+ type: CONST.ICON_TYPE_WORKSPACE,
+ },
+ ],
+ boldStyle: hasUnreadData(policy?.id),
+ keyForList: policy?.id,
+ isPolicyAdmin: PolicyUtils.isPolicyAdmin(policy),
+ isSelected: activeWorkspaceID === policy?.id,
+ }));
+ }, [policies, isOffline, getIndicatorTypeForPolicy, hasUnreadData, activeWorkspaceID]);
+
+ const filteredAndSortedUserWorkspaces = useMemo(
+ () =>
+ usersWorkspaces
+ .filter((policy) => policy.text?.toLowerCase().includes(debouncedSearchTerm?.toLowerCase() ?? ''))
+ .sort((policy1, policy2) => sortWorkspacesBySelected(policy1, policy2, activeWorkspaceID)),
+ [debouncedSearchTerm, usersWorkspaces, activeWorkspaceID],
+ );
+
+ const sections = useMemo(() => {
+ const options: Array> = [
+ {
+ title: translate('workspace.switcher.everythingSection'),
+ shouldShow: true,
+ indexOffset: 0,
+ data: [
+ {
+ text: CONST.WORKSPACE_SWITCHER.NAME,
+ policyID: '',
+ icons: [{source: Expensicons.ExpensifyAppIcon, name: CONST.WORKSPACE_SWITCHER.NAME, type: CONST.ICON_TYPE_AVATAR}],
+ brickRoadIndicator: getIndicatorTypeForPolicy(undefined),
+ isSelected: activeWorkspaceID === undefined,
+ keyForList: CONST.WORKSPACE_SWITCHER.NAME,
+ },
+ ],
+ },
+ ];
+ options.push({
+ CustomSectionHeader: WorkspacesSectionHeader,
+ data: filteredAndSortedUserWorkspaces,
+ shouldShow: true,
+ indexOffset: 1,
+ });
+ return options;
+ }, [activeWorkspaceID, filteredAndSortedUserWorkspaces, getIndicatorTypeForPolicy, translate]);
+
+ const headerMessage = filteredAndSortedUserWorkspaces.length === 0 && usersWorkspaces.length ? translate('common.noResultsFound') : '';
+ const shouldShowCreateWorkspace = usersWorkspaces.length === 0;
+
+ return (
+
+ {({didScreenTransitionEnd}) => (
+ <>
+
+
+ ListItem={UserListItem}
+ sections={didScreenTransitionEnd ? sections : CONST.EMPTY_ARRAY}
+ onSelectRow={selectPolicy}
+ textInputLabel={usersWorkspaces.length >= CONST.WORKSPACE_SWITCHER.MINIMUM_WORKSPACES_TO_SHOW_SEARCH ? translate('common.search') : undefined}
+ textInputValue={searchTerm}
+ onChangeText={setSearchTerm}
+ headerMessage={headerMessage}
+ listFooterContent={shouldShowCreateWorkspace ? WorkspaceCardCreateAWorkspaceInstance : null}
+ initiallyFocusedOptionKey={activeWorkspaceID ?? CONST.WORKSPACE_SWITCHER.NAME}
+ showLoadingPlaceholder
+ />
+ >
+ )}
+
+ );
+}
+
+WorkspaceSwitcherPage.displayName = 'WorkspaceSwitcherPage';
+
+export default withOnyx({
+ policies: {
+ key: ONYXKEYS.COLLECTION.POLICY,
+ },
+})(WorkspaceSwitcherPage);
diff --git a/src/pages/home/HeaderView.tsx b/src/pages/home/HeaderView.tsx
index 56828bce7847..0ccaf4d65530 100644
--- a/src/pages/home/HeaderView.tsx
+++ b/src/pages/home/HeaderView.tsx
@@ -63,7 +63,7 @@ type HeaderViewProps = HeaderViewOnyxProps & {
/** The report action the transaction is tied to from the parent report */
parentReportAction: OnyxEntry;
- /** The reportID of the request */
+ /** The reportID of the current report */
reportID: string;
};
diff --git a/src/pages/home/ReportScreen.tsx b/src/pages/home/ReportScreen.tsx
index 332e9b080558..cdd843f65fb3 100644
--- a/src/pages/home/ReportScreen.tsx
+++ b/src/pages/home/ReportScreen.tsx
@@ -22,6 +22,7 @@ import type {CurrentReportIDContextValue} from '@components/withCurrentReportID'
import withCurrentReportID from '@components/withCurrentReportID';
import useAppFocusEvent from '@hooks/useAppFocusEvent';
import useLocalize from '@hooks/useLocalize';
+import useNetwork from '@hooks/useNetwork';
import usePrevious from '@hooks/usePrevious';
import useThemeStyles from '@hooks/useThemeStyles';
import useViewportOffsetTop from '@hooks/useViewportOffsetTop';
@@ -157,6 +158,7 @@ function ReportScreen({
const firstRenderRef = useRef(true);
const flatListRef = useRef(null);
const reactionListRef = useRef(null);
+ const {isOffline} = useNetwork();
/**
* Create a lightweight Report so as to keep the re-rendering as light as possible by
* passing in only the required props.
@@ -334,7 +336,10 @@ function ReportScreen({
);
}
- const transactionThreadReportID = useMemo(() => ReportActionsUtils.getOneTransactionThreadReportID(report.reportID, reportActions ?? []), [report.reportID, reportActions]);
+ const transactionThreadReportID = useMemo(
+ () => ReportActionsUtils.getOneTransactionThreadReportID(report.reportID, reportActions ?? [], isOffline),
+ [report.reportID, reportActions, isOffline],
+ );
useEffect(() => {
if (!transactionThreadReportID || !route.params.reportActionID) {
@@ -363,7 +368,7 @@ function ReportScreen({
return reportIDFromRoute !== '' && !!report.reportID && !isTransitioning;
}, [report, reportIDFromRoute]);
- const isLoading = !ReportUtils.isValidReportIDFromPath(reportIDFromRoute) || !isSidebarLoaded || PersonalDetailsUtils.isPersonalDetailsEmpty();
+ const isLoading = !reportIDFromRoute || !isSidebarLoaded || PersonalDetailsUtils.isPersonalDetailsEmpty();
const shouldShowSkeleton =
!isLinkedMessageAvailable &&
(isLinkingToMessage ||
@@ -372,19 +377,14 @@ function ReportScreen({
isLoading ||
(!!reportActionIDFromRoute && reportMetadata?.isLoadingInitialReportActions));
const shouldShowReportActionList = isCurrentReportLoadedFromOnyx && !isLoading;
+ const currentReportIDFormRoute = route.params?.reportID;
// eslint-disable-next-line rulesdir/no-negated-variables
const shouldShowNotFoundPage = useMemo(
(): boolean =>
- !shouldShowSkeleton &&
- ((!wasReportAccessibleRef.current &&
- !firstRenderRef.current &&
- !report.reportID &&
- !isOptimisticDelete &&
- !reportMetadata?.isLoadingInitialReportActions &&
- !userLeavingStatus) ||
- shouldHideReport ||
- (!!reportIDFromRoute && !ReportUtils.isValidReportIDFromPath(reportIDFromRoute))),
- [shouldShowSkeleton, report.reportID, isOptimisticDelete, reportMetadata?.isLoadingInitialReportActions, userLeavingStatus, shouldHideReport, reportIDFromRoute],
+ (!wasReportAccessibleRef.current && !firstRenderRef.current && !report.reportID && !isOptimisticDelete && !reportMetadata?.isLoadingInitialReportActions && !userLeavingStatus) ||
+ shouldHideReport ||
+ (!!currentReportIDFormRoute && !ReportUtils.isValidReportIDFromPath(currentReportIDFormRoute)),
+ [report.reportID, isOptimisticDelete, reportMetadata?.isLoadingInitialReportActions, userLeavingStatus, shouldHideReport, currentReportIDFormRoute],
);
const fetchReport = useCallback(() => {
@@ -514,7 +514,7 @@ function ReportScreen({
Navigation.goBack(undefined, false, true);
}
if (prevReport.parentReportID) {
- // Prevent navigation to the Money Request Report if it is pending deletion.
+ // Prevent navigation to the IOU/Expense Report if it is pending deletion.
const parentReport = ReportUtils.getReport(prevReport.parentReportID);
if (ReportUtils.isMoneyRequestReportPendingDeletion(parentReport)) {
return;
diff --git a/src/pages/home/report/ContextMenu/ContextMenuActions.tsx b/src/pages/home/report/ContextMenu/ContextMenuActions.tsx
index 4f9d5e2788e6..d4497b983465 100644
--- a/src/pages/home/report/ContextMenu/ContextMenuActions.tsx
+++ b/src/pages/home/report/ContextMenu/ContextMenuActions.tsx
@@ -387,9 +387,9 @@ const ContextMenuActions: ContextMenuAction[] = [
} else if (ReportActionsUtils.isActionableTrackExpense(reportAction)) {
setClipboardMessage('What would you like to do with this expense?');
} else if (reportAction?.actionName === CONST.REPORT.ACTIONS.TYPE.HOLD) {
- Clipboard.setString(Localize.translateLocal('iou.heldRequest'));
+ Clipboard.setString(Localize.translateLocal('iou.heldExpense'));
} else if (reportAction?.actionName === CONST.REPORT.ACTIONS.TYPE.UNHOLD) {
- Clipboard.setString(Localize.translateLocal('iou.unheldRequest'));
+ Clipboard.setString(Localize.translateLocal('iou.unheldExpense'));
} else if (content) {
setClipboardMessage(
content.replace(/()(.*?)(<\/mention-user>)/gi, (match, openTag, innerContent, closeTag): string => {
diff --git a/src/pages/home/report/ReportActionCompose/AttachmentPickerWithMenuItems.tsx b/src/pages/home/report/ReportActionCompose/AttachmentPickerWithMenuItems.tsx
index 11a84c17f9ee..ab24c6667ad4 100644
--- a/src/pages/home/report/ReportActionCompose/AttachmentPickerWithMenuItems.tsx
+++ b/src/pages/home/report/ReportActionCompose/AttachmentPickerWithMenuItems.tsx
@@ -125,17 +125,17 @@ function AttachmentPickerWithMenuItems({
const options: MoneyRequestOptions = {
[CONST.IOU.TYPE.SPLIT]: {
icon: Expensicons.Receipt,
- text: translate('iou.splitBill'),
+ text: translate('iou.splitExpense'),
onSelected: () => IOU.startMoneyRequest(CONST.IOU.TYPE.SPLIT, report?.reportID ?? ''),
},
[CONST.IOU.TYPE.REQUEST]: {
icon: Expensicons.MoneyCircle,
- text: translate('iou.requestMoney'),
+ text: translate('iou.submitExpense'),
onSelected: () => IOU.startMoneyRequest(CONST.IOU.TYPE.REQUEST, report?.reportID ?? ''),
},
[CONST.IOU.TYPE.SEND]: {
icon: Expensicons.Send,
- text: translate('iou.sendMoney'),
+ text: translate('iou.paySomeone', {name: ReportUtils.getPayeeName(report)}),
onSelected: () => IOU.startMoneyRequest(CONST.IOU.TYPE.SEND, report?.reportID ?? ''),
},
[CONST.IOU.TYPE.TRACK_EXPENSE]: {
diff --git a/src/pages/home/report/ReportActionItem.tsx b/src/pages/home/report/ReportActionItem.tsx
index a23f3bab8dc8..4b87ca6e7eea 100644
--- a/src/pages/home/report/ReportActionItem.tsx
+++ b/src/pages/home/report/ReportActionItem.tsx
@@ -390,8 +390,8 @@ function ReportActionItem({
const transactionID = (action?.originalMessage as OriginalMessageActionableTrackedExpenseWhisper['originalMessage'])?.transactionID;
return [
{
- text: 'actionableMentionTrackExpense.request',
- key: `${action.reportActionID}-actionableMentionTrackExpense-request`,
+ text: 'actionableMentionTrackExpense.submit',
+ key: `${action.reportActionID}-actionableMentionTrackExpense-submit`,
onPress: () => {
ReportUtils.createDraftTransactionAndNavigateToParticipantSelector(transactionID, report.reportID, CONST.IOU.ACTION.MOVE, action.reportActionID);
},
@@ -464,7 +464,7 @@ function ReportActionItem({
const renderItemContent = (hovered = false, isWhisper = false, hasErrors = false): React.JSX.Element => {
let children;
- // Show the MoneyRequestPreview for when request was created, bill was split or money was sent
+ // Show the MoneyRequestPreview for when expense is present
if (
isIOUReport(action) &&
action.originalMessage &&
@@ -478,7 +478,7 @@ function ReportActionItem({
const iouReportID = action.originalMessage.IOUReportID ? action.originalMessage.IOUReportID.toString() : '0';
children = (
;
} else if (action.actionName === CONST.REPORT.ACTIONS.TYPE.HOLD) {
- children = ;
+ children = ;
} else if (action.actionName === CONST.REPORT.ACTIONS.TYPE.HOLDCOMMENT) {
children = ;
} else if (action.actionName === CONST.REPORT.ACTIONS.TYPE.UNHOLD) {
- children = ;
+ children = ;
} else {
const hasBeenFlagged =
![CONST.MODERATION.MODERATOR_DECISION_APPROVED, CONST.MODERATION.MODERATOR_DECISION_PENDING].some((item) => item === moderationDecision) &&
@@ -736,10 +736,8 @@ function ReportActionItem({
let message: TranslationPaths;
if (isReversedTransaction) {
message = 'parentReportAction.reversedTransaction';
- } else if (ReportActionsUtils.isTrackExpenseAction(parentReportAction)) {
- message = 'parentReportAction.deletedExpense';
} else {
- message = 'parentReportAction.deletedRequest';
+ message = 'parentReportAction.deletedExpense';
}
return (
@@ -851,7 +849,7 @@ function ReportActionItem({
);
}
- // For the `pay` IOU action on non-send money flow, we don't want to render anything if `isWaitingOnBankAccount` is true
+ // For the `pay` IOU action on non-pay expense flow, we don't want to render anything if `isWaitingOnBankAccount` is true
// Otherwise, we will see two system messages informing the payee needs to add a bank account or wallet
if (isIOUReport(action) && !!report?.isWaitingOnBankAccount && action.originalMessage.type === CONST.IOU.REPORT_ACTION_TYPE.PAY && !isSendingMoney) {
return null;
diff --git a/src/pages/home/report/ReportActionItemParentAction.tsx b/src/pages/home/report/ReportActionItemParentAction.tsx
index 6c225421e356..a4592075aa0c 100644
--- a/src/pages/home/report/ReportActionItemParentAction.tsx
+++ b/src/pages/home/report/ReportActionItemParentAction.tsx
@@ -108,9 +108,12 @@ function ReportActionItemParentAction({
{
const isVisibleAction = ReportActionsUtils.shouldReportActionBeVisible(ancestor.reportAction, ancestor.reportAction.reportActionID ?? '');
- Navigation.goBack(
- ROUTES.REPORT_WITH_ID.getRoute(ancestor.report.parentReportID ?? '', isVisibleAction && !isOffline ? ancestor.reportAction.reportActionID : undefined),
- );
+ // Pop the thread report screen before navigating to the chat report.
+ Navigation.goBack(ROUTES.REPORT_WITH_ID.getRoute(ancestor.report.parentReportID ?? ''));
+ if (isVisibleAction && !isOffline) {
+ // Pop the chat report screen before navigating to the linked report action.
+ Navigation.goBack(ROUTES.REPORT_WITH_ID.getRoute(ancestor.report.parentReportID ?? '', ancestor.reportAction.reportActionID));
+ }
}}
parentReportAction={parentReportAction}
report={ancestor.report}
diff --git a/src/pages/home/report/ReportActionItemSingle.tsx b/src/pages/home/report/ReportActionItemSingle.tsx
index 1e0dc432b3fc..4e4ebb2a017d 100644
--- a/src/pages/home/report/ReportActionItemSingle.tsx
+++ b/src/pages/home/report/ReportActionItemSingle.tsx
@@ -106,7 +106,7 @@ function ReportActionItemSingle({
let secondaryAvatar: Icon;
const primaryDisplayName = displayName;
if (displayAllActors) {
- // The ownerAccountID and actorAccountID can be the same if the a user requests money back from the IOU's original creator, in that case we need to use managerID to avoid displaying the same user twice
+ // The ownerAccountID and actorAccountID can be the same if the a user submits an expense back from the IOU's original creator, in that case we need to use managerID to avoid displaying the same user twice
const secondaryAccountId = iouReport?.ownerAccountID === actorAccountID ? iouReport?.managerID : iouReport?.ownerAccountID;
const secondaryUserAvatar = personalDetails?.[secondaryAccountId ?? -1]?.avatar ?? '';
const secondaryDisplayName = ReportUtils.getDisplayNameForParticipant(secondaryAccountId);
diff --git a/src/pages/home/report/ReportActionsView.tsx b/src/pages/home/report/ReportActionsView.tsx
index 392b1debc960..1af93186d133 100755
--- a/src/pages/home/report/ReportActionsView.tsx
+++ b/src/pages/home/report/ReportActionsView.tsx
@@ -145,7 +145,7 @@ function ReportActionsView({
// Filter out the created action from the transaction thread report actions, since we already have the parent report's created action in `reportActions`
const filteredTransactionThreadReportActions = transactionThreadReportActions?.filter((action) => action.actionName !== CONST.REPORT.ACTIONS.TYPE.CREATED);
- // Filter out the money request actions because we don't want to show any preview actions for one-transaction reports
+ // Filter out the expense actions because we don't want to show any preview actions for one-transaction reports
const filteredReportActions = [...allReportActions, ...filteredTransactionThreadReportActions].filter((action) => {
const actionType = (action as OnyxTypes.OriginalMessageIOU).originalMessage?.type ?? '';
return actionType !== CONST.IOU.REPORT_ACTION_TYPE.CREATE && actionType !== CONST.IOU.REPORT_ACTION_TYPE.TRACK && !ReportActionsUtils.isSentMoneyReportAction(action);
@@ -420,11 +420,11 @@ function ReportActionsView({
};
}, [isTheFirstReportActionIsLinked]);
- // When we are offline before opening a money request report,
- // the total of the report and sometimes the money request aren't displayed because these actions aren't returned until `OpenReport` API is complete.
+ // When we are offline before opening an IOU/Expense report,
+ // the total of the report and sometimes the expense aren't displayed because these actions aren't returned until `OpenReport` API is complete.
// We generate a fake created action here if it doesn't exist to display the total whenever possible because the total just depends on report data
- // and we also generate a money request action if the number of money requests in reportActions is less than the total number of money requests
- // to display at least one money request action to match the total data.
+ // and we also generate an expense action if the number of expenses in reportActions is less than the total number of expenses
+ // to display at least one expense action to match the total data.
const reportActionsToDisplay = useMemo(() => {
if (!ReportUtils.isMoneyRequestReport(report) || !reportActions.length) {
return reportActions;
diff --git a/src/pages/home/report/ReportFooter.tsx b/src/pages/home/report/ReportFooter.tsx
index bd143f9ef196..04fbd0308390 100644
--- a/src/pages/home/report/ReportFooter.tsx
+++ b/src/pages/home/report/ReportFooter.tsx
@@ -89,10 +89,10 @@ function ReportFooter({
/**
* Matching task rule by group
* Group 1: Start task rule with []
- * Group 2: Optional email group between \s+....\s* start rule with @+valid email
+ * Group 2: Optional email group between \s+....\s* start rule with @+valid email or short mention
* Group 3: Title is remaining characters
*/
- const taskRegex = /^\[\]\s+(?:@([^\s@]+@[\w.-]+\.[a-zA-Z]{2,}))?\s*([\s\S]*)/;
+ const taskRegex = /^\[\]\s+(?:@([^\s@]+(?:@\w+\.\w+)?))?\s*([\s\S]*)/;
const match = text.match(taskRegex);
if (!match) {
@@ -102,10 +102,13 @@ function ReportFooter({
if (!title) {
return false;
}
- const email = match[1] ? match[1].trim() : undefined;
+
+ const mention = match[1] ? match[1].trim() : undefined;
+ const mentionWithDomain = ReportUtils.addDomainToShortMention(mention ?? '') ?? mention;
+
let assignee: OnyxTypes.PersonalDetails | EmptyObject = {};
- if (email) {
- assignee = Object.values(allPersonalDetails).find((value) => value?.login === email) ?? {};
+ if (mentionWithDomain) {
+ assignee = Object.values(allPersonalDetails).find((value) => value?.login === mentionWithDomain) ?? {};
}
Task.createTaskAndNavigate(report.reportID, title, '', assignee?.login ?? '', assignee.accountID, undefined, report.policyID);
return true;
diff --git a/src/pages/home/report/ThreadDivider.tsx b/src/pages/home/report/ThreadDivider.tsx
index f8acf63832b7..edbe2c665752 100644
--- a/src/pages/home/report/ThreadDivider.tsx
+++ b/src/pages/home/report/ThreadDivider.tsx
@@ -31,7 +31,12 @@ function ThreadDivider({ancestor}: ThreadDividerProps) {
{
const isVisibleAction = ReportActionsUtils.shouldReportActionBeVisible(ancestor.reportAction, ancestor.reportAction.reportActionID ?? '');
- Navigation.goBack(ROUTES.REPORT_WITH_ID.getRoute(ancestor.report.parentReportID ?? '', isVisibleAction && !isOffline ? ancestor.reportAction.reportActionID : undefined));
+ // Pop the thread report screen before navigating to the chat report.
+ Navigation.goBack(ROUTES.REPORT_WITH_ID.getRoute(ancestor.report.parentReportID ?? ''));
+ if (isVisibleAction && !isOffline) {
+ // Pop the chat report screen before navigating to the linked report action.
+ Navigation.goBack(ROUTES.REPORT_WITH_ID.getRoute(ancestor.report.parentReportID ?? '', ancestor.reportAction.reportActionID));
+ }
}}
accessibilityLabel={translate('threads.thread')}
role={CONST.ROLE.BUTTON}
diff --git a/src/pages/home/sidebar/SidebarScreen/FloatingActionButtonAndPopover.tsx b/src/pages/home/sidebar/SidebarScreen/FloatingActionButtonAndPopover.tsx
index 212bf93166a1..b55fecf48ffb 100644
--- a/src/pages/home/sidebar/SidebarScreen/FloatingActionButtonAndPopover.tsx
+++ b/src/pages/home/sidebar/SidebarScreen/FloatingActionButtonAndPopover.tsx
@@ -294,12 +294,12 @@ function FloatingActionButtonAndPopover(
: []),
{
icon: Expensicons.MoneyCircle,
- text: translate('iou.requestMoney'),
+ text: translate('iou.submitExpense'),
onSelected: () =>
interceptAnonymousUser(() =>
IOU.startMoneyRequest(
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
+ // When starting to create an expense 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.
ReportUtils.generateReportID(),
),
@@ -307,12 +307,12 @@ function FloatingActionButtonAndPopover(
},
{
icon: Expensicons.Send,
- text: translate('iou.sendMoney'),
+ text: translate('iou.paySomeone', {}),
onSelected: () =>
interceptAnonymousUser(() =>
IOU.startMoneyRequest(
CONST.IOU.TYPE.SEND,
- // When starting to create a send money request from the global FAB, there is not an existing report yet. A random optimistic reportID is generated and used
+ // When starting to pay someone 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.
ReportUtils.generateReportID(),
),
diff --git a/src/pages/iou/HoldReasonPage.tsx b/src/pages/iou/HoldReasonPage.tsx
index 2a5cba810759..8f016ec0d8d9 100644
--- a/src/pages/iou/HoldReasonPage.tsx
+++ b/src/pages/iou/HoldReasonPage.tsx
@@ -91,12 +91,12 @@ function HoldReasonPage({route}: HoldReasonPageProps) {
testID={HoldReasonPage.displayName}
>
reportActions?.[route.params.reportActionID] ?? ({} as ReportAction), [reportActions, route.params.reportActionID]);
const participantAccountIDs = reportAction?.actionName === CONST.REPORT.ACTIONS.TYPE.IOU ? reportAction?.originalMessage.participantAccountIDs ?? [] : [];
- // In case this is workspace split bill, we manually add the workspace as the second participant of the split bill
+ // In case this is workspace split expense, we manually add the workspace as the second participant of the split expense
// because we don't save any accountID in the report action's originalMessage other than the payee's accountID
let participants: Array;
if (ReportUtils.isPolicyExpenseChat(report)) {
diff --git a/src/pages/iou/propTypes/index.js b/src/pages/iou/propTypes/index.js
index a03ed65dda9c..46abafac23e4 100644
--- a/src/pages/iou/propTypes/index.js
+++ b/src/pages/iou/propTypes/index.js
@@ -3,16 +3,16 @@ import participantPropTypes from '@components/participantPropTypes';
import CONST from '@src/CONST';
const iouPropTypes = PropTypes.shape({
- /** ID (iouType + reportID) of the request */
+ /** ID (iouType + reportID) of the expense */
id: PropTypes.string,
- /** Amount of the request */
+ /** Amount of the expense */
amount: PropTypes.number,
- /** Currency of the request */
+ /** Currency of the expense */
currency: PropTypes.string,
- /** Description of the request */
+ /** Description of the expense */
comment: PropTypes.string,
/** The merchant name */
@@ -21,16 +21,16 @@ const iouPropTypes = PropTypes.shape({
/** The category name */
category: PropTypes.string,
- /** Whether the request is billable */
+ /** Whether the expense is billable */
billable: PropTypes.bool,
/** The tag */
tag: PropTypes.string,
- /** Date that the request was created */
+ /** Date that the expense was created */
created: PropTypes.string,
- /** The path to an image of the receipt attached to the request */
+ /** The path to an image of the receipt attached to the expense */
receiptPath: PropTypes.string,
/** List of the participants */
diff --git a/src/pages/iou/request/IOURequestStartPage.tsx b/src/pages/iou/request/IOURequestStartPage.tsx
index 6c69598893c5..8d64598ed838 100644
--- a/src/pages/iou/request/IOURequestStartPage.tsx
+++ b/src/pages/iou/request/IOURequestStartPage.tsx
@@ -63,9 +63,9 @@ function IOURequestStartPage({
const navigation = useNavigation();
const [isDraggingOver, setIsDraggingOver] = useState(false);
const tabTitles = {
- [CONST.IOU.TYPE.REQUEST]: translate('iou.requestMoney'),
- [CONST.IOU.TYPE.SEND]: translate('iou.sendMoney'),
- [CONST.IOU.TYPE.SPLIT]: translate('iou.splitBill'),
+ [CONST.IOU.TYPE.REQUEST]: translate('iou.submitExpense'),
+ [CONST.IOU.TYPE.SEND]: translate('iou.paySomeone', {name: ReportUtils.getPayeeName(report)}),
+ [CONST.IOU.TYPE.SPLIT]: translate('iou.splitExpense'),
[CONST.IOU.TYPE.TRACK_EXPENSE]: translate('iou.trackExpense'),
};
const transactionRequestType = useRef(TransactionUtils.getRequestType(transaction));
@@ -88,7 +88,7 @@ function IOURequestStartPage({
}, []),
);
- // Clear out the temporary money request if the reportID in the URL has changed from the transaction's reportID
+ // Clear out the temporary expense if the reportID in the URL has changed from the transaction's reportID
useEffect(() => {
if (transaction?.reportID === reportID) {
return;
@@ -100,7 +100,7 @@ function IOURequestStartPage({
const isExpenseReport = ReportUtils.isExpenseReport(report);
const shouldDisplayDistanceRequest = !!canUseP2PDistanceRequests || isExpenseChat || isExpenseReport || isFromGlobalCreate;
- // Allow the user to create the request if we are creating the request in global menu or the report can create the request
+ // Allow the user to submit the expense if we are submitting the expense in global menu or the report can create the exoense
const isAllowedToCreateRequest = isEmptyObject(report?.reportID) || ReportUtils.canCreateRequest(report, policy, iouType);
const navigateBack = () => {
diff --git a/src/pages/iou/request/MoneyTemporaryForRefactorRequestParticipantsSelector.js b/src/pages/iou/request/MoneyTemporaryForRefactorRequestParticipantsSelector.js
index 87883972f84f..95cb043122d4 100644
--- a/src/pages/iou/request/MoneyTemporaryForRefactorRequestParticipantsSelector.js
+++ b/src/pages/iou/request/MoneyTemporaryForRefactorRequestParticipantsSelector.js
@@ -50,10 +50,10 @@ const propTypes = {
}),
),
- /** The type of IOU report, i.e. bill, request, send */
+ /** The type of IOU report, i.e. split, request, send, track */
iouType: PropTypes.oneOf(_.values(CONST.IOU.TYPE)).isRequired,
- /** The request type, ie. manual, scan, distance */
+ /** The expense type, ie. manual, scan, distance */
iouRequestType: PropTypes.oneOf(_.values(CONST.IOU.REQUEST_TYPE)).isRequired,
/** The action of the IOU, i.e. create, split, move */
@@ -107,8 +107,8 @@ function MoneyTemporaryForRefactorRequestParticipantsSelector({participants, onF
participants,
CONST.EXPENSIFY_EMAILS,
- // If we are using this component in the "Request money" flow then we pass the includeOwnedWorkspaceChats argument so that the current user
- // sees the option to request money from their admin on their own Workspace Chat.
+ // If we are using this component in the "Submit expense" flow then we pass the includeOwnedWorkspaceChats argument so that the current user
+ // sees the option to submit an expense from their admin on their own Workspace Chat.
iouType === CONST.IOU.TYPE.REQUEST && action !== CONST.IOU.ACTION.MOVE,
(canUseP2PDistanceRequests || iouRequestType !== CONST.IOU.REQUEST_TYPE.DISTANCE) && ![CONST.IOU.ACTION.CATEGORIZE, CONST.IOU.ACTION.SHARE].includes(action),
@@ -182,7 +182,7 @@ function MoneyTemporaryForRefactorRequestParticipantsSelector({participants, onF
]);
/**
- * Adds a single participant to the request
+ * Adds a single participant to the expense
*
* @param {Object} option
*/
@@ -256,7 +256,7 @@ function MoneyTemporaryForRefactorRequestParticipantsSelector({participants, onF
[maxParticipantsReached, newChatOptions, participants, debouncedSearchTerm],
);
- // Right now you can't split a request with a workspace and other additional participants
+ // Right now you can't split an expense with a workspace and other additional participants
// This is getting properly fixed in https://github.com/Expensify/App/issues/27508, but as a stop-gap to prevent
// the app from crashing on native when you try to do this, we'll going to hide the button if you have a workspace and other participants
const hasPolicyExpenseChatParticipant = _.some(participants, (participant) => participant.isPolicyExpenseChat);
@@ -304,14 +304,14 @@ function MoneyTemporaryForRefactorRequestParticipantsSelector({participants, onF
)}
{!!participants.length && (