diff --git a/.eslintrc.js b/.eslintrc.js
index ac4546567833..b5b4add538f6 100644
--- a/.eslintrc.js
+++ b/.eslintrc.js
@@ -24,7 +24,7 @@ const restrictedImportPatterns = [
];
module.exports = {
- extends: ['expensify', 'plugin:storybook/recommended', 'plugin:react-hooks/recommended', 'prettier', 'plugin:react-native-a11y/basic'],
+ extends: ['expensify', 'plugin:storybook/recommended', 'plugin:react-hooks/recommended', 'plugin:react-native-a11y/basic', 'prettier'],
plugins: ['react-hooks', 'react-native-a11y'],
parser: 'babel-eslint',
ignorePatterns: ['!.*', 'src/vendor', '.github/actions/**/index.js', 'desktop/dist/*.js', 'dist/*.js', 'node_modules/.bin/**', 'node_modules/.cache/**', '.git/**'],
@@ -75,6 +75,7 @@ module.exports = {
patterns: restrictedImportPatterns,
},
],
+ curly: 'error',
},
},
{
@@ -161,6 +162,7 @@ module.exports = {
patterns: restrictedImportPatterns,
},
],
+ curly: 'error',
},
},
{
diff --git a/.github/scripts/createDocsRoutes.js b/.github/scripts/createDocsRoutes.js
index 0fc9aa33ff27..39cd98383de1 100644
--- a/.github/scripts/createDocsRoutes.js
+++ b/.github/scripts/createDocsRoutes.js
@@ -2,10 +2,14 @@ const yaml = require('js-yaml');
const fs = require('fs');
const _ = require('underscore');
-const warn = 'Number of hubs in _routes.yml does not match number of hubs in docs/articles. Please update _routes.yml with hub info.';
+const warnMessage = (platform) => `Number of hubs in _routes.yml does not match number of hubs in docs/${platform}/articles. Please update _routes.yml with hub info.`;
const disclaimer = '# This file is auto-generated. Do not edit it directly. Use npm run createDocsRoutes instead.\n';
const docsDir = `${process.cwd()}/docs`;
const routes = yaml.load(fs.readFileSync(`${docsDir}/_data/_routes.yml`, 'utf8'));
+const platformNames = {
+ expensifyClassic: 'expensify-classic',
+ newExpensify: 'new-expensify',
+};
/**
* @param {String} str - The string to convert to title case
@@ -28,7 +32,7 @@ function getArticleObj(filename) {
}
/**
- * If the articlea / sections exist in the hub, then push the entry to the array.
+ * If the article / sections exist in the hub, then push the entry to the array.
* Otherwise, create the array and push the entry to it.
* @param {*} hubs - The hubs array
* @param {*} hub - The hub we are iterating
@@ -44,20 +48,20 @@ function pushOrCreateEntry(hubs, hub, key, entry) {
}
}
-function run() {
- const hubs = fs.readdirSync(`${docsDir}/articles`);
- if (hubs.length !== routes.hubs.length) {
- // If new hubs have been added without metadata addition to _routes.yml
- console.error(warn);
- process.exit(1);
- }
+/**
+ * Add articles and sections to hubs
+ * @param {Array} hubs - The hubs inside docs/articles/ for a platform
+ * @param {String} platformName - Expensify Classic or New Expensify
+ * @param {Array} routeHubs - The hubs insude docs/data/_routes.yml for a platform
+ */
+function createHubsWithArticles(hubs, platformName, routeHubs) {
_.each(hubs, (hub) => {
// Iterate through each directory in articles
- fs.readdirSync(`${docsDir}/articles/${hub}`).forEach((fileOrFolder) => {
+ fs.readdirSync(`${docsDir}/articles/${platformName}/${hub}`).forEach((fileOrFolder) => {
// If the directory content is a markdown file, then it is an article
if (fileOrFolder.endsWith('.md')) {
const articleObj = getArticleObj(fileOrFolder);
- pushOrCreateEntry(routes.hubs, hub, 'articles', articleObj);
+ pushOrCreateEntry(routeHubs, hub, 'articles', articleObj);
return;
}
@@ -66,17 +70,38 @@ function run() {
const articles = [];
// Each subfolder will be a section containing articles
- fs.readdirSync(`${docsDir}/articles/${hub}/${section}`).forEach((subArticle) => {
+ fs.readdirSync(`${docsDir}/articles/${platformName}/${hub}/${section}`).forEach((subArticle) => {
articles.push(getArticleObj(subArticle));
});
- pushOrCreateEntry(routes.hubs, hub, 'sections', {
+ pushOrCreateEntry(routeHubs, hub, 'sections', {
href: section,
title: toTitleCase(section.replaceAll('-', ' ')),
articles,
});
});
});
+}
+
+function run() {
+ const expensifyClassicArticleHubs = fs.readdirSync(`${docsDir}/articles/${platformNames.expensifyClassic}`);
+ const newExpensifyArticleHubs = fs.readdirSync(`${docsDir}/articles/${platformNames.newExpensify}`);
+
+ const expensifyClassicRoute = _.find(routes.platforms, (platform) => platform.href === platformNames.expensifyClassic);
+ const newExpensifyRoute = _.find(routes.platforms, (platform) => platform.href === platformNames.newExpensify);
+
+ if (expensifyClassicArticleHubs.length !== expensifyClassicRoute.hubs.length) {
+ console.error(warnMessage(platformNames.expensifyClassic));
+ process.exit(1);
+ }
+
+ if (newExpensifyArticleHubs.length !== newExpensifyRoute.hubs.length) {
+ console.error(warnMessage(platformNames.newExpensify));
+ process.exit(1);
+ }
+
+ createHubsWithArticles(expensifyClassicArticleHubs, platformNames.expensifyClassic, expensifyClassicRoute.hubs);
+ createHubsWithArticles(newExpensifyArticleHubs, platformNames.newExpensify, newExpensifyRoute.hubs);
// Convert the object to YAML and write it to the file
let yamlString = yaml.dump(routes);
diff --git a/.github/workflows/deployExpensifyHelp.yml b/.github/workflows/deployExpensifyHelp.yml
index ca7345ef9462..11f4897ab322 100644
--- a/.github/workflows/deployExpensifyHelp.yml
+++ b/.github/workflows/deployExpensifyHelp.yml
@@ -2,10 +2,6 @@
name: Deploy ExpensifyHelp
on:
- # Runs on pushes targeting the default branch
- push:
- branches: ["main"]
-
# Allows you to run this workflow manually from the Actions tab
workflow_dispatch:
diff --git a/Cloudflare_CA.crt b/Cloudflare_CA.crt
new file mode 100644
index 000000000000..f02f49a951fc
Binary files /dev/null and b/Cloudflare_CA.crt differ
diff --git a/Gemfile.lock b/Gemfile.lock
index 9487e3c45c7a..079b5a5b742b 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -9,7 +9,7 @@ GEM
minitest (>= 5.1)
tzinfo (~> 2.0)
zeitwerk (~> 2.3)
- addressable (2.8.3)
+ addressable (2.8.5)
public_suffix (>= 2.0.2, < 6.0)
algoliasearch (1.27.5)
httpclient (~> 2.8, >= 2.8.3)
@@ -19,20 +19,20 @@ GEM
artifactory (3.0.15)
atomos (0.1.3)
aws-eventstream (1.2.0)
- aws-partitions (1.646.0)
- aws-sdk-core (3.160.0)
+ aws-partitions (1.824.0)
+ aws-sdk-core (3.181.1)
aws-eventstream (~> 1, >= 1.0.2)
- aws-partitions (~> 1, >= 1.525.0)
- aws-sigv4 (~> 1.1)
+ aws-partitions (~> 1, >= 1.651.0)
+ aws-sigv4 (~> 1.5)
jmespath (~> 1, >= 1.6.1)
- aws-sdk-kms (1.58.0)
- aws-sdk-core (~> 3, >= 3.127.0)
+ aws-sdk-kms (1.71.0)
+ aws-sdk-core (~> 3, >= 3.177.0)
aws-sigv4 (~> 1.1)
- aws-sdk-s3 (1.114.0)
- aws-sdk-core (~> 3, >= 3.127.0)
+ aws-sdk-s3 (1.134.0)
+ aws-sdk-core (~> 3, >= 3.181.0)
aws-sdk-kms (~> 1)
- aws-sigv4 (~> 1.4)
- aws-sigv4 (1.5.2)
+ aws-sigv4 (~> 1.6)
+ aws-sigv4 (1.6.0)
aws-eventstream (~> 1, >= 1.0.2)
babosa (1.0.4)
claide (1.1.0)
@@ -79,7 +79,7 @@ GEM
highline (~> 2.0.0)
concurrent-ruby (1.2.2)
declarative (0.0.20)
- digest-crc (0.6.4)
+ digest-crc (0.6.5)
rake (>= 12.0.0, < 14.0.0)
domain_name (0.5.20190701)
unf (>= 0.0.5, < 1.0.0)
@@ -88,8 +88,8 @@ GEM
escape (0.0.4)
ethon (0.16.0)
ffi (>= 1.15.0)
- excon (0.93.0)
- faraday (1.10.2)
+ excon (0.103.0)
+ faraday (1.10.3)
faraday-em_http (~> 1.0)
faraday-em_synchrony (~> 1.0)
faraday-excon (~> 1.1)
@@ -117,8 +117,8 @@ GEM
faraday-retry (1.0.3)
faraday_middleware (1.2.0)
faraday (~> 1.0)
- fastimage (2.2.6)
- fastlane (2.210.1)
+ fastimage (2.2.7)
+ fastlane (2.215.1)
CFPropertyList (>= 2.3, < 4.0.0)
addressable (>= 2.8, < 3.0.0)
artifactory (~> 3.0)
@@ -139,10 +139,11 @@ GEM
google-apis-playcustomapp_v1 (~> 0.1)
google-cloud-storage (~> 1.31)
highline (~> 2.0)
+ http-cookie (~> 1.0.5)
json (< 3.0.0)
jwt (>= 2.1.0, < 3)
mini_magick (>= 4.9.4, < 5.0.0)
- multipart-post (~> 2.0.0)
+ multipart-post (>= 2.0.0, < 3.0.0)
naturally (~> 2.2)
optparse (~> 0.1.1)
plist (>= 3.1.0, < 4.0.0)
@@ -150,7 +151,7 @@ GEM
security (= 0.1.3)
simctl (~> 1.6.3)
terminal-notifier (>= 2.0.0, < 3.0.0)
- terminal-table (>= 1.4.5, < 2.0.0)
+ terminal-table (~> 3)
tty-screen (>= 0.6.3, < 1.0.0)
tty-spinner (>= 0.8.0, < 1.0.0)
word_wrap (~> 1.0.0)
@@ -165,9 +166,9 @@ GEM
fourflusher (2.3.1)
fuzzy_match (2.0.4)
gh_inspector (1.1.3)
- google-apis-androidpublisher_v3 (0.29.0)
- google-apis-core (>= 0.9.0, < 2.a)
- google-apis-core (0.9.0)
+ google-apis-androidpublisher_v3 (0.49.0)
+ google-apis-core (>= 0.11.0, < 2.a)
+ google-apis-core (0.11.1)
addressable (~> 2.5, >= 2.5.1)
googleauth (>= 0.16.2, < 2.a)
httpclient (>= 2.8.1, < 3.a)
@@ -176,10 +177,10 @@ GEM
retriable (>= 2.0, < 4.a)
rexml
webrick
- google-apis-iamcredentials_v1 (0.15.0)
- google-apis-core (>= 0.9.0, < 2.a)
- google-apis-playcustomapp_v1 (0.11.0)
- google-apis-core (>= 0.9.0, < 2.a)
+ google-apis-iamcredentials_v1 (0.17.0)
+ google-apis-core (>= 0.11.0, < 2.a)
+ google-apis-playcustomapp_v1 (0.13.0)
+ google-apis-core (>= 0.11.0, < 2.a)
google-apis-storage_v1 (0.19.0)
google-apis-core (>= 0.9.0, < 2.a)
google-cloud-core (1.6.0)
@@ -187,8 +188,8 @@ GEM
google-cloud-errors (~> 1.0)
google-cloud-env (1.6.0)
faraday (>= 0.17.3, < 3.0)
- google-cloud-errors (1.3.0)
- google-cloud-storage (1.43.0)
+ google-cloud-errors (1.3.1)
+ google-cloud-storage (1.44.0)
addressable (~> 2.8)
digest-crc (~> 0.4)
google-apis-iamcredentials_v1 (~> 0.1)
@@ -196,10 +197,9 @@ GEM
google-cloud-core (~> 1.6)
googleauth (>= 0.16.2, < 2.a)
mini_mime (~> 1.0)
- googleauth (1.2.0)
+ googleauth (1.8.0)
faraday (>= 0.17.3, < 3.a)
jwt (>= 1.4, < 3.0)
- memoist (~> 0.16)
multi_json (~> 1.11)
os (>= 0.9, < 2.0)
signet (>= 0.16, < 2.a)
@@ -209,26 +209,25 @@ GEM
httpclient (2.8.3)
i18n (1.13.0)
concurrent-ruby (~> 1.0)
- jmespath (1.6.1)
+ jmespath (1.6.2)
json (2.6.3)
- jwt (2.5.0)
- memoist (0.16.2)
+ jwt (2.7.1)
mime-types (3.4.1)
mime-types-data (~> 3.2015)
mime-types-data (3.2023.0218.1)
- mini_magick (4.11.0)
- mini_mime (1.1.2)
+ mini_magick (4.12.0)
+ mini_mime (1.1.5)
minitest (5.18.0)
molinillo (0.8.0)
multi_json (1.15.0)
- multipart-post (2.0.0)
+ multipart-post (2.3.0)
nanaimo (0.3.0)
nap (1.1.0)
naturally (2.2.1)
netrc (0.11.0)
optparse (0.1.1)
os (1.1.4)
- plist (3.6.0)
+ plist (3.7.0)
public_suffix (4.0.7)
rake (13.0.6)
representable (3.2.0)
@@ -236,23 +235,23 @@ GEM
trailblazer-option (>= 0.1.1, < 0.2.0)
uber (< 0.2.0)
retriable (3.1.2)
- rexml (3.2.5)
+ rexml (3.2.6)
rouge (2.0.7)
ruby-macho (2.5.1)
ruby2_keywords (0.0.5)
rubyzip (2.3.2)
security (0.1.3)
- signet (0.17.0)
+ signet (0.18.0)
addressable (~> 2.8)
faraday (>= 0.17.5, < 3.a)
jwt (>= 1.5, < 3.0)
multi_json (~> 1.10)
- simctl (1.6.8)
+ simctl (1.6.10)
CFPropertyList
naturally
terminal-notifier (2.0.0)
- terminal-table (1.8.0)
- unicode-display_width (~> 1.1, >= 1.1.1)
+ terminal-table (3.0.2)
+ unicode-display_width (>= 1.1.1, < 3)
trailblazer-option (0.1.2)
tty-cursor (0.7.1)
tty-screen (0.8.1)
@@ -266,8 +265,8 @@ GEM
unf (0.1.4)
unf_ext
unf_ext (0.0.8.2)
- unicode-display_width (1.8.0)
- webrick (1.7.0)
+ unicode-display_width (2.4.2)
+ webrick (1.8.1)
word_wrap (1.0.0)
xcodeproj (1.22.0)
CFPropertyList (>= 2.3.3, < 4.0)
diff --git a/android/app/build.gradle b/android/app/build.gradle
index d9eb60471773..1e34491b04ad 100644
--- a/android/app/build.gradle
+++ b/android/app/build.gradle
@@ -90,8 +90,8 @@ android {
minSdkVersion rootProject.ext.minSdkVersion
targetSdkVersion rootProject.ext.targetSdkVersion
multiDexEnabled rootProject.ext.multiDexEnabled
- versionCode 1001037005
- versionName "1.3.70-5"
+ versionCode 1001037209
+ versionName "1.3.72-9"
}
flavorDimensions "default"
diff --git a/contributingGuides/ACCESSIBILITY.md b/contributingGuides/ACCESSIBILITY.md
new file mode 100644
index 000000000000..b94cbf3087c8
--- /dev/null
+++ b/contributingGuides/ACCESSIBILITY.md
@@ -0,0 +1,47 @@
+# Accessibility of pressable components
+
+### Base Components
+
+- **GenericPressable**: A basic pressable component with generic functionality. It should generally only be used to creating a new, custom pressable components. Avoid using it directly.
+
+- **PressableWithFeedback**: A pressable component that provides standarised visual and haptic feedback upon pressing.
+
+- **PressableWithoutFeedback**: A pressable component without any visual or haptic feedback.
+
+- **PressableWithoutFocus**: A pressable component without visible efect of focus.
+
+- **PressableWithDelayToggle**: A pressable component that briefly disables then re-enables after a short delay upon pressing.
+
+Accessibility props are unified across all platforms.
+
+### Creating accessible flows
+When implementing pressable components, it's essential to create accessible flows to ensure that users with disabilities can efficiently interact with the app.
+
+- ensure that after performing press focus is set on the correct next element - this is especially important for keyboard users who rely on focus to navigate the app. All Pressable components have a `nextFocusRef` prop that can be used to set the next focusable element after the pressable component. This prop accepts a ref to the next focusable element. For example, if you have a button that opens a modal, you can set the next focus to the first focusable element in the modal. This way, when the user presses the button, focus will be set on the first focusable element in the modal, and the user can continue navigating the modal using the keyboard.
+
+- size of any pressable component should be at least 44x44dp. This is the minimum size recommended by Apple and Google for touch targets. If the pressable component is smaller than `44x44dp`, it will be difficult for users with motor disabilities to interact with it. Pressable components have a `autoHitSlop` prop that can be used to automatically increase the size of the pressable component to `44x44dp`. This prop accepts a boolean value. If set to true, the pressable component will automatically increase its touchable size to 44x44dp. If set to false, the pressable component will not increase its size. By default, this prop is set to false.
+
+- ensure that the pressable component has a label and hint. This is especially important for users with visual disabilities who rely on screen readers to navigate the app. All Pressable components have a `accessibilitylabel` prop that can be used to set the label of the pressable component. This prop accepts a string value. All Pressable components also have a `accessibilityHint` prop that can be used to set the hint of the pressable component. This prop accepts a string value. The accessibilityHint prop is optional. If not set, the pressable component will fallback to the accessibilityLabel prop. For example, if you have a button that opens a modal, you can set the accessibilityLabel to "Open modal" and the accessibilityHint to "Opens a modal with more information". This way, when the user focuses on the button, the screen reader will read "Open modal. Opens a modal with more information". This will help the user understand what the button does and what to expect after pressing it.
+
+- the `enableInScreenReaderStates` prop proves invaluable when aiming to enhance the accessibility of clickable elements, particularly when desiring to enlarge the clickable area of a component, such as an entire row. This can be especially useful, for instance, when dealing with tables where only a small portion of a row, like a checkbox, might traditionally trigger an action. By employing this prop, developers can ensure that the entirety of a designated component, in this case a row, is made accessible to users employing screen readers. This creates a more inclusive user experience, allowing individuals relying on screen readers to interact with the component effortlessly. For instance, in a table, using this prop on a row component enables users to click anywhere within the row to trigger an action, significantly improving accessibility and user-friendliness.
+
+- ensure that the pressable component has a role. This is especially important for users with visual disabilities who rely on screen readers to navigate the app. All Pressable components have a `accessibilityRole` prop that can be used to set the role of the pressable component.
+
+### Testing for accessibility
+It's important to test for accessibility to ensure that the created component has accessibility properties set correctly. This can be done using the following tools:
+
+- **iOS**
+For iOS, you can use the `accessibility inspector` app to test for accessibility. You can find it in the Xcode menu under `Xcode > Open Developer Tool > Accessibility Inspector`. This app allows you to inspect the accessibility properties of any element on the screen. You can also use it to simulate different accessibility settings, such as VoiceOver, color blindness, and more. It's a great tool for testing whether created component has accessibility properties set/passed correctly.
+
+- **Android**
+For Android, you can use the [accessibility scanner](https://support.google.com/accessibility/android/answer/6376570) app to test for accessibility. You can find it in the Google Play Store. This app allows you to inspect the accessibility properties of any element on the screen. You can also use it to simulate different accessibility settings, such as TalkBack, color blindness, and more. It's a great tool for testing whether created component has accessibility properties set correctly. The [result of the accessibility scanner](https://support.google.com/accessibility/android/answer/6376559) app has information about content labeling, implementation, touch target size and low contrast
+This tool requires an installed APK to test on.
+
+- **Web/Desktop**
+On Mac, you can use the [VoiceOver](https://www.apple.com/accessibility/mac/vision/) app to test for accessibility. You can find it in the Mac menu under `System Preferences > Accessibility > VoiceOver` or by pressing `Cmd + F5`. This app allows you to inspect the accessibility properties of any element on the screen. You can also use it to simulate different accessibility settings, such as VoiceOver, color blindness, and more. It's a great tool for testing whether created component has accessibility properties set correctly.
+
+
+### Valuable resources
+- [Apple accessibility guidelines](https://developer.apple.com/design/human-interface-guidelines/accessibility/overview/introduction/)
+- [Google accessibility guidelines](https://developer.android.com/guide/topics/ui/accessibility)
+- [Web accessibility guidelines](https://www.w3.org/WAI/standards-guidelines/wcag/)
\ No newline at end of file
diff --git a/contributingGuides/CONTRIBUTING.md b/contributingGuides/CONTRIBUTING.md
index ae2b98ece85b..b97d04b95e10 100644
--- a/contributingGuides/CONTRIBUTING.md
+++ b/contributingGuides/CONTRIBUTING.md
@@ -2,7 +2,7 @@
Welcome! Thanks for checking out the New Expensify app and taking the time to contribute!
## Getting Started
-If you would like to become an Expensify contributor, the first step is to read this document in its entirety. The second step is to review the README guidelines [here](https://github.com/Expensify/App/blob/main/README.md) to understand our coding philosophy and for a general overview of the code repository (i.e. how to run the app locally, testing, storage, our app philosophy, etc). Please read both documents before asking questions, as it may be covered within the documentation.
+If you would like to become an Expensify contributor, the first step is to read this document in its **entirety**. The second step is to review the README guidelines [here](https://github.com/Expensify/App/blob/main/README.md) to understand our coding philosophy and for a general overview of the code repository (i.e. how to run the app locally, testing, storage, our app philosophy, etc). Please read both documents before asking questions, as it may be covered within the documentation.
#### Test Accounts
You can create as many accounts as needed in order to test your changes directly from [the app](https://new.expensify.com/). An initial account can be created when logging in for the first time, and additional accounts can be created by opening the "New Chat" or "Group Chat" pages via the Global Create menu, inputting a valid email or phone number, and tapping the user's avatar.
@@ -47,7 +47,7 @@ Note: if you are hired for an Upwork job and have any job-specific questions, pl
If you've found a vulnerability, please email security@expensify.com with the subject `Vulnerability Report` instead of creating an issue.
## Payment for Contributions
-We hire and pay external contributors via Upwork.com. If you'd like to be paid for contributing or reporting a bug, please create an Upwork account, apply for an available job in [GitHub](https://github.com/Expensify/App/issues?q=is%3Aopen+is%3Aissue+label%3A%22Help+Wanted%22), and finally apply for the job in Upwork once your proposal gets selected in GitHub. If you think your compensation should be increased for a specific job, you can request a reevaluation by commenting in the Github issue where the Upwork job was posted.
+We hire and pay external contributors via Upwork.com. If you'd like to be paid for contributing or reporting a bug, please create an Upwork account, apply for an available job in [GitHub](https://github.com/Expensify/App/issues?q=is%3Aopen+is%3Aissue+label%3A%22Help+Wanted%22), and finally apply for the job in Upwork once your proposal gets selected in GitHub. PLease make sure your Upwork profile is **fully verified** before applying, otherwise you run the risk of not being paid. If you think your compensation should be increased for a specific job, you can request a reevaluation by commenting in the Github issue where the Upwork job was posted.
Payment for your contributions and bug reports will be made no less than 7 days after the pull request is deployed to production to allow for [regression](https://github.com/Expensify/App/blob/main/contributingGuides/CONTRIBUTING.md#regressions) testing. If you have not received payment after 8 days of the PR being deployed to production, and there are no [regressions](https://github.com/Expensify/App/blob/main/contributingGuides/CONTRIBUTING.md#regressions), please add a comment to the issue mentioning the BugZero team member (Look for the melvin-bot "Triggered auto assignment to... (`Bug`)" to see who this is).
@@ -89,13 +89,13 @@ It’s possible that you found a new bug that we haven’t posted as a job to th
Please follow these steps to propose a job or raise a bug:
1. Check to ensure a GH issue does not already exist for this job in the [New Expensify Issue list](https://github.com/Expensify/App/issues).
-2. Check to ensure the `Bug:` or `Feature Request:` was not already posted in Slack (specifically the #expensify-bugs or #expensify-open-source [Slack channels](https://github.com/Expensify/App/blob/main/contributingGuides/CONTRIBUTING.md#slack-channels)). Use your best judgement by searching for similar titles and issue descriptions.
+2. Check to ensure the `Bug:` or `Feature Request:` was not already posted in Slack (specifically the #expensify-bugs or #expensify-open-source [Slack channels](https://github.com/Expensify/App/blob/main/contributingGuides/CONTRIBUTING.md#slack-channels)). Use your best judgement by searching for similar titles, words and issue descriptions.
3. If your bug or new feature matches with an existing issue, please comment on that Slack thread or GitHub issue with your findings if you think it will help solve the issue.
4. If there is no existing GitHub issue or Upwork job, check if the issue is happening on prod (as opposed to only happening on dev)
5. If the issue is just in dev then it means it's a new issue and has not been deployed to production. In this case, you should try to find the offending PR and comment in the issue tied to the PR and ask the assigned users to add the `DeployBlockerCash` label. If you can't find it, follow the reporting instructions in the next item, but note that the issue is a regression only found in dev and not in prod.
-6. If the issue happens in main, staging, or production then report the issue(s) in the #expensify-bugs Slack channel, using the report bug workflow. You can do this by clicking 'Workflow > report Bug', or typing `/Report bug`. View [this guide](https://github.com/Expensify/App/blob/main/contributingGuides/HOW_TO_CREATE_A_PLAN.md) for help creating a plan when proposing a feature request.
+6. If the issue happens in main, staging, or production then report the issue(s) in the #expensify-bugs Slack channel, using the report bug workflow. You can do this by clicking 'Workflow > report Bug', or typing `/Report bug`. View [this guide](https://github.com/Expensify/App/blob/main/contributingGuides/HOW_TO_CREATE_A_PLAN.md) for help creating a plan when proposing a feature request. Please verify the bug's presence on **every** platform mentioned in the bug report template, and confirm this with a screen recording..
- **Important note/reminder**: never share any information pertaining to a customer of Expensify when describing the bug. This includes, and is not limited to, a customer's name, email, and contact information.
-7. The Expensify team will review your job proposal in the appropriate slack channel. If you've provided a quality proposal that we choose to implement, a GitHub issue will be created and your Slack handle will be included in the original post after `Issue reported by:`
+7. The Applause team will review your job proposal in the appropriate slack channel. If you've provided a quality proposal that we choose to implement, a GitHub issue will be created and your Slack handle will be included in the original post after `Issue reported by:`
8. If an external contributor other than yourself is hired to work on the issue, you will also be hired for the same job in Upwork to receive your payout. No additional work is required. If the issue is fixed internally, a dedicated job will be created to hire and pay you after the issue is fixed.
9. Payment will be made 7 days after code is deployed to production if there are no regressions. If a regression is discovered, payment will be issued 7 days after all regressions are fixed.
diff --git a/contributingGuides/STYLE.md b/contributingGuides/STYLE.md
index ce59438a0681..0a88ecd7bda8 100644
--- a/contributingGuides/STYLE.md
+++ b/contributingGuides/STYLE.md
@@ -491,6 +491,19 @@ When writing a function component you must ALWAYS add a `displayName` property a
export default Avatar;
```
+## Forwarding refs
+
+When forwarding a ref define named component and pass it directly to the `forwardRef`. By doing this we remove potential extra layer in React tree in form of anonymous component.
+
+```javascript
+ function FancyInput(props, ref) {
+ ...
+ return
+ }
+
+ export default React.forwardRef(FancyInput)
+```
+
## Stateless components vs Pure Components vs Class based components vs Render Props - When to use what?
Class components are DEPRECATED. Use function components and React hooks.
@@ -567,6 +580,28 @@ A `useEffect()` that does not include referenced props or state in its dependenc
There are pros and cons of each, but ultimately we have standardized on using the `function` keyword to align things more with modern React conventions. There are also some minor cognitive overhead benefits in that you don't need to think about adding and removing brackets when encountering an implicit return. The `function` syntax also has the benefit of being able to be hoisted where arrow functions do not.
+## How do I auto-focus a TextInput using `useFocusEffect()`?
+
+```javascript
+const focusTimeoutRef = useRef(null);
+
+useFocusEffect(useCallback(() => {
+ focusTimeoutRef.current = setTimeout(() => textInputRef.current.focus(), CONST.ANIMATED_TRANSITION);
+ return () => {
+ if (!focusTimeoutRef.current) {
+ return;
+ }
+ clearTimeout(focusTimeoutRef.current);
+ };
+}, []));
+```
+
+This works better than using `onTransitionEnd` because -
+1. `onTransitionEnd` is only fired for the top card in the stack, and therefore does not fire on the new top card when popping a card off the stack. For example - pressing the back button to go from the workspace invite page to the workspace members list.
+2. Using `InteractionsManager.runAfterInteractions` with `useFocusEffect` will interrupt an in-progress transition animation.
+
+Note - This is a solution from [this PR](https://github.com/Expensify/App/pull/26415). You can find detailed discussion in comments.
+
# Onyx Best Practices
[Onyx Documentation](https://github.com/expensify/react-native-onyx)
diff --git a/contributingGuides/TS_CHEATSHEET.md b/contributingGuides/TS_CHEATSHEET.md
index df6d70b5ae90..1e330dafb7cf 100644
--- a/contributingGuides/TS_CHEATSHEET.md
+++ b/contributingGuides/TS_CHEATSHEET.md
@@ -43,7 +43,9 @@
- [1.2](#forwardRef) **`forwardRef`**
```ts
- import { forwardRef, useRef, ReactNode } from "react";
+ // CustomTextInput.tsx
+
+ import { forwardRef, useRef, ReactNode, ForwardedRef } from "react";
import { TextInput, View } from "react-native";
export type CustomTextInputProps = {
@@ -51,16 +53,18 @@
children?: ReactNode;
};
- const CustomTextInput = forwardRef(
- (props, ref) => {
- return (
-
-
- {props.children}
-
- );
- }
- );
+ function CustomTextInput(props: CustomTextInputProps, ref: ForwardedRef) {
+ return (
+
+
+ {props.children}
+
+ );
+ };
+
+ export default forwardRef(CustomTextInput);
+
+ // ParentComponent.tsx
function ParentComponent() {
const ref = useRef();
diff --git a/docs/_data/_routes.yml b/docs/_data/_routes.yml
index 32c8a5211ee2..20582b6b8c7e 100644
--- a/docs/_data/_routes.yml
+++ b/docs/_data/_routes.yml
@@ -1,26 +1,142 @@
home:
href: home
title: Welcome to ExpensifyHelp!
- description: Find the answers to all of your questions about receipts, expenses, corporate cards, or anything else in the spend management universe.
-
-# Hubs are comprised of sections and articles. Sections contain multiple related articles, but there can be standalone articles as well
-hubs:
- - href: split-bills
- title: Split bills
- description: With only a couple of clicks, split bills with your friends or coworkers.
- icon: /assets/images/paper-airplane.svg
-
- - href: request-money
- title: Request money
- icon: /assets/images/money-case.svg
- description: Request money for work expenses, bills, or a night out with friends.
-
- - href: playbooks
- title: Playbooks
- icon: /assets/images/playbook.svg
- description: Best practices for how to best deploy Expensify for your business
-
- - href: other
- title: Other
- description: Everything else you're looking for is right here.
- icon: /assets/images/lightbulb.svg
+ description: Questions? Find the answers by clicking a Category or using the search bar located in the left-hand menu.
+
+platforms:
+ - href: expensify-classic
+ title: Expensify Classic
+ hub-title: Expensify Classic - Help & Resources
+ url: expensify.com
+ description: Your account settings will look something like this
+ image: /assets/images/paper-airplane.svg
+
+ # Hubs are comprised of sections and articles. Sections contain multiple related articles, but there can be standalone articles as well
+ hubs:
+ - href: account-settings
+ title: Account Settings
+ icon: /assets/images/gears.svg
+ description: With only a couple of clicks, split bills with your friends or coworkers.
+
+ - href: bank-accounts-and-credit-cards
+ title: Bank Accounts & Credit Cards
+ icon: /assets/images/bank-card.svg
+ description: Request money for work expenses, bills, or a night out with friends.
+
+ - href: billing-and-subscriptions
+ title: Billing & Subscriptions
+ icon: /assets/images/money-wings.svg
+ description: Best practices for how to best deploy Expensify for your business
+
+ - href: expense-and-report-features
+ title: Expense & Report Features
+ icon: /assets/images/money-receipt.svg
+ description: Everything else you're looking for is right here.
+
+ - href: expensify-card
+ title: Expensify Card
+ icon: /assets/images/hand-card.svg
+ description: Request money for work expenses, bills, or a night out with friends.
+
+ - href: exports
+ title: Exports
+ icon: /assets/images/monitor.svg
+ description: Best practices for how to best deploy Expensify for your business
+
+ - href: get-paid-back
+ title: Get Paid Back
+ description: Everything else you're looking for is right here.
+ icon: /assets/images/money-into-wallet.svg
+
+ - href: getting-started
+ title: Getting Started
+ description: Everything else you're looking for is right here.
+ icon: /assets/images/accounting.svg
+
+ - href: integrations
+ title: Integrations
+ description: Everything else you're looking for is right here.
+ icon: /assets/images/workflow.svg
+
+ - href: manage-employees-and-report-approvals
+ title: Manage Employees & Report Approvals
+ icon: /assets/images/envelope-receipt.svg
+ description: Everything else you're looking for is right here.
+
+ - href: policy-and-domain-settings
+ title: Policy & Domain Setting
+ icon: /assets/images/shield.svg
+ description: Everything else you're looking for is right here.
+
+ - href: send-payments
+ title: Send Payments
+ icon: /assets/images/money-wings.svg
+ description: Everything else you're looking for is right here.
+
+ - href: new-expensify
+ title: New Expensify
+ hub-title: New Expensify - Help & Resources
+ url: new.expensify.com
+ description: Your account settings will look something like this
+ image: /assets/images/paper-airplane.svg
+
+ hubs:
+ - href: account-settings
+ title: Account Settings
+ icon: /assets/images/gears.svg
+ description: With only a couple of clicks, split bills with your friends or coworkers.
+
+ - href: bank-accounts-and-credit-cards
+ title: Bank Accounts & Credit Cards
+ icon: /assets/images/bank-card.svg
+ description: description
+
+ - href: billing-and-plan-types
+ title: Billing & Plan Types
+ icon: /assets/images/money-wings.svg
+ description: description
+
+ - href: expense-and-report-features
+ title: Expense & Report Features
+ icon: /assets/images/money-receipt.svg
+ description: description
+
+ - href: expensify-card
+ title: Expensify Card
+ icon: /assets/images/hand-card.svg
+ description: description
+
+ - href: exports
+ title: Exports
+ icon: /assets/images/monitor.svg
+ description: description
+
+ - href: get-paid-back
+ title: Get Paid Back
+ icon: /assets/images/money-into-wallet.svg
+ description: description
+
+ - href: getting-started
+ title: Getting Started
+ icon: /assets/images/accounting.svg
+ description: description
+
+ - href: integrations
+ title: Integrations
+ icon: /assets/images/workflow.svg
+ description: description
+
+ - href: manage-employees-and-report-approvals
+ title: Manage Employees & Report Approvals
+ icon: /assets/images/envelope-receipt.svg
+ description: description
+
+ - href: send-payments
+ title: Send Payments
+ icon: /assets/images/money-wings.svg
+ description: description.
+
+ - href: workspace-and-domain-settings
+ title: Workspace & Domain Settings
+ icon: /assets/images/shield.svg
+ description: description.
diff --git a/docs/_sass/_search-bar.scss b/docs/_sass/_search-bar.scss
index ce085878af46..a5cc8ae2ff20 100644
--- a/docs/_sass/_search-bar.scss
+++ b/docs/_sass/_search-bar.scss
@@ -23,14 +23,25 @@ $color-gray-label: $color-gray-label;
#sidebar-search {
background-color: $color-appBG;
width: 375px;
- height: 100vh;
position: fixed;
- display: block;
+ display: flex;
+ flex-direction: column;
+ bottom: 0;
top: 0;
right: 0;
z-index: 2;
}
+#sidebar-search > div:last-child {
+ flex-grow: 1;
+ overflow-y: auto;
+ -ms-overflow-style: none;
+ scrollbar-width: none;
+ &::-webkit-scrollbar {
+ display: none;
+ }
+}
+
@media only screen and (max-width: $breakpoint-tablet) {
#sidebar-search {
width: 100%;
@@ -156,10 +167,6 @@ label.search-label {
background-color: $color-appBG;
border: $color-appBG;
font-family: "ExpensifyNeue", "Helvetica Neue", "Helvetica", Arial, sans-serif !important;
- max-height: 80vh;
- overflow-y: scroll;
- -ms-overflow-style: none;
- scrollbar-width: none;
}
/* Hide the scrollbar */
diff --git a/docs/articles/expensify-classic/account-settings/Account-Access.md b/docs/articles/expensify-classic/account-settings/Account-Access.md
new file mode 100644
index 000000000000..f04b45c42639
--- /dev/null
+++ b/docs/articles/expensify-classic/account-settings/Account-Access.md
@@ -0,0 +1,5 @@
+---
+title: Account Access
+description: Account Access
+---
+## Resources Coming Soon!
diff --git a/docs/articles/expensify-classic/account-settings/Close-Account.md b/docs/articles/expensify-classic/account-settings/Close-Account.md
new file mode 100644
index 000000000000..cf5052fa56f1
--- /dev/null
+++ b/docs/articles/expensify-classic/account-settings/Close-Account.md
@@ -0,0 +1,5 @@
+---
+title: Close Account
+description: Close Account
+---
+## Resources Coming Soon!
diff --git a/docs/articles/expensify-classic/account-settings/Merge-Accounts.md b/docs/articles/expensify-classic/account-settings/Merge-Accounts.md
new file mode 100644
index 000000000000..1c5f22478e17
--- /dev/null
+++ b/docs/articles/expensify-classic/account-settings/Merge-Accounts.md
@@ -0,0 +1,5 @@
+---
+title: Merge Accounts
+description: Merge Accounts
+---
+## Resources Coming Soon!
diff --git a/docs/articles/expensify-classic/account-settings/Preferences.md b/docs/articles/expensify-classic/account-settings/Preferences.md
new file mode 100644
index 000000000000..a3e53e1177a1
--- /dev/null
+++ b/docs/articles/expensify-classic/account-settings/Preferences.md
@@ -0,0 +1,5 @@
+---
+title: Preferences
+description: Preferences
+---
+## Resources Coming Soon!
diff --git a/docs/articles/expensify-classic/account-settings/Profile-Settings.md b/docs/articles/expensify-classic/account-settings/Profile-Settings.md
new file mode 100644
index 000000000000..bdc18036a46e
--- /dev/null
+++ b/docs/articles/expensify-classic/account-settings/Profile-Settings.md
@@ -0,0 +1,5 @@
+---
+title: Profile Settings
+description: Profile Settings
+---
+## Resources Coming Soon!
diff --git a/docs/articles/expensify-classic/bank-accounts-and-credit-cards/Business-Bank-Accounts-AUS.md b/docs/articles/expensify-classic/bank-accounts-and-credit-cards/Business-Bank-Accounts-AUS.md
new file mode 100644
index 000000000000..44488defcd67
--- /dev/null
+++ b/docs/articles/expensify-classic/bank-accounts-and-credit-cards/Business-Bank-Accounts-AUS.md
@@ -0,0 +1,5 @@
+---
+title: Business Bank Accounts - AUS
+description: Business Bank Accounts - AUS
+---
+## Resources Coming Soon!
diff --git a/docs/articles/expensify-classic/bank-accounts-and-credit-cards/Business-Bank-Accounts-USD.md b/docs/articles/expensify-classic/bank-accounts-and-credit-cards/Business-Bank-Accounts-USD.md
new file mode 100644
index 000000000000..218d6dcd1efa
--- /dev/null
+++ b/docs/articles/expensify-classic/bank-accounts-and-credit-cards/Business-Bank-Accounts-USD.md
@@ -0,0 +1,5 @@
+---
+title: Business Bank Accounts - USD
+description: Business Bank Accounts - USD
+---
+## Resources Coming Soon!
diff --git a/docs/articles/expensify-classic/bank-accounts-and-credit-cards/Deposit-Accounts-AUS.md b/docs/articles/expensify-classic/bank-accounts-and-credit-cards/Deposit-Accounts-AUS.md
new file mode 100644
index 000000000000..dba02f6fc52c
--- /dev/null
+++ b/docs/articles/expensify-classic/bank-accounts-and-credit-cards/Deposit-Accounts-AUS.md
@@ -0,0 +1,5 @@
+---
+title: Deposit Accounts - AUS
+description: Deposit Accounts - AUS
+---
+## Resources Coming Soon!
diff --git a/docs/articles/expensify-classic/bank-accounts-and-credit-cards/Deposit-Accounts-USD.md b/docs/articles/expensify-classic/bank-accounts-and-credit-cards/Deposit-Accounts-USD.md
new file mode 100644
index 000000000000..8d3fe6e51484
--- /dev/null
+++ b/docs/articles/expensify-classic/bank-accounts-and-credit-cards/Deposit-Accounts-USD.md
@@ -0,0 +1,5 @@
+---
+title: Deposit Accounts - USD
+description: Deposit Accounts - USD
+---
+## Resources Coming Soon!
diff --git a/docs/articles/expensify-classic/bank-accounts-and-credit-cards/Global-Reimbursement.md b/docs/articles/expensify-classic/bank-accounts-and-credit-cards/Global-Reimbursement.md
new file mode 100644
index 000000000000..40bdfb7741ab
--- /dev/null
+++ b/docs/articles/expensify-classic/bank-accounts-and-credit-cards/Global-Reimbursement.md
@@ -0,0 +1,5 @@
+---
+title: Global Reimbursement
+description: Global Reimbursement
+---
+## Resources Coming Soon!
diff --git a/docs/articles/expensify-classic/bank-accounts-and-credit-cards/Personal-Credit-Cards.md b/docs/articles/expensify-classic/bank-accounts-and-credit-cards/Personal-Credit-Cards.md
new file mode 100644
index 000000000000..016ca90ee7f7
--- /dev/null
+++ b/docs/articles/expensify-classic/bank-accounts-and-credit-cards/Personal-Credit-Cards.md
@@ -0,0 +1,5 @@
+---
+title: Personal Credit Cards
+description: Personal Credit Cards
+---
+## Resources Coming Soon!
diff --git a/docs/articles/expensify-classic/bank-accounts-and-credit-cards/company-cards/ANZ.md b/docs/articles/expensify-classic/bank-accounts-and-credit-cards/company-cards/ANZ.md
new file mode 100644
index 000000000000..6bfc7b14c09a
--- /dev/null
+++ b/docs/articles/expensify-classic/bank-accounts-and-credit-cards/company-cards/ANZ.md
@@ -0,0 +1,6 @@
+---
+title: ANZ
+description: A guide to integrate with your ANZ card
+---
+## Resources Coming Soon!
+Coming Soon!!
diff --git a/docs/articles/expensify-classic/bank-accounts-and-credit-cards/company-cards/Brex.md b/docs/articles/expensify-classic/bank-accounts-and-credit-cards/company-cards/Brex.md
new file mode 100644
index 000000000000..7d5ad7bf0315
--- /dev/null
+++ b/docs/articles/expensify-classic/bank-accounts-and-credit-cards/company-cards/Brex.md
@@ -0,0 +1,5 @@
+---
+title: Brex
+description: Brex
+---
+## Resources Coming Soon!
diff --git a/docs/articles/expensify-classic/bank-accounts-and-credit-cards/company-cards/CSV-Import.md b/docs/articles/expensify-classic/bank-accounts-and-credit-cards/company-cards/CSV-Import.md
new file mode 100644
index 000000000000..db68d4431a3a
--- /dev/null
+++ b/docs/articles/expensify-classic/bank-accounts-and-credit-cards/company-cards/CSV-Import.md
@@ -0,0 +1,5 @@
+---
+title: CSV Import
+description: CSV Import
+---
+## Resources Coming Soon!
diff --git a/docs/articles/expensify-classic/bank-accounts-and-credit-cards/company-cards/Commercial-Card-Feeds.md b/docs/articles/expensify-classic/bank-accounts-and-credit-cards/company-cards/Commercial-Card-Feeds.md
new file mode 100644
index 000000000000..e49d0d61855c
--- /dev/null
+++ b/docs/articles/expensify-classic/bank-accounts-and-credit-cards/company-cards/Commercial-Card-Feeds.md
@@ -0,0 +1,5 @@
+---
+title: Commercial Card Feeds
+description: Commercial Card Feeds
+---
+## Resources Coming Soon!
diff --git a/docs/articles/expensify-classic/bank-accounts-and-credit-cards/company-cards/Connect-Company-Cards.md b/docs/articles/expensify-classic/bank-accounts-and-credit-cards/company-cards/Connect-Company-Cards.md
new file mode 100644
index 000000000000..ecd4fc0a6538
--- /dev/null
+++ b/docs/articles/expensify-classic/bank-accounts-and-credit-cards/company-cards/Connect-Company-Cards.md
@@ -0,0 +1,5 @@
+---
+title: Connect Company Cards
+description: Connect Company Cards
+---
+## Resources Coming Soon!
diff --git a/docs/articles/expensify-classic/bank-accounts-and-credit-cards/company-cards/Direct-Bank-Connections.md b/docs/articles/expensify-classic/bank-accounts-and-credit-cards/company-cards/Direct-Bank-Connections.md
new file mode 100644
index 000000000000..6775b2684b61
--- /dev/null
+++ b/docs/articles/expensify-classic/bank-accounts-and-credit-cards/company-cards/Direct-Bank-Connections.md
@@ -0,0 +1,5 @@
+---
+title: Direct Bank Connections
+description: Direct Bank Connections
+---
+## Resources Coming Soon!
diff --git a/docs/articles/expensify-classic/bank-accounts-and-credit-cards/company-cards/Export-To-GL-Accounts.md b/docs/articles/expensify-classic/bank-accounts-and-credit-cards/company-cards/Export-To-GL-Accounts.md
new file mode 100644
index 000000000000..58485888b921
--- /dev/null
+++ b/docs/articles/expensify-classic/bank-accounts-and-credit-cards/company-cards/Export-To-GL-Accounts.md
@@ -0,0 +1,5 @@
+---
+title: Export to GL Accounts
+description: Export to GL Accounts
+---
+## Resources Coming Soon!
diff --git a/docs/articles/expensify-classic/bank-accounts-and-credit-cards/company-cards/Reconciliation.md b/docs/articles/expensify-classic/bank-accounts-and-credit-cards/company-cards/Reconciliation.md
new file mode 100644
index 000000000000..be400ee2c13c
--- /dev/null
+++ b/docs/articles/expensify-classic/bank-accounts-and-credit-cards/company-cards/Reconciliation.md
@@ -0,0 +1,5 @@
+---
+title: Reconciliation
+description: Reconciliation
+---
+## Resources Coming Soon!
diff --git a/docs/articles/expensify-classic/bank-accounts-and-credit-cards/company-cards/Troubleshooting.md b/docs/articles/expensify-classic/bank-accounts-and-credit-cards/company-cards/Troubleshooting.md
new file mode 100644
index 000000000000..d9e0d1bb994b
--- /dev/null
+++ b/docs/articles/expensify-classic/bank-accounts-and-credit-cards/company-cards/Troubleshooting.md
@@ -0,0 +1,5 @@
+---
+title: Troubleshooting
+description: Troubleshooting
+---
+## Resources Coming Soon!
diff --git a/docs/articles/expensify-classic/billing-and-subscriptions/Annual-Subscription.md b/docs/articles/expensify-classic/billing-and-subscriptions/Annual-Subscription.md
new file mode 100644
index 000000000000..c80a0d57400d
--- /dev/null
+++ b/docs/articles/expensify-classic/billing-and-subscriptions/Annual-Subscription.md
@@ -0,0 +1,5 @@
+---
+title: Annual Subscription
+description: Annual Subscription
+---
+## Resources Coming Soon!
diff --git a/docs/articles/expensify-classic/billing-and-subscriptions/Billing-Owner.md b/docs/articles/expensify-classic/billing-and-subscriptions/Billing-Owner.md
new file mode 100644
index 000000000000..590fbc78007e
--- /dev/null
+++ b/docs/articles/expensify-classic/billing-and-subscriptions/Billing-Owner.md
@@ -0,0 +1,5 @@
+---
+title: Billing-Owner
+description: Billing-Owner
+---
+## Resources Coming Soon!
\ No newline at end of file
diff --git a/docs/articles/expensify-classic/billing-and-subscriptions/Change-Plan-Or-Subscription.md b/docs/articles/expensify-classic/billing-and-subscriptions/Change-Plan-Or-Subscription.md
new file mode 100644
index 000000000000..2f593625a7d5
--- /dev/null
+++ b/docs/articles/expensify-classic/billing-and-subscriptions/Change-Plan-Or-Subscription.md
@@ -0,0 +1,5 @@
+---
+title: Change Plan or Subscription
+description: Change Plan or Subscription
+---
+## Resources Coming Soon!
diff --git a/docs/articles/expensify-classic/billing-and-subscriptions/Consolidated-Domain-Billing.md b/docs/articles/expensify-classic/billing-and-subscriptions/Consolidated-Domain-Billing.md
new file mode 100644
index 000000000000..de6ec4a4a466
--- /dev/null
+++ b/docs/articles/expensify-classic/billing-and-subscriptions/Consolidated-Domain-Billing.md
@@ -0,0 +1,5 @@
+---
+title: Consolidated Domain Billing
+description: Consolidated Domain Billing
+---
+## Resources Coming Soon!
diff --git a/docs/articles/expensify-classic/billing-and-subscriptions/Free-Trial.md b/docs/articles/expensify-classic/billing-and-subscriptions/Free-Trial.md
new file mode 100644
index 000000000000..8a7b7edd19d9
--- /dev/null
+++ b/docs/articles/expensify-classic/billing-and-subscriptions/Free-Trial.md
@@ -0,0 +1,5 @@
+---
+title: Free Trial
+description: Free Trial
+---
+## Resources Coming Soon!
diff --git a/docs/articles/expensify-classic/billing-and-subscriptions/Individual-Subscription.md b/docs/articles/expensify-classic/billing-and-subscriptions/Individual-Subscription.md
new file mode 100644
index 000000000000..d6be489a1146
--- /dev/null
+++ b/docs/articles/expensify-classic/billing-and-subscriptions/Individual-Subscription.md
@@ -0,0 +1,5 @@
+---
+title: Individual Subscription
+description: Individual Subscription
+---
+## Resources Coming Soon!
diff --git a/docs/articles/expensify-classic/billing-and-subscriptions/Overview.md b/docs/articles/expensify-classic/billing-and-subscriptions/Overview.md
new file mode 100644
index 000000000000..3352c72167cd
--- /dev/null
+++ b/docs/articles/expensify-classic/billing-and-subscriptions/Overview.md
@@ -0,0 +1,5 @@
+---
+title: Overview
+description: Overview
+---
+## Resources Coming Soon!
diff --git a/docs/articles/expensify-classic/billing-and-subscriptions/Pay-Per-Use-Subscription.md b/docs/articles/expensify-classic/billing-and-subscriptions/Pay-Per-Use-Subscription.md
new file mode 100644
index 000000000000..be431a287557
--- /dev/null
+++ b/docs/articles/expensify-classic/billing-and-subscriptions/Pay-Per-Use-Subscription.md
@@ -0,0 +1,5 @@
+---
+title: Pay-per-use Subscription
+description: Pay-per-use Subscription
+---
+## Resources Coming Soon!
diff --git a/docs/articles/expensify-classic/billing-and-subscriptions/Payment-Card.md b/docs/articles/expensify-classic/billing-and-subscriptions/Payment-Card.md
new file mode 100644
index 000000000000..91c5d4e91eda
--- /dev/null
+++ b/docs/articles/expensify-classic/billing-and-subscriptions/Payment-Card.md
@@ -0,0 +1,5 @@
+---
+title: Payment Card
+description: Payment Card
+---
+## Resources Coming Soon!
diff --git a/docs/articles/expensify-classic/billing-and-subscriptions/Tax-Exempt.md b/docs/articles/expensify-classic/billing-and-subscriptions/Tax-Exempt.md
new file mode 100644
index 000000000000..c8f781cbd59b
--- /dev/null
+++ b/docs/articles/expensify-classic/billing-and-subscriptions/Tax-Exempt.md
@@ -0,0 +1,5 @@
+---
+title: Tax Exempt
+description: Tax Exempt
+---
+## Resources Coming Soon!
diff --git a/docs/articles/expensify-classic/expense-and-report-features/Attendee-Tracking.md b/docs/articles/expensify-classic/expense-and-report-features/Attendee-Tracking.md
new file mode 100644
index 000000000000..bc7fbdfe84aa
--- /dev/null
+++ b/docs/articles/expensify-classic/expense-and-report-features/Attendee-Tracking.md
@@ -0,0 +1,5 @@
+---
+title: Attendee Tracking
+description: Attendee Tracking
+---
+## Resources Coming Soon!
diff --git a/docs/articles/expensify-classic/expense-and-report-features/Currency.md b/docs/articles/expensify-classic/expense-and-report-features/Currency.md
new file mode 100644
index 000000000000..611365aa5013
--- /dev/null
+++ b/docs/articles/expensify-classic/expense-and-report-features/Currency.md
@@ -0,0 +1,5 @@
+---
+title: Currency
+description: Currency
+---
+## Resources Coming Soon!
diff --git a/docs/articles/expensify-classic/expense-and-report-features/Expense-Rules.md b/docs/articles/expensify-classic/expense-and-report-features/Expense-Rules.md
new file mode 100644
index 000000000000..81c664497e14
--- /dev/null
+++ b/docs/articles/expensify-classic/expense-and-report-features/Expense-Rules.md
@@ -0,0 +1,5 @@
+---
+title: Expense Rules
+description: Expense Rules
+---
+## Resources Coming Soon!
diff --git a/docs/articles/expensify-classic/expense-and-report-features/Expense-Types.md b/docs/articles/expensify-classic/expense-and-report-features/Expense-Types.md
new file mode 100644
index 000000000000..a75209e4dfb1
--- /dev/null
+++ b/docs/articles/expensify-classic/expense-and-report-features/Expense-Types.md
@@ -0,0 +1,5 @@
+---
+title: Expense Types
+description: Expense Types
+---
+## Resources Coming Soon!
diff --git a/docs/articles/expensify-classic/expense-and-report-features/Report-Comments.md b/docs/articles/expensify-classic/expense-and-report-features/Report-Comments.md
new file mode 100644
index 000000000000..3938c02bd333
--- /dev/null
+++ b/docs/articles/expensify-classic/expense-and-report-features/Report-Comments.md
@@ -0,0 +1,5 @@
+---
+title: Report Comments
+description: Report Comments
+---
+## Resources Coming Soon!
diff --git a/docs/articles/expensify-classic/expense-and-report-features/The-Expenses-Page.md b/docs/articles/expensify-classic/expense-and-report-features/The-Expenses-Page.md
new file mode 100644
index 000000000000..f202587568e5
--- /dev/null
+++ b/docs/articles/expensify-classic/expense-and-report-features/The-Expenses-Page.md
@@ -0,0 +1,5 @@
+---
+title: The Expenses Page
+description: The Expenses Page
+---
+## Resources Coming Soon!
diff --git a/docs/articles/expensify-classic/expense-and-report-features/The-Reports-Page.md b/docs/articles/expensify-classic/expense-and-report-features/The-Reports-Page.md
new file mode 100644
index 000000000000..37da613e750a
--- /dev/null
+++ b/docs/articles/expensify-classic/expense-and-report-features/The-Reports-Page.md
@@ -0,0 +1,5 @@
+---
+title: The Reports Page
+description: The Reports Page
+---
+## Resources Coming Soon!
diff --git a/docs/articles/expensify-classic/expensify-card/Auto-Reconciliation.md b/docs/articles/expensify-classic/expensify-card/Auto-Reconciliation.md
new file mode 100644
index 000000000000..e1d1a990b166
--- /dev/null
+++ b/docs/articles/expensify-classic/expensify-card/Auto-Reconciliation.md
@@ -0,0 +1,5 @@
+---
+title: Auto-reconciliation
+description: Auto-reconciliation
+---
+## Resources Coming Soon!
diff --git a/docs/articles/expensify-classic/expensify-card/CPA-Card.md b/docs/articles/expensify-classic/expensify-card/CPA-Card.md
new file mode 100644
index 000000000000..9f4c47a6a402
--- /dev/null
+++ b/docs/articles/expensify-classic/expensify-card/CPA-Card.md
@@ -0,0 +1,5 @@
+---
+title: CPA Card
+description: CPA Card
+---
+## Resources Coming Soon!
diff --git a/docs/articles/expensify-classic/expensify-card/Card-Settings.md b/docs/articles/expensify-classic/expensify-card/Card-Settings.md
new file mode 100644
index 000000000000..ff9a959d38aa
--- /dev/null
+++ b/docs/articles/expensify-classic/expensify-card/Card-Settings.md
@@ -0,0 +1,5 @@
+---
+title: Card Settings
+description: Card Settings
+---
+## Resources Coming Soon!
diff --git a/docs/articles/expensify-classic/expensify-card/Connect-To-Indirect-Integration.md b/docs/articles/expensify-classic/expensify-card/Connect-To-Indirect-Integration.md
new file mode 100644
index 000000000000..0e05269f6501
--- /dev/null
+++ b/docs/articles/expensify-classic/expensify-card/Connect-To-Indirect-Integration.md
@@ -0,0 +1,5 @@
+---
+title: Connect to Indirect Integration
+description: Connect to Indirect Integration
+---
+## Resources Coming Soon!
diff --git a/docs/articles/expensify-classic/expensify-card/File-A-Dispute.md b/docs/articles/expensify-classic/expensify-card/File-A-Dispute.md
new file mode 100644
index 000000000000..296999410687
--- /dev/null
+++ b/docs/articles/expensify-classic/expensify-card/File-A-Dispute.md
@@ -0,0 +1,5 @@
+---
+title: File a Dispute
+description: File a Dispute
+---
+## Resources Coming Soon!
diff --git a/docs/articles/expensify-classic/expensify-card/Get-The-Card.md b/docs/articles/expensify-classic/expensify-card/Get-The-Card.md
new file mode 100644
index 000000000000..9c8e804f6363
--- /dev/null
+++ b/docs/articles/expensify-classic/expensify-card/Get-The-Card.md
@@ -0,0 +1,5 @@
+---
+title: Get the Card
+description: Get the Card
+---
+## Resources Coming Soon!
diff --git a/docs/articles/expensify-classic/expensify-card/Statements.md b/docs/articles/expensify-classic/expensify-card/Statements.md
new file mode 100644
index 000000000000..602fa610dd0b
--- /dev/null
+++ b/docs/articles/expensify-classic/expensify-card/Statements.md
@@ -0,0 +1,5 @@
+---
+title: Statements
+description: Statements
+---
+## Resources Coming Soon!
diff --git a/docs/articles/expensify-classic/expensify-card/The-Reports-Page.md b/docs/articles/expensify-classic/expensify-card/The-Reports-Page.md
new file mode 100644
index 000000000000..37da613e750a
--- /dev/null
+++ b/docs/articles/expensify-classic/expensify-card/The-Reports-Page.md
@@ -0,0 +1,5 @@
+---
+title: The Reports Page
+description: The Reports Page
+---
+## Resources Coming Soon!
diff --git a/docs/articles/expensify-classic/exports/Custom-Templates.md b/docs/articles/expensify-classic/exports/Custom-Templates.md
new file mode 100644
index 000000000000..5dcfe58b09f5
--- /dev/null
+++ b/docs/articles/expensify-classic/exports/Custom-Templates.md
@@ -0,0 +1,5 @@
+---
+title: Custom Templates
+description: Custom Templates
+---
+## Resources Coming Soon!
diff --git a/docs/articles/expensify-classic/exports/Default-Export-Templates.md b/docs/articles/expensify-classic/exports/Default-Export-Templates.md
new file mode 100644
index 000000000000..4dcb624698af
--- /dev/null
+++ b/docs/articles/expensify-classic/exports/Default-Export-Templates.md
@@ -0,0 +1,5 @@
+---
+title: Default Export Templates
+description: Default Export Templates
+---
+## Resources Coming Soon!
diff --git a/docs/articles/other/Insights.md b/docs/articles/expensify-classic/exports/Insights.md
similarity index 100%
rename from docs/articles/other/Insights.md
rename to docs/articles/expensify-classic/exports/Insights.md
diff --git a/docs/articles/expensify-classic/exports/The-Reports-Page.md b/docs/articles/expensify-classic/exports/The-Reports-Page.md
new file mode 100644
index 000000000000..37da613e750a
--- /dev/null
+++ b/docs/articles/expensify-classic/exports/The-Reports-Page.md
@@ -0,0 +1,5 @@
+---
+title: The Reports Page
+description: The Reports Page
+---
+## Resources Coming Soon!
diff --git a/docs/articles/expensify-classic/get-paid-back/Mileage.md b/docs/articles/expensify-classic/get-paid-back/Mileage.md
new file mode 100644
index 000000000000..381bc28626f9
--- /dev/null
+++ b/docs/articles/expensify-classic/get-paid-back/Mileage.md
@@ -0,0 +1,5 @@
+---
+title: Mileage
+description: Mileage
+---
+## Resources Coming Soon!
diff --git a/docs/articles/expensify-classic/get-paid-back/Per-Diem.md b/docs/articles/expensify-classic/get-paid-back/Per-Diem.md
new file mode 100644
index 000000000000..e5a57fc62bdf
--- /dev/null
+++ b/docs/articles/expensify-classic/get-paid-back/Per-Diem.md
@@ -0,0 +1,5 @@
+---
+title: Per Diem
+description: Per Diem
+---
+## Resources Coming Soon!
diff --git a/docs/articles/expensify-classic/get-paid-back/Third-Party-Payments.md b/docs/articles/expensify-classic/get-paid-back/Third-Party-Payments.md
new file mode 100644
index 000000000000..d472e54778e1
--- /dev/null
+++ b/docs/articles/expensify-classic/get-paid-back/Third-Party-Payments.md
@@ -0,0 +1,5 @@
+---
+title: Third Party Payments
+description: Third Party Payments
+---
+## Resources Coming Soon!
diff --git a/docs/articles/expensify-classic/get-paid-back/Trips.md b/docs/articles/expensify-classic/get-paid-back/Trips.md
new file mode 100644
index 000000000000..3499865c4ee9
--- /dev/null
+++ b/docs/articles/expensify-classic/get-paid-back/Trips.md
@@ -0,0 +1,5 @@
+---
+title: Trips
+description: Trips
+---
+## Resources Coming Soon!
diff --git a/docs/articles/expensify-classic/get-paid-back/expenses/Apply-Tax.md b/docs/articles/expensify-classic/get-paid-back/expenses/Apply-Tax.md
new file mode 100644
index 000000000000..224b622cec3f
--- /dev/null
+++ b/docs/articles/expensify-classic/get-paid-back/expenses/Apply-Tax.md
@@ -0,0 +1,5 @@
+---
+title: Apply Tax
+description: Apply Tax
+---
+## Resources Coming Soon!
diff --git a/docs/articles/expensify-classic/get-paid-back/expenses/Create-Expenses.md b/docs/articles/expensify-classic/get-paid-back/expenses/Create-Expenses.md
new file mode 100644
index 000000000000..8f4d035e1fe7
--- /dev/null
+++ b/docs/articles/expensify-classic/get-paid-back/expenses/Create-Expenses.md
@@ -0,0 +1,5 @@
+---
+title: Create Expenses
+description: Create Expenses
+---
+## Resources Coming Soon!
diff --git a/docs/articles/expensify-classic/get-paid-back/expenses/Merge-Expenses.md b/docs/articles/expensify-classic/get-paid-back/expenses/Merge-Expenses.md
new file mode 100644
index 000000000000..c628244c9b2e
--- /dev/null
+++ b/docs/articles/expensify-classic/get-paid-back/expenses/Merge-Expenses.md
@@ -0,0 +1,5 @@
+---
+title: Merge Expenses
+description: Merge Expenses
+---
+## Resources Coming Soon!
diff --git a/docs/articles/expensify-classic/get-paid-back/expenses/Upload-Receipts.md b/docs/articles/expensify-classic/get-paid-back/expenses/Upload-Receipts.md
new file mode 100644
index 000000000000..2091b5f3e7f0
--- /dev/null
+++ b/docs/articles/expensify-classic/get-paid-back/expenses/Upload-Receipts.md
@@ -0,0 +1,5 @@
+---
+title: Upload Receipts
+description: Upload Receipts
+---
+## Resources Coming Soon!
diff --git a/docs/articles/expensify-classic/get-paid-back/reports/Create-A-Report.md b/docs/articles/expensify-classic/get-paid-back/reports/Create-A-Report.md
new file mode 100644
index 000000000000..e6cc65290e73
--- /dev/null
+++ b/docs/articles/expensify-classic/get-paid-back/reports/Create-A-Report.md
@@ -0,0 +1,5 @@
+---
+title: Create a Report
+description: Create a Report
+---
+## Resources Coming Soon!
diff --git a/docs/articles/expensify-classic/get-paid-back/reports/Reimbursements.md b/docs/articles/expensify-classic/get-paid-back/reports/Reimbursements.md
new file mode 100644
index 000000000000..91c4459d2ebd
--- /dev/null
+++ b/docs/articles/expensify-classic/get-paid-back/reports/Reimbursements.md
@@ -0,0 +1,5 @@
+---
+title: Reimbursements
+description: Reimbursements
+---
+## Resources Coming Soon!
diff --git a/docs/articles/expensify-classic/getting-started/Best-Practices.md b/docs/articles/expensify-classic/getting-started/Best-Practices.md
new file mode 100644
index 000000000000..16b284ae60df
--- /dev/null
+++ b/docs/articles/expensify-classic/getting-started/Best-Practices.md
@@ -0,0 +1,5 @@
+---
+title: Best Practices
+description: Best Practices
+---
+## Resources Coming Soon!
diff --git a/docs/articles/expensify-classic/getting-started/Employees.md b/docs/articles/expensify-classic/getting-started/Employees.md
new file mode 100644
index 000000000000..f139c40be926
--- /dev/null
+++ b/docs/articles/expensify-classic/getting-started/Employees.md
@@ -0,0 +1,5 @@
+---
+title: Employees
+description: Employees
+---
+## Resources Coming Soon!
diff --git a/docs/articles/expensify-classic/getting-started/Individual-Users.md b/docs/articles/expensify-classic/getting-started/Individual-Users.md
new file mode 100644
index 000000000000..2e152ea515d7
--- /dev/null
+++ b/docs/articles/expensify-classic/getting-started/Individual-Users.md
@@ -0,0 +1,5 @@
+---
+title: Individual Users
+description: Individual Users
+---
+## Resources Coming Soon!
diff --git a/docs/articles/expensify-classic/getting-started/Invite-Employees.md b/docs/articles/expensify-classic/getting-started/Invite-Employees.md
new file mode 100644
index 000000000000..5cdb8eb086b0
--- /dev/null
+++ b/docs/articles/expensify-classic/getting-started/Invite-Employees.md
@@ -0,0 +1,5 @@
+---
+title: Invite Employees
+description: Invite Employees
+---
+## Resources Coming Soon!
diff --git a/docs/articles/expensify-classic/getting-started/Plan-Types.md b/docs/articles/expensify-classic/getting-started/Plan-Types.md
new file mode 100644
index 000000000000..7bb725a1aa35
--- /dev/null
+++ b/docs/articles/expensify-classic/getting-started/Plan-Types.md
@@ -0,0 +1,5 @@
+---
+title: Plan-Types
+description: Plan-Types
+---
+## Resources Coming Soon!
diff --git a/docs/articles/expensify-classic/getting-started/Policy-Admins.md b/docs/articles/expensify-classic/getting-started/Policy-Admins.md
new file mode 100644
index 000000000000..91d56b0c4f71
--- /dev/null
+++ b/docs/articles/expensify-classic/getting-started/Policy-Admins.md
@@ -0,0 +1,5 @@
+---
+title: Policy Admins
+description: Policy Admins
+---
+## Resources Coming Soon!
diff --git a/docs/articles/other/Referral-Program.md b/docs/articles/expensify-classic/getting-started/Referral-Program.md
similarity index 98%
rename from docs/articles/other/Referral-Program.md
rename to docs/articles/expensify-classic/getting-started/Referral-Program.md
index 1faff1c9ec4f..683e93d0277a 100644
--- a/docs/articles/other/Referral-Program.md
+++ b/docs/articles/expensify-classic/getting-started/Referral-Program.md
@@ -50,4 +50,4 @@ Please send a message to concierge@expensify.com with the billing owner of the c
Expensify members who are opted-in for our newsletters will have received an email containing their unique referral link.
-On the mobile app, go to **Settings** > **Invite a Friend** > **Share Invite Link** to retrieve your referral link.
+On the mobile app, go to **Settings** > **Invite a Friend** > **Share Invite Link** to retrieve your referral link.
diff --git a/docs/articles/expensify-classic/getting-started/Security.md b/docs/articles/expensify-classic/getting-started/Security.md
new file mode 100644
index 000000000000..41451e2ba958
--- /dev/null
+++ b/docs/articles/expensify-classic/getting-started/Security.md
@@ -0,0 +1,5 @@
+---
+title: Security
+description: Security
+---
+## Resources Coming Soon!
diff --git a/docs/articles/other/Your-Expensify-Account-Manager.md b/docs/articles/expensify-classic/getting-started/Support/Your-Expensify-Account-Manager.md
similarity index 99%
rename from docs/articles/other/Your-Expensify-Account-Manager.md
rename to docs/articles/expensify-classic/getting-started/Support/Your-Expensify-Account-Manager.md
index 70e0435e00e1..3ef47337a74c 100644
--- a/docs/articles/other/Your-Expensify-Account-Manager.md
+++ b/docs/articles/expensify-classic/getting-started/Support/Your-Expensify-Account-Manager.md
@@ -33,4 +33,4 @@ You will be able to see if they are online via their status, which will either s
If for some reason, you’re unable to reach your account manager, perhaps because they’re offline, then you can always reach out to Concierge for assistance. Your account manager will always get back to you when they’re online again.
## Can I get on a call with my account manager?
-Of course! You can ask your account manager to schedule a call whenever you think one might be helpful. We usually find that the most effective calls are those that deal with general setup questions. For technical troubleshooting, we typically recommend chat as that allows your account manager time to look into the issue, test things on their end, and, if needed, consult the wider Expensify technical team. It also allows you to easily refer back to instructions and links.
+Of course! You can ask your account manager to schedule a call whenever you think one might be helpful. We usually find that the most effective calls are those that deal with general setup questions. For technical troubleshooting, we typically recommend chat as that allows your account manager time to look into the issue, test things on their end, and, if needed, consult the wider Expensify technical team. It also allows you to easily refer back to instructions and links.
\ No newline at end of file
diff --git a/docs/articles/expensify-classic/getting-started/Using-The-App.md b/docs/articles/expensify-classic/getting-started/Using-The-App.md
new file mode 100644
index 000000000000..37767ea9d78d
--- /dev/null
+++ b/docs/articles/expensify-classic/getting-started/Using-The-App.md
@@ -0,0 +1,5 @@
+---
+title: Using the App
+description: Using the App
+---
+## Resources Coming Soon!
diff --git a/docs/articles/other/Card-Revenue-Share-for-ExpensifyApproved!-Partners.md b/docs/articles/expensify-classic/getting-started/approved-accountants/Card-Revenue-Share-For-Expensify-Approved-Partners.md
similarity index 99%
rename from docs/articles/other/Card-Revenue-Share-for-ExpensifyApproved!-Partners.md
rename to docs/articles/expensify-classic/getting-started/approved-accountants/Card-Revenue-Share-For-Expensify-Approved-Partners.md
index 44614d506d49..b18531d43200 100644
--- a/docs/articles/other/Card-Revenue-Share-for-ExpensifyApproved!-Partners.md
+++ b/docs/articles/expensify-classic/getting-started/approved-accountants/Card-Revenue-Share-For-Expensify-Approved-Partners.md
@@ -13,4 +13,4 @@ To benefit from this program, all you need to do is ensure that you are listed a
- What if my firm is not permitted to accept revenue share from our clients?
We understand that different firms may have different policies. If your firm is unable to accept this revenue share, you can pass the revenue share back to your client to give them an additional 0.5% of cash back using your own internal payment tools.
- What if my firm does not wish to participate in the program?
- Please reach out to your assigned partner manager at new.expensify.com to inform them you would not like to accept the revenue share nor do you want to pass the revenue share to your clients.
+ Please reach out to your assigned partner manager at new.expensify.com to inform them you would not like to accept the revenue share nor do you want to pass the revenue share to your clients.
\ No newline at end of file
diff --git a/docs/articles/other/Your-Expensify-Partner-Manager.md b/docs/articles/expensify-classic/getting-started/approved-accountants/Your-Expensify-Partner-Manager.md
similarity index 99%
rename from docs/articles/other/Your-Expensify-Partner-Manager.md
rename to docs/articles/expensify-classic/getting-started/approved-accountants/Your-Expensify-Partner-Manager.md
index 9a68fbfd8b39..c7a5dc5a04ab 100644
--- a/docs/articles/other/Your-Expensify-Partner-Manager.md
+++ b/docs/articles/expensify-classic/getting-started/approved-accountants/Your-Expensify-Partner-Manager.md
@@ -31,4 +31,4 @@ If you’re unable to contact your Partner Manager (i.e., they're out of office
## Can I get on a call with my Partner Manager?
Of course! You can ask your Partner Manager to schedule a call whenever you think one might be helpful. Partner Managers can discuss client onboarding strategies, firm wide training, and client setups.
-We recommend continuing to work with Concierge for **general support questions**, as this team is always online and available to help immediately.
+We recommend continuing to work with Concierge for **general support questions**, as this team is always online and available to help immediately.
\ No newline at end of file
diff --git a/docs/articles/playbooks/Expensify-Playbook-for-Small-to-Medium-Sized-Businesses.md b/docs/articles/expensify-classic/getting-started/playbooks/Expensify-Playbook-For-Small-To-Medium-Sized-Businesses.md
similarity index 99%
rename from docs/articles/playbooks/Expensify-Playbook-for-Small-to-Medium-Sized-Businesses.md
rename to docs/articles/expensify-classic/getting-started/playbooks/Expensify-Playbook-For-Small-To-Medium-Sized-Businesses.md
index a4004dbe1b88..2b95a1d13fde 100644
--- a/docs/articles/playbooks/Expensify-Playbook-for-Small-to-Medium-Sized-Businesses.md
+++ b/docs/articles/expensify-classic/getting-started/playbooks/Expensify-Playbook-For-Small-To-Medium-Sized-Businesses.md
@@ -280,4 +280,4 @@ Now that we’ve gone through all of the steps for setting up your account, let
4. Click *Accept Terms*
## You’re all set!
-Congrats, you are all set up! If you need any assistance with anything mentioned above or would like to understand other features available in Expensify, reach out to your Setup Specialist directly in *[new.expensify.com](https://new.expensify.com)*. Don’t have one yet? Create a Control Policy, and we’ll automatically assign a dedicated Setup Specialist to you.
+Congrats, you are all set up! If you need any assistance with anything mentioned above or would like to understand other features available in Expensify, reach out to your Setup Specialist directly in *[new.expensify.com](https://new.expensify.com)*. Don’t have one yet? Create a Control Policy, and we’ll automatically assign a dedicated Setup Specialist to you.
diff --git a/docs/articles/playbooks/Expensify-Playbook-for-US-Based-Bootstrapped-Startups.md b/docs/articles/expensify-classic/getting-started/playbooks/Expensify-Playbook-For-US-Based-Bootstrapped-Startups.md
similarity index 99%
rename from docs/articles/playbooks/Expensify-Playbook-for-US-Based-Bootstrapped-Startups.md
rename to docs/articles/expensify-classic/getting-started/playbooks/Expensify-Playbook-For-US-Based-Bootstrapped-Startups.md
index 089ad16834ac..86c6a583c758 100644
--- a/docs/articles/playbooks/Expensify-Playbook-for-US-Based-Bootstrapped-Startups.md
+++ b/docs/articles/expensify-classic/getting-started/playbooks/Expensify-Playbook-For-US-Based-Bootstrapped-Startups.md
@@ -88,4 +88,3 @@ When you have bills to pay you can click *View all bills* under the *Manage your
# You’re all set!
Congrats, you are all set up! If you need any assistance with anything mentioned above, reach out to either your Concierge directly in *[new.expensify.com](https://new.expensify.com/concierge)*, or email concierge@expensify.com. Create a Collect or Control Policy, and we’ll automatically assign a dedicated Setup Specialist to you.
-
diff --git a/docs/articles/playbooks/Expensify-Playbook-for-US-based-VC-Backed-Startups.md b/docs/articles/expensify-classic/getting-started/playbooks/Expensify-Playbook-For-US-Based-VC-Backed-Startups.md
similarity index 100%
rename from docs/articles/playbooks/Expensify-Playbook-for-US-based-VC-Backed-Startups.md
rename to docs/articles/expensify-classic/getting-started/playbooks/Expensify-Playbook-For-US-Based-VC-Backed-Startups.md
diff --git a/docs/articles/expensify-classic/getting-started/tips-and-tricks.md b/docs/articles/expensify-classic/getting-started/tips-and-tricks.md
new file mode 100644
index 000000000000..d85c7f3a0cb9
--- /dev/null
+++ b/docs/articles/expensify-classic/getting-started/tips-and-tricks.md
@@ -0,0 +1,5 @@
+---
+title: Tips and Tricks
+description: Tips and Tricks
+---
+## Resources Coming Soon!
diff --git a/docs/articles/other/Enable-Location-Access-on-Web.md b/docs/articles/expensify-classic/getting-started/tips-and-tricks/Enable-Location-Access-On-Web.md
similarity index 99%
rename from docs/articles/other/Enable-Location-Access-on-Web.md
rename to docs/articles/expensify-classic/getting-started/tips-and-tricks/Enable-Location-Access-On-Web.md
index 6cc0d19e4cde..649212b00f7b 100644
--- a/docs/articles/other/Enable-Location-Access-on-Web.md
+++ b/docs/articles/expensify-classic/getting-started/tips-and-tricks/Enable-Location-Access-On-Web.md
@@ -52,4 +52,4 @@ Ask: The site must ask if it can use your location.
Deny: The site can’t use your location.
Allow: The site can always use your location.
-[Safari help page](https://support.apple.com/guide/safari/websites-ibrwe2159f50/mac)
+[Safari help page](https://support.apple.com/guide/safari/websites-ibrwe2159f50/mac)
\ No newline at end of file
diff --git a/docs/articles/expensify-classic/integrations/accounting-integrations/Bill-dot-com.md b/docs/articles/expensify-classic/integrations/accounting-integrations/Bill-dot-com.md
new file mode 100644
index 000000000000..4c91b7095a4a
--- /dev/null
+++ b/docs/articles/expensify-classic/integrations/accounting-integrations/Bill-dot-com.md
@@ -0,0 +1,5 @@
+---
+title: Coming Soon
+description: Coming Soon
+---
+## Resources Coming Soon!
diff --git a/docs/articles/expensify-classic/integrations/accounting-integrations/FinancalForce.md b/docs/articles/expensify-classic/integrations/accounting-integrations/FinancalForce.md
new file mode 100644
index 000000000000..4c91b7095a4a
--- /dev/null
+++ b/docs/articles/expensify-classic/integrations/accounting-integrations/FinancalForce.md
@@ -0,0 +1,5 @@
+---
+title: Coming Soon
+description: Coming Soon
+---
+## Resources Coming Soon!
diff --git a/docs/articles/expensify-classic/integrations/accounting-integrations/NetSuite.md b/docs/articles/expensify-classic/integrations/accounting-integrations/NetSuite.md
new file mode 100644
index 000000000000..4c91b7095a4a
--- /dev/null
+++ b/docs/articles/expensify-classic/integrations/accounting-integrations/NetSuite.md
@@ -0,0 +1,5 @@
+---
+title: Coming Soon
+description: Coming Soon
+---
+## Resources Coming Soon!
diff --git a/docs/articles/expensify-classic/integrations/accounting-integrations/QuickBooks-Desktop.md b/docs/articles/expensify-classic/integrations/accounting-integrations/QuickBooks-Desktop.md
new file mode 100644
index 000000000000..4c91b7095a4a
--- /dev/null
+++ b/docs/articles/expensify-classic/integrations/accounting-integrations/QuickBooks-Desktop.md
@@ -0,0 +1,5 @@
+---
+title: Coming Soon
+description: Coming Soon
+---
+## Resources Coming Soon!
diff --git a/docs/articles/expensify-classic/integrations/accounting-integrations/QuickBooks-Online.md b/docs/articles/expensify-classic/integrations/accounting-integrations/QuickBooks-Online.md
new file mode 100644
index 000000000000..4c91b7095a4a
--- /dev/null
+++ b/docs/articles/expensify-classic/integrations/accounting-integrations/QuickBooks-Online.md
@@ -0,0 +1,5 @@
+---
+title: Coming Soon
+description: Coming Soon
+---
+## Resources Coming Soon!
diff --git a/docs/articles/expensify-classic/integrations/accounting-integrations/Sage-Intacct.md b/docs/articles/expensify-classic/integrations/accounting-integrations/Sage-Intacct.md
new file mode 100644
index 000000000000..4c91b7095a4a
--- /dev/null
+++ b/docs/articles/expensify-classic/integrations/accounting-integrations/Sage-Intacct.md
@@ -0,0 +1,5 @@
+---
+title: Coming Soon
+description: Coming Soon
+---
+## Resources Coming Soon!
diff --git a/docs/articles/expensify-classic/integrations/accounting-integrations/Xero.md b/docs/articles/expensify-classic/integrations/accounting-integrations/Xero.md
new file mode 100644
index 000000000000..4c91b7095a4a
--- /dev/null
+++ b/docs/articles/expensify-classic/integrations/accounting-integrations/Xero.md
@@ -0,0 +1,5 @@
+---
+title: Coming Soon
+description: Coming Soon
+---
+## Resources Coming Soon!
diff --git a/docs/articles/expensify-classic/integrations/hr-integrations/ADP.md b/docs/articles/expensify-classic/integrations/hr-integrations/ADP.md
new file mode 100644
index 000000000000..4c91b7095a4a
--- /dev/null
+++ b/docs/articles/expensify-classic/integrations/hr-integrations/ADP.md
@@ -0,0 +1,5 @@
+---
+title: Coming Soon
+description: Coming Soon
+---
+## Resources Coming Soon!
diff --git a/docs/articles/expensify-classic/integrations/hr-integrations/Greenhouse.md b/docs/articles/expensify-classic/integrations/hr-integrations/Greenhouse.md
new file mode 100644
index 000000000000..4c91b7095a4a
--- /dev/null
+++ b/docs/articles/expensify-classic/integrations/hr-integrations/Greenhouse.md
@@ -0,0 +1,5 @@
+---
+title: Coming Soon
+description: Coming Soon
+---
+## Resources Coming Soon!
diff --git a/docs/articles/expensify-classic/integrations/hr-integrations/Gusto.md b/docs/articles/expensify-classic/integrations/hr-integrations/Gusto.md
new file mode 100644
index 000000000000..4c91b7095a4a
--- /dev/null
+++ b/docs/articles/expensify-classic/integrations/hr-integrations/Gusto.md
@@ -0,0 +1,5 @@
+---
+title: Coming Soon
+description: Coming Soon
+---
+## Resources Coming Soon!
diff --git a/docs/articles/expensify-classic/integrations/hr-integrations/QuickBooks-Time.md b/docs/articles/expensify-classic/integrations/hr-integrations/QuickBooks-Time.md
new file mode 100644
index 000000000000..4c91b7095a4a
--- /dev/null
+++ b/docs/articles/expensify-classic/integrations/hr-integrations/QuickBooks-Time.md
@@ -0,0 +1,5 @@
+---
+title: Coming Soon
+description: Coming Soon
+---
+## Resources Coming Soon!
diff --git a/docs/articles/expensify-classic/integrations/hr-integrations/Rippling.md b/docs/articles/expensify-classic/integrations/hr-integrations/Rippling.md
new file mode 100644
index 000000000000..4c91b7095a4a
--- /dev/null
+++ b/docs/articles/expensify-classic/integrations/hr-integrations/Rippling.md
@@ -0,0 +1,5 @@
+---
+title: Coming Soon
+description: Coming Soon
+---
+## Resources Coming Soon!
diff --git a/docs/articles/expensify-classic/integrations/hr-integrations/Workday.md b/docs/articles/expensify-classic/integrations/hr-integrations/Workday.md
new file mode 100644
index 000000000000..4c91b7095a4a
--- /dev/null
+++ b/docs/articles/expensify-classic/integrations/hr-integrations/Workday.md
@@ -0,0 +1,5 @@
+---
+title: Coming Soon
+description: Coming Soon
+---
+## Resources Coming Soon!
diff --git a/docs/articles/expensify-classic/integrations/hr-integrations/Zenefits.md b/docs/articles/expensify-classic/integrations/hr-integrations/Zenefits.md
new file mode 100644
index 000000000000..4c91b7095a4a
--- /dev/null
+++ b/docs/articles/expensify-classic/integrations/hr-integrations/Zenefits.md
@@ -0,0 +1,5 @@
+---
+title: Coming Soon
+description: Coming Soon
+---
+## Resources Coming Soon!
diff --git a/docs/articles/expensify-classic/integrations/other-integrations/Google-Apps-SSO.md b/docs/articles/expensify-classic/integrations/other-integrations/Google-Apps-SSO.md
new file mode 100644
index 000000000000..4c91b7095a4a
--- /dev/null
+++ b/docs/articles/expensify-classic/integrations/other-integrations/Google-Apps-SSO.md
@@ -0,0 +1,5 @@
+---
+title: Coming Soon
+description: Coming Soon
+---
+## Resources Coming Soon!
diff --git a/docs/articles/expensify-classic/integrations/travel-integrations/Bolt.md b/docs/articles/expensify-classic/integrations/travel-integrations/Bolt.md
new file mode 100644
index 000000000000..4c91b7095a4a
--- /dev/null
+++ b/docs/articles/expensify-classic/integrations/travel-integrations/Bolt.md
@@ -0,0 +1,5 @@
+---
+title: Coming Soon
+description: Coming Soon
+---
+## Resources Coming Soon!
diff --git a/docs/articles/expensify-classic/integrations/travel-integrations/Egencia.md b/docs/articles/expensify-classic/integrations/travel-integrations/Egencia.md
new file mode 100644
index 000000000000..4c91b7095a4a
--- /dev/null
+++ b/docs/articles/expensify-classic/integrations/travel-integrations/Egencia.md
@@ -0,0 +1,5 @@
+---
+title: Coming Soon
+description: Coming Soon
+---
+## Resources Coming Soon!
diff --git a/docs/articles/expensify-classic/integrations/travel-integrations/Global-VaTax.md b/docs/articles/expensify-classic/integrations/travel-integrations/Global-VaTax.md
new file mode 100644
index 000000000000..4c91b7095a4a
--- /dev/null
+++ b/docs/articles/expensify-classic/integrations/travel-integrations/Global-VaTax.md
@@ -0,0 +1,5 @@
+---
+title: Coming Soon
+description: Coming Soon
+---
+## Resources Coming Soon!
diff --git a/docs/articles/expensify-classic/integrations/travel-integrations/Grab.md b/docs/articles/expensify-classic/integrations/travel-integrations/Grab.md
new file mode 100644
index 000000000000..4c91b7095a4a
--- /dev/null
+++ b/docs/articles/expensify-classic/integrations/travel-integrations/Grab.md
@@ -0,0 +1,5 @@
+---
+title: Coming Soon
+description: Coming Soon
+---
+## Resources Coming Soon!
diff --git a/docs/articles/expensify-classic/integrations/travel-integrations/Hotel-Tonight.md b/docs/articles/expensify-classic/integrations/travel-integrations/Hotel-Tonight.md
new file mode 100644
index 000000000000..4c91b7095a4a
--- /dev/null
+++ b/docs/articles/expensify-classic/integrations/travel-integrations/Hotel-Tonight.md
@@ -0,0 +1,5 @@
+---
+title: Coming Soon
+description: Coming Soon
+---
+## Resources Coming Soon!
diff --git a/docs/articles/expensify-classic/integrations/travel-integrations/Kayak.md b/docs/articles/expensify-classic/integrations/travel-integrations/Kayak.md
new file mode 100644
index 000000000000..4c91b7095a4a
--- /dev/null
+++ b/docs/articles/expensify-classic/integrations/travel-integrations/Kayak.md
@@ -0,0 +1,5 @@
+---
+title: Coming Soon
+description: Coming Soon
+---
+## Resources Coming Soon!
diff --git a/docs/articles/expensify-classic/integrations/travel-integrations/Lyft.md b/docs/articles/expensify-classic/integrations/travel-integrations/Lyft.md
new file mode 100644
index 000000000000..4c91b7095a4a
--- /dev/null
+++ b/docs/articles/expensify-classic/integrations/travel-integrations/Lyft.md
@@ -0,0 +1,5 @@
+---
+title: Coming Soon
+description: Coming Soon
+---
+## Resources Coming Soon!
diff --git a/docs/articles/expensify-classic/integrations/travel-integrations/TrainLine.md b/docs/articles/expensify-classic/integrations/travel-integrations/TrainLine.md
new file mode 100644
index 000000000000..4c91b7095a4a
--- /dev/null
+++ b/docs/articles/expensify-classic/integrations/travel-integrations/TrainLine.md
@@ -0,0 +1,5 @@
+---
+title: Coming Soon
+description: Coming Soon
+---
+## Resources Coming Soon!
diff --git a/docs/articles/expensify-classic/integrations/travel-integrations/TravelPerk.md b/docs/articles/expensify-classic/integrations/travel-integrations/TravelPerk.md
new file mode 100644
index 000000000000..4c91b7095a4a
--- /dev/null
+++ b/docs/articles/expensify-classic/integrations/travel-integrations/TravelPerk.md
@@ -0,0 +1,5 @@
+---
+title: Coming Soon
+description: Coming Soon
+---
+## Resources Coming Soon!
diff --git a/docs/articles/expensify-classic/integrations/travel-integrations/Trip-Actions.md b/docs/articles/expensify-classic/integrations/travel-integrations/Trip-Actions.md
new file mode 100644
index 000000000000..4c91b7095a4a
--- /dev/null
+++ b/docs/articles/expensify-classic/integrations/travel-integrations/Trip-Actions.md
@@ -0,0 +1,5 @@
+---
+title: Coming Soon
+description: Coming Soon
+---
+## Resources Coming Soon!
diff --git a/docs/articles/expensify-classic/integrations/travel-integrations/TripCatcher.md b/docs/articles/expensify-classic/integrations/travel-integrations/TripCatcher.md
new file mode 100644
index 000000000000..4c91b7095a4a
--- /dev/null
+++ b/docs/articles/expensify-classic/integrations/travel-integrations/TripCatcher.md
@@ -0,0 +1,5 @@
+---
+title: Coming Soon
+description: Coming Soon
+---
+## Resources Coming Soon!
diff --git a/docs/articles/expensify-classic/integrations/travel-integrations/Uber.md b/docs/articles/expensify-classic/integrations/travel-integrations/Uber.md
new file mode 100644
index 000000000000..4c91b7095a4a
--- /dev/null
+++ b/docs/articles/expensify-classic/integrations/travel-integrations/Uber.md
@@ -0,0 +1,5 @@
+---
+title: Coming Soon
+description: Coming Soon
+---
+## Resources Coming Soon!
diff --git a/docs/articles/expensify-classic/manage-employees-and-report-approvals/Adding-Users.md b/docs/articles/expensify-classic/manage-employees-and-report-approvals/Adding-Users.md
new file mode 100644
index 000000000000..4c91b7095a4a
--- /dev/null
+++ b/docs/articles/expensify-classic/manage-employees-and-report-approvals/Adding-Users.md
@@ -0,0 +1,5 @@
+---
+title: Coming Soon
+description: Coming Soon
+---
+## Resources Coming Soon!
diff --git a/docs/articles/expensify-classic/manage-employees-and-report-approvals/Approval-Workflows.md b/docs/articles/expensify-classic/manage-employees-and-report-approvals/Approval-Workflows.md
new file mode 100644
index 000000000000..4c91b7095a4a
--- /dev/null
+++ b/docs/articles/expensify-classic/manage-employees-and-report-approvals/Approval-Workflows.md
@@ -0,0 +1,5 @@
+---
+title: Coming Soon
+description: Coming Soon
+---
+## Resources Coming Soon!
diff --git a/docs/articles/expensify-classic/manage-employees-and-report-approvals/Approving-Reports.md b/docs/articles/expensify-classic/manage-employees-and-report-approvals/Approving-Reports.md
new file mode 100644
index 000000000000..4c91b7095a4a
--- /dev/null
+++ b/docs/articles/expensify-classic/manage-employees-and-report-approvals/Approving-Reports.md
@@ -0,0 +1,5 @@
+---
+title: Coming Soon
+description: Coming Soon
+---
+## Resources Coming Soon!
diff --git a/docs/articles/expensify-classic/manage-employees-and-report-approvals/User-Roles.md b/docs/articles/expensify-classic/manage-employees-and-report-approvals/User-Roles.md
new file mode 100644
index 000000000000..4c91b7095a4a
--- /dev/null
+++ b/docs/articles/expensify-classic/manage-employees-and-report-approvals/User-Roles.md
@@ -0,0 +1,5 @@
+---
+title: Coming Soon
+description: Coming Soon
+---
+## Resources Coming Soon!
diff --git a/docs/articles/expensify-classic/manage-employees-and-report-approvals/Vacation-Delegate.md b/docs/articles/expensify-classic/manage-employees-and-report-approvals/Vacation-Delegate.md
new file mode 100644
index 000000000000..e10e0fafb77d
--- /dev/null
+++ b/docs/articles/expensify-classic/manage-employees-and-report-approvals/Vacation-Delegate.md
@@ -0,0 +1,8 @@
+---
+title: Coming Soon
+description: Coming Soon
+---
+## Resources Coming Soon!
+
+
+Kayak.md Lyft.md TrainLine.md TravelPerk.md Trip Actions.md TripCatcher.md Uber.md
\ No newline at end of file
diff --git a/docs/articles/expensify-classic/policy-and-domain-settings/Admins.md b/docs/articles/expensify-classic/policy-and-domain-settings/Admins.md
new file mode 100644
index 000000000000..8c1267068d6b
--- /dev/null
+++ b/docs/articles/expensify-classic/policy-and-domain-settings/Admins.md
@@ -0,0 +1,5 @@
+---
+title: Admins
+description: Admins
+---
+## Resources Coming Soon!
diff --git a/docs/articles/expensify-classic/policy-and-domain-settings/Categories.md b/docs/articles/expensify-classic/policy-and-domain-settings/Categories.md
new file mode 100644
index 000000000000..00ade2b9d04f
--- /dev/null
+++ b/docs/articles/expensify-classic/policy-and-domain-settings/Categories.md
@@ -0,0 +1,5 @@
+---
+title: Categories
+description: Categories
+---
+## Resources Coming Soon!
diff --git a/docs/articles/expensify-classic/policy-and-domain-settings/Domain-Admins.md b/docs/articles/expensify-classic/policy-and-domain-settings/Domain-Admins.md
new file mode 100644
index 000000000000..4c91b7095a4a
--- /dev/null
+++ b/docs/articles/expensify-classic/policy-and-domain-settings/Domain-Admins.md
@@ -0,0 +1,5 @@
+---
+title: Coming Soon
+description: Coming Soon
+---
+## Resources Coming Soon!
diff --git a/docs/articles/expensify-classic/policy-and-domain-settings/Domain-Members.md b/docs/articles/expensify-classic/policy-and-domain-settings/Domain-Members.md
new file mode 100644
index 000000000000..4c91b7095a4a
--- /dev/null
+++ b/docs/articles/expensify-classic/policy-and-domain-settings/Domain-Members.md
@@ -0,0 +1,5 @@
+---
+title: Coming Soon
+description: Coming Soon
+---
+## Resources Coming Soon!
diff --git a/docs/articles/expensify-classic/policy-and-domain-settings/Domains-Overview.md b/docs/articles/expensify-classic/policy-and-domain-settings/Domains-Overview.md
new file mode 100644
index 000000000000..4c91b7095a4a
--- /dev/null
+++ b/docs/articles/expensify-classic/policy-and-domain-settings/Domains-Overview.md
@@ -0,0 +1,5 @@
+---
+title: Coming Soon
+description: Coming Soon
+---
+## Resources Coming Soon!
diff --git a/docs/articles/expensify-classic/policy-and-domain-settings/Expenses.md b/docs/articles/expensify-classic/policy-and-domain-settings/Expenses.md
new file mode 100644
index 000000000000..4c91b7095a4a
--- /dev/null
+++ b/docs/articles/expensify-classic/policy-and-domain-settings/Expenses.md
@@ -0,0 +1,5 @@
+---
+title: Coming Soon
+description: Coming Soon
+---
+## Resources Coming Soon!
diff --git a/docs/articles/expensify-classic/policy-and-domain-settings/Invoicing.md b/docs/articles/expensify-classic/policy-and-domain-settings/Invoicing.md
new file mode 100644
index 000000000000..4c91b7095a4a
--- /dev/null
+++ b/docs/articles/expensify-classic/policy-and-domain-settings/Invoicing.md
@@ -0,0 +1,5 @@
+---
+title: Coming Soon
+description: Coming Soon
+---
+## Resources Coming Soon!
diff --git a/docs/articles/expensify-classic/policy-and-domain-settings/Overview.md b/docs/articles/expensify-classic/policy-and-domain-settings/Overview.md
new file mode 100644
index 000000000000..4c91b7095a4a
--- /dev/null
+++ b/docs/articles/expensify-classic/policy-and-domain-settings/Overview.md
@@ -0,0 +1,5 @@
+---
+title: Coming Soon
+description: Coming Soon
+---
+## Resources Coming Soon!
diff --git a/docs/articles/expensify-classic/policy-and-domain-settings/Per-Diem.md b/docs/articles/expensify-classic/policy-and-domain-settings/Per-Diem.md
new file mode 100644
index 000000000000..4c91b7095a4a
--- /dev/null
+++ b/docs/articles/expensify-classic/policy-and-domain-settings/Per-Diem.md
@@ -0,0 +1,5 @@
+---
+title: Coming Soon
+description: Coming Soon
+---
+## Resources Coming Soon!
diff --git a/docs/articles/expensify-classic/policy-and-domain-settings/Reimbursement.md b/docs/articles/expensify-classic/policy-and-domain-settings/Reimbursement.md
new file mode 100644
index 000000000000..4c91b7095a4a
--- /dev/null
+++ b/docs/articles/expensify-classic/policy-and-domain-settings/Reimbursement.md
@@ -0,0 +1,5 @@
+---
+title: Coming Soon
+description: Coming Soon
+---
+## Resources Coming Soon!
diff --git a/docs/articles/expensify-classic/policy-and-domain-settings/Reports.md b/docs/articles/expensify-classic/policy-and-domain-settings/Reports.md
new file mode 100644
index 000000000000..4c91b7095a4a
--- /dev/null
+++ b/docs/articles/expensify-classic/policy-and-domain-settings/Reports.md
@@ -0,0 +1,5 @@
+---
+title: Coming Soon
+description: Coming Soon
+---
+## Resources Coming Soon!
diff --git a/docs/articles/expensify-classic/policy-and-domain-settings/SAML.md b/docs/articles/expensify-classic/policy-and-domain-settings/SAML.md
new file mode 100644
index 000000000000..4c91b7095a4a
--- /dev/null
+++ b/docs/articles/expensify-classic/policy-and-domain-settings/SAML.md
@@ -0,0 +1,5 @@
+---
+title: Coming Soon
+description: Coming Soon
+---
+## Resources Coming Soon!
diff --git a/docs/articles/expensify-classic/policy-and-domain-settings/Tags.md b/docs/articles/expensify-classic/policy-and-domain-settings/Tags.md
new file mode 100644
index 000000000000..4c91b7095a4a
--- /dev/null
+++ b/docs/articles/expensify-classic/policy-and-domain-settings/Tags.md
@@ -0,0 +1,5 @@
+---
+title: Coming Soon
+description: Coming Soon
+---
+## Resources Coming Soon!
diff --git a/docs/articles/expensify-classic/policy-and-domain-settings/Tax.md b/docs/articles/expensify-classic/policy-and-domain-settings/Tax.md
new file mode 100644
index 000000000000..4c91b7095a4a
--- /dev/null
+++ b/docs/articles/expensify-classic/policy-and-domain-settings/Tax.md
@@ -0,0 +1,5 @@
+---
+title: Coming Soon
+description: Coming Soon
+---
+## Resources Coming Soon!
diff --git a/docs/articles/expensify-classic/policy-and-domain-settings/Trips.md b/docs/articles/expensify-classic/policy-and-domain-settings/Trips.md
new file mode 100644
index 000000000000..4c91b7095a4a
--- /dev/null
+++ b/docs/articles/expensify-classic/policy-and-domain-settings/Trips.md
@@ -0,0 +1,5 @@
+---
+title: Coming Soon
+description: Coming Soon
+---
+## Resources Coming Soon!
diff --git a/docs/articles/expensify-classic/send-payments/Pay-Bills.md b/docs/articles/expensify-classic/send-payments/Pay-Bills.md
new file mode 100644
index 000000000000..e319196eb4bd
--- /dev/null
+++ b/docs/articles/expensify-classic/send-payments/Pay-Bills.md
@@ -0,0 +1,5 @@
+---
+title: Pay Bills
+description: Pay Bills
+---
+## Resources Coming Soon!
diff --git a/docs/articles/expensify-classic/send-payments/Pay-Invoices.md b/docs/articles/expensify-classic/send-payments/Pay-Invoices.md
new file mode 100644
index 000000000000..0ea4d28a731a
--- /dev/null
+++ b/docs/articles/expensify-classic/send-payments/Pay-Invoices.md
@@ -0,0 +1,5 @@
+---
+title: Pay Invoices
+description: Pay Invoices
+---
+## Resources Coming Soon!
diff --git a/docs/articles/expensify-classic/send-payments/Reimbursing-Reports.md b/docs/articles/expensify-classic/send-payments/Reimbursing-Reports.md
new file mode 100644
index 000000000000..6c3309310ba8
--- /dev/null
+++ b/docs/articles/expensify-classic/send-payments/Reimbursing-Reports.md
@@ -0,0 +1,5 @@
+---
+title: Reimbursing Reports
+description: Reimbursing Reports
+---
+## Resources Coming Soon!
diff --git a/docs/articles/expensify-classic/send-payments/Third-Party-Payments.md b/docs/articles/expensify-classic/send-payments/Third-Party-Payments.md
new file mode 100644
index 000000000000..4b1166cc9c00
--- /dev/null
+++ b/docs/articles/expensify-classic/send-payments/Third-Party-Payments.md
@@ -0,0 +1,8 @@
+---
+title: Third Party Payments
+description: Third Party Payments
+---
+## Resources Coming Soon!
+
+
+
\ No newline at end of file
diff --git a/docs/articles/new-expensify/account-settings/Coming-Soon.md b/docs/articles/new-expensify/account-settings/Coming-Soon.md
new file mode 100644
index 000000000000..6b85bb0364b5
--- /dev/null
+++ b/docs/articles/new-expensify/account-settings/Coming-Soon.md
@@ -0,0 +1,4 @@
+---
+title: Coming Soon
+description: Coming Soon
+---
diff --git a/docs/articles/new-expensify/bank-accounts-and-credit-cards/Coming-Soon.md b/docs/articles/new-expensify/bank-accounts-and-credit-cards/Coming-Soon.md
new file mode 100644
index 000000000000..6b85bb0364b5
--- /dev/null
+++ b/docs/articles/new-expensify/bank-accounts-and-credit-cards/Coming-Soon.md
@@ -0,0 +1,4 @@
+---
+title: Coming Soon
+description: Coming Soon
+---
diff --git a/docs/articles/split-bills/workspaces/The-Free-Plan.md b/docs/articles/new-expensify/billing-and-plan-types/The-Free-Plan.md
similarity index 98%
rename from docs/articles/split-bills/workspaces/The-Free-Plan.md
rename to docs/articles/new-expensify/billing-and-plan-types/The-Free-Plan.md
index 45c9d09d4777..0a8d6b3493e0 100644
--- a/docs/articles/split-bills/workspaces/The-Free-Plan.md
+++ b/docs/articles/new-expensify/billing-and-plan-types/The-Free-Plan.md
@@ -59,4 +59,4 @@ Categories are standardized on the Free Plan and can’t be edited. Custom categ
## With the Free Plan, can I export reports using a custom format?
-The Free Plan offers standard report export formats. You'll need to upgrade to a paid plan to create a custom export format.
+The Free Plan offers standard report export formats. You'll need to upgrade to a paid plan to create a custom export format.
\ No newline at end of file
diff --git a/docs/articles/new-expensify/expense-and-report-features/Coming-Soon.md b/docs/articles/new-expensify/expense-and-report-features/Coming-Soon.md
new file mode 100644
index 000000000000..6b85bb0364b5
--- /dev/null
+++ b/docs/articles/new-expensify/expense-and-report-features/Coming-Soon.md
@@ -0,0 +1,4 @@
+---
+title: Coming Soon
+description: Coming Soon
+---
diff --git a/docs/articles/new-expensify/expensify-card/Coming-Soon.md b/docs/articles/new-expensify/expensify-card/Coming-Soon.md
new file mode 100644
index 000000000000..6b85bb0364b5
--- /dev/null
+++ b/docs/articles/new-expensify/expensify-card/Coming-Soon.md
@@ -0,0 +1,4 @@
+---
+title: Coming Soon
+description: Coming Soon
+---
diff --git a/docs/articles/new-expensify/exports/Coming-Soon.md b/docs/articles/new-expensify/exports/Coming-Soon.md
new file mode 100644
index 000000000000..6b85bb0364b5
--- /dev/null
+++ b/docs/articles/new-expensify/exports/Coming-Soon.md
@@ -0,0 +1,4 @@
+---
+title: Coming Soon
+description: Coming Soon
+---
diff --git a/docs/articles/new-expensify/get-paid-back/Request-Money.md b/docs/articles/new-expensify/get-paid-back/Request-Money.md
new file mode 100644
index 000000000000..55a3f3c8172e
--- /dev/null
+++ b/docs/articles/new-expensify/get-paid-back/Request-Money.md
@@ -0,0 +1,5 @@
+---
+title: Request Money
+description: Request Money
+---
+## Resources Coming Soon!
diff --git a/docs/articles/other/Expensify-Lounge.md b/docs/articles/new-expensify/getting-started/Expensify-Lounge.md
similarity index 100%
rename from docs/articles/other/Expensify-Lounge.md
rename to docs/articles/new-expensify/getting-started/Expensify-Lounge.md
diff --git a/docs/articles/new-expensify/getting-started/Referral-Program.md b/docs/articles/new-expensify/getting-started/Referral-Program.md
new file mode 100644
index 000000000000..683e93d0277a
--- /dev/null
+++ b/docs/articles/new-expensify/getting-started/Referral-Program.md
@@ -0,0 +1,53 @@
+---
+title: Expensify Referral Program
+description: Send your joining link, submit a receipt or invoice, and we'll pay you if your referral adopts Expensify.
+---
+
+
+# About
+
+Expensify has grown thanks to our users who love Expensify so much that they tell their friends, colleagues, managers, and fellow business founders to use it, too.
+
+As a thank you, every time you bring a new user into the platform who directly or indirectly leads to the adoption of a paid annual plan on Expensify, you will earn $250.
+
+# How to get paid for referring people to Expensify
+
+1. Submit a report or invoice, or share your referral link with anyone you know who is spending too much time on expenses, or works at a company that could benefit from using Expensify.
+
+2. You will get $250 for any referred business that commits to an annual subscription, has 2 or more active users, and makes two monthly payments.
+
+That’s right! You can refer anyone working at any company you know.
+
+If their company goes on to become an Expensify customer with an annual subscription, and you are the earliest recorded referrer of a user on that company’s paid Expensify Policy, you'll get paid a referral reward.
+
+The best way to start is to submit any receipt to your manager (you'll get paid back and set yourself up for $250 if they start a subscription: win-win!)
+
+Referral rewards for the Spring/Summer 2023 campaign will be paid by direct deposit.
+
+# FAQ
+
+- **How will I know if I am the first person to refer a company to Expensify?**
+
+Successful referrers are notified after their referral pays for 2 months of an annual subscription. We will check for the earliest recorded referrer of a user on the policy, and if that is you, then we will let you know.
+
+- **How will you pay me if I am successful?**
+
+In the Spring 2023 campaign, Expensify will be paying successful referrers via direct deposit to the Deposit-Only account you have on file. Referral payouts will happen once a month for the duration of the campaign. If you do not have a Deposit-Only account at the time of your referral payout, your deposit will be processed in the next batch.
+
+Learn how to add a Deposit-Only account [here](https://community.expensify.com/discussion/4641/how-to-add-a-deposit-only-bank-account-both-personal-and-business).
+
+- **I’m outside of the US, how do I get paid?**
+
+While our referral payouts are in USD, you will be able to get paid via a Wise Borderless account. Learn more [here](https://community.expensify.com/discussion/5940/how-to-get-reimbursed-outside-the-us-with-wise-for-non-us-employees).
+
+- **My referral wasn’t counted! How can I appeal?**
+
+Expensify reserves the right to modify the terms of the referral program at any time, and pays out referral bonuses for eligible companies at its own discretion.
+
+Please send a message to concierge@expensify.com with the billing owner of the company you have referred and our team will review the referral and get back to you.
+
+- **Where can I find my referral link?**
+
+Expensify members who are opted-in for our newsletters will have received an email containing their unique referral link.
+
+On the mobile app, go to **Settings** > **Invite a Friend** > **Share Invite Link** to retrieve your referral link.
diff --git a/docs/articles/other/Everything-About-Chat.md b/docs/articles/new-expensify/getting-started/chat/Everything-About-Chat.md
similarity index 99%
rename from docs/articles/other/Everything-About-Chat.md
rename to docs/articles/new-expensify/getting-started/chat/Everything-About-Chat.md
index d52932daa5ff..9f73d1c759c2 100644
--- a/docs/articles/other/Everything-About-Chat.md
+++ b/docs/articles/new-expensify/getting-started/chat/Everything-About-Chat.md
@@ -83,5 +83,3 @@ You will receive a whisper from Concierge any time your content has been flagged
![Moderation Reporter Whisper](https://help.expensify.com/assets/images/moderation-reporter-whisper.png){:width="100%"}
*Note: Any message sent in public chat rooms are automatically reviewed by an automated system looking for offensive content and sent to our moderators for final decisions if it is found.*
-
-
diff --git a/docs/articles/other/Expensify-Chat-For-Admins.md b/docs/articles/new-expensify/getting-started/chat/Expensify-Chat-For-Admins.md
similarity index 99%
rename from docs/articles/other/Expensify-Chat-For-Admins.md
rename to docs/articles/new-expensify/getting-started/chat/Expensify-Chat-For-Admins.md
index 247b2b0e03d0..31de150d5b5e 100644
--- a/docs/articles/other/Expensify-Chat-For-Admins.md
+++ b/docs/articles/new-expensify/getting-started/chat/Expensify-Chat-For-Admins.md
@@ -24,3 +24,4 @@ In order to get the most out of Expensify Chat, we created a list of best practi
- The rooms will all stay open after the conference ends, so encourage speakers to keep engaging as long as the conversation is going in their session room.
- Continue sharing photos and videos from the event or anything fun in #social as part of a wrap up for everyone.
- Use the #announce room to give attendees a sneak preview of your next event.
+-
\ No newline at end of file
diff --git a/docs/articles/other/Expensify-Chat-For-Conference-Attendees.md b/docs/articles/new-expensify/getting-started/chat/Expensify-Chat-For-Conference-Attendees.md
similarity index 100%
rename from docs/articles/other/Expensify-Chat-For-Conference-Attendees.md
rename to docs/articles/new-expensify/getting-started/chat/Expensify-Chat-For-Conference-Attendees.md
diff --git a/docs/articles/other/Expensify-Chat-For-Conference-Speakers.md b/docs/articles/new-expensify/getting-started/chat/Expensify-Chat-For-Conference-Speakers.md
similarity index 100%
rename from docs/articles/other/Expensify-Chat-For-Conference-Speakers.md
rename to docs/articles/new-expensify/getting-started/chat/Expensify-Chat-For-Conference-Speakers.md
diff --git a/docs/articles/playbooks/Expensify-Chat-Playbook-for-Conferences.md b/docs/articles/new-expensify/getting-started/chat/Expensify-Chat-Playbook-For-Conferences.md
similarity index 100%
rename from docs/articles/playbooks/Expensify-Chat-Playbook-for-Conferences.md
rename to docs/articles/new-expensify/getting-started/chat/Expensify-Chat-Playbook-For-Conferences.md
diff --git a/docs/articles/new-expensify/integrations/accounting-integrations/QuickBooks-Online.md b/docs/articles/new-expensify/integrations/accounting-integrations/QuickBooks-Online.md
new file mode 100644
index 000000000000..ed4d127d5c26
--- /dev/null
+++ b/docs/articles/new-expensify/integrations/accounting-integrations/QuickBooks-Online.md
@@ -0,0 +1,5 @@
+---
+title: QuickBooks Online
+description: QuickBooks Online
+---
+## Resources Coming Soon!
diff --git a/docs/articles/new-expensify/manage-employees-and-report-approvals/Coming-Soon.md b/docs/articles/new-expensify/manage-employees-and-report-approvals/Coming-Soon.md
new file mode 100644
index 000000000000..6b85bb0364b5
--- /dev/null
+++ b/docs/articles/new-expensify/manage-employees-and-report-approvals/Coming-Soon.md
@@ -0,0 +1,4 @@
+---
+title: Coming Soon
+description: Coming Soon
+---
diff --git a/docs/articles/new-expensify/send-payments/Coming-Soon.md b/docs/articles/new-expensify/send-payments/Coming-Soon.md
new file mode 100644
index 000000000000..6b85bb0364b5
--- /dev/null
+++ b/docs/articles/new-expensify/send-payments/Coming-Soon.md
@@ -0,0 +1,4 @@
+---
+title: Coming Soon
+description: Coming Soon
+---
diff --git a/docs/articles/new-expensify/workspace-and-domain-settings/Coming-Soon.md b/docs/articles/new-expensify/workspace-and-domain-settings/Coming-Soon.md
new file mode 100644
index 000000000000..6b85bb0364b5
--- /dev/null
+++ b/docs/articles/new-expensify/workspace-and-domain-settings/Coming-Soon.md
@@ -0,0 +1,4 @@
+---
+title: Coming Soon
+description: Coming Soon
+---
diff --git a/docs/articles/request-money/Request-and-Split-Bills.md b/docs/articles/request-money/Request-and-Split-Bills.md
deleted file mode 100644
index bb27cd75c742..000000000000
--- a/docs/articles/request-money/Request-and-Split-Bills.md
+++ /dev/null
@@ -1,38 +0,0 @@
----
-title: Request Money and Split Bills with Friends
-description: Everything you need to know about Requesting Money and Splitting Bills with Friends!
----
-
-
-
-# How do these Payment Features work?
-Our suite of money movement features enables you to request money owed by an individual or split a bill with a group.
-
-**Request Money** lets your friends pay you back directly in Expensify. When you send a payment request to a friend, Expensify will display the amount owed and the option to pay the corresponding request in a chat between you.
-
-**Split Bill** allows you to split payments between friends and ensures the person who settled the tab gets paid back.
-
-These two features ensure you can live in the moment and settle up afterward.
-
-# How to Request Money
-- Select the Green **+** button and choose **Request Money**
-- Select the relevant option:
- - **Manual:** Enter the merchant and amount manually.
- - **Scan:** Take a photo of the receipt to have the merchant and amount auto-filled.
- - **Distance:** Enter the details of your trip, plus any stops along the way, and the mileage and amount will be automatically calculated.
-- Search for the user or enter their email!
-- Enter a reason for the request (optional)
-- Click **Request!**
-- If you change your mind, all you have to do is click **Cancel**
-- The user will be able to **Settle up outside of Expensify** or pay you via **Venmo** or **PayPal.me**
-
-# How to Split a Bill
-- Select the Green **+** button and choose **Split Bill**
-- Enter the total amount for the bill and click **Next**
-- Search for users or enter their emails and **Select**
-- Enter a reason for the split
-- The split is then shared equally between the attendees
-
-# FAQs
-## Can I request money from more than one person at a time?
-If you need to request money for more than one person at a time, you’ll want to use the Split Bill feature. The Request Money option is for one-to-one payments between two people.
diff --git a/docs/articles/split-bills/paying-friends/Request-and-Split-Bills.md b/docs/articles/split-bills/paying-friends/Request-and-Split-Bills.md
deleted file mode 100644
index a2c63cf6f8f7..000000000000
--- a/docs/articles/split-bills/paying-friends/Request-and-Split-Bills.md
+++ /dev/null
@@ -1,35 +0,0 @@
----
-title: Request Money and Split Bills with Friends
-description: Everything you need to know about Requesting Money and Splitting Bills with Friends!
----
-
-
-
-# How do these Payment Features work?
-Our suite of money movement features enables you to request money owed by an individual or split a bill with a group.
-
-**Request Money** lets your friends pay you back directly in Expensify. When you send a payment request to a friend, Expensify will display the amount owed and the option to pay the corresponding request in a chat between you.
-
-**Split Bill** allows you to split payments between friends and ensures the person who settled the tab gets paid back.
-
-These two features ensure you can live in the moment and settle up afterward.
-
-# How to Request Money
-- Select the Green **+** button and choose **Request Money**
-- Enter the amount **$** they owe and click **Next**
-- Search for the user or enter their email!
-- Enter a reason for the request (optional)
-- Click **Request!**
-- If you change your mind, all you have to do is click **Cancel**
-- The user will be able to **Settle up outside of Expensify** or pay you via **Venmo** or **PayPal.me**
-
-# How to Split a Bill
-- Select the Green **+** button and choose **Split Bill**
-- Enter the total amount for the bill and click **Next**
-- Search for users or enter their emails and **Select**
-- Enter a reason for the split
-- The split is then shared equally between the attendees
-
-# FAQs
-## Can I request money from more than one person at a time?
-If you need to request money for more than one person at a time, you’ll want to use the Split Bill feature. The Request Money option is for one-to-one payments between two people.
diff --git a/docs/expensify-classic/hubs/account-settings.html b/docs/expensify-classic/hubs/account-settings.html
new file mode 100644
index 000000000000..434761a6c4fa
--- /dev/null
+++ b/docs/expensify-classic/hubs/account-settings.html
@@ -0,0 +1,6 @@
+---
+layout: default
+title: Account Settings
+---
+
+{% include hub.html %}
\ No newline at end of file
diff --git a/docs/expensify-classic/hubs/bank-accounts-and-credit-cards.html b/docs/expensify-classic/hubs/bank-accounts-and-credit-cards.html
new file mode 100644
index 000000000000..2f91f0913d6e
--- /dev/null
+++ b/docs/expensify-classic/hubs/bank-accounts-and-credit-cards.html
@@ -0,0 +1,6 @@
+---
+layout: default
+title: Bank Accounts & Credit Cards
+---
+
+{% include hub.html %}
\ No newline at end of file
diff --git a/docs/expensify-classic/hubs/billing-and-subscriptions.html b/docs/expensify-classic/hubs/billing-and-subscriptions.html
new file mode 100644
index 000000000000..05dc38835b51
--- /dev/null
+++ b/docs/expensify-classic/hubs/billing-and-subscriptions.html
@@ -0,0 +1,6 @@
+---
+layout: default
+title: Billing & Subscriptions
+---
+
+{% include hub.html %}
\ No newline at end of file
diff --git a/docs/expensify-classic/hubs/expense-and-report-features.html b/docs/expensify-classic/hubs/expense-and-report-features.html
new file mode 100644
index 000000000000..44afa4b18b51
--- /dev/null
+++ b/docs/expensify-classic/hubs/expense-and-report-features.html
@@ -0,0 +1,6 @@
+---
+layout: default
+title: Expense & Report Settings
+---
+
+{% include hub.html %}
\ No newline at end of file
diff --git a/docs/expensify-classic/hubs/expensify-card.html b/docs/expensify-classic/hubs/expensify-card.html
new file mode 100644
index 000000000000..3afd8ac662e7
--- /dev/null
+++ b/docs/expensify-classic/hubs/expensify-card.html
@@ -0,0 +1,6 @@
+---
+layout: default
+title: Expensify Card
+---
+
+{% include hub.html %}
\ No newline at end of file
diff --git a/docs/expensify-classic/hubs/exports.html b/docs/expensify-classic/hubs/exports.html
new file mode 100644
index 000000000000..16c96cb51d01
--- /dev/null
+++ b/docs/expensify-classic/hubs/exports.html
@@ -0,0 +1,6 @@
+---
+layout: default
+title: Exports
+---
+
+{% include hub.html %}
\ No newline at end of file
diff --git a/docs/expensify-classic/hubs/get-paid-back.html b/docs/expensify-classic/hubs/get-paid-back.html
new file mode 100644
index 000000000000..1f84c1510b92
--- /dev/null
+++ b/docs/expensify-classic/hubs/get-paid-back.html
@@ -0,0 +1,6 @@
+---
+layout: default
+title: Get Paid Back
+---
+
+{% include hub.html %}
\ No newline at end of file
diff --git a/docs/expensify-classic/hubs/getting-started.html b/docs/expensify-classic/hubs/getting-started.html
new file mode 100644
index 000000000000..14ca13d0c2e8
--- /dev/null
+++ b/docs/expensify-classic/hubs/getting-started.html
@@ -0,0 +1,6 @@
+---
+layout: default
+title: Getting Started
+---
+
+{% include hub.html %}
\ No newline at end of file
diff --git a/docs/expensify-classic/hubs/index.html b/docs/expensify-classic/hubs/index.html
new file mode 100644
index 000000000000..05c7b52bfa2d
--- /dev/null
+++ b/docs/expensify-classic/hubs/index.html
@@ -0,0 +1,6 @@
+---
+layout: default
+title: Expensify Classic
+---
+
+{% include platform.html %}
\ No newline at end of file
diff --git a/docs/expensify-classic/hubs/integrations.html b/docs/expensify-classic/hubs/integrations.html
new file mode 100644
index 000000000000..d1f173534c8a
--- /dev/null
+++ b/docs/expensify-classic/hubs/integrations.html
@@ -0,0 +1,6 @@
+---
+layout: default
+title: Integrations
+---
+
+{% include hub.html %}
\ No newline at end of file
diff --git a/docs/expensify-classic/hubs/manage-employees-and-report-approvals.html b/docs/expensify-classic/hubs/manage-employees-and-report-approvals.html
new file mode 100644
index 000000000000..788e445ebc91
--- /dev/null
+++ b/docs/expensify-classic/hubs/manage-employees-and-report-approvals.html
@@ -0,0 +1,6 @@
+---
+layout: default
+title: Manage Employees And Report Approvals
+---
+
+{% include hub.html %}
\ No newline at end of file
diff --git a/docs/expensify-classic/hubs/policy-and-domain-settings.html b/docs/expensify-classic/hubs/policy-and-domain-settings.html
new file mode 100644
index 000000000000..ffd514fcb6fa
--- /dev/null
+++ b/docs/expensify-classic/hubs/policy-and-domain-settings.html
@@ -0,0 +1,6 @@
+---
+layout: default
+title: Policy And Domain Settings
+---
+
+{% include hub.html %}
\ No newline at end of file
diff --git a/docs/expensify-classic/hubs/send-payments.html b/docs/expensify-classic/hubs/send-payments.html
new file mode 100644
index 000000000000..c8275af5c353
--- /dev/null
+++ b/docs/expensify-classic/hubs/send-payments.html
@@ -0,0 +1,6 @@
+---
+layout: default
+title: Send Payments
+---
+
+{% include hub.html %}
\ No newline at end of file
diff --git a/docs/hubs/other.html b/docs/hubs/other.html
deleted file mode 100644
index 7d6d7f204062..000000000000
--- a/docs/hubs/other.html
+++ /dev/null
@@ -1,6 +0,0 @@
----
-layout: default
-title: Other
----
-
-{% include hub.html %}
diff --git a/docs/hubs/playbooks.html b/docs/hubs/playbooks.html
deleted file mode 100644
index 0f15922fd061..000000000000
--- a/docs/hubs/playbooks.html
+++ /dev/null
@@ -1,6 +0,0 @@
----
-layout: default
-title: Playbooks
----
-
-{% include hub.html %}
diff --git a/docs/hubs/request-money.html b/docs/hubs/request-money.html
deleted file mode 100644
index e3ef68f5c050..000000000000
--- a/docs/hubs/request-money.html
+++ /dev/null
@@ -1,6 +0,0 @@
----
-layout: default
-title: Request money
----
-
-{% include hub.html %}
diff --git a/docs/hubs/split-bills.html b/docs/hubs/split-bills.html
deleted file mode 100644
index 6464ab62ef07..000000000000
--- a/docs/hubs/split-bills.html
+++ /dev/null
@@ -1,6 +0,0 @@
----
-layout: default
-title: Split bills
----
-
-{% include hub.html %}
diff --git a/docs/new-expensify/hubs/account-settings.html b/docs/new-expensify/hubs/account-settings.html
new file mode 100644
index 000000000000..434761a6c4fa
--- /dev/null
+++ b/docs/new-expensify/hubs/account-settings.html
@@ -0,0 +1,6 @@
+---
+layout: default
+title: Account Settings
+---
+
+{% include hub.html %}
\ No newline at end of file
diff --git a/docs/new-expensify/hubs/bank-accounts-and-credit-cards.html b/docs/new-expensify/hubs/bank-accounts-and-credit-cards.html
new file mode 100644
index 000000000000..2f91f0913d6e
--- /dev/null
+++ b/docs/new-expensify/hubs/bank-accounts-and-credit-cards.html
@@ -0,0 +1,6 @@
+---
+layout: default
+title: Bank Accounts & Credit Cards
+---
+
+{% include hub.html %}
\ No newline at end of file
diff --git a/docs/new-expensify/hubs/billing-and-plan-types.html b/docs/new-expensify/hubs/billing-and-plan-types.html
new file mode 100644
index 000000000000..b49b2b62b1d6
--- /dev/null
+++ b/docs/new-expensify/hubs/billing-and-plan-types.html
@@ -0,0 +1,6 @@
+---
+layout: default
+title: Billing & Plan Types
+---
+
+{% include hub.html %}
\ No newline at end of file
diff --git a/docs/new-expensify/hubs/expense-and-report-features.html b/docs/new-expensify/hubs/expense-and-report-features.html
new file mode 100644
index 000000000000..0057ae0fa46c
--- /dev/null
+++ b/docs/new-expensify/hubs/expense-and-report-features.html
@@ -0,0 +1,6 @@
+---
+layout: default
+title: Expense and Report Features
+---
+
+{% include hub.html %}
\ No newline at end of file
diff --git a/docs/new-expensify/hubs/expensify-card.html b/docs/new-expensify/hubs/expensify-card.html
new file mode 100644
index 000000000000..3afd8ac662e7
--- /dev/null
+++ b/docs/new-expensify/hubs/expensify-card.html
@@ -0,0 +1,6 @@
+---
+layout: default
+title: Expensify Card
+---
+
+{% include hub.html %}
\ No newline at end of file
diff --git a/docs/new-expensify/hubs/exports.html b/docs/new-expensify/hubs/exports.html
new file mode 100644
index 000000000000..e69de29bb2d1
diff --git a/docs/new-expensify/hubs/get-paid-back.html b/docs/new-expensify/hubs/get-paid-back.html
new file mode 100644
index 000000000000..1f84c1510b92
--- /dev/null
+++ b/docs/new-expensify/hubs/get-paid-back.html
@@ -0,0 +1,6 @@
+---
+layout: default
+title: Get Paid Back
+---
+
+{% include hub.html %}
\ No newline at end of file
diff --git a/docs/new-expensify/hubs/getting-started.html b/docs/new-expensify/hubs/getting-started.html
new file mode 100644
index 000000000000..14ca13d0c2e8
--- /dev/null
+++ b/docs/new-expensify/hubs/getting-started.html
@@ -0,0 +1,6 @@
+---
+layout: default
+title: Getting Started
+---
+
+{% include hub.html %}
\ No newline at end of file
diff --git a/docs/new-expensify/hubs/index.html b/docs/new-expensify/hubs/index.html
new file mode 100644
index 000000000000..de046b16e755
--- /dev/null
+++ b/docs/new-expensify/hubs/index.html
@@ -0,0 +1,6 @@
+---
+layout: default
+title: New Expensify
+---
+
+{% include platform.html %}
\ No newline at end of file
diff --git a/docs/new-expensify/hubs/integrations.html b/docs/new-expensify/hubs/integrations.html
new file mode 100644
index 000000000000..d1f173534c8a
--- /dev/null
+++ b/docs/new-expensify/hubs/integrations.html
@@ -0,0 +1,6 @@
+---
+layout: default
+title: Integrations
+---
+
+{% include hub.html %}
\ No newline at end of file
diff --git a/docs/new-expensify/hubs/manage-employees-and-report-approvals.html b/docs/new-expensify/hubs/manage-employees-and-report-approvals.html
new file mode 100644
index 000000000000..31e992f32d5d
--- /dev/null
+++ b/docs/new-expensify/hubs/manage-employees-and-report-approvals.html
@@ -0,0 +1,6 @@
+---
+layout: default
+title: Manage Employees & Report Approvals
+---
+
+{% include hub.html %}
\ No newline at end of file
diff --git a/docs/new-expensify/hubs/send-payments.html b/docs/new-expensify/hubs/send-payments.html
new file mode 100644
index 000000000000..c8275af5c353
--- /dev/null
+++ b/docs/new-expensify/hubs/send-payments.html
@@ -0,0 +1,6 @@
+---
+layout: default
+title: Send Payments
+---
+
+{% include hub.html %}
\ No newline at end of file
diff --git a/docs/new-expensify/hubs/workspace-and-domain-settings.html b/docs/new-expensify/hubs/workspace-and-domain-settings.html
new file mode 100644
index 000000000000..c4e5d06d6b8a
--- /dev/null
+++ b/docs/new-expensify/hubs/workspace-and-domain-settings.html
@@ -0,0 +1,6 @@
+---
+layout: default
+title: Workspace & Domain Settings
+---
+
+{% include hub.html %}
\ No newline at end of file
diff --git a/fastlane/Fastfile b/fastlane/Fastfile
index ecec05f1cec1..c7d0f2f4f0f5 100644
--- a/fastlane/Fastfile
+++ b/fastlane/Fastfile
@@ -67,6 +67,8 @@ platform :android do
desc "Build and upload app to Google Play"
lane :beta do
ENV["ENVFILE"]=".env.production"
+ # Google is very unreliable, so we retry a few times
+ ENV["SUPPLY_UPLOAD_MAX_RETRIES"]="5"
gradle(
project_dir: './android',
@@ -86,6 +88,8 @@ platform :android do
desc "Deploy app to Google Play production"
lane :production do
+ # Google is very unreliable, so we retry a few times
+ ENV["SUPPLY_UPLOAD_MAX_RETRIES"]="5"
google_play_track_version_codes(
package_name: "com.expensify.chat",
json_key: './android/app/android-fastlane-json-key.json',
diff --git a/ios/NewExpensify/Info.plist b/ios/NewExpensify/Info.plist
index 440e48e0c83d..441bd2feab92 100644
--- a/ios/NewExpensify/Info.plist
+++ b/ios/NewExpensify/Info.plist
@@ -19,7 +19,7 @@
CFBundlePackageTypeAPPLCFBundleShortVersionString
- 1.3.70
+ 1.3.72CFBundleSignature????CFBundleURLTypes
@@ -40,7 +40,7 @@
CFBundleVersion
- 1.3.70.5
+ 1.3.72.9ITSAppUsesNonExemptEncryptionLSApplicationQueriesSchemes
diff --git a/ios/NewExpensifyTests/Info.plist b/ios/NewExpensifyTests/Info.plist
index 9d7fc804acd9..27273c7f3866 100644
--- a/ios/NewExpensifyTests/Info.plist
+++ b/ios/NewExpensifyTests/Info.plist
@@ -15,10 +15,10 @@
CFBundlePackageTypeBNDLCFBundleShortVersionString
- 1.3.70
+ 1.3.72CFBundleSignature????CFBundleVersion
- 1.3.70.5
+ 1.3.72.9
diff --git a/ios/Podfile.lock b/ios/Podfile.lock
index aeb1887223cd..51b9f6af0e21 100644
--- a/ios/Podfile.lock
+++ b/ios/Podfile.lock
@@ -31,14 +31,14 @@ PODS:
- React-Core
- CocoaAsyncSocket (7.6.5)
- DoubleConversion (1.1.6)
- - FBLazyVector (0.72.3)
- - FBReactNativeSpec (0.72.3):
+ - FBLazyVector (0.72.4)
+ - FBReactNativeSpec (0.72.4):
- RCT-Folly (= 2021.07.22.00)
- - RCTRequired (= 0.72.3)
- - RCTTypeSafety (= 0.72.3)
- - React-Core (= 0.72.3)
- - React-jsi (= 0.72.3)
- - ReactCommon/turbomodule/core (= 0.72.3)
+ - RCTRequired (= 0.72.4)
+ - RCTTypeSafety (= 0.72.4)
+ - React-Core (= 0.72.4)
+ - React-jsi (= 0.72.4)
+ - ReactCommon/turbomodule/core (= 0.72.4)
- Firebase/Analytics (8.8.0):
- Firebase/Core
- Firebase/Core (8.8.0):
@@ -220,9 +220,9 @@ PODS:
- AppAuth/Core (~> 1.6)
- GTMSessionFetcher/Core (< 4.0, >= 1.5)
- GTMSessionFetcher/Core (3.1.1)
- - hermes-engine (0.72.3):
- - hermes-engine/Pre-built (= 0.72.3)
- - hermes-engine/Pre-built (0.72.3)
+ - hermes-engine (0.72.4):
+ - hermes-engine/Pre-built (= 0.72.4)
+ - hermes-engine/Pre-built (0.72.4)
- libevent (2.1.12)
- libwebp (1.2.4):
- libwebp/demux (= 1.2.4)
@@ -283,26 +283,26 @@ PODS:
- fmt (~> 6.2.1)
- glog
- libevent
- - RCTRequired (0.72.3)
- - RCTTypeSafety (0.72.3):
- - FBLazyVector (= 0.72.3)
- - RCTRequired (= 0.72.3)
- - React-Core (= 0.72.3)
- - React (0.72.3):
- - React-Core (= 0.72.3)
- - React-Core/DevSupport (= 0.72.3)
- - React-Core/RCTWebSocket (= 0.72.3)
- - React-RCTActionSheet (= 0.72.3)
- - React-RCTAnimation (= 0.72.3)
- - React-RCTBlob (= 0.72.3)
- - React-RCTImage (= 0.72.3)
- - React-RCTLinking (= 0.72.3)
- - React-RCTNetwork (= 0.72.3)
- - React-RCTSettings (= 0.72.3)
- - React-RCTText (= 0.72.3)
- - React-RCTVibration (= 0.72.3)
- - React-callinvoker (0.72.3)
- - React-Codegen (0.72.3):
+ - RCTRequired (0.72.4)
+ - RCTTypeSafety (0.72.4):
+ - FBLazyVector (= 0.72.4)
+ - RCTRequired (= 0.72.4)
+ - React-Core (= 0.72.4)
+ - React (0.72.4):
+ - React-Core (= 0.72.4)
+ - React-Core/DevSupport (= 0.72.4)
+ - React-Core/RCTWebSocket (= 0.72.4)
+ - React-RCTActionSheet (= 0.72.4)
+ - React-RCTAnimation (= 0.72.4)
+ - React-RCTBlob (= 0.72.4)
+ - React-RCTImage (= 0.72.4)
+ - React-RCTLinking (= 0.72.4)
+ - React-RCTNetwork (= 0.72.4)
+ - React-RCTSettings (= 0.72.4)
+ - React-RCTText (= 0.72.4)
+ - React-RCTVibration (= 0.72.4)
+ - React-callinvoker (0.72.4)
+ - React-Codegen (0.72.4):
- DoubleConversion
- FBReactNativeSpec
- glog
@@ -317,11 +317,11 @@ PODS:
- React-rncore
- ReactCommon/turbomodule/bridging
- ReactCommon/turbomodule/core
- - React-Core (0.72.3):
+ - React-Core (0.72.4):
- glog
- hermes-engine
- RCT-Folly (= 2021.07.22.00)
- - React-Core/Default (= 0.72.3)
+ - React-Core/Default (= 0.72.4)
- React-cxxreact
- React-hermes
- React-jsi
@@ -331,7 +331,7 @@ PODS:
- React-utils
- SocketRocket (= 0.6.1)
- Yoga
- - React-Core/CoreModulesHeaders (0.72.3):
+ - React-Core/CoreModulesHeaders (0.72.4):
- glog
- hermes-engine
- RCT-Folly (= 2021.07.22.00)
@@ -345,7 +345,7 @@ PODS:
- React-utils
- SocketRocket (= 0.6.1)
- Yoga
- - React-Core/Default (0.72.3):
+ - React-Core/Default (0.72.4):
- glog
- hermes-engine
- RCT-Folly (= 2021.07.22.00)
@@ -358,23 +358,23 @@ PODS:
- React-utils
- SocketRocket (= 0.6.1)
- Yoga
- - React-Core/DevSupport (0.72.3):
+ - React-Core/DevSupport (0.72.4):
- glog
- hermes-engine
- RCT-Folly (= 2021.07.22.00)
- - React-Core/Default (= 0.72.3)
- - React-Core/RCTWebSocket (= 0.72.3)
+ - React-Core/Default (= 0.72.4)
+ - React-Core/RCTWebSocket (= 0.72.4)
- React-cxxreact
- React-hermes
- React-jsi
- React-jsiexecutor
- - React-jsinspector (= 0.72.3)
+ - React-jsinspector (= 0.72.4)
- React-perflogger
- React-runtimeexecutor
- React-utils
- SocketRocket (= 0.6.1)
- Yoga
- - React-Core/RCTActionSheetHeaders (0.72.3):
+ - React-Core/RCTActionSheetHeaders (0.72.4):
- glog
- hermes-engine
- RCT-Folly (= 2021.07.22.00)
@@ -388,7 +388,7 @@ PODS:
- React-utils
- SocketRocket (= 0.6.1)
- Yoga
- - React-Core/RCTAnimationHeaders (0.72.3):
+ - React-Core/RCTAnimationHeaders (0.72.4):
- glog
- hermes-engine
- RCT-Folly (= 2021.07.22.00)
@@ -402,7 +402,7 @@ PODS:
- React-utils
- SocketRocket (= 0.6.1)
- Yoga
- - React-Core/RCTBlobHeaders (0.72.3):
+ - React-Core/RCTBlobHeaders (0.72.4):
- glog
- hermes-engine
- RCT-Folly (= 2021.07.22.00)
@@ -416,7 +416,7 @@ PODS:
- React-utils
- SocketRocket (= 0.6.1)
- Yoga
- - React-Core/RCTImageHeaders (0.72.3):
+ - React-Core/RCTImageHeaders (0.72.4):
- glog
- hermes-engine
- RCT-Folly (= 2021.07.22.00)
@@ -430,7 +430,7 @@ PODS:
- React-utils
- SocketRocket (= 0.6.1)
- Yoga
- - React-Core/RCTLinkingHeaders (0.72.3):
+ - React-Core/RCTLinkingHeaders (0.72.4):
- glog
- hermes-engine
- RCT-Folly (= 2021.07.22.00)
@@ -444,7 +444,7 @@ PODS:
- React-utils
- SocketRocket (= 0.6.1)
- Yoga
- - React-Core/RCTNetworkHeaders (0.72.3):
+ - React-Core/RCTNetworkHeaders (0.72.4):
- glog
- hermes-engine
- RCT-Folly (= 2021.07.22.00)
@@ -458,7 +458,7 @@ PODS:
- React-utils
- SocketRocket (= 0.6.1)
- Yoga
- - React-Core/RCTSettingsHeaders (0.72.3):
+ - React-Core/RCTSettingsHeaders (0.72.4):
- glog
- hermes-engine
- RCT-Folly (= 2021.07.22.00)
@@ -472,7 +472,7 @@ PODS:
- React-utils
- SocketRocket (= 0.6.1)
- Yoga
- - React-Core/RCTTextHeaders (0.72.3):
+ - React-Core/RCTTextHeaders (0.72.4):
- glog
- hermes-engine
- RCT-Folly (= 2021.07.22.00)
@@ -486,7 +486,7 @@ PODS:
- React-utils
- SocketRocket (= 0.6.1)
- Yoga
- - React-Core/RCTVibrationHeaders (0.72.3):
+ - React-Core/RCTVibrationHeaders (0.72.4):
- glog
- hermes-engine
- RCT-Folly (= 2021.07.22.00)
@@ -500,11 +500,11 @@ PODS:
- React-utils
- SocketRocket (= 0.6.1)
- Yoga
- - React-Core/RCTWebSocket (0.72.3):
+ - React-Core/RCTWebSocket (0.72.4):
- glog
- hermes-engine
- RCT-Folly (= 2021.07.22.00)
- - React-Core/Default (= 0.72.3)
+ - React-Core/Default (= 0.72.4)
- React-cxxreact
- React-hermes
- React-jsi
@@ -514,57 +514,57 @@ PODS:
- React-utils
- SocketRocket (= 0.6.1)
- Yoga
- - React-CoreModules (0.72.3):
+ - React-CoreModules (0.72.4):
- RCT-Folly (= 2021.07.22.00)
- - RCTTypeSafety (= 0.72.3)
- - React-Codegen (= 0.72.3)
- - React-Core/CoreModulesHeaders (= 0.72.3)
- - React-jsi (= 0.72.3)
+ - RCTTypeSafety (= 0.72.4)
+ - React-Codegen (= 0.72.4)
+ - React-Core/CoreModulesHeaders (= 0.72.4)
+ - React-jsi (= 0.72.4)
- React-RCTBlob
- - React-RCTImage (= 0.72.3)
- - ReactCommon/turbomodule/core (= 0.72.3)
+ - React-RCTImage (= 0.72.4)
+ - ReactCommon/turbomodule/core (= 0.72.4)
- SocketRocket (= 0.6.1)
- - React-cxxreact (0.72.3):
+ - React-cxxreact (0.72.4):
- boost (= 1.76.0)
- DoubleConversion
- glog
- hermes-engine
- RCT-Folly (= 2021.07.22.00)
- - React-callinvoker (= 0.72.3)
- - React-debug (= 0.72.3)
- - React-jsi (= 0.72.3)
- - React-jsinspector (= 0.72.3)
- - React-logger (= 0.72.3)
- - React-perflogger (= 0.72.3)
- - React-runtimeexecutor (= 0.72.3)
- - React-debug (0.72.3)
- - React-hermes (0.72.3):
+ - React-callinvoker (= 0.72.4)
+ - React-debug (= 0.72.4)
+ - React-jsi (= 0.72.4)
+ - React-jsinspector (= 0.72.4)
+ - React-logger (= 0.72.4)
+ - React-perflogger (= 0.72.4)
+ - React-runtimeexecutor (= 0.72.4)
+ - React-debug (0.72.4)
+ - React-hermes (0.72.4):
- DoubleConversion
- glog
- hermes-engine
- RCT-Folly (= 2021.07.22.00)
- RCT-Folly/Futures (= 2021.07.22.00)
- - React-cxxreact (= 0.72.3)
+ - React-cxxreact (= 0.72.4)
- React-jsi
- - React-jsiexecutor (= 0.72.3)
- - React-jsinspector (= 0.72.3)
- - React-perflogger (= 0.72.3)
- - React-jsi (0.72.3):
+ - React-jsiexecutor (= 0.72.4)
+ - React-jsinspector (= 0.72.4)
+ - React-perflogger (= 0.72.4)
+ - React-jsi (0.72.4):
- boost (= 1.76.0)
- DoubleConversion
- glog
- hermes-engine
- RCT-Folly (= 2021.07.22.00)
- - React-jsiexecutor (0.72.3):
+ - React-jsiexecutor (0.72.4):
- DoubleConversion
- glog
- hermes-engine
- RCT-Folly (= 2021.07.22.00)
- - React-cxxreact (= 0.72.3)
- - React-jsi (= 0.72.3)
- - React-perflogger (= 0.72.3)
- - React-jsinspector (0.72.3)
- - React-logger (0.72.3):
+ - React-cxxreact (= 0.72.4)
+ - React-jsi (= 0.72.4)
+ - React-perflogger (= 0.72.4)
+ - React-jsinspector (0.72.4)
+ - React-logger (0.72.4):
- glog
- react-native-airship (15.2.6):
- AirshipFrameworkProxy (= 2.0.8)
@@ -593,7 +593,7 @@ PODS:
- React-Core
- react-native-pdf (6.7.1):
- React-Core
- - react-native-performance (4.0.0):
+ - react-native-performance (5.1.0):
- React-Core
- react-native-plaid-link-sdk (10.0.0):
- Plaid (~> 4.1.0)
@@ -614,7 +614,7 @@ PODS:
- React-Core
- react-native-webview (11.23.0):
- React-Core
- - React-NativeModulesApple (0.72.3):
+ - React-NativeModulesApple (0.72.4):
- hermes-engine
- React-callinvoker
- React-Core
@@ -623,17 +623,17 @@ PODS:
- React-runtimeexecutor
- ReactCommon/turbomodule/bridging
- ReactCommon/turbomodule/core
- - React-perflogger (0.72.3)
- - React-RCTActionSheet (0.72.3):
- - React-Core/RCTActionSheetHeaders (= 0.72.3)
- - React-RCTAnimation (0.72.3):
+ - React-perflogger (0.72.4)
+ - React-RCTActionSheet (0.72.4):
+ - React-Core/RCTActionSheetHeaders (= 0.72.4)
+ - React-RCTAnimation (0.72.4):
- RCT-Folly (= 2021.07.22.00)
- - RCTTypeSafety (= 0.72.3)
- - React-Codegen (= 0.72.3)
- - React-Core/RCTAnimationHeaders (= 0.72.3)
- - React-jsi (= 0.72.3)
- - ReactCommon/turbomodule/core (= 0.72.3)
- - React-RCTAppDelegate (0.72.3):
+ - RCTTypeSafety (= 0.72.4)
+ - React-Codegen (= 0.72.4)
+ - React-Core/RCTAnimationHeaders (= 0.72.4)
+ - React-jsi (= 0.72.4)
+ - ReactCommon/turbomodule/core (= 0.72.4)
+ - React-RCTAppDelegate (0.72.4):
- RCT-Folly
- RCTRequired
- RCTTypeSafety
@@ -645,54 +645,54 @@ PODS:
- React-RCTNetwork
- React-runtimescheduler
- ReactCommon/turbomodule/core
- - React-RCTBlob (0.72.3):
+ - React-RCTBlob (0.72.4):
- hermes-engine
- RCT-Folly (= 2021.07.22.00)
- - React-Codegen (= 0.72.3)
- - React-Core/RCTBlobHeaders (= 0.72.3)
- - React-Core/RCTWebSocket (= 0.72.3)
- - React-jsi (= 0.72.3)
- - React-RCTNetwork (= 0.72.3)
- - ReactCommon/turbomodule/core (= 0.72.3)
- - React-RCTImage (0.72.3):
+ - React-Codegen (= 0.72.4)
+ - React-Core/RCTBlobHeaders (= 0.72.4)
+ - React-Core/RCTWebSocket (= 0.72.4)
+ - React-jsi (= 0.72.4)
+ - React-RCTNetwork (= 0.72.4)
+ - ReactCommon/turbomodule/core (= 0.72.4)
+ - React-RCTImage (0.72.4):
- RCT-Folly (= 2021.07.22.00)
- - RCTTypeSafety (= 0.72.3)
- - React-Codegen (= 0.72.3)
- - React-Core/RCTImageHeaders (= 0.72.3)
- - React-jsi (= 0.72.3)
- - React-RCTNetwork (= 0.72.3)
- - ReactCommon/turbomodule/core (= 0.72.3)
- - React-RCTLinking (0.72.3):
- - React-Codegen (= 0.72.3)
- - React-Core/RCTLinkingHeaders (= 0.72.3)
- - React-jsi (= 0.72.3)
- - ReactCommon/turbomodule/core (= 0.72.3)
- - React-RCTNetwork (0.72.3):
+ - RCTTypeSafety (= 0.72.4)
+ - React-Codegen (= 0.72.4)
+ - React-Core/RCTImageHeaders (= 0.72.4)
+ - React-jsi (= 0.72.4)
+ - React-RCTNetwork (= 0.72.4)
+ - ReactCommon/turbomodule/core (= 0.72.4)
+ - React-RCTLinking (0.72.4):
+ - React-Codegen (= 0.72.4)
+ - React-Core/RCTLinkingHeaders (= 0.72.4)
+ - React-jsi (= 0.72.4)
+ - ReactCommon/turbomodule/core (= 0.72.4)
+ - React-RCTNetwork (0.72.4):
- RCT-Folly (= 2021.07.22.00)
- - RCTTypeSafety (= 0.72.3)
- - React-Codegen (= 0.72.3)
- - React-Core/RCTNetworkHeaders (= 0.72.3)
- - React-jsi (= 0.72.3)
- - ReactCommon/turbomodule/core (= 0.72.3)
- - React-RCTSettings (0.72.3):
+ - RCTTypeSafety (= 0.72.4)
+ - React-Codegen (= 0.72.4)
+ - React-Core/RCTNetworkHeaders (= 0.72.4)
+ - React-jsi (= 0.72.4)
+ - ReactCommon/turbomodule/core (= 0.72.4)
+ - React-RCTSettings (0.72.4):
- RCT-Folly (= 2021.07.22.00)
- - RCTTypeSafety (= 0.72.3)
- - React-Codegen (= 0.72.3)
- - React-Core/RCTSettingsHeaders (= 0.72.3)
- - React-jsi (= 0.72.3)
- - ReactCommon/turbomodule/core (= 0.72.3)
- - React-RCTText (0.72.3):
- - React-Core/RCTTextHeaders (= 0.72.3)
- - React-RCTVibration (0.72.3):
+ - RCTTypeSafety (= 0.72.4)
+ - React-Codegen (= 0.72.4)
+ - React-Core/RCTSettingsHeaders (= 0.72.4)
+ - React-jsi (= 0.72.4)
+ - ReactCommon/turbomodule/core (= 0.72.4)
+ - React-RCTText (0.72.4):
+ - React-Core/RCTTextHeaders (= 0.72.4)
+ - React-RCTVibration (0.72.4):
- RCT-Folly (= 2021.07.22.00)
- - React-Codegen (= 0.72.3)
- - React-Core/RCTVibrationHeaders (= 0.72.3)
- - React-jsi (= 0.72.3)
- - ReactCommon/turbomodule/core (= 0.72.3)
- - React-rncore (0.72.3)
- - React-runtimeexecutor (0.72.3):
- - React-jsi (= 0.72.3)
- - React-runtimescheduler (0.72.3):
+ - React-Codegen (= 0.72.4)
+ - React-Core/RCTVibrationHeaders (= 0.72.4)
+ - React-jsi (= 0.72.4)
+ - ReactCommon/turbomodule/core (= 0.72.4)
+ - React-rncore (0.72.4)
+ - React-runtimeexecutor (0.72.4):
+ - React-jsi (= 0.72.4)
+ - React-runtimescheduler (0.72.4):
- glog
- hermes-engine
- RCT-Folly (= 2021.07.22.00)
@@ -700,30 +700,30 @@ PODS:
- React-debug
- React-jsi
- React-runtimeexecutor
- - React-utils (0.72.3):
+ - React-utils (0.72.4):
- glog
- RCT-Folly (= 2021.07.22.00)
- React-debug
- - ReactCommon/turbomodule/bridging (0.72.3):
+ - ReactCommon/turbomodule/bridging (0.72.4):
- DoubleConversion
- glog
- hermes-engine
- RCT-Folly (= 2021.07.22.00)
- - React-callinvoker (= 0.72.3)
- - React-cxxreact (= 0.72.3)
- - React-jsi (= 0.72.3)
- - React-logger (= 0.72.3)
- - React-perflogger (= 0.72.3)
- - ReactCommon/turbomodule/core (0.72.3):
+ - React-callinvoker (= 0.72.4)
+ - React-cxxreact (= 0.72.4)
+ - React-jsi (= 0.72.4)
+ - React-logger (= 0.72.4)
+ - React-perflogger (= 0.72.4)
+ - ReactCommon/turbomodule/core (0.72.4):
- DoubleConversion
- glog
- hermes-engine
- RCT-Folly (= 2021.07.22.00)
- - React-callinvoker (= 0.72.3)
- - React-cxxreact (= 0.72.3)
- - React-jsi (= 0.72.3)
- - React-logger (= 0.72.3)
- - React-perflogger (= 0.72.3)
+ - React-callinvoker (= 0.72.4)
+ - React-cxxreact (= 0.72.4)
+ - React-jsi (= 0.72.4)
+ - React-logger (= 0.72.4)
+ - React-perflogger (= 0.72.4)
- RNAppleAuthentication (2.2.2):
- React-Core
- RNCAsyncStorage (1.17.11):
@@ -815,7 +815,7 @@ PODS:
- RNScreens (3.21.0):
- React-Core
- React-RCTImage
- - RNSVG (13.9.0):
+ - RNSVG (13.13.0):
- React-Core
- SDWebImage (5.11.1):
- SDWebImage/Core (= 5.11.1)
@@ -1010,7 +1010,7 @@ EXTERNAL SOURCES:
:podspec: "../node_modules/react-native/third-party-podspecs/glog.podspec"
hermes-engine:
:podspec: "../node_modules/react-native/sdks/hermes-engine/hermes-engine.podspec"
- :tag: hermes-2023-03-20-RNv0.72.0-49794cfc7c81fb8f69fd60c3bbf85a7480cc5a77
+ :tag: hermes-2023-08-07-RNv0.72.4-813b2def12bc9df02654b3e3653ae4a68d0572e0
lottie-react-native:
:path: "../node_modules/lottie-react-native"
onfido-react-native-sdk:
@@ -1182,8 +1182,8 @@ SPEC CHECKSUMS:
BVLinearGradient: 421743791a59d259aec53f4c58793aad031da2ca
CocoaAsyncSocket: 065fd1e645c7abab64f7a6a2007a48038fdc6a99
DoubleConversion: 5189b271737e1565bdce30deb4a08d647e3f5f54
- FBLazyVector: 4cce221dd782d3ff7c4172167bba09d58af67ccb
- FBReactNativeSpec: c6bd9e179757b3c0ecf815864fae8032377903ef
+ FBLazyVector: 5d4a3b7f411219a45a6d952f77d2c0a6c9989da5
+ FBReactNativeSpec: 3fc2d478e1c4b08276f9dd9128f80ec6d5d85c1f
Firebase: 629510f1a9ddb235f3a7c5c8ceb23ba887f0f814
FirebaseABTesting: 10cbce8db9985ae2e3847ea44e9947dd18f94e10
FirebaseAnalytics: 5506ea8b867d8423485a84b4cd612d279f7b0b8a
@@ -1209,7 +1209,7 @@ SPEC CHECKSUMS:
GoogleUtilities: 9aa0ad5a7bc171f8bae016300bfcfa3fb8425749
GTMAppAuth: 99fb010047ba3973b7026e45393f51f27ab965ae
GTMSessionFetcher: e8647203b65cee28c5f73d0f473d096653945e72
- hermes-engine: 10fbd3f62405c41ea07e71973ea61e1878d07322
+ hermes-engine: 81191603c4eaa01f5e4ae5737a9efcf64756c7b2
libevent: 4049cae6c81cdb3654a443be001fb9bdceff7913
libwebp: f62cb61d0a484ba548448a4bd52aabf150ff6eef
lottie-ios: 8f97d3271e155c2d688875c29cd3c74908aef5f8
@@ -1229,20 +1229,20 @@ SPEC CHECKSUMS:
Plaid: 7d340abeadb46c7aa1a91f896c5b22395a31fcf2
PromisesObjC: 09985d6d70fbe7878040aa746d78236e6946d2ef
RCT-Folly: 424b8c9a7a0b9ab2886ffe9c3b041ef628fd4fb1
- RCTRequired: a2faf4bad4e438ca37b2040cb8f7799baa065c18
- RCTTypeSafety: cb09f3e4747b6d18331a15eb05271de7441ca0b3
- React: 13109005b5353095c052f26af37413340ccf7a5d
- React-callinvoker: c8c87bce983aa499c13cb06d4447c025a35274d6
- React-Codegen: 712d523524d89d71f1cf7cc624854941be983c4d
- React-Core: 688f88b7f3a3d30b4848036223f8b07102c687e5
- React-CoreModules: 63c063a3ade8fb3b1bec5fd9a50f17b0421558c6
- React-cxxreact: 37765b4975541105b2a3322a4b473417c158c869
- React-debug: 51f11ef8db14b47f24e71c42a4916d4192972156
- React-hermes: 935ae71fb3d7654e947beba8498835cd5e479707
- React-jsi: ec628dc7a15ffea969f237b0ea6d2fde212b19dd
- React-jsiexecutor: 59d1eb03af7d30b7d66589c410f13151271e8006
- React-jsinspector: b511447170f561157547bc0bef3f169663860be7
- React-logger: c5b527272d5f22eaa09bb3c3a690fee8f237ae95
+ RCTRequired: c0569ecc035894e4a68baecb30fe6a7ea6e399f9
+ RCTTypeSafety: e90354072c21236e0bcf1699011e39acd25fea2f
+ React: a1be3c6dc0a6e949ccd3e659781aa47bbae1868f
+ React-callinvoker: 1020b33f6cb1a1824f9ca2a86609fbce2a73c6ed
+ React-Codegen: a0a26badf098d4a779acda922caf74f6ecabed28
+ React-Core: 52075b80f10c26f62219d7b5d13d7d8089f027b3
+ React-CoreModules: 21abab85d7ad9038ce2b1c33d39e3baaf7dc9244
+ React-cxxreact: 4ad1cc861e32fb533dad6ff7a4ea25680fa1c994
+ React-debug: 17366a3d5c5d2f5fc04f09101a4af38cb42b54ae
+ React-hermes: 37377d0a56aa0cf55c65248271866ce3268cde3f
+ React-jsi: 6de8b0ccc6b765b58e4eee9ee38049dbeaf5c221
+ React-jsiexecutor: c7f826e40fa9cab5d37cab6130b1af237332b594
+ React-jsinspector: aaed4cf551c4a1c98092436518c2d267b13a673f
+ React-logger: da1ebe05ae06eb6db4b162202faeafac4b435e77
react-native-airship: 5d19f4ba303481cf4101ff9dee9249ef6a8a6b64
react-native-blob-util: 99f4d79189252f597fe0d810c57a3733b1b1dea6
react-native-cameraroll: 8ffb0af7a5e5de225fd667610e2979fc1f0c2151
@@ -1255,30 +1255,30 @@ SPEC CHECKSUMS:
react-native-netinfo: ccbe1085dffd16592791d550189772e13bf479e2
react-native-pager-view: 0ccb8bf60e2ebd38b1f3669fa3650ecce81db2df
react-native-pdf: 7c0e91ada997bac8bac3bb5bea5b6b81f5a3caae
- react-native-performance: 224bd53e6a835fda4353302cf891d088a0af7406
+ react-native-performance: cef2b618d47b277fb5c3280b81a3aad1e72f2886
react-native-plaid-link-sdk: 9eb0f71dad94b3bdde649c7a384cba93024af46c
react-native-quick-sqlite: bcc7a7a250a40222f18913a97cd356bf82d0a6c4
react-native-render-html: 96c979fe7452a0a41559685d2f83b12b93edac8c
react-native-safe-area-context: 99b24a0c5acd0d5dcac2b1a7f18c49ea317be99a
react-native-view-shot: 705f999ac2a24e4e6c909c0ca65c732ed33ca2ff
react-native-webview: e771bc375f789ebfa02a26939a57dbc6fa897336
- React-NativeModulesApple: c57f3efe0df288a6532b726ad2d0322a9bf38472
- React-perflogger: 6bd153e776e6beed54c56b0847e1220a3ff92ba5
- React-RCTActionSheet: c0b62af44e610e69d9a2049a682f5dba4e9dff17
- React-RCTAnimation: f9bf9719258926aea9ecb8a2aa2595d3ff9a6022
- React-RCTAppDelegate: e5ac35d4dbd1fae7df3a62b47db04b6a8d151592
- React-RCTBlob: c4f1e69a6ef739aa42586b876d637dab4e3b5bed
- React-RCTImage: e5798f01aba248416c02a506cf5e6dfcba827638
- React-RCTLinking: f5b6227c879e33206f34e68924c458f57bbb96d9
- React-RCTNetwork: d5554fbfac1c618da3c8fa29933108ea22837788
- React-RCTSettings: 189c71e3e6146ba59f4f7e2cbeb494cf2ad42afa
- React-RCTText: 19425aea9d8b6ccae55a27916355b17ab577e56e
- React-RCTVibration: 388ac0e1455420895d1ca2548401eed964b038a6
- React-rncore: 755a331dd67b74662108f2d66a384454bf8dc1a1
- React-runtimeexecutor: 369ae9bb3f83b65201c0c8f7d50b72280b5a1dbc
- React-runtimescheduler: 837c1bebd2f84572db17698cd702ceaf585b0d9a
- React-utils: bcb57da67eec2711f8b353f6e3d33bd8e4b2efa3
- ReactCommon: 3ccb8fb14e6b3277e38c73b0ff5e4a1b8db017a9
+ React-NativeModulesApple: edb5ace14f73f4969df6e7b1f3e41bef0012740f
+ React-perflogger: 496a1a3dc6737f964107cb3ddae7f9e265ddda58
+ React-RCTActionSheet: 02904b932b50e680f4e26e7a686b33ebf7ef3c00
+ React-RCTAnimation: 88feaf0a85648fb8fd497ce749829774910276d6
+ React-RCTAppDelegate: 5792ac0f0feccb584765fdd7aa81ea320c4d9b0b
+ React-RCTBlob: 0dbc9e2a13d241b37d46b53e54630cbad1f0e141
+ React-RCTImage: b111645ab901f8e59fc68fbe31f5731bdbeef087
+ React-RCTLinking: 3d719727b4c098aad3588aa3559361ee0579f5de
+ React-RCTNetwork: b44d3580be05d74556ba4efbf53570f17e38f734
+ React-RCTSettings: c0c54b330442c29874cd4dae6e94190dc11a6f6f
+ React-RCTText: 9b9f5589d9b649d7246c3f336e116496df28cfe6
+ React-RCTVibration: 691c67f3beaf1d084ceed5eb5c1dddd9afa8591e
+ React-rncore: 142268f6c92e296dc079aadda3fade778562f9e4
+ React-runtimeexecutor: d465ba0c47ef3ed8281143f59605cacc2244d5c7
+ React-runtimescheduler: 4941cc1b3cf08b792fbf666342c9fc95f1969035
+ React-utils: b79f2411931f9d3ea5781404dcbb2fa8a837e13a
+ ReactCommon: 4b2bdcb50a3543e1c2b2849ad44533686610826d
RNAppleAuthentication: 0571c08da8c327ae2afc0261b48b4a515b0286a6
RNCAsyncStorage: 8616bd5a58af409453ea4e1b246521bb76578d60
RNCClipboard: 41d8d918092ae8e676f18adada19104fa3e68495
@@ -1300,13 +1300,13 @@ SPEC CHECKSUMS:
RNReactNativeHapticFeedback: 1e3efeca9628ff9876ee7cdd9edec1b336913f8c
RNReanimated: 020859659f64be2d30849a1fe88c821a7c3e0cbf
RNScreens: d037903436160a4b039d32606668350d2a808806
- RNSVG: 53c661b76829783cdaf9b7a57258f3d3b4c28315
+ RNSVG: ed492aaf3af9ca01bc945f7a149d76d62e73ec82
SDWebImage: a7f831e1a65eb5e285e3fb046a23fcfbf08e696d
SDWebImageWebPCoder: 908b83b6adda48effe7667cd2b7f78c897e5111d
SocketRocket: f32cd54efbe0f095c4d7594881e52619cfe80b17
Turf: 469ce2c3d22e5e8e4818d5a3b254699a5c89efa4
VisionCamera: d3ec8883417a6a4a0e3a6ba37d81d22db7611601
- Yoga: 8796b55dba14d7004f980b54bcc9833ee45b28ce
+ Yoga: 3efc43e0d48686ce2e8c60f99d4e6bd349aff981
YogaKit: f782866e155069a2cca2517aafea43200b01fd5a
PODFILE CHECKSUM: 2daf34c870819a933f3fefe426801d54b2ff2a14
diff --git a/jest.config.js b/jest.config.js
index 1f540a679b9a..6cf44b6b3695 100644
--- a/jest.config.js
+++ b/jest.config.js
@@ -23,6 +23,6 @@ module.exports = {
},
testEnvironment: 'jsdom',
setupFiles: ['/jest/setup.js', './node_modules/@react-native-google-signin/google-signin/jest/build/setup.js'],
- setupFilesAfterEnv: ['@testing-library/jest-native/extend-expect'],
+ setupFilesAfterEnv: ['@testing-library/jest-native/extend-expect', '/jest/setupAfterEnv.js'],
cacheDirectory: '/.jest-cache',
};
diff --git a/jest/setupAfterEnv.js b/jest/setupAfterEnv.js
new file mode 100644
index 000000000000..6f7836b64dbb
--- /dev/null
+++ b/jest/setupAfterEnv.js
@@ -0,0 +1 @@
+jest.useRealTimers();
diff --git a/package-lock.json b/package-lock.json
index 3e08356a76ef..c2e322bf8ba2 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,12 +1,12 @@
{
"name": "new.expensify",
- "version": "1.3.70-5",
+ "version": "1.3.72-9",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "new.expensify",
- "version": "1.3.70-5",
+ "version": "1.3.72-9",
"hasInstallScript": true,
"license": "MIT",
"dependencies": {
@@ -52,7 +52,6 @@
"domhandler": "^4.3.0",
"expensify-common": "git+ssh://git@github.com/Expensify/expensify-common.git#35bff866a8d345b460ea6256f0a0f0a8a7f81086",
"fbjs": "^3.0.2",
- "focus-trap-react": "^10.2.1",
"htmlparser2": "^7.2.0",
"idb-keyval": "^6.2.1",
"jest-when": "^3.5.2",
@@ -70,8 +69,9 @@
"react-collapse": "^5.1.0",
"react-content-loader": "^6.1.0",
"react-dom": "18.1.0",
+ "react-error-boundary": "^4.0.11",
"react-map-gl": "^7.1.3",
- "react-native": "0.72.3",
+ "react-native": "0.72.4",
"react-native-blob-util": "^0.17.3",
"react-native-collapsible": "^1.6.0",
"react-native-config": "^1.4.5",
@@ -81,7 +81,7 @@
"react-native-fast-image": "^8.6.3",
"react-native-fs": "^2.20.0",
"react-native-gesture-handler": "2.12.0",
- "react-native-google-places-autocomplete": "git+https://github.com/Expensify/react-native-google-places-autocomplete.git#ee87343c3e827ff7818abc71b6bb04fcc1f120e0",
+ "react-native-google-places-autocomplete": "git+https://github.com/Expensify/react-native-google-places-autocomplete.git#cef3ac29d9501091453136e1219e24c4ec9f9d76",
"react-native-haptic-feedback": "^1.13.0",
"react-native-image-pan-zoom": "^2.1.12",
"react-native-image-picker": "^5.1.0",
@@ -90,10 +90,10 @@
"react-native-linear-gradient": "^2.8.1",
"react-native-localize": "^2.2.6",
"react-native-modal": "^13.0.0",
- "react-native-onyx": "1.0.76",
+ "react-native-onyx": "1.0.84",
"react-native-pager-view": "^6.2.0",
"react-native-pdf": "^6.7.1",
- "react-native-performance": "^4.0.0",
+ "react-native-performance": "^5.1.0",
"react-native-permissions": "^3.0.1",
"react-native-picker-select": "git+https://github.com/Expensify/react-native-picker-select.git#eae05855286dc699954415cc1d629bfd8e8e47e2",
"react-native-plaid-link-sdk": "^10.0.0",
@@ -103,7 +103,7 @@
"react-native-render-html": "6.3.1",
"react-native-safe-area-context": "4.4.1",
"react-native-screens": "3.21.0",
- "react-native-svg": "^13.9.0",
+ "react-native-svg": "^13.13.0",
"react-native-tab-view": "^3.5.2",
"react-native-url-polyfill": "^2.0.0",
"react-native-view-shot": "^3.6.0",
@@ -139,7 +139,7 @@
"@octokit/plugin-paginate-rest": "3.1.0",
"@octokit/plugin-throttling": "4.1.0",
"@react-native-community/eslint-config": "3.0.0",
- "@react-native/metro-config": "^0.72.9",
+ "@react-native/metro-config": "^0.72.11",
"@react-navigation/devtools": "^6.0.10",
"@storybook/addon-a11y": "^6.5.9",
"@storybook/addon-essentials": "^7.0.0",
@@ -190,7 +190,7 @@
"electron-builder": "24.6.4",
"eslint": "^7.6.0",
"eslint-config-airbnb-typescript": "^17.1.0",
- "eslint-config-expensify": "^2.0.38",
+ "eslint-config-expensify": "^2.0.39",
"eslint-config-prettier": "^8.8.0",
"eslint-plugin-jest": "^24.1.0",
"eslint-plugin-jsdoc": "^46.2.6",
@@ -204,7 +204,7 @@
"jest-circus": "29.4.1",
"jest-cli": "29.4.1",
"jest-environment-jsdom": "^29.4.1",
- "metro-react-native-babel-preset": "0.76.7",
+ "metro-react-native-babel-preset": "0.76.8",
"mock-fs": "^4.13.0",
"onchange": "^7.1.0",
"portfinder": "^1.0.28",
@@ -6726,19 +6726,19 @@
}
},
"node_modules/@react-native-community/cli": {
- "version": "11.3.5",
- "resolved": "https://registry.npmjs.org/@react-native-community/cli/-/cli-11.3.5.tgz",
- "integrity": "sha512-wMXgKEWe6uesw7vyXKKjx5EDRog0QdXHxdgRguG14AjQRao1+4gXEWq2yyExOTi/GDY6dfJBUGTCwGQxhnk/Lg==",
- "dependencies": {
- "@react-native-community/cli-clean": "11.3.5",
- "@react-native-community/cli-config": "11.3.5",
- "@react-native-community/cli-debugger-ui": "11.3.5",
- "@react-native-community/cli-doctor": "11.3.5",
- "@react-native-community/cli-hermes": "11.3.5",
- "@react-native-community/cli-plugin-metro": "11.3.5",
- "@react-native-community/cli-server-api": "11.3.5",
- "@react-native-community/cli-tools": "11.3.5",
- "@react-native-community/cli-types": "11.3.5",
+ "version": "11.3.6",
+ "resolved": "https://registry.npmjs.org/@react-native-community/cli/-/cli-11.3.6.tgz",
+ "integrity": "sha512-bdwOIYTBVQ9VK34dsf6t3u6vOUU5lfdhKaAxiAVArjsr7Je88Bgs4sAbsOYsNK3tkE8G77U6wLpekknXcanlww==",
+ "dependencies": {
+ "@react-native-community/cli-clean": "11.3.6",
+ "@react-native-community/cli-config": "11.3.6",
+ "@react-native-community/cli-debugger-ui": "11.3.6",
+ "@react-native-community/cli-doctor": "11.3.6",
+ "@react-native-community/cli-hermes": "11.3.6",
+ "@react-native-community/cli-plugin-metro": "11.3.6",
+ "@react-native-community/cli-server-api": "11.3.6",
+ "@react-native-community/cli-tools": "11.3.6",
+ "@react-native-community/cli-types": "11.3.6",
"chalk": "^4.1.2",
"commander": "^9.4.1",
"execa": "^5.0.0",
@@ -6746,7 +6746,7 @@
"fs-extra": "^8.1.0",
"graceful-fs": "^4.1.3",
"prompts": "^2.4.0",
- "semver": "^6.3.0"
+ "semver": "^7.5.2"
},
"bin": {
"react-native": "build/bin.js"
@@ -6756,11 +6756,11 @@
}
},
"node_modules/@react-native-community/cli-clean": {
- "version": "11.3.5",
- "resolved": "https://registry.npmjs.org/@react-native-community/cli-clean/-/cli-clean-11.3.5.tgz",
- "integrity": "sha512-1+7BU962wKkIkHRp/uW3jYbQKKGtU7L+R3g59D8K6uLccuxJYUBJv18753ojMa6SD3SAq5Xh31bAre+YwVcOTA==",
+ "version": "11.3.6",
+ "resolved": "https://registry.npmjs.org/@react-native-community/cli-clean/-/cli-clean-11.3.6.tgz",
+ "integrity": "sha512-jOOaeG5ebSXTHweq1NznVJVAFKtTFWL4lWgUXl845bCGX7t1lL8xQNWHKwT8Oh1pGR2CI3cKmRjY4hBg+pEI9g==",
"dependencies": {
- "@react-native-community/cli-tools": "11.3.5",
+ "@react-native-community/cli-tools": "11.3.6",
"chalk": "^4.1.2",
"execa": "^5.0.0",
"prompts": "^2.4.0"
@@ -6831,11 +6831,11 @@
}
},
"node_modules/@react-native-community/cli-config": {
- "version": "11.3.5",
- "resolved": "https://registry.npmjs.org/@react-native-community/cli-config/-/cli-config-11.3.5.tgz",
- "integrity": "sha512-fMblIsHlUleKfGsgWyjFJYfx1SqrsnhS/QXfA8w7iT6GrNOOjBp5UWx8+xlMDFcmOb9e42g1ExFDKl3n8FWkxQ==",
+ "version": "11.3.6",
+ "resolved": "https://registry.npmjs.org/@react-native-community/cli-config/-/cli-config-11.3.6.tgz",
+ "integrity": "sha512-edy7fwllSFLan/6BG6/rznOBCLPrjmJAE10FzkEqNLHowi0bckiAPg1+1jlgQ2qqAxV5kuk+c9eajVfQvPLYDA==",
"dependencies": {
- "@react-native-community/cli-tools": "11.3.5",
+ "@react-native-community/cli-tools": "11.3.6",
"chalk": "^4.1.2",
"cosmiconfig": "^5.1.0",
"deepmerge": "^4.3.0",
@@ -6954,22 +6954,22 @@
}
},
"node_modules/@react-native-community/cli-debugger-ui": {
- "version": "11.3.5",
- "resolved": "https://registry.npmjs.org/@react-native-community/cli-debugger-ui/-/cli-debugger-ui-11.3.5.tgz",
- "integrity": "sha512-o5JVCKEpPUXMX4r3p1cYjiy3FgdOEkezZcQ6owWEae2dYvV19lLYyJwnocm9Y7aG9PvpgI3PIMVh3KZbhS21eA==",
+ "version": "11.3.6",
+ "resolved": "https://registry.npmjs.org/@react-native-community/cli-debugger-ui/-/cli-debugger-ui-11.3.6.tgz",
+ "integrity": "sha512-jhMOSN/iOlid9jn/A2/uf7HbC3u7+lGktpeGSLnHNw21iahFBzcpuO71ekEdlmTZ4zC/WyxBXw9j2ka33T358w==",
"dependencies": {
"serve-static": "^1.13.1"
}
},
"node_modules/@react-native-community/cli-doctor": {
- "version": "11.3.5",
- "resolved": "https://registry.npmjs.org/@react-native-community/cli-doctor/-/cli-doctor-11.3.5.tgz",
- "integrity": "sha512-+4BuFHjoV4FFjX5y60l0s6nS0agidb1izTVwsFixeFKW73LUkOLu+Ae5HI94RAFEPE4ePEVNgYX3FynIau6K0g==",
- "dependencies": {
- "@react-native-community/cli-config": "11.3.5",
- "@react-native-community/cli-platform-android": "11.3.5",
- "@react-native-community/cli-platform-ios": "11.3.5",
- "@react-native-community/cli-tools": "11.3.5",
+ "version": "11.3.6",
+ "resolved": "https://registry.npmjs.org/@react-native-community/cli-doctor/-/cli-doctor-11.3.6.tgz",
+ "integrity": "sha512-UT/Tt6omVPi1j6JEX+CObc85eVFghSZwy4GR9JFMsO7gNg2Tvcu1RGWlUkrbmWMAMHw127LUu6TGK66Ugu1NLA==",
+ "dependencies": {
+ "@react-native-community/cli-config": "11.3.6",
+ "@react-native-community/cli-platform-android": "11.3.6",
+ "@react-native-community/cli-platform-ios": "11.3.6",
+ "@react-native-community/cli-tools": "11.3.6",
"chalk": "^4.1.2",
"command-exists": "^1.2.8",
"envinfo": "^7.7.2",
@@ -6979,7 +6979,7 @@
"node-stream-zip": "^1.9.1",
"ora": "^5.4.1",
"prompts": "^2.4.0",
- "semver": "^6.3.0",
+ "semver": "^7.5.2",
"strip-ansi": "^5.2.0",
"sudo-prompt": "^9.0.0",
"wcwidth": "^1.0.1",
@@ -7044,14 +7044,6 @@
"resolved": "https://registry.npmjs.org/ip/-/ip-1.1.8.tgz",
"integrity": "sha512-PuExPYUiu6qMBQb4l06ecm6T6ujzhmh+MeJcW9wa89PoAz5pvd4zPgN5WJV104mb6S2T1AwNIAaB70JNrLQWhg=="
},
- "node_modules/@react-native-community/cli-doctor/node_modules/semver": {
- "version": "6.3.1",
- "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
- "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
- "bin": {
- "semver": "bin/semver.js"
- }
- },
"node_modules/@react-native-community/cli-doctor/node_modules/strip-ansi": {
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz",
@@ -7075,12 +7067,12 @@
}
},
"node_modules/@react-native-community/cli-hermes": {
- "version": "11.3.5",
- "resolved": "https://registry.npmjs.org/@react-native-community/cli-hermes/-/cli-hermes-11.3.5.tgz",
- "integrity": "sha512-+3m34hiaJpFel8BlJE7kJOaPzWR/8U8APZG2LXojbAdBAg99EGmQcwXIgsSVJFvH8h/nezf4DHbsPKigIe33zA==",
+ "version": "11.3.6",
+ "resolved": "https://registry.npmjs.org/@react-native-community/cli-hermes/-/cli-hermes-11.3.6.tgz",
+ "integrity": "sha512-O55YAYGZ3XynpUdePPVvNuUPGPY0IJdctLAOHme73OvS80gNwfntHDXfmY70TGHWIfkK2zBhA0B+2v8s5aTyTA==",
"dependencies": {
- "@react-native-community/cli-platform-android": "11.3.5",
- "@react-native-community/cli-tools": "11.3.5",
+ "@react-native-community/cli-platform-android": "11.3.6",
+ "@react-native-community/cli-tools": "11.3.6",
"chalk": "^4.1.2",
"hermes-profile-transformer": "^0.0.6",
"ip": "^1.1.5"
@@ -7156,11 +7148,11 @@
}
},
"node_modules/@react-native-community/cli-platform-android": {
- "version": "11.3.5",
- "resolved": "https://registry.npmjs.org/@react-native-community/cli-platform-android/-/cli-platform-android-11.3.5.tgz",
- "integrity": "sha512-s4Lj7FKxJ/BofGi/ifjPfrA9MjFwIgYpHnHBSlqtbsvPoSYzmVCU2qlWM8fb3AmkXIwyYt4A6MEr3MmNT2UoBg==",
+ "version": "11.3.6",
+ "resolved": "https://registry.npmjs.org/@react-native-community/cli-platform-android/-/cli-platform-android-11.3.6.tgz",
+ "integrity": "sha512-ZARrpLv5tn3rmhZc//IuDM1LSAdYnjUmjrp58RynlvjLDI4ZEjBAGCQmgysRgXAsK7ekMrfkZgemUczfn9td2A==",
"dependencies": {
- "@react-native-community/cli-tools": "11.3.5",
+ "@react-native-community/cli-tools": "11.3.6",
"chalk": "^4.1.2",
"execa": "^5.0.0",
"glob": "^7.1.3",
@@ -7232,11 +7224,11 @@
}
},
"node_modules/@react-native-community/cli-platform-ios": {
- "version": "11.3.5",
- "resolved": "https://registry.npmjs.org/@react-native-community/cli-platform-ios/-/cli-platform-ios-11.3.5.tgz",
- "integrity": "sha512-ytJC/YCFD7P+KuQHOT5Jzh1ho2XbJEjq71yHa1gJP2PG/Q/uB4h1x2XpxDqv5iXU6E250yjvKMmkReKTW4CTig==",
+ "version": "11.3.6",
+ "resolved": "https://registry.npmjs.org/@react-native-community/cli-platform-ios/-/cli-platform-ios-11.3.6.tgz",
+ "integrity": "sha512-tZ9VbXWiRW+F+fbZzpLMZlj93g3Q96HpuMsS6DRhrTiG+vMQ3o6oPWSEEmMGOvJSYU7+y68Dc9ms2liC7VD6cw==",
"dependencies": {
- "@react-native-community/cli-tools": "11.3.5",
+ "@react-native-community/cli-tools": "11.3.6",
"chalk": "^4.1.2",
"execa": "^5.0.0",
"fast-xml-parser": "^4.0.12",
@@ -7309,12 +7301,12 @@
}
},
"node_modules/@react-native-community/cli-plugin-metro": {
- "version": "11.3.5",
- "resolved": "https://registry.npmjs.org/@react-native-community/cli-plugin-metro/-/cli-plugin-metro-11.3.5.tgz",
- "integrity": "sha512-r9AekfeLKdblB7LfWB71IrNy1XM03WrByQlUQajUOZAP2NmUUBLl9pMZscPjJeOSgLpHB9ixEFTIOhTabri/qg==",
+ "version": "11.3.6",
+ "resolved": "https://registry.npmjs.org/@react-native-community/cli-plugin-metro/-/cli-plugin-metro-11.3.6.tgz",
+ "integrity": "sha512-D97racrPX3069ibyabJNKw9aJpVcaZrkYiEzsEnx50uauQtPDoQ1ELb/5c6CtMhAEGKoZ0B5MS23BbsSZcLs2g==",
"dependencies": {
- "@react-native-community/cli-server-api": "11.3.5",
- "@react-native-community/cli-tools": "11.3.5",
+ "@react-native-community/cli-server-api": "11.3.6",
+ "@react-native-community/cli-tools": "11.3.6",
"chalk": "^4.1.2",
"execa": "^5.0.0",
"metro": "0.76.7",
@@ -7326,6 +7318,29 @@
"readline": "^1.3.0"
}
},
+ "node_modules/@react-native-community/cli-plugin-metro/node_modules/@jest/types": {
+ "version": "27.5.1",
+ "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.5.1.tgz",
+ "integrity": "sha512-Cx46iJ9QpwQTjIdq5VJu2QTMMs3QlEjI0x1QbBP5W1+nMzyc2XmimiRR/CbX9TO0cPTeUlxWMOu8mslYsJ8DEw==",
+ "dependencies": {
+ "@types/istanbul-lib-coverage": "^2.0.0",
+ "@types/istanbul-reports": "^3.0.0",
+ "@types/node": "*",
+ "@types/yargs": "^16.0.0",
+ "chalk": "^4.0.0"
+ },
+ "engines": {
+ "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0"
+ }
+ },
+ "node_modules/@react-native-community/cli-plugin-metro/node_modules/@types/yargs": {
+ "version": "16.0.5",
+ "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.5.tgz",
+ "integrity": "sha512-AxO/ADJOBFJScHbWhq2xAhlWP24rY4aCEG/NFaMvbT3X2MgRsLjhjQwsn0Zi5zn0LG9jUhCCZMeX9Dkuw6k+vQ==",
+ "dependencies": {
+ "@types/yargs-parser": "*"
+ }
+ },
"node_modules/@react-native-community/cli-plugin-metro/node_modules/ansi-styles": {
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
@@ -7355,6 +7370,24 @@
"url": "https://github.com/chalk/chalk?sponsor=1"
}
},
+ "node_modules/@react-native-community/cli-plugin-metro/node_modules/ci-info": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz",
+ "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ=="
+ },
+ "node_modules/@react-native-community/cli-plugin-metro/node_modules/cliui": {
+ "version": "8.0.1",
+ "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz",
+ "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==",
+ "dependencies": {
+ "string-width": "^4.2.0",
+ "strip-ansi": "^6.0.1",
+ "wrap-ansi": "^7.0.0"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
"node_modules/@react-native-community/cli-plugin-metro/node_modules/color-convert": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
@@ -7371,6 +7404,28 @@
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
},
+ "node_modules/@react-native-community/cli-plugin-metro/node_modules/cosmiconfig": {
+ "version": "5.2.1",
+ "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-5.2.1.tgz",
+ "integrity": "sha512-H65gsXo1SKjf8zmrJ67eJk8aIRKV5ff2D4uKZIBZShbhGSpEmsQOPW/SKMKYhSTrqR7ufy6RP69rPogdaPh/kA==",
+ "dependencies": {
+ "import-fresh": "^2.0.0",
+ "is-directory": "^0.3.1",
+ "js-yaml": "^3.13.1",
+ "parse-json": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/@react-native-community/cli-plugin-metro/node_modules/debug": {
+ "version": "2.6.9",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+ "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+ "dependencies": {
+ "ms": "2.0.0"
+ }
+ },
"node_modules/@react-native-community/cli-plugin-metro/node_modules/has-flag": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
@@ -7379,6 +7434,492 @@
"node": ">=8"
}
},
+ "node_modules/@react-native-community/cli-plugin-metro/node_modules/import-fresh": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-2.0.0.tgz",
+ "integrity": "sha512-eZ5H8rcgYazHbKC3PG4ClHNykCSxtAhxSSEM+2mb+7evD2CKF5V7c0dNum7AdpDh0ZdICwZY9sRSn8f+KH96sg==",
+ "dependencies": {
+ "caller-path": "^2.0.0",
+ "resolve-from": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/@react-native-community/cli-plugin-metro/node_modules/jest-regex-util": {
+ "version": "27.5.1",
+ "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-27.5.1.tgz",
+ "integrity": "sha512-4bfKq2zie+x16okqDXjXn9ql2B0dScQu+vcwe4TvFVhkVyuWLqpZrZtXxLLWoXYgn0E87I6r6GRYHF7wFZBUvg==",
+ "engines": {
+ "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0"
+ }
+ },
+ "node_modules/@react-native-community/cli-plugin-metro/node_modules/jest-util": {
+ "version": "27.5.1",
+ "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-27.5.1.tgz",
+ "integrity": "sha512-Kv2o/8jNvX1MQ0KGtw480E/w4fBCDOnH6+6DmeKi6LZUIlKA5kwY0YNdlzaWTiVgxqAqik11QyxDOKk543aKXw==",
+ "dependencies": {
+ "@jest/types": "^27.5.1",
+ "@types/node": "*",
+ "chalk": "^4.0.0",
+ "ci-info": "^3.2.0",
+ "graceful-fs": "^4.2.9",
+ "picomatch": "^2.2.3"
+ },
+ "engines": {
+ "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0"
+ }
+ },
+ "node_modules/@react-native-community/cli-plugin-metro/node_modules/jest-util/node_modules/ci-info": {
+ "version": "3.8.0",
+ "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.8.0.tgz",
+ "integrity": "sha512-eXTggHWSooYhq49F2opQhuHWgzucfF2YgODK4e1566GQs5BIfP30B0oenwBJHfWxAs2fyPB1s7Mg949zLf61Yw==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/sibiraj-s"
+ }
+ ],
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/@react-native-community/cli-plugin-metro/node_modules/jest-worker": {
+ "version": "27.5.1",
+ "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz",
+ "integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==",
+ "dependencies": {
+ "@types/node": "*",
+ "merge-stream": "^2.0.0",
+ "supports-color": "^8.0.0"
+ },
+ "engines": {
+ "node": ">= 10.13.0"
+ }
+ },
+ "node_modules/@react-native-community/cli-plugin-metro/node_modules/jest-worker/node_modules/supports-color": {
+ "version": "8.1.1",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz",
+ "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==",
+ "dependencies": {
+ "has-flag": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/supports-color?sponsor=1"
+ }
+ },
+ "node_modules/@react-native-community/cli-plugin-metro/node_modules/metro": {
+ "version": "0.76.7",
+ "resolved": "https://registry.npmjs.org/metro/-/metro-0.76.7.tgz",
+ "integrity": "sha512-67ZGwDeumEPnrHI+pEDSKH2cx+C81Gx8Mn5qOtmGUPm/Up9Y4I1H2dJZ5n17MWzejNo0XAvPh0QL0CrlJEODVQ==",
+ "dependencies": {
+ "@babel/code-frame": "^7.0.0",
+ "@babel/core": "^7.20.0",
+ "@babel/generator": "^7.20.0",
+ "@babel/parser": "^7.20.0",
+ "@babel/template": "^7.0.0",
+ "@babel/traverse": "^7.20.0",
+ "@babel/types": "^7.20.0",
+ "accepts": "^1.3.7",
+ "async": "^3.2.2",
+ "chalk": "^4.0.0",
+ "ci-info": "^2.0.0",
+ "connect": "^3.6.5",
+ "debug": "^2.2.0",
+ "denodeify": "^1.2.1",
+ "error-stack-parser": "^2.0.6",
+ "graceful-fs": "^4.2.4",
+ "hermes-parser": "0.12.0",
+ "image-size": "^1.0.2",
+ "invariant": "^2.2.4",
+ "jest-worker": "^27.2.0",
+ "jsc-safe-url": "^0.2.2",
+ "lodash.throttle": "^4.1.1",
+ "metro-babel-transformer": "0.76.7",
+ "metro-cache": "0.76.7",
+ "metro-cache-key": "0.76.7",
+ "metro-config": "0.76.7",
+ "metro-core": "0.76.7",
+ "metro-file-map": "0.76.7",
+ "metro-inspector-proxy": "0.76.7",
+ "metro-minify-terser": "0.76.7",
+ "metro-minify-uglify": "0.76.7",
+ "metro-react-native-babel-preset": "0.76.7",
+ "metro-resolver": "0.76.7",
+ "metro-runtime": "0.76.7",
+ "metro-source-map": "0.76.7",
+ "metro-symbolicate": "0.76.7",
+ "metro-transform-plugins": "0.76.7",
+ "metro-transform-worker": "0.76.7",
+ "mime-types": "^2.1.27",
+ "node-fetch": "^2.2.0",
+ "nullthrows": "^1.1.1",
+ "rimraf": "^3.0.2",
+ "serialize-error": "^2.1.0",
+ "source-map": "^0.5.6",
+ "strip-ansi": "^6.0.0",
+ "throat": "^5.0.0",
+ "ws": "^7.5.1",
+ "yargs": "^17.6.2"
+ },
+ "bin": {
+ "metro": "src/cli.js"
+ },
+ "engines": {
+ "node": ">=16"
+ }
+ },
+ "node_modules/@react-native-community/cli-plugin-metro/node_modules/metro-babel-transformer": {
+ "version": "0.76.7",
+ "resolved": "https://registry.npmjs.org/metro-babel-transformer/-/metro-babel-transformer-0.76.7.tgz",
+ "integrity": "sha512-bgr2OFn0J4r0qoZcHrwEvccF7g9k3wdgTOgk6gmGHrtlZ1Jn3oCpklW/DfZ9PzHfjY2mQammKTc19g/EFGyOJw==",
+ "dependencies": {
+ "@babel/core": "^7.20.0",
+ "hermes-parser": "0.12.0",
+ "nullthrows": "^1.1.1"
+ },
+ "engines": {
+ "node": ">=16"
+ }
+ },
+ "node_modules/@react-native-community/cli-plugin-metro/node_modules/metro-cache": {
+ "version": "0.76.7",
+ "resolved": "https://registry.npmjs.org/metro-cache/-/metro-cache-0.76.7.tgz",
+ "integrity": "sha512-nWBMztrs5RuSxZRI7hgFgob5PhYDmxICh9FF8anm9/ito0u0vpPvRxt7sRu8fyeD2AHdXqE7kX32rWY0LiXgeg==",
+ "dependencies": {
+ "metro-core": "0.76.7",
+ "rimraf": "^3.0.2"
+ },
+ "engines": {
+ "node": ">=16"
+ }
+ },
+ "node_modules/@react-native-community/cli-plugin-metro/node_modules/metro-cache-key": {
+ "version": "0.76.7",
+ "resolved": "https://registry.npmjs.org/metro-cache-key/-/metro-cache-key-0.76.7.tgz",
+ "integrity": "sha512-0pecoIzwsD/Whn/Qfa+SDMX2YyasV0ndbcgUFx7w1Ct2sLHClujdhQ4ik6mvQmsaOcnGkIyN0zcceMDjC2+BFQ==",
+ "engines": {
+ "node": ">=16"
+ }
+ },
+ "node_modules/@react-native-community/cli-plugin-metro/node_modules/metro-config": {
+ "version": "0.76.7",
+ "resolved": "https://registry.npmjs.org/metro-config/-/metro-config-0.76.7.tgz",
+ "integrity": "sha512-CFDyNb9bqxZemiChC/gNdXZ7OQkIwmXzkrEXivcXGbgzlt/b2juCv555GWJHyZSlorwnwJfY3uzAFu4A9iRVfg==",
+ "dependencies": {
+ "connect": "^3.6.5",
+ "cosmiconfig": "^5.0.5",
+ "jest-validate": "^29.2.1",
+ "metro": "0.76.7",
+ "metro-cache": "0.76.7",
+ "metro-core": "0.76.7",
+ "metro-runtime": "0.76.7"
+ },
+ "engines": {
+ "node": ">=16"
+ }
+ },
+ "node_modules/@react-native-community/cli-plugin-metro/node_modules/metro-core": {
+ "version": "0.76.7",
+ "resolved": "https://registry.npmjs.org/metro-core/-/metro-core-0.76.7.tgz",
+ "integrity": "sha512-0b8KfrwPmwCMW+1V7ZQPkTy2tsEKZjYG9Pu1PTsu463Z9fxX7WaR0fcHFshv+J1CnQSUTwIGGjbNvj1teKe+pw==",
+ "dependencies": {
+ "lodash.throttle": "^4.1.1",
+ "metro-resolver": "0.76.7"
+ },
+ "engines": {
+ "node": ">=16"
+ }
+ },
+ "node_modules/@react-native-community/cli-plugin-metro/node_modules/metro-file-map": {
+ "version": "0.76.7",
+ "resolved": "https://registry.npmjs.org/metro-file-map/-/metro-file-map-0.76.7.tgz",
+ "integrity": "sha512-s+zEkTcJ4mOJTgEE2ht4jIo1DZfeWreQR3tpT3gDV/Y/0UQ8aJBTv62dE775z0GLsWZApiblAYZsj7ZE8P06nw==",
+ "dependencies": {
+ "anymatch": "^3.0.3",
+ "debug": "^2.2.0",
+ "fb-watchman": "^2.0.0",
+ "graceful-fs": "^4.2.4",
+ "invariant": "^2.2.4",
+ "jest-regex-util": "^27.0.6",
+ "jest-util": "^27.2.0",
+ "jest-worker": "^27.2.0",
+ "micromatch": "^4.0.4",
+ "node-abort-controller": "^3.1.1",
+ "nullthrows": "^1.1.1",
+ "walker": "^1.0.7"
+ },
+ "engines": {
+ "node": ">=16"
+ },
+ "optionalDependencies": {
+ "fsevents": "^2.3.2"
+ }
+ },
+ "node_modules/@react-native-community/cli-plugin-metro/node_modules/metro-inspector-proxy": {
+ "version": "0.76.7",
+ "resolved": "https://registry.npmjs.org/metro-inspector-proxy/-/metro-inspector-proxy-0.76.7.tgz",
+ "integrity": "sha512-rNZ/6edTl/1qUekAhAbaFjczMphM50/UjtxiKulo6vqvgn/Mjd9hVqDvVYfAMZXqPvlusD88n38UjVYPkruLSg==",
+ "dependencies": {
+ "connect": "^3.6.5",
+ "debug": "^2.2.0",
+ "node-fetch": "^2.2.0",
+ "ws": "^7.5.1",
+ "yargs": "^17.6.2"
+ },
+ "bin": {
+ "metro-inspector-proxy": "src/cli.js"
+ },
+ "engines": {
+ "node": ">=16"
+ }
+ },
+ "node_modules/@react-native-community/cli-plugin-metro/node_modules/metro-minify-terser": {
+ "version": "0.76.7",
+ "resolved": "https://registry.npmjs.org/metro-minify-terser/-/metro-minify-terser-0.76.7.tgz",
+ "integrity": "sha512-FQiZGhIxCzhDwK4LxyPMLlq0Tsmla10X7BfNGlYFK0A5IsaVKNJbETyTzhpIwc+YFRT4GkFFwgo0V2N5vxO5HA==",
+ "dependencies": {
+ "terser": "^5.15.0"
+ },
+ "engines": {
+ "node": ">=16"
+ }
+ },
+ "node_modules/@react-native-community/cli-plugin-metro/node_modules/metro-minify-uglify": {
+ "version": "0.76.7",
+ "resolved": "https://registry.npmjs.org/metro-minify-uglify/-/metro-minify-uglify-0.76.7.tgz",
+ "integrity": "sha512-FuXIU3j2uNcSvQtPrAJjYWHruPiQ+EpE++J9Z+VznQKEHcIxMMoQZAfIF2IpZSrZYfLOjVFyGMvj41jQMxV1Vw==",
+ "dependencies": {
+ "uglify-es": "^3.1.9"
+ },
+ "engines": {
+ "node": ">=16"
+ }
+ },
+ "node_modules/@react-native-community/cli-plugin-metro/node_modules/metro-react-native-babel-preset": {
+ "version": "0.76.7",
+ "resolved": "https://registry.npmjs.org/metro-react-native-babel-preset/-/metro-react-native-babel-preset-0.76.7.tgz",
+ "integrity": "sha512-R25wq+VOSorAK3hc07NW0SmN8z9S/IR0Us0oGAsBcMZnsgkbOxu77Mduqf+f4is/wnWHc5+9bfiqdLnaMngiVw==",
+ "dependencies": {
+ "@babel/core": "^7.20.0",
+ "@babel/plugin-proposal-async-generator-functions": "^7.0.0",
+ "@babel/plugin-proposal-class-properties": "^7.18.0",
+ "@babel/plugin-proposal-export-default-from": "^7.0.0",
+ "@babel/plugin-proposal-nullish-coalescing-operator": "^7.18.0",
+ "@babel/plugin-proposal-numeric-separator": "^7.0.0",
+ "@babel/plugin-proposal-object-rest-spread": "^7.20.0",
+ "@babel/plugin-proposal-optional-catch-binding": "^7.0.0",
+ "@babel/plugin-proposal-optional-chaining": "^7.20.0",
+ "@babel/plugin-syntax-dynamic-import": "^7.8.0",
+ "@babel/plugin-syntax-export-default-from": "^7.0.0",
+ "@babel/plugin-syntax-flow": "^7.18.0",
+ "@babel/plugin-syntax-nullish-coalescing-operator": "^7.0.0",
+ "@babel/plugin-syntax-optional-chaining": "^7.0.0",
+ "@babel/plugin-transform-arrow-functions": "^7.0.0",
+ "@babel/plugin-transform-async-to-generator": "^7.20.0",
+ "@babel/plugin-transform-block-scoping": "^7.0.0",
+ "@babel/plugin-transform-classes": "^7.0.0",
+ "@babel/plugin-transform-computed-properties": "^7.0.0",
+ "@babel/plugin-transform-destructuring": "^7.20.0",
+ "@babel/plugin-transform-flow-strip-types": "^7.20.0",
+ "@babel/plugin-transform-function-name": "^7.0.0",
+ "@babel/plugin-transform-literals": "^7.0.0",
+ "@babel/plugin-transform-modules-commonjs": "^7.0.0",
+ "@babel/plugin-transform-named-capturing-groups-regex": "^7.0.0",
+ "@babel/plugin-transform-parameters": "^7.0.0",
+ "@babel/plugin-transform-react-display-name": "^7.0.0",
+ "@babel/plugin-transform-react-jsx": "^7.0.0",
+ "@babel/plugin-transform-react-jsx-self": "^7.0.0",
+ "@babel/plugin-transform-react-jsx-source": "^7.0.0",
+ "@babel/plugin-transform-runtime": "^7.0.0",
+ "@babel/plugin-transform-shorthand-properties": "^7.0.0",
+ "@babel/plugin-transform-spread": "^7.0.0",
+ "@babel/plugin-transform-sticky-regex": "^7.0.0",
+ "@babel/plugin-transform-typescript": "^7.5.0",
+ "@babel/plugin-transform-unicode-regex": "^7.0.0",
+ "@babel/template": "^7.0.0",
+ "babel-plugin-transform-flow-enums": "^0.0.2",
+ "react-refresh": "^0.4.0"
+ },
+ "engines": {
+ "node": ">=16"
+ },
+ "peerDependencies": {
+ "@babel/core": "*"
+ }
+ },
+ "node_modules/@react-native-community/cli-plugin-metro/node_modules/metro-react-native-babel-transformer": {
+ "version": "0.76.7",
+ "resolved": "https://registry.npmjs.org/metro-react-native-babel-transformer/-/metro-react-native-babel-transformer-0.76.7.tgz",
+ "integrity": "sha512-W6lW3J7y/05ph3c2p3KKJNhH0IdyxdOCbQ5it7aM2MAl0SM4wgKjaV6EYv9b3rHklpV6K3qMH37UKVcjMooWiA==",
+ "dependencies": {
+ "@babel/core": "^7.20.0",
+ "babel-preset-fbjs": "^3.4.0",
+ "hermes-parser": "0.12.0",
+ "metro-react-native-babel-preset": "0.76.7",
+ "nullthrows": "^1.1.1"
+ },
+ "engines": {
+ "node": ">=16"
+ },
+ "peerDependencies": {
+ "@babel/core": "*"
+ }
+ },
+ "node_modules/@react-native-community/cli-plugin-metro/node_modules/metro-resolver": {
+ "version": "0.76.7",
+ "resolved": "https://registry.npmjs.org/metro-resolver/-/metro-resolver-0.76.7.tgz",
+ "integrity": "sha512-pC0Wgq29HHIHrwz23xxiNgylhI8Rq1V01kQaJ9Kz11zWrIdlrH0ZdnJ7GC6qA0ErROG+cXmJ0rJb8/SW1Zp2IA==",
+ "engines": {
+ "node": ">=16"
+ }
+ },
+ "node_modules/@react-native-community/cli-plugin-metro/node_modules/metro-runtime": {
+ "version": "0.76.7",
+ "resolved": "https://registry.npmjs.org/metro-runtime/-/metro-runtime-0.76.7.tgz",
+ "integrity": "sha512-MuWHubQHymUWBpZLwuKZQgA/qbb35WnDAKPo83rk7JRLIFPvzXSvFaC18voPuzJBt1V98lKQIonh6MiC9gd8Ug==",
+ "dependencies": {
+ "@babel/runtime": "^7.0.0",
+ "react-refresh": "^0.4.0"
+ },
+ "engines": {
+ "node": ">=16"
+ }
+ },
+ "node_modules/@react-native-community/cli-plugin-metro/node_modules/metro-source-map": {
+ "version": "0.76.7",
+ "resolved": "https://registry.npmjs.org/metro-source-map/-/metro-source-map-0.76.7.tgz",
+ "integrity": "sha512-Prhx7PeRV1LuogT0Kn5VjCuFu9fVD68eefntdWabrksmNY6mXK8pRqzvNJOhTojh6nek+RxBzZeD6MIOOyXS6w==",
+ "dependencies": {
+ "@babel/traverse": "^7.20.0",
+ "@babel/types": "^7.20.0",
+ "invariant": "^2.2.4",
+ "metro-symbolicate": "0.76.7",
+ "nullthrows": "^1.1.1",
+ "ob1": "0.76.7",
+ "source-map": "^0.5.6",
+ "vlq": "^1.0.0"
+ },
+ "engines": {
+ "node": ">=16"
+ }
+ },
+ "node_modules/@react-native-community/cli-plugin-metro/node_modules/metro-symbolicate": {
+ "version": "0.76.7",
+ "resolved": "https://registry.npmjs.org/metro-symbolicate/-/metro-symbolicate-0.76.7.tgz",
+ "integrity": "sha512-p0zWEME5qLSL1bJb93iq+zt5fz3sfVn9xFYzca1TJIpY5MommEaS64Va87lp56O0sfEIvh4307Oaf/ZzRjuLiQ==",
+ "dependencies": {
+ "invariant": "^2.2.4",
+ "metro-source-map": "0.76.7",
+ "nullthrows": "^1.1.1",
+ "source-map": "^0.5.6",
+ "through2": "^2.0.1",
+ "vlq": "^1.0.0"
+ },
+ "bin": {
+ "metro-symbolicate": "src/index.js"
+ },
+ "engines": {
+ "node": ">=16"
+ }
+ },
+ "node_modules/@react-native-community/cli-plugin-metro/node_modules/metro-transform-plugins": {
+ "version": "0.76.7",
+ "resolved": "https://registry.npmjs.org/metro-transform-plugins/-/metro-transform-plugins-0.76.7.tgz",
+ "integrity": "sha512-iSmnjVApbdivjuzb88Orb0JHvcEt5veVyFAzxiS5h0QB+zV79w6JCSqZlHCrbNOkOKBED//LqtKbFVakxllnNg==",
+ "dependencies": {
+ "@babel/core": "^7.20.0",
+ "@babel/generator": "^7.20.0",
+ "@babel/template": "^7.0.0",
+ "@babel/traverse": "^7.20.0",
+ "nullthrows": "^1.1.1"
+ },
+ "engines": {
+ "node": ">=16"
+ }
+ },
+ "node_modules/@react-native-community/cli-plugin-metro/node_modules/metro-transform-worker": {
+ "version": "0.76.7",
+ "resolved": "https://registry.npmjs.org/metro-transform-worker/-/metro-transform-worker-0.76.7.tgz",
+ "integrity": "sha512-cGvELqFMVk9XTC15CMVzrCzcO6sO1lURfcbgjuuPdzaWuD11eEyocvkTX0DPiRjsvgAmicz4XYxVzgYl3MykDw==",
+ "dependencies": {
+ "@babel/core": "^7.20.0",
+ "@babel/generator": "^7.20.0",
+ "@babel/parser": "^7.20.0",
+ "@babel/types": "^7.20.0",
+ "babel-preset-fbjs": "^3.4.0",
+ "metro": "0.76.7",
+ "metro-babel-transformer": "0.76.7",
+ "metro-cache": "0.76.7",
+ "metro-cache-key": "0.76.7",
+ "metro-source-map": "0.76.7",
+ "metro-transform-plugins": "0.76.7",
+ "nullthrows": "^1.1.1"
+ },
+ "engines": {
+ "node": ">=16"
+ }
+ },
+ "node_modules/@react-native-community/cli-plugin-metro/node_modules/ms": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+ "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="
+ },
+ "node_modules/@react-native-community/cli-plugin-metro/node_modules/ob1": {
+ "version": "0.76.7",
+ "resolved": "https://registry.npmjs.org/ob1/-/ob1-0.76.7.tgz",
+ "integrity": "sha512-BQdRtxxoUNfSoZxqeBGOyuT9nEYSn18xZHwGMb0mMVpn2NBcYbnyKY4BK2LIHRgw33CBGlUmE+KMaNvyTpLLtQ==",
+ "engines": {
+ "node": ">=16"
+ }
+ },
+ "node_modules/@react-native-community/cli-plugin-metro/node_modules/parse-json": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz",
+ "integrity": "sha512-aOIos8bujGN93/8Ox/jPLh7RwVnPEysynVFE+fQZyg6jKELEHwzgKdLRFHUgXJL6kylijVSBC4BvN9OmsB48Rw==",
+ "dependencies": {
+ "error-ex": "^1.3.1",
+ "json-parse-better-errors": "^1.0.1"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/@react-native-community/cli-plugin-metro/node_modules/react-refresh": {
+ "version": "0.4.3",
+ "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.4.3.tgz",
+ "integrity": "sha512-Hwln1VNuGl/6bVwnd0Xdn1e84gT/8T9aYNL+HAKDArLCS7LWjwr7StE30IEYbIkx0Vi3vs+coQxe+SQDbGbbpA==",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/@react-native-community/cli-plugin-metro/node_modules/resolve-from": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-3.0.0.tgz",
+ "integrity": "sha512-GnlH6vxLymXJNMBo7XP1fJIzBFbdYt49CuTwmB/6N53t+kMPRMFKz783LlQ4tv28XoQfMWinAJX6WCGf2IlaIw==",
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/@react-native-community/cli-plugin-metro/node_modules/serialize-error": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/serialize-error/-/serialize-error-2.1.0.tgz",
+ "integrity": "sha512-ghgmKt5o4Tly5yEG/UJp8qTd0AN7Xalw4XBtDEKP655B699qMEtra1WlXeE6WIvdEG481JvRxULKsInq/iNysw==",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/@react-native-community/cli-plugin-metro/node_modules/source-map": {
+ "version": "0.5.7",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
+ "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
"node_modules/@react-native-community/cli-plugin-metro/node_modules/supports-color": {
"version": "7.2.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
@@ -7390,13 +7931,66 @@
"node": ">=8"
}
},
+ "node_modules/@react-native-community/cli-plugin-metro/node_modules/ws": {
+ "version": "7.5.9",
+ "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.9.tgz",
+ "integrity": "sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q==",
+ "engines": {
+ "node": ">=8.3.0"
+ },
+ "peerDependencies": {
+ "bufferutil": "^4.0.1",
+ "utf-8-validate": "^5.0.2"
+ },
+ "peerDependenciesMeta": {
+ "bufferutil": {
+ "optional": true
+ },
+ "utf-8-validate": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@react-native-community/cli-plugin-metro/node_modules/y18n": {
+ "version": "5.0.8",
+ "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz",
+ "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==",
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/@react-native-community/cli-plugin-metro/node_modules/yargs": {
+ "version": "17.7.2",
+ "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz",
+ "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==",
+ "dependencies": {
+ "cliui": "^8.0.1",
+ "escalade": "^3.1.1",
+ "get-caller-file": "^2.0.5",
+ "require-directory": "^2.1.1",
+ "string-width": "^4.2.3",
+ "y18n": "^5.0.5",
+ "yargs-parser": "^21.1.1"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@react-native-community/cli-plugin-metro/node_modules/yargs-parser": {
+ "version": "21.1.1",
+ "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz",
+ "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==",
+ "engines": {
+ "node": ">=12"
+ }
+ },
"node_modules/@react-native-community/cli-server-api": {
- "version": "11.3.5",
- "resolved": "https://registry.npmjs.org/@react-native-community/cli-server-api/-/cli-server-api-11.3.5.tgz",
- "integrity": "sha512-PM/jF13uD1eAKuC84lntNuM5ZvJAtyb+H896P1dBIXa9boPLa3KejfUvNVoyOUJ5s8Ht25JKbc3yieV2+GMBDA==",
+ "version": "11.3.6",
+ "resolved": "https://registry.npmjs.org/@react-native-community/cli-server-api/-/cli-server-api-11.3.6.tgz",
+ "integrity": "sha512-8GUKodPnURGtJ9JKg8yOHIRtWepPciI3ssXVw5jik7+dZ43yN8P5BqCoDaq8e1H1yRer27iiOfT7XVnwk8Dueg==",
"dependencies": {
- "@react-native-community/cli-debugger-ui": "11.3.5",
- "@react-native-community/cli-tools": "11.3.5",
+ "@react-native-community/cli-debugger-ui": "11.3.6",
+ "@react-native-community/cli-tools": "11.3.6",
"compression": "^1.7.1",
"connect": "^3.6.5",
"errorhandler": "^1.5.1",
@@ -7484,9 +8078,9 @@
}
},
"node_modules/@react-native-community/cli-tools": {
- "version": "11.3.5",
- "resolved": "https://registry.npmjs.org/@react-native-community/cli-tools/-/cli-tools-11.3.5.tgz",
- "integrity": "sha512-zDklE1+ah/zL4BLxut5XbzqCj9KTHzbYBKX7//cXw2/0TpkNCaY9c+iKx//gZ5m7U1OKbb86Fm2b0AKtKVRf6Q==",
+ "version": "11.3.6",
+ "resolved": "https://registry.npmjs.org/@react-native-community/cli-tools/-/cli-tools-11.3.6.tgz",
+ "integrity": "sha512-JpmUTcDwAGiTzLsfMlIAYpCMSJ9w2Qlf7PU7mZIRyEu61UzEawyw83DkqfbzDPBuRwRnaeN44JX2CP/yTO3ThQ==",
"dependencies": {
"appdirsjs": "^1.2.4",
"chalk": "^4.1.2",
@@ -7495,7 +8089,7 @@
"node-fetch": "^2.6.0",
"open": "^6.2.0",
"ora": "^5.4.1",
- "semver": "^6.3.0",
+ "semver": "^7.5.2",
"shell-quote": "^1.7.3"
}
},
@@ -7571,14 +8165,6 @@
"node": ">=8"
}
},
- "node_modules/@react-native-community/cli-tools/node_modules/semver": {
- "version": "6.3.1",
- "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
- "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
- "bin": {
- "semver": "bin/semver.js"
- }
- },
"node_modules/@react-native-community/cli-tools/node_modules/supports-color": {
"version": "7.2.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
@@ -7591,9 +8177,9 @@
}
},
"node_modules/@react-native-community/cli-types": {
- "version": "11.3.5",
- "resolved": "https://registry.npmjs.org/@react-native-community/cli-types/-/cli-types-11.3.5.tgz",
- "integrity": "sha512-pf0kdWMEfPSV/+8rcViDCFzbLMtWIHMZ8ay7hKwqaoWegsJ0oprSF2tSTH+LSC/7X1Beb9ssIvHj1m5C4es5Xg==",
+ "version": "11.3.6",
+ "resolved": "https://registry.npmjs.org/@react-native-community/cli-types/-/cli-types-11.3.6.tgz",
+ "integrity": "sha512-6DxjrMKx5x68N/tCJYVYRKAtlRHbtUVBZrnAvkxbRWFD9v4vhNgsPM0RQm8i2vRugeksnao5mbnRGpS6c0awCw==",
"dependencies": {
"joi": "^17.2.1"
}
@@ -7736,14 +8322,6 @@
"node": ">=8"
}
},
- "node_modules/@react-native-community/cli/node_modules/semver": {
- "version": "6.3.1",
- "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
- "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
- "bin": {
- "semver": "bin/semver.js"
- }
- },
"node_modules/@react-native-community/cli/node_modules/supports-color": {
"version": "7.2.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
@@ -7990,15 +8568,15 @@
"license": "MIT"
},
"node_modules/@react-native/metro-config": {
- "version": "0.72.9",
- "resolved": "https://registry.npmjs.org/@react-native/metro-config/-/metro-config-0.72.9.tgz",
- "integrity": "sha512-5MGmyDnXPeprRuvgPGE4LZ+e+ovofSd5YY6nFDwg6wbjRGOkeCRRlaTlQT+fjmv+zr4vYG+MUTKBlaO+fui/vA==",
+ "version": "0.72.11",
+ "resolved": "https://registry.npmjs.org/@react-native/metro-config/-/metro-config-0.72.11.tgz",
+ "integrity": "sha512-661EyQnDdVelyc0qP/ew7kKkGAh6N6KlkuPLC2SQ8sxaXskVU6fSuNlpLW4bUTBUDFKG8gEOU2hp6rzk4wQnGQ==",
"dev": true,
"dependencies": {
"@react-native/js-polyfills": "^0.72.1",
- "metro-config": "0.76.7",
- "metro-react-native-babel-transformer": "0.76.7",
- "metro-runtime": "0.76.7"
+ "metro-config": "0.76.8",
+ "metro-react-native-babel-transformer": "0.76.8",
+ "metro-runtime": "0.76.8"
}
},
"node_modules/@react-native/normalize-color": {
@@ -8011,9 +8589,9 @@
"integrity": "sha512-285lfdqSXaqKuBbbtP9qL2tDrfxdOFtIMvkKadtleRQkdOxx+uzGvFr82KHmc/sSiMtfXGp7JnFYWVh4sFl7Yw=="
},
"node_modules/@react-native/virtualized-lists": {
- "version": "0.72.6",
- "resolved": "https://registry.npmjs.org/@react-native/virtualized-lists/-/virtualized-lists-0.72.6.tgz",
- "integrity": "sha512-JhT6ydu35LvbSKdwnhWDuGHMOwM0WAh9oza/X8vXHA8ELHRyQ/4p8eKz/bTQcbQziJaaleUURToGhFuCtgiMoA==",
+ "version": "0.72.8",
+ "resolved": "https://registry.npmjs.org/@react-native/virtualized-lists/-/virtualized-lists-0.72.8.tgz",
+ "integrity": "sha512-J3Q4Bkuo99k7mu+jPS9gSUSgq+lLRSI/+ahXNwV92XgJ/8UgOTxu2LPwhJnBk/sQKxq7E8WkZBnBiozukQMqrw==",
"dependencies": {
"invariant": "^2.2.4",
"nullthrows": "^1.1.1"
@@ -21038,7 +21616,8 @@
},
"node_modules/babel-plugin-syntax-trailing-function-commas": {
"version": "7.0.0-beta.0",
- "license": "MIT"
+ "resolved": "https://registry.npmjs.org/babel-plugin-syntax-trailing-function-commas/-/babel-plugin-syntax-trailing-function-commas-7.0.0-beta.0.tgz",
+ "integrity": "sha512-Xj9XuRuz3nTSbaTXWv3itLOcxyF4oPD8douBBmj7U9BBC6nEBYfyOJYQMf/8PJAFotC62UY5dFfIGEPr7WswzQ=="
},
"node_modules/babel-plugin-transform-class-properties": {
"version": "6.24.1",
@@ -21104,7 +21683,8 @@
},
"node_modules/babel-preset-fbjs": {
"version": "3.4.0",
- "license": "MIT",
+ "resolved": "https://registry.npmjs.org/babel-preset-fbjs/-/babel-preset-fbjs-3.4.0.tgz",
+ "integrity": "sha512-9ywCsCvo1ojrw0b+XYk7aFvTH6D9064t0RIL1rtMf3nsa02Xw41MS7sZw216Im35xj/UY0PDBQsa1brUDDF1Ow==",
"dependencies": {
"@babel/plugin-proposal-class-properties": "^7.0.0",
"@babel/plugin-proposal-object-rest-spread": "^7.0.0",
@@ -22669,9 +23249,9 @@
}
},
"node_modules/cli-spinners": {
- "version": "2.9.0",
- "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.9.0.tgz",
- "integrity": "sha512-4/aL9X3Wh0yiMQlE+eeRhWP6vclO3QRtw1JHKIT0FFUs5FjpFmESqtMvYZ0+lbzBw900b95mS0hohy+qn2VK/g==",
+ "version": "2.9.1",
+ "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.9.1.tgz",
+ "integrity": "sha512-jHgecW0pxkonBJdrKsqxgRX9AcG+u/5k0Q7WPDfi8AogLAdwxEkyYYNWwZ5GvVFoFx2uiY1eNcSK00fh+1+FyQ==",
"engines": {
"node": ">=6"
},
@@ -23138,7 +23718,8 @@
},
"node_modules/connect": {
"version": "3.7.0",
- "license": "MIT",
+ "resolved": "https://registry.npmjs.org/connect/-/connect-3.7.0.tgz",
+ "integrity": "sha512-ZqRXc+tZukToSNmh5C2iWMSoV3X1YUcPbqEM4DkEG5tNQXrQUZCNVGGv3IuicnkMtPfGf3Xtp8WCXs295iQ1pQ==",
"dependencies": {
"debug": "2.6.9",
"finalhandler": "1.1.2",
@@ -23159,14 +23740,16 @@
},
"node_modules/connect/node_modules/debug": {
"version": "2.6.9",
- "license": "MIT",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+ "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
"dependencies": {
"ms": "2.0.0"
}
},
"node_modules/connect/node_modules/finalhandler": {
"version": "1.1.2",
- "license": "MIT",
+ "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz",
+ "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==",
"dependencies": {
"debug": "2.6.9",
"encodeurl": "~1.0.2",
@@ -23182,11 +23765,13 @@
},
"node_modules/connect/node_modules/ms": {
"version": "2.0.0",
- "license": "MIT"
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+ "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="
},
"node_modules/connect/node_modules/on-finished": {
"version": "2.3.0",
- "license": "MIT",
+ "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz",
+ "integrity": "sha512-ikqdkGAAyf/X/gPhXGvfgAytDZtDbr+bkNUJ0N9h5MI/dmdgCs3l6hoHrcUv41sRKew3jIwrp4qQDXiK99Utww==",
"dependencies": {
"ee-first": "1.1.1"
},
@@ -23196,7 +23781,8 @@
},
"node_modules/connect/node_modules/statuses": {
"version": "1.5.0",
- "license": "MIT",
+ "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz",
+ "integrity": "sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==",
"engines": {
"node": ">= 0.6"
}
@@ -24572,7 +25158,8 @@
},
"node_modules/denodeify": {
"version": "1.2.1",
- "license": "MIT"
+ "resolved": "https://registry.npmjs.org/denodeify/-/denodeify-1.2.1.tgz",
+ "integrity": "sha512-KNTihKNmQENUZeKu5fzfpzRqR5S2VMp4gl9RFHiWzj9DfvYQPMJ6XHKNaQxaGCXwPk6y9yme3aUoaiAe+KX+vg=="
},
"node_modules/depd": {
"version": "2.0.0",
@@ -25905,9 +26492,9 @@
}
},
"node_modules/eslint-config-expensify": {
- "version": "2.0.38",
- "resolved": "https://registry.npmjs.org/eslint-config-expensify/-/eslint-config-expensify-2.0.38.tgz",
- "integrity": "sha512-jAlrYSjkDV8YESUUPcaTjUM8Fgru+37FS+Hq6zzcRR0FbA5bLiOPguhJHo77XpYh5N+PEf4wrpgsS04sjdgDPg==",
+ "version": "2.0.39",
+ "resolved": "https://registry.npmjs.org/eslint-config-expensify/-/eslint-config-expensify-2.0.39.tgz",
+ "integrity": "sha512-DIxR3k99ZIDPE2NK+WLLRWpoDq06gTXdY8XZg9Etd1UqZ7fXm/Yz3/QkTxu7CH7UaXbCH3P4PTo023ULQGKOSw==",
"dev": true,
"dependencies": {
"@lwc/eslint-plugin-lwc": "^0.11.0",
@@ -28295,28 +28882,6 @@
"readable-stream": "^2.3.6"
}
},
- "node_modules/focus-trap": {
- "version": "7.5.2",
- "resolved": "https://registry.npmjs.org/focus-trap/-/focus-trap-7.5.2.tgz",
- "integrity": "sha512-p6vGNNWLDGwJCiEjkSK6oERj/hEyI9ITsSwIUICBoKLlWiTWXJRfQibCwcoi50rTZdbi87qDtUlMCmQwsGSgPw==",
- "dependencies": {
- "tabbable": "^6.2.0"
- }
- },
- "node_modules/focus-trap-react": {
- "version": "10.2.1",
- "resolved": "https://registry.npmjs.org/focus-trap-react/-/focus-trap-react-10.2.1.tgz",
- "integrity": "sha512-UrAKOn52lvfHF6lkUMfFhlQxFgahyNW5i6FpHWkDxAeD4FSk3iwx9n4UEA4Sims0G5WiGIi0fAyoq3/UVeNCYA==",
- "dependencies": {
- "focus-trap": "^7.5.2",
- "tabbable": "^6.2.0"
- },
- "peerDependencies": {
- "prop-types": "^15.8.1",
- "react": ">=16.3.0",
- "react-dom": ">=16.3.0"
- }
- },
"node_modules/follow-redirects": {
"version": "1.15.2",
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz",
@@ -33594,9 +34159,9 @@
}
},
"node_modules/joi": {
- "version": "17.9.2",
- "resolved": "https://registry.npmjs.org/joi/-/joi-17.9.2.tgz",
- "integrity": "sha512-Itk/r+V4Dx0V3c7RLFdRh12IOjySm2/WGPMubBT92cQvRfYZhPM2W0hZlctjj72iES8jsRCwp7S/cRmWBnJ4nw==",
+ "version": "17.10.1",
+ "resolved": "https://registry.npmjs.org/joi/-/joi-17.10.1.tgz",
+ "integrity": "sha512-vIiDxQKmRidUVp8KngT8MZSOcmRVm2zV7jbMjNYWuHcJWI0bUck3nRTGQjhpPlQenIQIBC5Vp9AhcnHbWQqafw==",
"dependencies": {
"@hapi/hoek": "^9.0.0",
"@hapi/topo": "^5.0.0",
@@ -34256,7 +34821,8 @@
},
"node_modules/lodash.throttle": {
"version": "4.1.1",
- "license": "MIT"
+ "resolved": "https://registry.npmjs.org/lodash.throttle/-/lodash.throttle-4.1.1.tgz",
+ "integrity": "sha512-wIkUCfVKpVsWo3JSZlc+8MB5it+2AN5W8J7YVMST30UrvcQNZ1Okbj+rbVniijTWE6FGYy4XJq/rHkas8qJMLQ=="
},
"node_modules/lodash.truncate": {
"version": "4.4.2",
@@ -35643,9 +36209,10 @@
}
},
"node_modules/metro": {
- "version": "0.76.7",
- "resolved": "https://registry.npmjs.org/metro/-/metro-0.76.7.tgz",
- "integrity": "sha512-67ZGwDeumEPnrHI+pEDSKH2cx+C81Gx8Mn5qOtmGUPm/Up9Y4I1H2dJZ5n17MWzejNo0XAvPh0QL0CrlJEODVQ==",
+ "version": "0.76.8",
+ "resolved": "https://registry.npmjs.org/metro/-/metro-0.76.8.tgz",
+ "integrity": "sha512-oQA3gLzrrYv3qKtuWArMgHPbHu8odZOD9AoavrqSFllkPgOtmkBvNNDLCELqv5SjBfqjISNffypg+5UGG3y0pg==",
+ "dev": true,
"dependencies": {
"@babel/code-frame": "^7.0.0",
"@babel/core": "^7.20.0",
@@ -35669,22 +36236,22 @@
"jest-worker": "^27.2.0",
"jsc-safe-url": "^0.2.2",
"lodash.throttle": "^4.1.1",
- "metro-babel-transformer": "0.76.7",
- "metro-cache": "0.76.7",
- "metro-cache-key": "0.76.7",
- "metro-config": "0.76.7",
- "metro-core": "0.76.7",
- "metro-file-map": "0.76.7",
- "metro-inspector-proxy": "0.76.7",
- "metro-minify-terser": "0.76.7",
- "metro-minify-uglify": "0.76.7",
- "metro-react-native-babel-preset": "0.76.7",
- "metro-resolver": "0.76.7",
- "metro-runtime": "0.76.7",
- "metro-source-map": "0.76.7",
- "metro-symbolicate": "0.76.7",
- "metro-transform-plugins": "0.76.7",
- "metro-transform-worker": "0.76.7",
+ "metro-babel-transformer": "0.76.8",
+ "metro-cache": "0.76.8",
+ "metro-cache-key": "0.76.8",
+ "metro-config": "0.76.8",
+ "metro-core": "0.76.8",
+ "metro-file-map": "0.76.8",
+ "metro-inspector-proxy": "0.76.8",
+ "metro-minify-terser": "0.76.8",
+ "metro-minify-uglify": "0.76.8",
+ "metro-react-native-babel-preset": "0.76.8",
+ "metro-resolver": "0.76.8",
+ "metro-runtime": "0.76.8",
+ "metro-source-map": "0.76.8",
+ "metro-symbolicate": "0.76.8",
+ "metro-transform-plugins": "0.76.8",
+ "metro-transform-worker": "0.76.8",
"mime-types": "^2.1.27",
"node-fetch": "^2.2.0",
"nullthrows": "^1.1.1",
@@ -35704,9 +36271,10 @@
}
},
"node_modules/metro-babel-transformer": {
- "version": "0.76.7",
- "resolved": "https://registry.npmjs.org/metro-babel-transformer/-/metro-babel-transformer-0.76.7.tgz",
- "integrity": "sha512-bgr2OFn0J4r0qoZcHrwEvccF7g9k3wdgTOgk6gmGHrtlZ1Jn3oCpklW/DfZ9PzHfjY2mQammKTc19g/EFGyOJw==",
+ "version": "0.76.8",
+ "resolved": "https://registry.npmjs.org/metro-babel-transformer/-/metro-babel-transformer-0.76.8.tgz",
+ "integrity": "sha512-Hh6PW34Ug/nShlBGxkwQJSgPGAzSJ9FwQXhUImkzdsDgVu6zj5bx258J8cJVSandjNoQ8nbaHK6CaHlnbZKbyA==",
+ "dev": true,
"dependencies": {
"@babel/core": "^7.20.0",
"hermes-parser": "0.12.0",
@@ -35717,11 +36285,12 @@
}
},
"node_modules/metro-cache": {
- "version": "0.76.7",
- "resolved": "https://registry.npmjs.org/metro-cache/-/metro-cache-0.76.7.tgz",
- "integrity": "sha512-nWBMztrs5RuSxZRI7hgFgob5PhYDmxICh9FF8anm9/ito0u0vpPvRxt7sRu8fyeD2AHdXqE7kX32rWY0LiXgeg==",
+ "version": "0.76.8",
+ "resolved": "https://registry.npmjs.org/metro-cache/-/metro-cache-0.76.8.tgz",
+ "integrity": "sha512-QBJSJIVNH7Hc/Yo6br/U/qQDUpiUdRgZ2ZBJmvAbmAKp2XDzsapnMwK/3BGj8JNWJF7OLrqrYHsRsukSbUBpvQ==",
+ "dev": true,
"dependencies": {
- "metro-core": "0.76.7",
+ "metro-core": "0.76.8",
"rimraf": "^3.0.2"
},
"engines": {
@@ -35729,25 +36298,27 @@
}
},
"node_modules/metro-cache-key": {
- "version": "0.76.7",
- "resolved": "https://registry.npmjs.org/metro-cache-key/-/metro-cache-key-0.76.7.tgz",
- "integrity": "sha512-0pecoIzwsD/Whn/Qfa+SDMX2YyasV0ndbcgUFx7w1Ct2sLHClujdhQ4ik6mvQmsaOcnGkIyN0zcceMDjC2+BFQ==",
+ "version": "0.76.8",
+ "resolved": "https://registry.npmjs.org/metro-cache-key/-/metro-cache-key-0.76.8.tgz",
+ "integrity": "sha512-buKQ5xentPig9G6T37Ww/R/bC+/V1MA5xU/D8zjnhlelsrPG6w6LtHUS61ID3zZcMZqYaELWk5UIadIdDsaaLw==",
+ "dev": true,
"engines": {
"node": ">=16"
}
},
"node_modules/metro-config": {
- "version": "0.76.7",
- "resolved": "https://registry.npmjs.org/metro-config/-/metro-config-0.76.7.tgz",
- "integrity": "sha512-CFDyNb9bqxZemiChC/gNdXZ7OQkIwmXzkrEXivcXGbgzlt/b2juCv555GWJHyZSlorwnwJfY3uzAFu4A9iRVfg==",
+ "version": "0.76.8",
+ "resolved": "https://registry.npmjs.org/metro-config/-/metro-config-0.76.8.tgz",
+ "integrity": "sha512-SL1lfKB0qGHALcAk2zBqVgQZpazDYvYFGwCK1ikz0S6Y/CM2i2/HwuZN31kpX6z3mqjv/6KvlzaKoTb1otuSAA==",
+ "dev": true,
"dependencies": {
"connect": "^3.6.5",
"cosmiconfig": "^5.0.5",
"jest-validate": "^29.2.1",
- "metro": "0.76.7",
- "metro-cache": "0.76.7",
- "metro-core": "0.76.7",
- "metro-runtime": "0.76.7"
+ "metro": "0.76.8",
+ "metro-cache": "0.76.8",
+ "metro-core": "0.76.8",
+ "metro-runtime": "0.76.8"
},
"engines": {
"node": ">=16"
@@ -35757,6 +36328,7 @@
"version": "5.2.1",
"resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-5.2.1.tgz",
"integrity": "sha512-H65gsXo1SKjf8zmrJ67eJk8aIRKV5ff2D4uKZIBZShbhGSpEmsQOPW/SKMKYhSTrqR7ufy6RP69rPogdaPh/kA==",
+ "dev": true,
"dependencies": {
"import-fresh": "^2.0.0",
"is-directory": "^0.3.1",
@@ -35771,6 +36343,7 @@
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-2.0.0.tgz",
"integrity": "sha512-eZ5H8rcgYazHbKC3PG4ClHNykCSxtAhxSSEM+2mb+7evD2CKF5V7c0dNum7AdpDh0ZdICwZY9sRSn8f+KH96sg==",
+ "dev": true,
"dependencies": {
"caller-path": "^2.0.0",
"resolve-from": "^3.0.0"
@@ -35783,6 +36356,7 @@
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz",
"integrity": "sha512-aOIos8bujGN93/8Ox/jPLh7RwVnPEysynVFE+fQZyg6jKELEHwzgKdLRFHUgXJL6kylijVSBC4BvN9OmsB48Rw==",
+ "dev": true,
"dependencies": {
"error-ex": "^1.3.1",
"json-parse-better-errors": "^1.0.1"
@@ -35795,26 +36369,29 @@
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-3.0.0.tgz",
"integrity": "sha512-GnlH6vxLymXJNMBo7XP1fJIzBFbdYt49CuTwmB/6N53t+kMPRMFKz783LlQ4tv28XoQfMWinAJX6WCGf2IlaIw==",
+ "dev": true,
"engines": {
"node": ">=4"
}
},
"node_modules/metro-core": {
- "version": "0.76.7",
- "resolved": "https://registry.npmjs.org/metro-core/-/metro-core-0.76.7.tgz",
- "integrity": "sha512-0b8KfrwPmwCMW+1V7ZQPkTy2tsEKZjYG9Pu1PTsu463Z9fxX7WaR0fcHFshv+J1CnQSUTwIGGjbNvj1teKe+pw==",
+ "version": "0.76.8",
+ "resolved": "https://registry.npmjs.org/metro-core/-/metro-core-0.76.8.tgz",
+ "integrity": "sha512-sl2QLFI3d1b1XUUGxwzw/KbaXXU/bvFYrSKz6Sg19AdYGWFyzsgZ1VISRIDf+HWm4R/TJXluhWMEkEtZuqi3qA==",
+ "dev": true,
"dependencies": {
"lodash.throttle": "^4.1.1",
- "metro-resolver": "0.76.7"
+ "metro-resolver": "0.76.8"
},
"engines": {
"node": ">=16"
}
},
"node_modules/metro-file-map": {
- "version": "0.76.7",
- "resolved": "https://registry.npmjs.org/metro-file-map/-/metro-file-map-0.76.7.tgz",
- "integrity": "sha512-s+zEkTcJ4mOJTgEE2ht4jIo1DZfeWreQR3tpT3gDV/Y/0UQ8aJBTv62dE775z0GLsWZApiblAYZsj7ZE8P06nw==",
+ "version": "0.76.8",
+ "resolved": "https://registry.npmjs.org/metro-file-map/-/metro-file-map-0.76.8.tgz",
+ "integrity": "sha512-A/xP1YNEVwO1SUV9/YYo6/Y1MmzhL4ZnVgcJC3VmHp/BYVOXVStzgVbWv2wILe56IIMkfXU+jpXrGKKYhFyHVw==",
+ "dev": true,
"dependencies": {
"anymatch": "^3.0.3",
"debug": "^2.2.0",
@@ -35840,6 +36417,7 @@
"version": "27.5.1",
"resolved": "https://registry.npmjs.org/@jest/types/-/types-27.5.1.tgz",
"integrity": "sha512-Cx46iJ9QpwQTjIdq5VJu2QTMMs3QlEjI0x1QbBP5W1+nMzyc2XmimiRR/CbX9TO0cPTeUlxWMOu8mslYsJ8DEw==",
+ "dev": true,
"dependencies": {
"@types/istanbul-lib-coverage": "^2.0.0",
"@types/istanbul-reports": "^3.0.0",
@@ -35855,6 +36433,7 @@
"version": "16.0.5",
"resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.5.tgz",
"integrity": "sha512-AxO/ADJOBFJScHbWhq2xAhlWP24rY4aCEG/NFaMvbT3X2MgRsLjhjQwsn0Zi5zn0LG9jUhCCZMeX9Dkuw6k+vQ==",
+ "dev": true,
"dependencies": {
"@types/yargs-parser": "*"
}
@@ -35863,6 +36442,7 @@
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
"integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+ "dev": true,
"dependencies": {
"color-convert": "^2.0.1"
},
@@ -35877,6 +36457,7 @@
"version": "4.1.2",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
"integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+ "dev": true,
"dependencies": {
"ansi-styles": "^4.1.0",
"supports-color": "^7.1.0"
@@ -35892,6 +36473,7 @@
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+ "dev": true,
"dependencies": {
"color-name": "~1.1.4"
},
@@ -35902,12 +36484,14 @@
"node_modules/metro-file-map/node_modules/color-name": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
- "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
+ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+ "dev": true
},
"node_modules/metro-file-map/node_modules/debug": {
"version": "2.6.9",
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
"integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+ "dev": true,
"dependencies": {
"ms": "2.0.0"
}
@@ -35916,6 +36500,7 @@
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
"integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+ "dev": true,
"engines": {
"node": ">=8"
}
@@ -35924,6 +36509,7 @@
"version": "27.5.1",
"resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-27.5.1.tgz",
"integrity": "sha512-4bfKq2zie+x16okqDXjXn9ql2B0dScQu+vcwe4TvFVhkVyuWLqpZrZtXxLLWoXYgn0E87I6r6GRYHF7wFZBUvg==",
+ "dev": true,
"engines": {
"node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0"
}
@@ -35932,6 +36518,7 @@
"version": "27.5.1",
"resolved": "https://registry.npmjs.org/jest-util/-/jest-util-27.5.1.tgz",
"integrity": "sha512-Kv2o/8jNvX1MQ0KGtw480E/w4fBCDOnH6+6DmeKi6LZUIlKA5kwY0YNdlzaWTiVgxqAqik11QyxDOKk543aKXw==",
+ "dev": true,
"dependencies": {
"@jest/types": "^27.5.1",
"@types/node": "*",
@@ -35948,6 +36535,7 @@
"version": "27.5.1",
"resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz",
"integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==",
+ "dev": true,
"dependencies": {
"@types/node": "*",
"merge-stream": "^2.0.0",
@@ -35961,6 +36549,7 @@
"version": "8.1.1",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz",
"integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==",
+ "dev": true,
"dependencies": {
"has-flag": "^4.0.0"
},
@@ -35974,12 +36563,14 @@
"node_modules/metro-file-map/node_modules/ms": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
- "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="
+ "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
+ "dev": true
},
"node_modules/metro-file-map/node_modules/supports-color": {
"version": "7.2.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
"integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+ "dev": true,
"dependencies": {
"has-flag": "^4.0.0"
},
@@ -35988,9 +36579,10 @@
}
},
"node_modules/metro-inspector-proxy": {
- "version": "0.76.7",
- "resolved": "https://registry.npmjs.org/metro-inspector-proxy/-/metro-inspector-proxy-0.76.7.tgz",
- "integrity": "sha512-rNZ/6edTl/1qUekAhAbaFjczMphM50/UjtxiKulo6vqvgn/Mjd9hVqDvVYfAMZXqPvlusD88n38UjVYPkruLSg==",
+ "version": "0.76.8",
+ "resolved": "https://registry.npmjs.org/metro-inspector-proxy/-/metro-inspector-proxy-0.76.8.tgz",
+ "integrity": "sha512-Us5o5UEd4Smgn1+TfHX4LvVPoWVo9VsVMn4Ldbk0g5CQx3Gu0ygc/ei2AKPGTwsOZmKxJeACj7yMH2kgxQP/iw==",
+ "dev": true,
"dependencies": {
"connect": "^3.6.5",
"debug": "^2.2.0",
@@ -36009,6 +36601,7 @@
"version": "8.0.1",
"resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz",
"integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==",
+ "dev": true,
"dependencies": {
"string-width": "^4.2.0",
"strip-ansi": "^6.0.1",
@@ -36022,6 +36615,7 @@
"version": "2.6.9",
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
"integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+ "dev": true,
"dependencies": {
"ms": "2.0.0"
}
@@ -36029,12 +36623,14 @@
"node_modules/metro-inspector-proxy/node_modules/ms": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
- "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="
+ "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
+ "dev": true
},
"node_modules/metro-inspector-proxy/node_modules/ws": {
"version": "7.5.9",
"resolved": "https://registry.npmjs.org/ws/-/ws-7.5.9.tgz",
"integrity": "sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q==",
+ "dev": true,
"engines": {
"node": ">=8.3.0"
},
@@ -36055,6 +36651,7 @@
"version": "5.0.8",
"resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz",
"integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==",
+ "dev": true,
"engines": {
"node": ">=10"
}
@@ -36063,6 +36660,7 @@
"version": "17.7.2",
"resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz",
"integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==",
+ "dev": true,
"dependencies": {
"cliui": "^8.0.1",
"escalade": "^3.1.1",
@@ -36080,14 +36678,16 @@
"version": "21.1.1",
"resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz",
"integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==",
+ "dev": true,
"engines": {
"node": ">=12"
}
},
"node_modules/metro-minify-terser": {
- "version": "0.76.7",
- "resolved": "https://registry.npmjs.org/metro-minify-terser/-/metro-minify-terser-0.76.7.tgz",
- "integrity": "sha512-FQiZGhIxCzhDwK4LxyPMLlq0Tsmla10X7BfNGlYFK0A5IsaVKNJbETyTzhpIwc+YFRT4GkFFwgo0V2N5vxO5HA==",
+ "version": "0.76.8",
+ "resolved": "https://registry.npmjs.org/metro-minify-terser/-/metro-minify-terser-0.76.8.tgz",
+ "integrity": "sha512-Orbvg18qXHCrSj1KbaeSDVYRy/gkro2PC7Fy2tDSH1c9RB4aH8tuMOIXnKJE+1SXxBtjWmQ5Yirwkth2DyyEZA==",
+ "dev": true,
"dependencies": {
"terser": "^5.15.0"
},
@@ -36096,9 +36696,10 @@
}
},
"node_modules/metro-minify-uglify": {
- "version": "0.76.7",
- "resolved": "https://registry.npmjs.org/metro-minify-uglify/-/metro-minify-uglify-0.76.7.tgz",
- "integrity": "sha512-FuXIU3j2uNcSvQtPrAJjYWHruPiQ+EpE++J9Z+VznQKEHcIxMMoQZAfIF2IpZSrZYfLOjVFyGMvj41jQMxV1Vw==",
+ "version": "0.76.8",
+ "resolved": "https://registry.npmjs.org/metro-minify-uglify/-/metro-minify-uglify-0.76.8.tgz",
+ "integrity": "sha512-6l8/bEvtVaTSuhG1FqS0+Mc8lZ3Bl4RI8SeRIifVLC21eeSDp4CEBUWSGjpFyUDfi6R5dXzYaFnSgMNyfxADiQ==",
+ "dev": true,
"dependencies": {
"uglify-es": "^3.1.9"
},
@@ -36107,9 +36708,10 @@
}
},
"node_modules/metro-react-native-babel-preset": {
- "version": "0.76.7",
- "resolved": "https://registry.npmjs.org/metro-react-native-babel-preset/-/metro-react-native-babel-preset-0.76.7.tgz",
- "integrity": "sha512-R25wq+VOSorAK3hc07NW0SmN8z9S/IR0Us0oGAsBcMZnsgkbOxu77Mduqf+f4is/wnWHc5+9bfiqdLnaMngiVw==",
+ "version": "0.76.8",
+ "resolved": "https://registry.npmjs.org/metro-react-native-babel-preset/-/metro-react-native-babel-preset-0.76.8.tgz",
+ "integrity": "sha512-Ptza08GgqzxEdK8apYsjTx2S8WDUlS2ilBlu9DR1CUcHmg4g3kOkFylZroogVAUKtpYQNYwAvdsjmrSdDNtiAg==",
+ "dev": true,
"dependencies": {
"@babel/core": "^7.20.0",
"@babel/plugin-proposal-async-generator-functions": "^7.0.0",
@@ -36160,20 +36762,22 @@
},
"node_modules/metro-react-native-babel-preset/node_modules/react-refresh": {
"version": "0.4.3",
+ "dev": true,
"license": "MIT",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/metro-react-native-babel-transformer": {
- "version": "0.76.7",
- "resolved": "https://registry.npmjs.org/metro-react-native-babel-transformer/-/metro-react-native-babel-transformer-0.76.7.tgz",
- "integrity": "sha512-W6lW3J7y/05ph3c2p3KKJNhH0IdyxdOCbQ5it7aM2MAl0SM4wgKjaV6EYv9b3rHklpV6K3qMH37UKVcjMooWiA==",
+ "version": "0.76.8",
+ "resolved": "https://registry.npmjs.org/metro-react-native-babel-transformer/-/metro-react-native-babel-transformer-0.76.8.tgz",
+ "integrity": "sha512-3h+LfS1WG1PAzhq8QF0kfXjxuXetbY/lgz8vYMQhgrMMp17WM1DNJD0gjx8tOGYbpbBC1qesJ45KMS4o5TA73A==",
+ "dev": true,
"dependencies": {
"@babel/core": "^7.20.0",
"babel-preset-fbjs": "^3.4.0",
"hermes-parser": "0.12.0",
- "metro-react-native-babel-preset": "0.76.7",
+ "metro-react-native-babel-preset": "0.76.8",
"nullthrows": "^1.1.1"
},
"engines": {
@@ -36184,17 +36788,18 @@
}
},
"node_modules/metro-resolver": {
- "version": "0.76.7",
- "resolved": "https://registry.npmjs.org/metro-resolver/-/metro-resolver-0.76.7.tgz",
- "integrity": "sha512-pC0Wgq29HHIHrwz23xxiNgylhI8Rq1V01kQaJ9Kz11zWrIdlrH0ZdnJ7GC6qA0ErROG+cXmJ0rJb8/SW1Zp2IA==",
+ "version": "0.76.8",
+ "resolved": "https://registry.npmjs.org/metro-resolver/-/metro-resolver-0.76.8.tgz",
+ "integrity": "sha512-KccOqc10vrzS7ZhG2NSnL2dh3uVydarB7nOhjreQ7C4zyWuiW9XpLC4h47KtGQv3Rnv/NDLJYeDqaJ4/+140HQ==",
+ "dev": true,
"engines": {
"node": ">=16"
}
},
"node_modules/metro-runtime": {
- "version": "0.76.7",
- "resolved": "https://registry.npmjs.org/metro-runtime/-/metro-runtime-0.76.7.tgz",
- "integrity": "sha512-MuWHubQHymUWBpZLwuKZQgA/qbb35WnDAKPo83rk7JRLIFPvzXSvFaC18voPuzJBt1V98lKQIonh6MiC9gd8Ug==",
+ "version": "0.76.8",
+ "resolved": "https://registry.npmjs.org/metro-runtime/-/metro-runtime-0.76.8.tgz",
+ "integrity": "sha512-XKahvB+iuYJSCr3QqCpROli4B4zASAYpkK+j3a0CJmokxCDNbgyI4Fp88uIL6rNaZfN0Mv35S0b99SdFXIfHjg==",
"dependencies": {
"@babel/runtime": "^7.0.0",
"react-refresh": "^0.4.0"
@@ -36212,16 +36817,16 @@
}
},
"node_modules/metro-source-map": {
- "version": "0.76.7",
- "resolved": "https://registry.npmjs.org/metro-source-map/-/metro-source-map-0.76.7.tgz",
- "integrity": "sha512-Prhx7PeRV1LuogT0Kn5VjCuFu9fVD68eefntdWabrksmNY6mXK8pRqzvNJOhTojh6nek+RxBzZeD6MIOOyXS6w==",
+ "version": "0.76.8",
+ "resolved": "https://registry.npmjs.org/metro-source-map/-/metro-source-map-0.76.8.tgz",
+ "integrity": "sha512-Hh0ncPsHPVf6wXQSqJqB3K9Zbudht4aUtNpNXYXSxH+pteWqGAXnjtPsRAnCsCWl38wL0jYF0rJDdMajUI3BDw==",
"dependencies": {
"@babel/traverse": "^7.20.0",
"@babel/types": "^7.20.0",
"invariant": "^2.2.4",
- "metro-symbolicate": "0.76.7",
+ "metro-symbolicate": "0.76.8",
"nullthrows": "^1.1.1",
- "ob1": "0.76.7",
+ "ob1": "0.76.8",
"source-map": "^0.5.6",
"vlq": "^1.0.0"
},
@@ -36238,12 +36843,12 @@
}
},
"node_modules/metro-symbolicate": {
- "version": "0.76.7",
- "resolved": "https://registry.npmjs.org/metro-symbolicate/-/metro-symbolicate-0.76.7.tgz",
- "integrity": "sha512-p0zWEME5qLSL1bJb93iq+zt5fz3sfVn9xFYzca1TJIpY5MommEaS64Va87lp56O0sfEIvh4307Oaf/ZzRjuLiQ==",
+ "version": "0.76.8",
+ "resolved": "https://registry.npmjs.org/metro-symbolicate/-/metro-symbolicate-0.76.8.tgz",
+ "integrity": "sha512-LrRL3uy2VkzrIXVlxoPtqb40J6Bf1mlPNmUQewipc3qfKKFgtPHBackqDy1YL0njDsWopCKcfGtFYLn0PTUn3w==",
"dependencies": {
"invariant": "^2.2.4",
- "metro-source-map": "0.76.7",
+ "metro-source-map": "0.76.8",
"nullthrows": "^1.1.1",
"source-map": "^0.5.6",
"through2": "^2.0.1",
@@ -36265,9 +36870,10 @@
}
},
"node_modules/metro-transform-plugins": {
- "version": "0.76.7",
- "resolved": "https://registry.npmjs.org/metro-transform-plugins/-/metro-transform-plugins-0.76.7.tgz",
- "integrity": "sha512-iSmnjVApbdivjuzb88Orb0JHvcEt5veVyFAzxiS5h0QB+zV79w6JCSqZlHCrbNOkOKBED//LqtKbFVakxllnNg==",
+ "version": "0.76.8",
+ "resolved": "https://registry.npmjs.org/metro-transform-plugins/-/metro-transform-plugins-0.76.8.tgz",
+ "integrity": "sha512-PlkGTQNqS51Bx4vuufSQCdSn2R2rt7korzngo+b5GCkeX5pjinPjnO2kNhQ8l+5bO0iUD/WZ9nsM2PGGKIkWFA==",
+ "dev": true,
"dependencies": {
"@babel/core": "^7.20.0",
"@babel/generator": "^7.20.0",
@@ -36280,21 +36886,22 @@
}
},
"node_modules/metro-transform-worker": {
- "version": "0.76.7",
- "resolved": "https://registry.npmjs.org/metro-transform-worker/-/metro-transform-worker-0.76.7.tgz",
- "integrity": "sha512-cGvELqFMVk9XTC15CMVzrCzcO6sO1lURfcbgjuuPdzaWuD11eEyocvkTX0DPiRjsvgAmicz4XYxVzgYl3MykDw==",
+ "version": "0.76.8",
+ "resolved": "https://registry.npmjs.org/metro-transform-worker/-/metro-transform-worker-0.76.8.tgz",
+ "integrity": "sha512-mE1fxVAnJKmwwJyDtThildxxos9+DGs9+vTrx2ktSFMEVTtXS/bIv2W6hux1pqivqAfyJpTeACXHk5u2DgGvIQ==",
+ "dev": true,
"dependencies": {
"@babel/core": "^7.20.0",
"@babel/generator": "^7.20.0",
"@babel/parser": "^7.20.0",
"@babel/types": "^7.20.0",
"babel-preset-fbjs": "^3.4.0",
- "metro": "0.76.7",
- "metro-babel-transformer": "0.76.7",
- "metro-cache": "0.76.7",
- "metro-cache-key": "0.76.7",
- "metro-source-map": "0.76.7",
- "metro-transform-plugins": "0.76.7",
+ "metro": "0.76.8",
+ "metro-babel-transformer": "0.76.8",
+ "metro-cache": "0.76.8",
+ "metro-cache-key": "0.76.8",
+ "metro-source-map": "0.76.8",
+ "metro-transform-plugins": "0.76.8",
"nullthrows": "^1.1.1"
},
"engines": {
@@ -36305,6 +36912,7 @@
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
"integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+ "dev": true,
"dependencies": {
"color-convert": "^2.0.1"
},
@@ -36319,6 +36927,7 @@
"version": "4.1.2",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
"integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+ "dev": true,
"dependencies": {
"ansi-styles": "^4.1.0",
"supports-color": "^7.1.0"
@@ -36333,12 +36942,14 @@
"node_modules/metro/node_modules/ci-info": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz",
- "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ=="
+ "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==",
+ "dev": true
},
"node_modules/metro/node_modules/cliui": {
"version": "8.0.1",
"resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz",
"integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==",
+ "dev": true,
"dependencies": {
"string-width": "^4.2.0",
"strip-ansi": "^6.0.1",
@@ -36352,6 +36963,7 @@
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+ "dev": true,
"dependencies": {
"color-name": "~1.1.4"
},
@@ -36362,12 +36974,14 @@
"node_modules/metro/node_modules/color-name": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
- "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
+ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+ "dev": true
},
"node_modules/metro/node_modules/debug": {
"version": "2.6.9",
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
"integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+ "dev": true,
"dependencies": {
"ms": "2.0.0"
}
@@ -36376,6 +36990,7 @@
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
"integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+ "dev": true,
"engines": {
"node": ">=8"
}
@@ -36384,6 +36999,7 @@
"version": "27.5.1",
"resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz",
"integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==",
+ "dev": true,
"dependencies": {
"@types/node": "*",
"merge-stream": "^2.0.0",
@@ -36397,6 +37013,7 @@
"version": "8.1.1",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz",
"integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==",
+ "dev": true,
"dependencies": {
"has-flag": "^4.0.0"
},
@@ -36410,12 +37027,14 @@
"node_modules/metro/node_modules/ms": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
- "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="
+ "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
+ "dev": true
},
"node_modules/metro/node_modules/serialize-error": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/serialize-error/-/serialize-error-2.1.0.tgz",
"integrity": "sha512-ghgmKt5o4Tly5yEG/UJp8qTd0AN7Xalw4XBtDEKP655B699qMEtra1WlXeE6WIvdEG481JvRxULKsInq/iNysw==",
+ "dev": true,
"engines": {
"node": ">=0.10.0"
}
@@ -36424,6 +37043,7 @@
"version": "0.5.7",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
"integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==",
+ "dev": true,
"engines": {
"node": ">=0.10.0"
}
@@ -36432,6 +37052,7 @@
"version": "7.2.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
"integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+ "dev": true,
"dependencies": {
"has-flag": "^4.0.0"
},
@@ -36443,6 +37064,7 @@
"version": "7.5.9",
"resolved": "https://registry.npmjs.org/ws/-/ws-7.5.9.tgz",
"integrity": "sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q==",
+ "dev": true,
"engines": {
"node": ">=8.3.0"
},
@@ -36463,6 +37085,7 @@
"version": "5.0.8",
"resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz",
"integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==",
+ "dev": true,
"engines": {
"node": ">=10"
}
@@ -36471,6 +37094,7 @@
"version": "17.7.2",
"resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz",
"integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==",
+ "dev": true,
"dependencies": {
"cliui": "^8.0.1",
"escalade": "^3.1.1",
@@ -36488,6 +37112,7 @@
"version": "21.1.1",
"resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz",
"integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==",
+ "dev": true,
"engines": {
"node": ">=12"
}
@@ -38030,9 +38655,9 @@
"license": "MIT"
},
"node_modules/ob1": {
- "version": "0.76.7",
- "resolved": "https://registry.npmjs.org/ob1/-/ob1-0.76.7.tgz",
- "integrity": "sha512-BQdRtxxoUNfSoZxqeBGOyuT9nEYSn18xZHwGMb0mMVpn2NBcYbnyKY4BK2LIHRgw33CBGlUmE+KMaNvyTpLLtQ==",
+ "version": "0.76.8",
+ "resolved": "https://registry.npmjs.org/ob1/-/ob1-0.76.8.tgz",
+ "integrity": "sha512-dlBkJJV5M/msj9KYA9upc+nUWVwuOFFTbu28X6kZeGwcuW+JxaHSBZ70SYQnk5M+j5JbNLR6yKHmgW4M5E7X5g==",
"engines": {
"node": ">=16"
}
@@ -40210,6 +40835,17 @@
"react": "^18.1.0"
}
},
+ "node_modules/react-error-boundary": {
+ "version": "4.0.11",
+ "resolved": "https://registry.npmjs.org/react-error-boundary/-/react-error-boundary-4.0.11.tgz",
+ "integrity": "sha512-U13ul67aP5DOSPNSCWQ/eO0AQEYzEFkVljULQIjMV0KlffTAhxuDoBKdO0pb/JZ8mDhMKFZ9NZi0BmLGUiNphw==",
+ "dependencies": {
+ "@babel/runtime": "^7.12.5"
+ },
+ "peerDependencies": {
+ "react": ">=16.13.1"
+ }
+ },
"node_modules/react-freeze": {
"version": "1.0.3",
"license": "MIT",
@@ -40257,20 +40893,20 @@
}
},
"node_modules/react-native": {
- "version": "0.72.3",
- "resolved": "https://registry.npmjs.org/react-native/-/react-native-0.72.3.tgz",
- "integrity": "sha512-QqISi+JVmCssNP2FlQ4MWhlc4O/I00MRE1/GClvyZ8h/6kdsyk/sOirkYdZqX3+DrJfI3q+OnyMnsyaXIQ/5tQ==",
+ "version": "0.72.4",
+ "resolved": "https://registry.npmjs.org/react-native/-/react-native-0.72.4.tgz",
+ "integrity": "sha512-+vrObi0wZR+NeqL09KihAAdVlQ9IdplwznJWtYrjnQ4UbCW6rkzZJebRsugwUneSOKNFaHFEo1uKU89HsgtYBg==",
"dependencies": {
"@jest/create-cache-key-function": "^29.2.1",
- "@react-native-community/cli": "11.3.5",
- "@react-native-community/cli-platform-android": "11.3.5",
- "@react-native-community/cli-platform-ios": "11.3.5",
+ "@react-native-community/cli": "11.3.6",
+ "@react-native-community/cli-platform-android": "11.3.6",
+ "@react-native-community/cli-platform-ios": "11.3.6",
"@react-native/assets-registry": "^0.72.0",
"@react-native/codegen": "^0.72.6",
"@react-native/gradle-plugin": "^0.72.11",
"@react-native/js-polyfills": "^0.72.1",
"@react-native/normalize-colors": "^0.72.0",
- "@react-native/virtualized-lists": "^0.72.6",
+ "@react-native/virtualized-lists": "^0.72.8",
"abort-controller": "^3.0.0",
"anser": "^1.4.9",
"base64-js": "^1.1.2",
@@ -40281,8 +40917,8 @@
"jest-environment-node": "^29.2.1",
"jsc-android": "^250231.0.0",
"memoize-one": "^5.0.0",
- "metro-runtime": "0.76.7",
- "metro-source-map": "0.76.7",
+ "metro-runtime": "0.76.8",
+ "metro-source-map": "0.76.8",
"mkdirp": "^0.5.1",
"nullthrows": "^1.1.1",
"pretty-format": "^26.5.2",
@@ -40462,8 +41098,8 @@
},
"node_modules/react-native-google-places-autocomplete": {
"version": "2.5.1",
- "resolved": "git+ssh://git@github.com/Expensify/react-native-google-places-autocomplete.git#ee87343c3e827ff7818abc71b6bb04fcc1f120e0",
- "integrity": "sha512-OJWCz4Epj1p8tyNImWNykAqpd/X1MkNCFPY0dSbgiTJGbW4J5T4bC0PIUQ+ExjxWpWjcFaielTLdoSz0HfeIpw==",
+ "resolved": "git+ssh://git@github.com/Expensify/react-native-google-places-autocomplete.git#cef3ac29d9501091453136e1219e24c4ec9f9d76",
+ "integrity": "sha512-2z3ED8jOXasPTzBqvPwpG10LQsBArTRsYszmoz+TfqbgZrSBmP3c8rhaC//lx6Pvfs2r+KYWqJUrLf4mbCrjZw==",
"license": "MIT",
"dependencies": {
"lodash.debounce": "^4.0.8",
@@ -40567,9 +41203,9 @@
}
},
"node_modules/react-native-onyx": {
- "version": "1.0.76",
- "resolved": "https://registry.npmjs.org/react-native-onyx/-/react-native-onyx-1.0.76.tgz",
- "integrity": "sha512-mcMlYQCo1B/kom+4hu7CQKKLwvPFjQAJsVIzV2s9aa8XKNlcnYiJbfuM6RSJ1fFmSIeud4Y66rhv4/oWUkSl5A==",
+ "version": "1.0.84",
+ "resolved": "https://registry.npmjs.org/react-native-onyx/-/react-native-onyx-1.0.84.tgz",
+ "integrity": "sha512-qQ+o+qS5ucZLbKbG5kI0UsC42N4h1Pprg/1D7PqjDeVanS3iUv33rT4fbrHuar77g0DSTA1/M8bC2WmYrShS9A==",
"dependencies": {
"ascii-table": "0.0.9",
"fast-equals": "^4.0.3",
@@ -40582,8 +41218,9 @@
"peerDependencies": {
"idb-keyval": "^6.2.1",
"react": ">=18.1.0",
+ "react-dom": ">=18.1.0",
"react-native-device-info": "^10.3.0",
- "react-native-performance": "^4.0.0",
+ "react-native-performance": "^5.1.0",
"react-native-quick-sqlite": "^8.0.0-beta.2"
},
"peerDependenciesMeta": {
@@ -40625,8 +41262,9 @@
}
},
"node_modules/react-native-performance": {
- "version": "4.0.0",
- "license": "MIT",
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/react-native-performance/-/react-native-performance-5.1.0.tgz",
+ "integrity": "sha512-rq/YBf0/GptSOM/Lj64/1yRq8uN2YE0psFB16wFbYBbTcIEp/0rrgN2HyS5lhvfBOFgKoDRWQ53jHSCb+QJ5eA==",
"peerDependencies": {
"react-native": "*"
}
@@ -40783,8 +41421,9 @@
}
},
"node_modules/react-native-svg": {
- "version": "13.9.0",
- "license": "MIT",
+ "version": "13.13.0",
+ "resolved": "https://registry.npmjs.org/react-native-svg/-/react-native-svg-13.13.0.tgz",
+ "integrity": "sha512-L8y8uEiMG0Tr++Nb2+24wlMuv18+bmq/CMoFFtTUlEqVvGCoK2ea8WamPl/9bV8gjL+Rngg5NqEBvKS23sbYoA==",
"dependencies": {
"css-select": "^5.1.0",
"css-tree": "^1.1.3"
@@ -44692,11 +45331,6 @@
"dev": true,
"license": "BSD-3-Clause"
},
- "node_modules/tabbable": {
- "version": "6.2.0",
- "resolved": "https://registry.npmjs.org/tabbable/-/tabbable-6.2.0.tgz",
- "integrity": "sha512-Cat63mxsVJlzYvN51JmVXIgNoUokrIaT2zLclCXjRd8boZ0004U4KCs/sToJ75C6sdlByWxpYnb5Boif1VSFew=="
- },
"node_modules/table": {
"version": "6.8.1",
"resolved": "https://registry.npmjs.org/table/-/table-6.8.1.tgz",
@@ -45023,7 +45657,8 @@
},
"node_modules/throat": {
"version": "5.0.0",
- "license": "MIT"
+ "resolved": "https://registry.npmjs.org/throat/-/throat-5.0.0.tgz",
+ "integrity": "sha512-fcwX4mndzpLQKBS1DVYhGAcYaYt7vsHNIvQV+WXMvnow5cgjPphq5CaayLaGsjRdSCKZFNGt7/GYAuXaNOiYCA=="
},
"node_modules/throttle-debounce": {
"version": "3.0.1",
@@ -46394,7 +47029,8 @@
},
"node_modules/vlq": {
"version": "1.0.1",
- "license": "MIT"
+ "resolved": "https://registry.npmjs.org/vlq/-/vlq-1.0.1.tgz",
+ "integrity": "sha512-gQpnTgkubC6hQgdIcRdYGDSDc+SaujOdyesZQMv6JlfQee/9Mp0Qhnys6WxDWvQnL5WZdT7o2Ul187aSt0Rq+w=="
},
"node_modules/vm-browserify": {
"version": "1.1.2",
@@ -52518,19 +53154,19 @@
"requires": {}
},
"@react-native-community/cli": {
- "version": "11.3.5",
- "resolved": "https://registry.npmjs.org/@react-native-community/cli/-/cli-11.3.5.tgz",
- "integrity": "sha512-wMXgKEWe6uesw7vyXKKjx5EDRog0QdXHxdgRguG14AjQRao1+4gXEWq2yyExOTi/GDY6dfJBUGTCwGQxhnk/Lg==",
- "requires": {
- "@react-native-community/cli-clean": "11.3.5",
- "@react-native-community/cli-config": "11.3.5",
- "@react-native-community/cli-debugger-ui": "11.3.5",
- "@react-native-community/cli-doctor": "11.3.5",
- "@react-native-community/cli-hermes": "11.3.5",
- "@react-native-community/cli-plugin-metro": "11.3.5",
- "@react-native-community/cli-server-api": "11.3.5",
- "@react-native-community/cli-tools": "11.3.5",
- "@react-native-community/cli-types": "11.3.5",
+ "version": "11.3.6",
+ "resolved": "https://registry.npmjs.org/@react-native-community/cli/-/cli-11.3.6.tgz",
+ "integrity": "sha512-bdwOIYTBVQ9VK34dsf6t3u6vOUU5lfdhKaAxiAVArjsr7Je88Bgs4sAbsOYsNK3tkE8G77U6wLpekknXcanlww==",
+ "requires": {
+ "@react-native-community/cli-clean": "11.3.6",
+ "@react-native-community/cli-config": "11.3.6",
+ "@react-native-community/cli-debugger-ui": "11.3.6",
+ "@react-native-community/cli-doctor": "11.3.6",
+ "@react-native-community/cli-hermes": "11.3.6",
+ "@react-native-community/cli-plugin-metro": "11.3.6",
+ "@react-native-community/cli-server-api": "11.3.6",
+ "@react-native-community/cli-tools": "11.3.6",
+ "@react-native-community/cli-types": "11.3.6",
"chalk": "^4.1.2",
"commander": "^9.4.1",
"execa": "^5.0.0",
@@ -52538,7 +53174,7 @@
"fs-extra": "^8.1.0",
"graceful-fs": "^4.1.3",
"prompts": "^2.4.0",
- "semver": "^6.3.0"
+ "semver": "^7.5.2"
},
"dependencies": {
"ansi-styles": {
@@ -52637,11 +53273,6 @@
"resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
"integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w=="
},
- "semver": {
- "version": "6.3.1",
- "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
- "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="
- },
"supports-color": {
"version": "7.2.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
@@ -52658,11 +53289,11 @@
}
},
"@react-native-community/cli-clean": {
- "version": "11.3.5",
- "resolved": "https://registry.npmjs.org/@react-native-community/cli-clean/-/cli-clean-11.3.5.tgz",
- "integrity": "sha512-1+7BU962wKkIkHRp/uW3jYbQKKGtU7L+R3g59D8K6uLccuxJYUBJv18753ojMa6SD3SAq5Xh31bAre+YwVcOTA==",
+ "version": "11.3.6",
+ "resolved": "https://registry.npmjs.org/@react-native-community/cli-clean/-/cli-clean-11.3.6.tgz",
+ "integrity": "sha512-jOOaeG5ebSXTHweq1NznVJVAFKtTFWL4lWgUXl845bCGX7t1lL8xQNWHKwT8Oh1pGR2CI3cKmRjY4hBg+pEI9g==",
"requires": {
- "@react-native-community/cli-tools": "11.3.5",
+ "@react-native-community/cli-tools": "11.3.6",
"chalk": "^4.1.2",
"execa": "^5.0.0",
"prompts": "^2.4.0"
@@ -52714,11 +53345,11 @@
}
},
"@react-native-community/cli-config": {
- "version": "11.3.5",
- "resolved": "https://registry.npmjs.org/@react-native-community/cli-config/-/cli-config-11.3.5.tgz",
- "integrity": "sha512-fMblIsHlUleKfGsgWyjFJYfx1SqrsnhS/QXfA8w7iT6GrNOOjBp5UWx8+xlMDFcmOb9e42g1ExFDKl3n8FWkxQ==",
+ "version": "11.3.6",
+ "resolved": "https://registry.npmjs.org/@react-native-community/cli-config/-/cli-config-11.3.6.tgz",
+ "integrity": "sha512-edy7fwllSFLan/6BG6/rznOBCLPrjmJAE10FzkEqNLHowi0bckiAPg1+1jlgQ2qqAxV5kuk+c9eajVfQvPLYDA==",
"requires": {
- "@react-native-community/cli-tools": "11.3.5",
+ "@react-native-community/cli-tools": "11.3.6",
"chalk": "^4.1.2",
"cosmiconfig": "^5.1.0",
"deepmerge": "^4.3.0",
@@ -52806,22 +53437,22 @@
}
},
"@react-native-community/cli-debugger-ui": {
- "version": "11.3.5",
- "resolved": "https://registry.npmjs.org/@react-native-community/cli-debugger-ui/-/cli-debugger-ui-11.3.5.tgz",
- "integrity": "sha512-o5JVCKEpPUXMX4r3p1cYjiy3FgdOEkezZcQ6owWEae2dYvV19lLYyJwnocm9Y7aG9PvpgI3PIMVh3KZbhS21eA==",
+ "version": "11.3.6",
+ "resolved": "https://registry.npmjs.org/@react-native-community/cli-debugger-ui/-/cli-debugger-ui-11.3.6.tgz",
+ "integrity": "sha512-jhMOSN/iOlid9jn/A2/uf7HbC3u7+lGktpeGSLnHNw21iahFBzcpuO71ekEdlmTZ4zC/WyxBXw9j2ka33T358w==",
"requires": {
"serve-static": "^1.13.1"
}
},
"@react-native-community/cli-doctor": {
- "version": "11.3.5",
- "resolved": "https://registry.npmjs.org/@react-native-community/cli-doctor/-/cli-doctor-11.3.5.tgz",
- "integrity": "sha512-+4BuFHjoV4FFjX5y60l0s6nS0agidb1izTVwsFixeFKW73LUkOLu+Ae5HI94RAFEPE4ePEVNgYX3FynIau6K0g==",
- "requires": {
- "@react-native-community/cli-config": "11.3.5",
- "@react-native-community/cli-platform-android": "11.3.5",
- "@react-native-community/cli-platform-ios": "11.3.5",
- "@react-native-community/cli-tools": "11.3.5",
+ "version": "11.3.6",
+ "resolved": "https://registry.npmjs.org/@react-native-community/cli-doctor/-/cli-doctor-11.3.6.tgz",
+ "integrity": "sha512-UT/Tt6omVPi1j6JEX+CObc85eVFghSZwy4GR9JFMsO7gNg2Tvcu1RGWlUkrbmWMAMHw127LUu6TGK66Ugu1NLA==",
+ "requires": {
+ "@react-native-community/cli-config": "11.3.6",
+ "@react-native-community/cli-platform-android": "11.3.6",
+ "@react-native-community/cli-platform-ios": "11.3.6",
+ "@react-native-community/cli-tools": "11.3.6",
"chalk": "^4.1.2",
"command-exists": "^1.2.8",
"envinfo": "^7.7.2",
@@ -52831,7 +53462,7 @@
"node-stream-zip": "^1.9.1",
"ora": "^5.4.1",
"prompts": "^2.4.0",
- "semver": "^6.3.0",
+ "semver": "^7.5.2",
"strip-ansi": "^5.2.0",
"sudo-prompt": "^9.0.0",
"wcwidth": "^1.0.1",
@@ -52878,11 +53509,6 @@
"resolved": "https://registry.npmjs.org/ip/-/ip-1.1.8.tgz",
"integrity": "sha512-PuExPYUiu6qMBQb4l06ecm6T6ujzhmh+MeJcW9wa89PoAz5pvd4zPgN5WJV104mb6S2T1AwNIAaB70JNrLQWhg=="
},
- "semver": {
- "version": "6.3.1",
- "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
- "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="
- },
"strip-ansi": {
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz",
@@ -52902,12 +53528,12 @@
}
},
"@react-native-community/cli-hermes": {
- "version": "11.3.5",
- "resolved": "https://registry.npmjs.org/@react-native-community/cli-hermes/-/cli-hermes-11.3.5.tgz",
- "integrity": "sha512-+3m34hiaJpFel8BlJE7kJOaPzWR/8U8APZG2LXojbAdBAg99EGmQcwXIgsSVJFvH8h/nezf4DHbsPKigIe33zA==",
+ "version": "11.3.6",
+ "resolved": "https://registry.npmjs.org/@react-native-community/cli-hermes/-/cli-hermes-11.3.6.tgz",
+ "integrity": "sha512-O55YAYGZ3XynpUdePPVvNuUPGPY0IJdctLAOHme73OvS80gNwfntHDXfmY70TGHWIfkK2zBhA0B+2v8s5aTyTA==",
"requires": {
- "@react-native-community/cli-platform-android": "11.3.5",
- "@react-native-community/cli-tools": "11.3.5",
+ "@react-native-community/cli-platform-android": "11.3.6",
+ "@react-native-community/cli-tools": "11.3.6",
"chalk": "^4.1.2",
"hermes-profile-transformer": "^0.0.6",
"ip": "^1.1.5"
@@ -52964,11 +53590,11 @@
}
},
"@react-native-community/cli-platform-android": {
- "version": "11.3.5",
- "resolved": "https://registry.npmjs.org/@react-native-community/cli-platform-android/-/cli-platform-android-11.3.5.tgz",
- "integrity": "sha512-s4Lj7FKxJ/BofGi/ifjPfrA9MjFwIgYpHnHBSlqtbsvPoSYzmVCU2qlWM8fb3AmkXIwyYt4A6MEr3MmNT2UoBg==",
+ "version": "11.3.6",
+ "resolved": "https://registry.npmjs.org/@react-native-community/cli-platform-android/-/cli-platform-android-11.3.6.tgz",
+ "integrity": "sha512-ZARrpLv5tn3rmhZc//IuDM1LSAdYnjUmjrp58RynlvjLDI4ZEjBAGCQmgysRgXAsK7ekMrfkZgemUczfn9td2A==",
"requires": {
- "@react-native-community/cli-tools": "11.3.5",
+ "@react-native-community/cli-tools": "11.3.6",
"chalk": "^4.1.2",
"execa": "^5.0.0",
"glob": "^7.1.3",
@@ -53021,11 +53647,11 @@
}
},
"@react-native-community/cli-platform-ios": {
- "version": "11.3.5",
- "resolved": "https://registry.npmjs.org/@react-native-community/cli-platform-ios/-/cli-platform-ios-11.3.5.tgz",
- "integrity": "sha512-ytJC/YCFD7P+KuQHOT5Jzh1ho2XbJEjq71yHa1gJP2PG/Q/uB4h1x2XpxDqv5iXU6E250yjvKMmkReKTW4CTig==",
+ "version": "11.3.6",
+ "resolved": "https://registry.npmjs.org/@react-native-community/cli-platform-ios/-/cli-platform-ios-11.3.6.tgz",
+ "integrity": "sha512-tZ9VbXWiRW+F+fbZzpLMZlj93g3Q96HpuMsS6DRhrTiG+vMQ3o6oPWSEEmMGOvJSYU7+y68Dc9ms2liC7VD6cw==",
"requires": {
- "@react-native-community/cli-tools": "11.3.5",
+ "@react-native-community/cli-tools": "11.3.6",
"chalk": "^4.1.2",
"execa": "^5.0.0",
"fast-xml-parser": "^4.0.12",
@@ -53079,12 +53705,12 @@
}
},
"@react-native-community/cli-plugin-metro": {
- "version": "11.3.5",
- "resolved": "https://registry.npmjs.org/@react-native-community/cli-plugin-metro/-/cli-plugin-metro-11.3.5.tgz",
- "integrity": "sha512-r9AekfeLKdblB7LfWB71IrNy1XM03WrByQlUQajUOZAP2NmUUBLl9pMZscPjJeOSgLpHB9ixEFTIOhTabri/qg==",
+ "version": "11.3.6",
+ "resolved": "https://registry.npmjs.org/@react-native-community/cli-plugin-metro/-/cli-plugin-metro-11.3.6.tgz",
+ "integrity": "sha512-D97racrPX3069ibyabJNKw9aJpVcaZrkYiEzsEnx50uauQtPDoQ1ELb/5c6CtMhAEGKoZ0B5MS23BbsSZcLs2g==",
"requires": {
- "@react-native-community/cli-server-api": "11.3.5",
- "@react-native-community/cli-tools": "11.3.5",
+ "@react-native-community/cli-server-api": "11.3.6",
+ "@react-native-community/cli-tools": "11.3.6",
"chalk": "^4.1.2",
"execa": "^5.0.0",
"metro": "0.76.7",
@@ -53096,6 +53722,26 @@
"readline": "^1.3.0"
},
"dependencies": {
+ "@jest/types": {
+ "version": "27.5.1",
+ "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.5.1.tgz",
+ "integrity": "sha512-Cx46iJ9QpwQTjIdq5VJu2QTMMs3QlEjI0x1QbBP5W1+nMzyc2XmimiRR/CbX9TO0cPTeUlxWMOu8mslYsJ8DEw==",
+ "requires": {
+ "@types/istanbul-lib-coverage": "^2.0.0",
+ "@types/istanbul-reports": "^3.0.0",
+ "@types/node": "*",
+ "@types/yargs": "^16.0.0",
+ "chalk": "^4.0.0"
+ }
+ },
+ "@types/yargs": {
+ "version": "16.0.5",
+ "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.5.tgz",
+ "integrity": "sha512-AxO/ADJOBFJScHbWhq2xAhlWP24rY4aCEG/NFaMvbT3X2MgRsLjhjQwsn0Zi5zn0LG9jUhCCZMeX9Dkuw6k+vQ==",
+ "requires": {
+ "@types/yargs-parser": "*"
+ }
+ },
"ansi-styles": {
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
@@ -53113,6 +53759,21 @@
"supports-color": "^7.1.0"
}
},
+ "ci-info": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz",
+ "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ=="
+ },
+ "cliui": {
+ "version": "8.0.1",
+ "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz",
+ "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==",
+ "requires": {
+ "string-width": "^4.2.0",
+ "strip-ansi": "^6.0.1",
+ "wrap-ansi": "^7.0.0"
+ }
+ },
"color-convert": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
@@ -53126,11 +53787,404 @@
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
},
+ "cosmiconfig": {
+ "version": "5.2.1",
+ "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-5.2.1.tgz",
+ "integrity": "sha512-H65gsXo1SKjf8zmrJ67eJk8aIRKV5ff2D4uKZIBZShbhGSpEmsQOPW/SKMKYhSTrqR7ufy6RP69rPogdaPh/kA==",
+ "requires": {
+ "import-fresh": "^2.0.0",
+ "is-directory": "^0.3.1",
+ "js-yaml": "^3.13.1",
+ "parse-json": "^4.0.0"
+ }
+ },
+ "debug": {
+ "version": "2.6.9",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+ "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+ "requires": {
+ "ms": "2.0.0"
+ }
+ },
"has-flag": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
"integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ=="
},
+ "import-fresh": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-2.0.0.tgz",
+ "integrity": "sha512-eZ5H8rcgYazHbKC3PG4ClHNykCSxtAhxSSEM+2mb+7evD2CKF5V7c0dNum7AdpDh0ZdICwZY9sRSn8f+KH96sg==",
+ "requires": {
+ "caller-path": "^2.0.0",
+ "resolve-from": "^3.0.0"
+ }
+ },
+ "jest-regex-util": {
+ "version": "27.5.1",
+ "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-27.5.1.tgz",
+ "integrity": "sha512-4bfKq2zie+x16okqDXjXn9ql2B0dScQu+vcwe4TvFVhkVyuWLqpZrZtXxLLWoXYgn0E87I6r6GRYHF7wFZBUvg=="
+ },
+ "jest-util": {
+ "version": "27.5.1",
+ "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-27.5.1.tgz",
+ "integrity": "sha512-Kv2o/8jNvX1MQ0KGtw480E/w4fBCDOnH6+6DmeKi6LZUIlKA5kwY0YNdlzaWTiVgxqAqik11QyxDOKk543aKXw==",
+ "requires": {
+ "@jest/types": "^27.5.1",
+ "@types/node": "*",
+ "chalk": "^4.0.0",
+ "ci-info": "^3.2.0",
+ "graceful-fs": "^4.2.9",
+ "picomatch": "^2.2.3"
+ },
+ "dependencies": {
+ "ci-info": {
+ "version": "3.8.0",
+ "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.8.0.tgz",
+ "integrity": "sha512-eXTggHWSooYhq49F2opQhuHWgzucfF2YgODK4e1566GQs5BIfP30B0oenwBJHfWxAs2fyPB1s7Mg949zLf61Yw=="
+ }
+ }
+ },
+ "jest-worker": {
+ "version": "27.5.1",
+ "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz",
+ "integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==",
+ "requires": {
+ "@types/node": "*",
+ "merge-stream": "^2.0.0",
+ "supports-color": "^8.0.0"
+ },
+ "dependencies": {
+ "supports-color": {
+ "version": "8.1.1",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz",
+ "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==",
+ "requires": {
+ "has-flag": "^4.0.0"
+ }
+ }
+ }
+ },
+ "metro": {
+ "version": "0.76.7",
+ "resolved": "https://registry.npmjs.org/metro/-/metro-0.76.7.tgz",
+ "integrity": "sha512-67ZGwDeumEPnrHI+pEDSKH2cx+C81Gx8Mn5qOtmGUPm/Up9Y4I1H2dJZ5n17MWzejNo0XAvPh0QL0CrlJEODVQ==",
+ "requires": {
+ "@babel/code-frame": "^7.0.0",
+ "@babel/core": "^7.20.0",
+ "@babel/generator": "^7.20.0",
+ "@babel/parser": "^7.20.0",
+ "@babel/template": "^7.0.0",
+ "@babel/traverse": "^7.20.0",
+ "@babel/types": "^7.20.0",
+ "accepts": "^1.3.7",
+ "async": "^3.2.2",
+ "chalk": "^4.0.0",
+ "ci-info": "^2.0.0",
+ "connect": "^3.6.5",
+ "debug": "^2.2.0",
+ "denodeify": "^1.2.1",
+ "error-stack-parser": "^2.0.6",
+ "graceful-fs": "^4.2.4",
+ "hermes-parser": "0.12.0",
+ "image-size": "^1.0.2",
+ "invariant": "^2.2.4",
+ "jest-worker": "^27.2.0",
+ "jsc-safe-url": "^0.2.2",
+ "lodash.throttle": "^4.1.1",
+ "metro-babel-transformer": "0.76.7",
+ "metro-cache": "0.76.7",
+ "metro-cache-key": "0.76.7",
+ "metro-config": "0.76.7",
+ "metro-core": "0.76.7",
+ "metro-file-map": "0.76.7",
+ "metro-inspector-proxy": "0.76.7",
+ "metro-minify-terser": "0.76.7",
+ "metro-minify-uglify": "0.76.7",
+ "metro-react-native-babel-preset": "0.76.7",
+ "metro-resolver": "0.76.7",
+ "metro-runtime": "0.76.7",
+ "metro-source-map": "0.76.7",
+ "metro-symbolicate": "0.76.7",
+ "metro-transform-plugins": "0.76.7",
+ "metro-transform-worker": "0.76.7",
+ "mime-types": "^2.1.27",
+ "node-fetch": "^2.2.0",
+ "nullthrows": "^1.1.1",
+ "rimraf": "^3.0.2",
+ "serialize-error": "^2.1.0",
+ "source-map": "^0.5.6",
+ "strip-ansi": "^6.0.0",
+ "throat": "^5.0.0",
+ "ws": "^7.5.1",
+ "yargs": "^17.6.2"
+ }
+ },
+ "metro-babel-transformer": {
+ "version": "0.76.7",
+ "resolved": "https://registry.npmjs.org/metro-babel-transformer/-/metro-babel-transformer-0.76.7.tgz",
+ "integrity": "sha512-bgr2OFn0J4r0qoZcHrwEvccF7g9k3wdgTOgk6gmGHrtlZ1Jn3oCpklW/DfZ9PzHfjY2mQammKTc19g/EFGyOJw==",
+ "requires": {
+ "@babel/core": "^7.20.0",
+ "hermes-parser": "0.12.0",
+ "nullthrows": "^1.1.1"
+ }
+ },
+ "metro-cache": {
+ "version": "0.76.7",
+ "resolved": "https://registry.npmjs.org/metro-cache/-/metro-cache-0.76.7.tgz",
+ "integrity": "sha512-nWBMztrs5RuSxZRI7hgFgob5PhYDmxICh9FF8anm9/ito0u0vpPvRxt7sRu8fyeD2AHdXqE7kX32rWY0LiXgeg==",
+ "requires": {
+ "metro-core": "0.76.7",
+ "rimraf": "^3.0.2"
+ }
+ },
+ "metro-cache-key": {
+ "version": "0.76.7",
+ "resolved": "https://registry.npmjs.org/metro-cache-key/-/metro-cache-key-0.76.7.tgz",
+ "integrity": "sha512-0pecoIzwsD/Whn/Qfa+SDMX2YyasV0ndbcgUFx7w1Ct2sLHClujdhQ4ik6mvQmsaOcnGkIyN0zcceMDjC2+BFQ=="
+ },
+ "metro-config": {
+ "version": "0.76.7",
+ "resolved": "https://registry.npmjs.org/metro-config/-/metro-config-0.76.7.tgz",
+ "integrity": "sha512-CFDyNb9bqxZemiChC/gNdXZ7OQkIwmXzkrEXivcXGbgzlt/b2juCv555GWJHyZSlorwnwJfY3uzAFu4A9iRVfg==",
+ "requires": {
+ "connect": "^3.6.5",
+ "cosmiconfig": "^5.0.5",
+ "jest-validate": "^29.2.1",
+ "metro": "0.76.7",
+ "metro-cache": "0.76.7",
+ "metro-core": "0.76.7",
+ "metro-runtime": "0.76.7"
+ }
+ },
+ "metro-core": {
+ "version": "0.76.7",
+ "resolved": "https://registry.npmjs.org/metro-core/-/metro-core-0.76.7.tgz",
+ "integrity": "sha512-0b8KfrwPmwCMW+1V7ZQPkTy2tsEKZjYG9Pu1PTsu463Z9fxX7WaR0fcHFshv+J1CnQSUTwIGGjbNvj1teKe+pw==",
+ "requires": {
+ "lodash.throttle": "^4.1.1",
+ "metro-resolver": "0.76.7"
+ }
+ },
+ "metro-file-map": {
+ "version": "0.76.7",
+ "resolved": "https://registry.npmjs.org/metro-file-map/-/metro-file-map-0.76.7.tgz",
+ "integrity": "sha512-s+zEkTcJ4mOJTgEE2ht4jIo1DZfeWreQR3tpT3gDV/Y/0UQ8aJBTv62dE775z0GLsWZApiblAYZsj7ZE8P06nw==",
+ "requires": {
+ "anymatch": "^3.0.3",
+ "debug": "^2.2.0",
+ "fb-watchman": "^2.0.0",
+ "fsevents": "^2.3.2",
+ "graceful-fs": "^4.2.4",
+ "invariant": "^2.2.4",
+ "jest-regex-util": "^27.0.6",
+ "jest-util": "^27.2.0",
+ "jest-worker": "^27.2.0",
+ "micromatch": "^4.0.4",
+ "node-abort-controller": "^3.1.1",
+ "nullthrows": "^1.1.1",
+ "walker": "^1.0.7"
+ }
+ },
+ "metro-inspector-proxy": {
+ "version": "0.76.7",
+ "resolved": "https://registry.npmjs.org/metro-inspector-proxy/-/metro-inspector-proxy-0.76.7.tgz",
+ "integrity": "sha512-rNZ/6edTl/1qUekAhAbaFjczMphM50/UjtxiKulo6vqvgn/Mjd9hVqDvVYfAMZXqPvlusD88n38UjVYPkruLSg==",
+ "requires": {
+ "connect": "^3.6.5",
+ "debug": "^2.2.0",
+ "node-fetch": "^2.2.0",
+ "ws": "^7.5.1",
+ "yargs": "^17.6.2"
+ }
+ },
+ "metro-minify-terser": {
+ "version": "0.76.7",
+ "resolved": "https://registry.npmjs.org/metro-minify-terser/-/metro-minify-terser-0.76.7.tgz",
+ "integrity": "sha512-FQiZGhIxCzhDwK4LxyPMLlq0Tsmla10X7BfNGlYFK0A5IsaVKNJbETyTzhpIwc+YFRT4GkFFwgo0V2N5vxO5HA==",
+ "requires": {
+ "terser": "^5.15.0"
+ }
+ },
+ "metro-minify-uglify": {
+ "version": "0.76.7",
+ "resolved": "https://registry.npmjs.org/metro-minify-uglify/-/metro-minify-uglify-0.76.7.tgz",
+ "integrity": "sha512-FuXIU3j2uNcSvQtPrAJjYWHruPiQ+EpE++J9Z+VznQKEHcIxMMoQZAfIF2IpZSrZYfLOjVFyGMvj41jQMxV1Vw==",
+ "requires": {
+ "uglify-es": "^3.1.9"
+ }
+ },
+ "metro-react-native-babel-preset": {
+ "version": "0.76.7",
+ "resolved": "https://registry.npmjs.org/metro-react-native-babel-preset/-/metro-react-native-babel-preset-0.76.7.tgz",
+ "integrity": "sha512-R25wq+VOSorAK3hc07NW0SmN8z9S/IR0Us0oGAsBcMZnsgkbOxu77Mduqf+f4is/wnWHc5+9bfiqdLnaMngiVw==",
+ "requires": {
+ "@babel/core": "^7.20.0",
+ "@babel/plugin-proposal-async-generator-functions": "^7.0.0",
+ "@babel/plugin-proposal-class-properties": "^7.18.0",
+ "@babel/plugin-proposal-export-default-from": "^7.0.0",
+ "@babel/plugin-proposal-nullish-coalescing-operator": "^7.18.0",
+ "@babel/plugin-proposal-numeric-separator": "^7.0.0",
+ "@babel/plugin-proposal-object-rest-spread": "^7.20.0",
+ "@babel/plugin-proposal-optional-catch-binding": "^7.0.0",
+ "@babel/plugin-proposal-optional-chaining": "^7.20.0",
+ "@babel/plugin-syntax-dynamic-import": "^7.8.0",
+ "@babel/plugin-syntax-export-default-from": "^7.0.0",
+ "@babel/plugin-syntax-flow": "^7.18.0",
+ "@babel/plugin-syntax-nullish-coalescing-operator": "^7.0.0",
+ "@babel/plugin-syntax-optional-chaining": "^7.0.0",
+ "@babel/plugin-transform-arrow-functions": "^7.0.0",
+ "@babel/plugin-transform-async-to-generator": "^7.20.0",
+ "@babel/plugin-transform-block-scoping": "^7.0.0",
+ "@babel/plugin-transform-classes": "^7.0.0",
+ "@babel/plugin-transform-computed-properties": "^7.0.0",
+ "@babel/plugin-transform-destructuring": "^7.20.0",
+ "@babel/plugin-transform-flow-strip-types": "^7.20.0",
+ "@babel/plugin-transform-function-name": "^7.0.0",
+ "@babel/plugin-transform-literals": "^7.0.0",
+ "@babel/plugin-transform-modules-commonjs": "^7.0.0",
+ "@babel/plugin-transform-named-capturing-groups-regex": "^7.0.0",
+ "@babel/plugin-transform-parameters": "^7.0.0",
+ "@babel/plugin-transform-react-display-name": "^7.0.0",
+ "@babel/plugin-transform-react-jsx": "^7.0.0",
+ "@babel/plugin-transform-react-jsx-self": "^7.0.0",
+ "@babel/plugin-transform-react-jsx-source": "^7.0.0",
+ "@babel/plugin-transform-runtime": "^7.0.0",
+ "@babel/plugin-transform-shorthand-properties": "^7.0.0",
+ "@babel/plugin-transform-spread": "^7.0.0",
+ "@babel/plugin-transform-sticky-regex": "^7.0.0",
+ "@babel/plugin-transform-typescript": "^7.5.0",
+ "@babel/plugin-transform-unicode-regex": "^7.0.0",
+ "@babel/template": "^7.0.0",
+ "babel-plugin-transform-flow-enums": "^0.0.2",
+ "react-refresh": "^0.4.0"
+ }
+ },
+ "metro-react-native-babel-transformer": {
+ "version": "0.76.7",
+ "resolved": "https://registry.npmjs.org/metro-react-native-babel-transformer/-/metro-react-native-babel-transformer-0.76.7.tgz",
+ "integrity": "sha512-W6lW3J7y/05ph3c2p3KKJNhH0IdyxdOCbQ5it7aM2MAl0SM4wgKjaV6EYv9b3rHklpV6K3qMH37UKVcjMooWiA==",
+ "requires": {
+ "@babel/core": "^7.20.0",
+ "babel-preset-fbjs": "^3.4.0",
+ "hermes-parser": "0.12.0",
+ "metro-react-native-babel-preset": "0.76.7",
+ "nullthrows": "^1.1.1"
+ }
+ },
+ "metro-resolver": {
+ "version": "0.76.7",
+ "resolved": "https://registry.npmjs.org/metro-resolver/-/metro-resolver-0.76.7.tgz",
+ "integrity": "sha512-pC0Wgq29HHIHrwz23xxiNgylhI8Rq1V01kQaJ9Kz11zWrIdlrH0ZdnJ7GC6qA0ErROG+cXmJ0rJb8/SW1Zp2IA=="
+ },
+ "metro-runtime": {
+ "version": "0.76.7",
+ "resolved": "https://registry.npmjs.org/metro-runtime/-/metro-runtime-0.76.7.tgz",
+ "integrity": "sha512-MuWHubQHymUWBpZLwuKZQgA/qbb35WnDAKPo83rk7JRLIFPvzXSvFaC18voPuzJBt1V98lKQIonh6MiC9gd8Ug==",
+ "requires": {
+ "@babel/runtime": "^7.0.0",
+ "react-refresh": "^0.4.0"
+ }
+ },
+ "metro-source-map": {
+ "version": "0.76.7",
+ "resolved": "https://registry.npmjs.org/metro-source-map/-/metro-source-map-0.76.7.tgz",
+ "integrity": "sha512-Prhx7PeRV1LuogT0Kn5VjCuFu9fVD68eefntdWabrksmNY6mXK8pRqzvNJOhTojh6nek+RxBzZeD6MIOOyXS6w==",
+ "requires": {
+ "@babel/traverse": "^7.20.0",
+ "@babel/types": "^7.20.0",
+ "invariant": "^2.2.4",
+ "metro-symbolicate": "0.76.7",
+ "nullthrows": "^1.1.1",
+ "ob1": "0.76.7",
+ "source-map": "^0.5.6",
+ "vlq": "^1.0.0"
+ }
+ },
+ "metro-symbolicate": {
+ "version": "0.76.7",
+ "resolved": "https://registry.npmjs.org/metro-symbolicate/-/metro-symbolicate-0.76.7.tgz",
+ "integrity": "sha512-p0zWEME5qLSL1bJb93iq+zt5fz3sfVn9xFYzca1TJIpY5MommEaS64Va87lp56O0sfEIvh4307Oaf/ZzRjuLiQ==",
+ "requires": {
+ "invariant": "^2.2.4",
+ "metro-source-map": "0.76.7",
+ "nullthrows": "^1.1.1",
+ "source-map": "^0.5.6",
+ "through2": "^2.0.1",
+ "vlq": "^1.0.0"
+ }
+ },
+ "metro-transform-plugins": {
+ "version": "0.76.7",
+ "resolved": "https://registry.npmjs.org/metro-transform-plugins/-/metro-transform-plugins-0.76.7.tgz",
+ "integrity": "sha512-iSmnjVApbdivjuzb88Orb0JHvcEt5veVyFAzxiS5h0QB+zV79w6JCSqZlHCrbNOkOKBED//LqtKbFVakxllnNg==",
+ "requires": {
+ "@babel/core": "^7.20.0",
+ "@babel/generator": "^7.20.0",
+ "@babel/template": "^7.0.0",
+ "@babel/traverse": "^7.20.0",
+ "nullthrows": "^1.1.1"
+ }
+ },
+ "metro-transform-worker": {
+ "version": "0.76.7",
+ "resolved": "https://registry.npmjs.org/metro-transform-worker/-/metro-transform-worker-0.76.7.tgz",
+ "integrity": "sha512-cGvELqFMVk9XTC15CMVzrCzcO6sO1lURfcbgjuuPdzaWuD11eEyocvkTX0DPiRjsvgAmicz4XYxVzgYl3MykDw==",
+ "requires": {
+ "@babel/core": "^7.20.0",
+ "@babel/generator": "^7.20.0",
+ "@babel/parser": "^7.20.0",
+ "@babel/types": "^7.20.0",
+ "babel-preset-fbjs": "^3.4.0",
+ "metro": "0.76.7",
+ "metro-babel-transformer": "0.76.7",
+ "metro-cache": "0.76.7",
+ "metro-cache-key": "0.76.7",
+ "metro-source-map": "0.76.7",
+ "metro-transform-plugins": "0.76.7",
+ "nullthrows": "^1.1.1"
+ }
+ },
+ "ms": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+ "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="
+ },
+ "ob1": {
+ "version": "0.76.7",
+ "resolved": "https://registry.npmjs.org/ob1/-/ob1-0.76.7.tgz",
+ "integrity": "sha512-BQdRtxxoUNfSoZxqeBGOyuT9nEYSn18xZHwGMb0mMVpn2NBcYbnyKY4BK2LIHRgw33CBGlUmE+KMaNvyTpLLtQ=="
+ },
+ "parse-json": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz",
+ "integrity": "sha512-aOIos8bujGN93/8Ox/jPLh7RwVnPEysynVFE+fQZyg6jKELEHwzgKdLRFHUgXJL6kylijVSBC4BvN9OmsB48Rw==",
+ "requires": {
+ "error-ex": "^1.3.1",
+ "json-parse-better-errors": "^1.0.1"
+ }
+ },
+ "react-refresh": {
+ "version": "0.4.3",
+ "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.4.3.tgz",
+ "integrity": "sha512-Hwln1VNuGl/6bVwnd0Xdn1e84gT/8T9aYNL+HAKDArLCS7LWjwr7StE30IEYbIkx0Vi3vs+coQxe+SQDbGbbpA=="
+ },
+ "resolve-from": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-3.0.0.tgz",
+ "integrity": "sha512-GnlH6vxLymXJNMBo7XP1fJIzBFbdYt49CuTwmB/6N53t+kMPRMFKz783LlQ4tv28XoQfMWinAJX6WCGf2IlaIw=="
+ },
+ "serialize-error": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/serialize-error/-/serialize-error-2.1.0.tgz",
+ "integrity": "sha512-ghgmKt5o4Tly5yEG/UJp8qTd0AN7Xalw4XBtDEKP655B699qMEtra1WlXeE6WIvdEG481JvRxULKsInq/iNysw=="
+ },
+ "source-map": {
+ "version": "0.5.7",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
+ "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ=="
+ },
"supports-color": {
"version": "7.2.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
@@ -53138,16 +54192,46 @@
"requires": {
"has-flag": "^4.0.0"
}
+ },
+ "ws": {
+ "version": "7.5.9",
+ "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.9.tgz",
+ "integrity": "sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q==",
+ "requires": {}
+ },
+ "y18n": {
+ "version": "5.0.8",
+ "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz",
+ "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA=="
+ },
+ "yargs": {
+ "version": "17.7.2",
+ "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz",
+ "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==",
+ "requires": {
+ "cliui": "^8.0.1",
+ "escalade": "^3.1.1",
+ "get-caller-file": "^2.0.5",
+ "require-directory": "^2.1.1",
+ "string-width": "^4.2.3",
+ "y18n": "^5.0.5",
+ "yargs-parser": "^21.1.1"
+ }
+ },
+ "yargs-parser": {
+ "version": "21.1.1",
+ "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz",
+ "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw=="
}
}
},
"@react-native-community/cli-server-api": {
- "version": "11.3.5",
- "resolved": "https://registry.npmjs.org/@react-native-community/cli-server-api/-/cli-server-api-11.3.5.tgz",
- "integrity": "sha512-PM/jF13uD1eAKuC84lntNuM5ZvJAtyb+H896P1dBIXa9boPLa3KejfUvNVoyOUJ5s8Ht25JKbc3yieV2+GMBDA==",
+ "version": "11.3.6",
+ "resolved": "https://registry.npmjs.org/@react-native-community/cli-server-api/-/cli-server-api-11.3.6.tgz",
+ "integrity": "sha512-8GUKodPnURGtJ9JKg8yOHIRtWepPciI3ssXVw5jik7+dZ43yN8P5BqCoDaq8e1H1yRer27iiOfT7XVnwk8Dueg==",
"requires": {
- "@react-native-community/cli-debugger-ui": "11.3.5",
- "@react-native-community/cli-tools": "11.3.5",
+ "@react-native-community/cli-debugger-ui": "11.3.6",
+ "@react-native-community/cli-tools": "11.3.6",
"compression": "^1.7.1",
"connect": "^3.6.5",
"errorhandler": "^1.5.1",
@@ -53208,9 +54292,9 @@
}
},
"@react-native-community/cli-tools": {
- "version": "11.3.5",
- "resolved": "https://registry.npmjs.org/@react-native-community/cli-tools/-/cli-tools-11.3.5.tgz",
- "integrity": "sha512-zDklE1+ah/zL4BLxut5XbzqCj9KTHzbYBKX7//cXw2/0TpkNCaY9c+iKx//gZ5m7U1OKbb86Fm2b0AKtKVRf6Q==",
+ "version": "11.3.6",
+ "resolved": "https://registry.npmjs.org/@react-native-community/cli-tools/-/cli-tools-11.3.6.tgz",
+ "integrity": "sha512-JpmUTcDwAGiTzLsfMlIAYpCMSJ9w2Qlf7PU7mZIRyEu61UzEawyw83DkqfbzDPBuRwRnaeN44JX2CP/yTO3ThQ==",
"requires": {
"appdirsjs": "^1.2.4",
"chalk": "^4.1.2",
@@ -53219,7 +54303,7 @@
"node-fetch": "^2.6.0",
"open": "^6.2.0",
"ora": "^5.4.1",
- "semver": "^6.3.0",
+ "semver": "^7.5.2",
"shell-quote": "^1.7.3"
},
"dependencies": {
@@ -53271,11 +54355,6 @@
"is-wsl": "^1.1.0"
}
},
- "semver": {
- "version": "6.3.1",
- "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
- "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="
- },
"supports-color": {
"version": "7.2.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
@@ -53287,9 +54366,9 @@
}
},
"@react-native-community/cli-types": {
- "version": "11.3.5",
- "resolved": "https://registry.npmjs.org/@react-native-community/cli-types/-/cli-types-11.3.5.tgz",
- "integrity": "sha512-pf0kdWMEfPSV/+8rcViDCFzbLMtWIHMZ8ay7hKwqaoWegsJ0oprSF2tSTH+LSC/7X1Beb9ssIvHj1m5C4es5Xg==",
+ "version": "11.3.6",
+ "resolved": "https://registry.npmjs.org/@react-native-community/cli-types/-/cli-types-11.3.6.tgz",
+ "integrity": "sha512-6DxjrMKx5x68N/tCJYVYRKAtlRHbtUVBZrnAvkxbRWFD9v4vhNgsPM0RQm8i2vRugeksnao5mbnRGpS6c0awCw==",
"requires": {
"joi": "^17.2.1"
}
@@ -53433,15 +54512,15 @@
"version": "0.72.1"
},
"@react-native/metro-config": {
- "version": "0.72.9",
- "resolved": "https://registry.npmjs.org/@react-native/metro-config/-/metro-config-0.72.9.tgz",
- "integrity": "sha512-5MGmyDnXPeprRuvgPGE4LZ+e+ovofSd5YY6nFDwg6wbjRGOkeCRRlaTlQT+fjmv+zr4vYG+MUTKBlaO+fui/vA==",
+ "version": "0.72.11",
+ "resolved": "https://registry.npmjs.org/@react-native/metro-config/-/metro-config-0.72.11.tgz",
+ "integrity": "sha512-661EyQnDdVelyc0qP/ew7kKkGAh6N6KlkuPLC2SQ8sxaXskVU6fSuNlpLW4bUTBUDFKG8gEOU2hp6rzk4wQnGQ==",
"dev": true,
"requires": {
"@react-native/js-polyfills": "^0.72.1",
- "metro-config": "0.76.7",
- "metro-react-native-babel-transformer": "0.76.7",
- "metro-runtime": "0.76.7"
+ "metro-config": "0.76.8",
+ "metro-react-native-babel-transformer": "0.76.8",
+ "metro-runtime": "0.76.8"
}
},
"@react-native/normalize-color": {
@@ -53453,9 +54532,9 @@
"integrity": "sha512-285lfdqSXaqKuBbbtP9qL2tDrfxdOFtIMvkKadtleRQkdOxx+uzGvFr82KHmc/sSiMtfXGp7JnFYWVh4sFl7Yw=="
},
"@react-native/virtualized-lists": {
- "version": "0.72.6",
- "resolved": "https://registry.npmjs.org/@react-native/virtualized-lists/-/virtualized-lists-0.72.6.tgz",
- "integrity": "sha512-JhT6ydu35LvbSKdwnhWDuGHMOwM0WAh9oza/X8vXHA8ELHRyQ/4p8eKz/bTQcbQziJaaleUURToGhFuCtgiMoA==",
+ "version": "0.72.8",
+ "resolved": "https://registry.npmjs.org/@react-native/virtualized-lists/-/virtualized-lists-0.72.8.tgz",
+ "integrity": "sha512-J3Q4Bkuo99k7mu+jPS9gSUSgq+lLRSI/+ahXNwV92XgJ/8UgOTxu2LPwhJnBk/sQKxq7E8WkZBnBiozukQMqrw==",
"requires": {
"invariant": "^2.2.4",
"nullthrows": "^1.1.1"
@@ -62665,7 +63744,9 @@
"dev": true
},
"babel-plugin-syntax-trailing-function-commas": {
- "version": "7.0.0-beta.0"
+ "version": "7.0.0-beta.0",
+ "resolved": "https://registry.npmjs.org/babel-plugin-syntax-trailing-function-commas/-/babel-plugin-syntax-trailing-function-commas-7.0.0-beta.0.tgz",
+ "integrity": "sha512-Xj9XuRuz3nTSbaTXWv3itLOcxyF4oPD8douBBmj7U9BBC6nEBYfyOJYQMf/8PJAFotC62UY5dFfIGEPr7WswzQ=="
},
"babel-plugin-transform-class-properties": {
"version": "6.24.1",
@@ -62722,6 +63803,8 @@
},
"babel-preset-fbjs": {
"version": "3.4.0",
+ "resolved": "https://registry.npmjs.org/babel-preset-fbjs/-/babel-preset-fbjs-3.4.0.tgz",
+ "integrity": "sha512-9ywCsCvo1ojrw0b+XYk7aFvTH6D9064t0RIL1rtMf3nsa02Xw41MS7sZw216Im35xj/UY0PDBQsa1brUDDF1Ow==",
"requires": {
"@babel/plugin-proposal-class-properties": "^7.0.0",
"@babel/plugin-proposal-object-rest-spread": "^7.0.0",
@@ -63801,9 +64884,9 @@
}
},
"cli-spinners": {
- "version": "2.9.0",
- "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.9.0.tgz",
- "integrity": "sha512-4/aL9X3Wh0yiMQlE+eeRhWP6vclO3QRtw1JHKIT0FFUs5FjpFmESqtMvYZ0+lbzBw900b95mS0hohy+qn2VK/g=="
+ "version": "2.9.1",
+ "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.9.1.tgz",
+ "integrity": "sha512-jHgecW0pxkonBJdrKsqxgRX9AcG+u/5k0Q7WPDfi8AogLAdwxEkyYYNWwZ5GvVFoFx2uiY1eNcSK00fh+1+FyQ=="
},
"cli-table3": {
"version": "0.6.3",
@@ -64122,6 +65205,8 @@
},
"connect": {
"version": "3.7.0",
+ "resolved": "https://registry.npmjs.org/connect/-/connect-3.7.0.tgz",
+ "integrity": "sha512-ZqRXc+tZukToSNmh5C2iWMSoV3X1YUcPbqEM4DkEG5tNQXrQUZCNVGGv3IuicnkMtPfGf3Xtp8WCXs295iQ1pQ==",
"requires": {
"debug": "2.6.9",
"finalhandler": "1.1.2",
@@ -64131,12 +65216,16 @@
"dependencies": {
"debug": {
"version": "2.6.9",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+ "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
"requires": {
"ms": "2.0.0"
}
},
"finalhandler": {
"version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz",
+ "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==",
"requires": {
"debug": "2.6.9",
"encodeurl": "~1.0.2",
@@ -64148,16 +65237,22 @@
}
},
"ms": {
- "version": "2.0.0"
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+ "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="
},
"on-finished": {
"version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz",
+ "integrity": "sha512-ikqdkGAAyf/X/gPhXGvfgAytDZtDbr+bkNUJ0N9h5MI/dmdgCs3l6hoHrcUv41sRKew3jIwrp4qQDXiK99Utww==",
"requires": {
"ee-first": "1.1.1"
}
},
"statuses": {
- "version": "1.5.0"
+ "version": "1.5.0",
+ "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz",
+ "integrity": "sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA=="
}
}
},
@@ -65089,7 +66184,9 @@
"dev": true
},
"denodeify": {
- "version": "1.2.1"
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/denodeify/-/denodeify-1.2.1.tgz",
+ "integrity": "sha512-KNTihKNmQENUZeKu5fzfpzRqR5S2VMp4gl9RFHiWzj9DfvYQPMJ6XHKNaQxaGCXwPk6y9yme3aUoaiAe+KX+vg=="
},
"depd": {
"version": "2.0.0"
@@ -66168,9 +67265,9 @@
}
},
"eslint-config-expensify": {
- "version": "2.0.38",
- "resolved": "https://registry.npmjs.org/eslint-config-expensify/-/eslint-config-expensify-2.0.38.tgz",
- "integrity": "sha512-jAlrYSjkDV8YESUUPcaTjUM8Fgru+37FS+Hq6zzcRR0FbA5bLiOPguhJHo77XpYh5N+PEf4wrpgsS04sjdgDPg==",
+ "version": "2.0.39",
+ "resolved": "https://registry.npmjs.org/eslint-config-expensify/-/eslint-config-expensify-2.0.39.tgz",
+ "integrity": "sha512-DIxR3k99ZIDPE2NK+WLLRWpoDq06gTXdY8XZg9Etd1UqZ7fXm/Yz3/QkTxu7CH7UaXbCH3P4PTo023ULQGKOSw==",
"dev": true,
"requires": {
"@lwc/eslint-plugin-lwc": "^0.11.0",
@@ -67708,23 +68805,6 @@
"readable-stream": "^2.3.6"
}
},
- "focus-trap": {
- "version": "7.5.2",
- "resolved": "https://registry.npmjs.org/focus-trap/-/focus-trap-7.5.2.tgz",
- "integrity": "sha512-p6vGNNWLDGwJCiEjkSK6oERj/hEyI9ITsSwIUICBoKLlWiTWXJRfQibCwcoi50rTZdbi87qDtUlMCmQwsGSgPw==",
- "requires": {
- "tabbable": "^6.2.0"
- }
- },
- "focus-trap-react": {
- "version": "10.2.1",
- "resolved": "https://registry.npmjs.org/focus-trap-react/-/focus-trap-react-10.2.1.tgz",
- "integrity": "sha512-UrAKOn52lvfHF6lkUMfFhlQxFgahyNW5i6FpHWkDxAeD4FSk3iwx9n4UEA4Sims0G5WiGIi0fAyoq3/UVeNCYA==",
- "requires": {
- "focus-trap": "^7.5.2",
- "tabbable": "^6.2.0"
- }
- },
"follow-redirects": {
"version": "1.15.2",
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz",
@@ -71206,9 +72286,9 @@
}
},
"joi": {
- "version": "17.9.2",
- "resolved": "https://registry.npmjs.org/joi/-/joi-17.9.2.tgz",
- "integrity": "sha512-Itk/r+V4Dx0V3c7RLFdRh12IOjySm2/WGPMubBT92cQvRfYZhPM2W0hZlctjj72iES8jsRCwp7S/cRmWBnJ4nw==",
+ "version": "17.10.1",
+ "resolved": "https://registry.npmjs.org/joi/-/joi-17.10.1.tgz",
+ "integrity": "sha512-vIiDxQKmRidUVp8KngT8MZSOcmRVm2zV7jbMjNYWuHcJWI0bUck3nRTGQjhpPlQenIQIBC5Vp9AhcnHbWQqafw==",
"requires": {
"@hapi/hoek": "^9.0.0",
"@hapi/topo": "^5.0.0",
@@ -71666,7 +72746,9 @@
"dev": true
},
"lodash.throttle": {
- "version": "4.1.1"
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/lodash.throttle/-/lodash.throttle-4.1.1.tgz",
+ "integrity": "sha512-wIkUCfVKpVsWo3JSZlc+8MB5it+2AN5W8J7YVMST30UrvcQNZ1Okbj+rbVniijTWE6FGYy4XJq/rHkas8qJMLQ=="
},
"lodash.truncate": {
"version": "4.4.2",
@@ -72670,9 +73752,10 @@
"dev": true
},
"metro": {
- "version": "0.76.7",
- "resolved": "https://registry.npmjs.org/metro/-/metro-0.76.7.tgz",
- "integrity": "sha512-67ZGwDeumEPnrHI+pEDSKH2cx+C81Gx8Mn5qOtmGUPm/Up9Y4I1H2dJZ5n17MWzejNo0XAvPh0QL0CrlJEODVQ==",
+ "version": "0.76.8",
+ "resolved": "https://registry.npmjs.org/metro/-/metro-0.76.8.tgz",
+ "integrity": "sha512-oQA3gLzrrYv3qKtuWArMgHPbHu8odZOD9AoavrqSFllkPgOtmkBvNNDLCELqv5SjBfqjISNffypg+5UGG3y0pg==",
+ "dev": true,
"requires": {
"@babel/code-frame": "^7.0.0",
"@babel/core": "^7.20.0",
@@ -72696,22 +73779,22 @@
"jest-worker": "^27.2.0",
"jsc-safe-url": "^0.2.2",
"lodash.throttle": "^4.1.1",
- "metro-babel-transformer": "0.76.7",
- "metro-cache": "0.76.7",
- "metro-cache-key": "0.76.7",
- "metro-config": "0.76.7",
- "metro-core": "0.76.7",
- "metro-file-map": "0.76.7",
- "metro-inspector-proxy": "0.76.7",
- "metro-minify-terser": "0.76.7",
- "metro-minify-uglify": "0.76.7",
- "metro-react-native-babel-preset": "0.76.7",
- "metro-resolver": "0.76.7",
- "metro-runtime": "0.76.7",
- "metro-source-map": "0.76.7",
- "metro-symbolicate": "0.76.7",
- "metro-transform-plugins": "0.76.7",
- "metro-transform-worker": "0.76.7",
+ "metro-babel-transformer": "0.76.8",
+ "metro-cache": "0.76.8",
+ "metro-cache-key": "0.76.8",
+ "metro-config": "0.76.8",
+ "metro-core": "0.76.8",
+ "metro-file-map": "0.76.8",
+ "metro-inspector-proxy": "0.76.8",
+ "metro-minify-terser": "0.76.8",
+ "metro-minify-uglify": "0.76.8",
+ "metro-react-native-babel-preset": "0.76.8",
+ "metro-resolver": "0.76.8",
+ "metro-runtime": "0.76.8",
+ "metro-source-map": "0.76.8",
+ "metro-symbolicate": "0.76.8",
+ "metro-transform-plugins": "0.76.8",
+ "metro-transform-worker": "0.76.8",
"mime-types": "^2.1.27",
"node-fetch": "^2.2.0",
"nullthrows": "^1.1.1",
@@ -72728,6 +73811,7 @@
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
"integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+ "dev": true,
"requires": {
"color-convert": "^2.0.1"
}
@@ -72736,6 +73820,7 @@
"version": "4.1.2",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
"integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+ "dev": true,
"requires": {
"ansi-styles": "^4.1.0",
"supports-color": "^7.1.0"
@@ -72744,12 +73829,14 @@
"ci-info": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz",
- "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ=="
+ "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==",
+ "dev": true
},
"cliui": {
"version": "8.0.1",
"resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz",
"integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==",
+ "dev": true,
"requires": {
"string-width": "^4.2.0",
"strip-ansi": "^6.0.1",
@@ -72760,6 +73847,7 @@
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+ "dev": true,
"requires": {
"color-name": "~1.1.4"
}
@@ -72767,12 +73855,14 @@
"color-name": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
- "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
+ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+ "dev": true
},
"debug": {
"version": "2.6.9",
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
"integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+ "dev": true,
"requires": {
"ms": "2.0.0"
}
@@ -72780,12 +73870,14 @@
"has-flag": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
- "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ=="
+ "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+ "dev": true
},
"jest-worker": {
"version": "27.5.1",
"resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz",
"integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==",
+ "dev": true,
"requires": {
"@types/node": "*",
"merge-stream": "^2.0.0",
@@ -72796,6 +73888,7 @@
"version": "8.1.1",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz",
"integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==",
+ "dev": true,
"requires": {
"has-flag": "^4.0.0"
}
@@ -72805,22 +73898,26 @@
"ms": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
- "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="
+ "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
+ "dev": true
},
"serialize-error": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/serialize-error/-/serialize-error-2.1.0.tgz",
- "integrity": "sha512-ghgmKt5o4Tly5yEG/UJp8qTd0AN7Xalw4XBtDEKP655B699qMEtra1WlXeE6WIvdEG481JvRxULKsInq/iNysw=="
+ "integrity": "sha512-ghgmKt5o4Tly5yEG/UJp8qTd0AN7Xalw4XBtDEKP655B699qMEtra1WlXeE6WIvdEG481JvRxULKsInq/iNysw==",
+ "dev": true
},
"source-map": {
"version": "0.5.7",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
- "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ=="
+ "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==",
+ "dev": true
},
"supports-color": {
"version": "7.2.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
"integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+ "dev": true,
"requires": {
"has-flag": "^4.0.0"
}
@@ -72829,17 +73926,20 @@
"version": "7.5.9",
"resolved": "https://registry.npmjs.org/ws/-/ws-7.5.9.tgz",
"integrity": "sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q==",
+ "dev": true,
"requires": {}
},
"y18n": {
"version": "5.0.8",
"resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz",
- "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA=="
+ "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==",
+ "dev": true
},
"yargs": {
"version": "17.7.2",
"resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz",
"integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==",
+ "dev": true,
"requires": {
"cliui": "^8.0.1",
"escalade": "^3.1.1",
@@ -72853,14 +73953,16 @@
"yargs-parser": {
"version": "21.1.1",
"resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz",
- "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw=="
+ "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==",
+ "dev": true
}
}
},
"metro-babel-transformer": {
- "version": "0.76.7",
- "resolved": "https://registry.npmjs.org/metro-babel-transformer/-/metro-babel-transformer-0.76.7.tgz",
- "integrity": "sha512-bgr2OFn0J4r0qoZcHrwEvccF7g9k3wdgTOgk6gmGHrtlZ1Jn3oCpklW/DfZ9PzHfjY2mQammKTc19g/EFGyOJw==",
+ "version": "0.76.8",
+ "resolved": "https://registry.npmjs.org/metro-babel-transformer/-/metro-babel-transformer-0.76.8.tgz",
+ "integrity": "sha512-Hh6PW34Ug/nShlBGxkwQJSgPGAzSJ9FwQXhUImkzdsDgVu6zj5bx258J8cJVSandjNoQ8nbaHK6CaHlnbZKbyA==",
+ "dev": true,
"requires": {
"@babel/core": "^7.20.0",
"hermes-parser": "0.12.0",
@@ -72868,37 +73970,41 @@
}
},
"metro-cache": {
- "version": "0.76.7",
- "resolved": "https://registry.npmjs.org/metro-cache/-/metro-cache-0.76.7.tgz",
- "integrity": "sha512-nWBMztrs5RuSxZRI7hgFgob5PhYDmxICh9FF8anm9/ito0u0vpPvRxt7sRu8fyeD2AHdXqE7kX32rWY0LiXgeg==",
+ "version": "0.76.8",
+ "resolved": "https://registry.npmjs.org/metro-cache/-/metro-cache-0.76.8.tgz",
+ "integrity": "sha512-QBJSJIVNH7Hc/Yo6br/U/qQDUpiUdRgZ2ZBJmvAbmAKp2XDzsapnMwK/3BGj8JNWJF7OLrqrYHsRsukSbUBpvQ==",
+ "dev": true,
"requires": {
- "metro-core": "0.76.7",
+ "metro-core": "0.76.8",
"rimraf": "^3.0.2"
}
},
"metro-cache-key": {
- "version": "0.76.7",
- "resolved": "https://registry.npmjs.org/metro-cache-key/-/metro-cache-key-0.76.7.tgz",
- "integrity": "sha512-0pecoIzwsD/Whn/Qfa+SDMX2YyasV0ndbcgUFx7w1Ct2sLHClujdhQ4ik6mvQmsaOcnGkIyN0zcceMDjC2+BFQ=="
+ "version": "0.76.8",
+ "resolved": "https://registry.npmjs.org/metro-cache-key/-/metro-cache-key-0.76.8.tgz",
+ "integrity": "sha512-buKQ5xentPig9G6T37Ww/R/bC+/V1MA5xU/D8zjnhlelsrPG6w6LtHUS61ID3zZcMZqYaELWk5UIadIdDsaaLw==",
+ "dev": true
},
"metro-config": {
- "version": "0.76.7",
- "resolved": "https://registry.npmjs.org/metro-config/-/metro-config-0.76.7.tgz",
- "integrity": "sha512-CFDyNb9bqxZemiChC/gNdXZ7OQkIwmXzkrEXivcXGbgzlt/b2juCv555GWJHyZSlorwnwJfY3uzAFu4A9iRVfg==",
+ "version": "0.76.8",
+ "resolved": "https://registry.npmjs.org/metro-config/-/metro-config-0.76.8.tgz",
+ "integrity": "sha512-SL1lfKB0qGHALcAk2zBqVgQZpazDYvYFGwCK1ikz0S6Y/CM2i2/HwuZN31kpX6z3mqjv/6KvlzaKoTb1otuSAA==",
+ "dev": true,
"requires": {
"connect": "^3.6.5",
"cosmiconfig": "^5.0.5",
"jest-validate": "^29.2.1",
- "metro": "0.76.7",
- "metro-cache": "0.76.7",
- "metro-core": "0.76.7",
- "metro-runtime": "0.76.7"
+ "metro": "0.76.8",
+ "metro-cache": "0.76.8",
+ "metro-core": "0.76.8",
+ "metro-runtime": "0.76.8"
},
"dependencies": {
"cosmiconfig": {
"version": "5.2.1",
"resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-5.2.1.tgz",
"integrity": "sha512-H65gsXo1SKjf8zmrJ67eJk8aIRKV5ff2D4uKZIBZShbhGSpEmsQOPW/SKMKYhSTrqR7ufy6RP69rPogdaPh/kA==",
+ "dev": true,
"requires": {
"import-fresh": "^2.0.0",
"is-directory": "^0.3.1",
@@ -72910,6 +74016,7 @@
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-2.0.0.tgz",
"integrity": "sha512-eZ5H8rcgYazHbKC3PG4ClHNykCSxtAhxSSEM+2mb+7evD2CKF5V7c0dNum7AdpDh0ZdICwZY9sRSn8f+KH96sg==",
+ "dev": true,
"requires": {
"caller-path": "^2.0.0",
"resolve-from": "^3.0.0"
@@ -72919,6 +74026,7 @@
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz",
"integrity": "sha512-aOIos8bujGN93/8Ox/jPLh7RwVnPEysynVFE+fQZyg6jKELEHwzgKdLRFHUgXJL6kylijVSBC4BvN9OmsB48Rw==",
+ "dev": true,
"requires": {
"error-ex": "^1.3.1",
"json-parse-better-errors": "^1.0.1"
@@ -72927,23 +74035,26 @@
"resolve-from": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-3.0.0.tgz",
- "integrity": "sha512-GnlH6vxLymXJNMBo7XP1fJIzBFbdYt49CuTwmB/6N53t+kMPRMFKz783LlQ4tv28XoQfMWinAJX6WCGf2IlaIw=="
+ "integrity": "sha512-GnlH6vxLymXJNMBo7XP1fJIzBFbdYt49CuTwmB/6N53t+kMPRMFKz783LlQ4tv28XoQfMWinAJX6WCGf2IlaIw==",
+ "dev": true
}
}
},
"metro-core": {
- "version": "0.76.7",
- "resolved": "https://registry.npmjs.org/metro-core/-/metro-core-0.76.7.tgz",
- "integrity": "sha512-0b8KfrwPmwCMW+1V7ZQPkTy2tsEKZjYG9Pu1PTsu463Z9fxX7WaR0fcHFshv+J1CnQSUTwIGGjbNvj1teKe+pw==",
+ "version": "0.76.8",
+ "resolved": "https://registry.npmjs.org/metro-core/-/metro-core-0.76.8.tgz",
+ "integrity": "sha512-sl2QLFI3d1b1XUUGxwzw/KbaXXU/bvFYrSKz6Sg19AdYGWFyzsgZ1VISRIDf+HWm4R/TJXluhWMEkEtZuqi3qA==",
+ "dev": true,
"requires": {
"lodash.throttle": "^4.1.1",
- "metro-resolver": "0.76.7"
+ "metro-resolver": "0.76.8"
}
},
"metro-file-map": {
- "version": "0.76.7",
- "resolved": "https://registry.npmjs.org/metro-file-map/-/metro-file-map-0.76.7.tgz",
- "integrity": "sha512-s+zEkTcJ4mOJTgEE2ht4jIo1DZfeWreQR3tpT3gDV/Y/0UQ8aJBTv62dE775z0GLsWZApiblAYZsj7ZE8P06nw==",
+ "version": "0.76.8",
+ "resolved": "https://registry.npmjs.org/metro-file-map/-/metro-file-map-0.76.8.tgz",
+ "integrity": "sha512-A/xP1YNEVwO1SUV9/YYo6/Y1MmzhL4ZnVgcJC3VmHp/BYVOXVStzgVbWv2wILe56IIMkfXU+jpXrGKKYhFyHVw==",
+ "dev": true,
"requires": {
"anymatch": "^3.0.3",
"debug": "^2.2.0",
@@ -72964,6 +74075,7 @@
"version": "27.5.1",
"resolved": "https://registry.npmjs.org/@jest/types/-/types-27.5.1.tgz",
"integrity": "sha512-Cx46iJ9QpwQTjIdq5VJu2QTMMs3QlEjI0x1QbBP5W1+nMzyc2XmimiRR/CbX9TO0cPTeUlxWMOu8mslYsJ8DEw==",
+ "dev": true,
"requires": {
"@types/istanbul-lib-coverage": "^2.0.0",
"@types/istanbul-reports": "^3.0.0",
@@ -72976,6 +74088,7 @@
"version": "16.0.5",
"resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.5.tgz",
"integrity": "sha512-AxO/ADJOBFJScHbWhq2xAhlWP24rY4aCEG/NFaMvbT3X2MgRsLjhjQwsn0Zi5zn0LG9jUhCCZMeX9Dkuw6k+vQ==",
+ "dev": true,
"requires": {
"@types/yargs-parser": "*"
}
@@ -72984,6 +74097,7 @@
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
"integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+ "dev": true,
"requires": {
"color-convert": "^2.0.1"
}
@@ -72992,6 +74106,7 @@
"version": "4.1.2",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
"integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+ "dev": true,
"requires": {
"ansi-styles": "^4.1.0",
"supports-color": "^7.1.0"
@@ -73001,6 +74116,7 @@
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+ "dev": true,
"requires": {
"color-name": "~1.1.4"
}
@@ -73008,12 +74124,14 @@
"color-name": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
- "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
+ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+ "dev": true
},
"debug": {
"version": "2.6.9",
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
"integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+ "dev": true,
"requires": {
"ms": "2.0.0"
}
@@ -73021,17 +74139,20 @@
"has-flag": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
- "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ=="
+ "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+ "dev": true
},
"jest-regex-util": {
"version": "27.5.1",
"resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-27.5.1.tgz",
- "integrity": "sha512-4bfKq2zie+x16okqDXjXn9ql2B0dScQu+vcwe4TvFVhkVyuWLqpZrZtXxLLWoXYgn0E87I6r6GRYHF7wFZBUvg=="
+ "integrity": "sha512-4bfKq2zie+x16okqDXjXn9ql2B0dScQu+vcwe4TvFVhkVyuWLqpZrZtXxLLWoXYgn0E87I6r6GRYHF7wFZBUvg==",
+ "dev": true
},
"jest-util": {
"version": "27.5.1",
"resolved": "https://registry.npmjs.org/jest-util/-/jest-util-27.5.1.tgz",
"integrity": "sha512-Kv2o/8jNvX1MQ0KGtw480E/w4fBCDOnH6+6DmeKi6LZUIlKA5kwY0YNdlzaWTiVgxqAqik11QyxDOKk543aKXw==",
+ "dev": true,
"requires": {
"@jest/types": "^27.5.1",
"@types/node": "*",
@@ -73045,6 +74166,7 @@
"version": "27.5.1",
"resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz",
"integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==",
+ "dev": true,
"requires": {
"@types/node": "*",
"merge-stream": "^2.0.0",
@@ -73055,6 +74177,7 @@
"version": "8.1.1",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz",
"integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==",
+ "dev": true,
"requires": {
"has-flag": "^4.0.0"
}
@@ -73064,12 +74187,14 @@
"ms": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
- "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="
+ "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
+ "dev": true
},
"supports-color": {
"version": "7.2.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
"integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+ "dev": true,
"requires": {
"has-flag": "^4.0.0"
}
@@ -73077,9 +74202,10 @@
}
},
"metro-inspector-proxy": {
- "version": "0.76.7",
- "resolved": "https://registry.npmjs.org/metro-inspector-proxy/-/metro-inspector-proxy-0.76.7.tgz",
- "integrity": "sha512-rNZ/6edTl/1qUekAhAbaFjczMphM50/UjtxiKulo6vqvgn/Mjd9hVqDvVYfAMZXqPvlusD88n38UjVYPkruLSg==",
+ "version": "0.76.8",
+ "resolved": "https://registry.npmjs.org/metro-inspector-proxy/-/metro-inspector-proxy-0.76.8.tgz",
+ "integrity": "sha512-Us5o5UEd4Smgn1+TfHX4LvVPoWVo9VsVMn4Ldbk0g5CQx3Gu0ygc/ei2AKPGTwsOZmKxJeACj7yMH2kgxQP/iw==",
+ "dev": true,
"requires": {
"connect": "^3.6.5",
"debug": "^2.2.0",
@@ -73092,6 +74218,7 @@
"version": "8.0.1",
"resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz",
"integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==",
+ "dev": true,
"requires": {
"string-width": "^4.2.0",
"strip-ansi": "^6.0.1",
@@ -73102,6 +74229,7 @@
"version": "2.6.9",
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
"integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+ "dev": true,
"requires": {
"ms": "2.0.0"
}
@@ -73109,23 +74237,27 @@
"ms": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
- "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="
+ "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
+ "dev": true
},
"ws": {
"version": "7.5.9",
"resolved": "https://registry.npmjs.org/ws/-/ws-7.5.9.tgz",
"integrity": "sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q==",
+ "dev": true,
"requires": {}
},
"y18n": {
"version": "5.0.8",
"resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz",
- "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA=="
+ "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==",
+ "dev": true
},
"yargs": {
"version": "17.7.2",
"resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz",
"integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==",
+ "dev": true,
"requires": {
"cliui": "^8.0.1",
"escalade": "^3.1.1",
@@ -73139,30 +74271,34 @@
"yargs-parser": {
"version": "21.1.1",
"resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz",
- "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw=="
+ "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==",
+ "dev": true
}
}
},
"metro-minify-terser": {
- "version": "0.76.7",
- "resolved": "https://registry.npmjs.org/metro-minify-terser/-/metro-minify-terser-0.76.7.tgz",
- "integrity": "sha512-FQiZGhIxCzhDwK4LxyPMLlq0Tsmla10X7BfNGlYFK0A5IsaVKNJbETyTzhpIwc+YFRT4GkFFwgo0V2N5vxO5HA==",
+ "version": "0.76.8",
+ "resolved": "https://registry.npmjs.org/metro-minify-terser/-/metro-minify-terser-0.76.8.tgz",
+ "integrity": "sha512-Orbvg18qXHCrSj1KbaeSDVYRy/gkro2PC7Fy2tDSH1c9RB4aH8tuMOIXnKJE+1SXxBtjWmQ5Yirwkth2DyyEZA==",
+ "dev": true,
"requires": {
"terser": "^5.15.0"
}
},
"metro-minify-uglify": {
- "version": "0.76.7",
- "resolved": "https://registry.npmjs.org/metro-minify-uglify/-/metro-minify-uglify-0.76.7.tgz",
- "integrity": "sha512-FuXIU3j2uNcSvQtPrAJjYWHruPiQ+EpE++J9Z+VznQKEHcIxMMoQZAfIF2IpZSrZYfLOjVFyGMvj41jQMxV1Vw==",
+ "version": "0.76.8",
+ "resolved": "https://registry.npmjs.org/metro-minify-uglify/-/metro-minify-uglify-0.76.8.tgz",
+ "integrity": "sha512-6l8/bEvtVaTSuhG1FqS0+Mc8lZ3Bl4RI8SeRIifVLC21eeSDp4CEBUWSGjpFyUDfi6R5dXzYaFnSgMNyfxADiQ==",
+ "dev": true,
"requires": {
"uglify-es": "^3.1.9"
}
},
"metro-react-native-babel-preset": {
- "version": "0.76.7",
- "resolved": "https://registry.npmjs.org/metro-react-native-babel-preset/-/metro-react-native-babel-preset-0.76.7.tgz",
- "integrity": "sha512-R25wq+VOSorAK3hc07NW0SmN8z9S/IR0Us0oGAsBcMZnsgkbOxu77Mduqf+f4is/wnWHc5+9bfiqdLnaMngiVw==",
+ "version": "0.76.8",
+ "resolved": "https://registry.npmjs.org/metro-react-native-babel-preset/-/metro-react-native-babel-preset-0.76.8.tgz",
+ "integrity": "sha512-Ptza08GgqzxEdK8apYsjTx2S8WDUlS2ilBlu9DR1CUcHmg4g3kOkFylZroogVAUKtpYQNYwAvdsjmrSdDNtiAg==",
+ "dev": true,
"requires": {
"@babel/core": "^7.20.0",
"@babel/plugin-proposal-async-generator-functions": "^7.0.0",
@@ -73206,31 +74342,34 @@
},
"dependencies": {
"react-refresh": {
- "version": "0.4.3"
+ "version": "0.4.3",
+ "dev": true
}
}
},
"metro-react-native-babel-transformer": {
- "version": "0.76.7",
- "resolved": "https://registry.npmjs.org/metro-react-native-babel-transformer/-/metro-react-native-babel-transformer-0.76.7.tgz",
- "integrity": "sha512-W6lW3J7y/05ph3c2p3KKJNhH0IdyxdOCbQ5it7aM2MAl0SM4wgKjaV6EYv9b3rHklpV6K3qMH37UKVcjMooWiA==",
+ "version": "0.76.8",
+ "resolved": "https://registry.npmjs.org/metro-react-native-babel-transformer/-/metro-react-native-babel-transformer-0.76.8.tgz",
+ "integrity": "sha512-3h+LfS1WG1PAzhq8QF0kfXjxuXetbY/lgz8vYMQhgrMMp17WM1DNJD0gjx8tOGYbpbBC1qesJ45KMS4o5TA73A==",
+ "dev": true,
"requires": {
"@babel/core": "^7.20.0",
"babel-preset-fbjs": "^3.4.0",
"hermes-parser": "0.12.0",
- "metro-react-native-babel-preset": "0.76.7",
+ "metro-react-native-babel-preset": "0.76.8",
"nullthrows": "^1.1.1"
}
},
"metro-resolver": {
- "version": "0.76.7",
- "resolved": "https://registry.npmjs.org/metro-resolver/-/metro-resolver-0.76.7.tgz",
- "integrity": "sha512-pC0Wgq29HHIHrwz23xxiNgylhI8Rq1V01kQaJ9Kz11zWrIdlrH0ZdnJ7GC6qA0ErROG+cXmJ0rJb8/SW1Zp2IA=="
+ "version": "0.76.8",
+ "resolved": "https://registry.npmjs.org/metro-resolver/-/metro-resolver-0.76.8.tgz",
+ "integrity": "sha512-KccOqc10vrzS7ZhG2NSnL2dh3uVydarB7nOhjreQ7C4zyWuiW9XpLC4h47KtGQv3Rnv/NDLJYeDqaJ4/+140HQ==",
+ "dev": true
},
"metro-runtime": {
- "version": "0.76.7",
- "resolved": "https://registry.npmjs.org/metro-runtime/-/metro-runtime-0.76.7.tgz",
- "integrity": "sha512-MuWHubQHymUWBpZLwuKZQgA/qbb35WnDAKPo83rk7JRLIFPvzXSvFaC18voPuzJBt1V98lKQIonh6MiC9gd8Ug==",
+ "version": "0.76.8",
+ "resolved": "https://registry.npmjs.org/metro-runtime/-/metro-runtime-0.76.8.tgz",
+ "integrity": "sha512-XKahvB+iuYJSCr3QqCpROli4B4zASAYpkK+j3a0CJmokxCDNbgyI4Fp88uIL6rNaZfN0Mv35S0b99SdFXIfHjg==",
"requires": {
"@babel/runtime": "^7.0.0",
"react-refresh": "^0.4.0"
@@ -73244,16 +74383,16 @@
}
},
"metro-source-map": {
- "version": "0.76.7",
- "resolved": "https://registry.npmjs.org/metro-source-map/-/metro-source-map-0.76.7.tgz",
- "integrity": "sha512-Prhx7PeRV1LuogT0Kn5VjCuFu9fVD68eefntdWabrksmNY6mXK8pRqzvNJOhTojh6nek+RxBzZeD6MIOOyXS6w==",
+ "version": "0.76.8",
+ "resolved": "https://registry.npmjs.org/metro-source-map/-/metro-source-map-0.76.8.tgz",
+ "integrity": "sha512-Hh0ncPsHPVf6wXQSqJqB3K9Zbudht4aUtNpNXYXSxH+pteWqGAXnjtPsRAnCsCWl38wL0jYF0rJDdMajUI3BDw==",
"requires": {
"@babel/traverse": "^7.20.0",
"@babel/types": "^7.20.0",
"invariant": "^2.2.4",
- "metro-symbolicate": "0.76.7",
+ "metro-symbolicate": "0.76.8",
"nullthrows": "^1.1.1",
- "ob1": "0.76.7",
+ "ob1": "0.76.8",
"source-map": "^0.5.6",
"vlq": "^1.0.0"
},
@@ -73266,12 +74405,12 @@
}
},
"metro-symbolicate": {
- "version": "0.76.7",
- "resolved": "https://registry.npmjs.org/metro-symbolicate/-/metro-symbolicate-0.76.7.tgz",
- "integrity": "sha512-p0zWEME5qLSL1bJb93iq+zt5fz3sfVn9xFYzca1TJIpY5MommEaS64Va87lp56O0sfEIvh4307Oaf/ZzRjuLiQ==",
+ "version": "0.76.8",
+ "resolved": "https://registry.npmjs.org/metro-symbolicate/-/metro-symbolicate-0.76.8.tgz",
+ "integrity": "sha512-LrRL3uy2VkzrIXVlxoPtqb40J6Bf1mlPNmUQewipc3qfKKFgtPHBackqDy1YL0njDsWopCKcfGtFYLn0PTUn3w==",
"requires": {
"invariant": "^2.2.4",
- "metro-source-map": "0.76.7",
+ "metro-source-map": "0.76.8",
"nullthrows": "^1.1.1",
"source-map": "^0.5.6",
"through2": "^2.0.1",
@@ -73286,9 +74425,10 @@
}
},
"metro-transform-plugins": {
- "version": "0.76.7",
- "resolved": "https://registry.npmjs.org/metro-transform-plugins/-/metro-transform-plugins-0.76.7.tgz",
- "integrity": "sha512-iSmnjVApbdivjuzb88Orb0JHvcEt5veVyFAzxiS5h0QB+zV79w6JCSqZlHCrbNOkOKBED//LqtKbFVakxllnNg==",
+ "version": "0.76.8",
+ "resolved": "https://registry.npmjs.org/metro-transform-plugins/-/metro-transform-plugins-0.76.8.tgz",
+ "integrity": "sha512-PlkGTQNqS51Bx4vuufSQCdSn2R2rt7korzngo+b5GCkeX5pjinPjnO2kNhQ8l+5bO0iUD/WZ9nsM2PGGKIkWFA==",
+ "dev": true,
"requires": {
"@babel/core": "^7.20.0",
"@babel/generator": "^7.20.0",
@@ -73298,21 +74438,22 @@
}
},
"metro-transform-worker": {
- "version": "0.76.7",
- "resolved": "https://registry.npmjs.org/metro-transform-worker/-/metro-transform-worker-0.76.7.tgz",
- "integrity": "sha512-cGvELqFMVk9XTC15CMVzrCzcO6sO1lURfcbgjuuPdzaWuD11eEyocvkTX0DPiRjsvgAmicz4XYxVzgYl3MykDw==",
+ "version": "0.76.8",
+ "resolved": "https://registry.npmjs.org/metro-transform-worker/-/metro-transform-worker-0.76.8.tgz",
+ "integrity": "sha512-mE1fxVAnJKmwwJyDtThildxxos9+DGs9+vTrx2ktSFMEVTtXS/bIv2W6hux1pqivqAfyJpTeACXHk5u2DgGvIQ==",
+ "dev": true,
"requires": {
"@babel/core": "^7.20.0",
"@babel/generator": "^7.20.0",
"@babel/parser": "^7.20.0",
"@babel/types": "^7.20.0",
"babel-preset-fbjs": "^3.4.0",
- "metro": "0.76.7",
- "metro-babel-transformer": "0.76.7",
- "metro-cache": "0.76.7",
- "metro-cache-key": "0.76.7",
- "metro-source-map": "0.76.7",
- "metro-transform-plugins": "0.76.7",
+ "metro": "0.76.8",
+ "metro-babel-transformer": "0.76.8",
+ "metro-cache": "0.76.8",
+ "metro-cache-key": "0.76.8",
+ "metro-source-map": "0.76.8",
+ "metro-transform-plugins": "0.76.8",
"nullthrows": "^1.1.1"
}
},
@@ -74354,9 +75495,9 @@
"dev": true
},
"ob1": {
- "version": "0.76.7",
- "resolved": "https://registry.npmjs.org/ob1/-/ob1-0.76.7.tgz",
- "integrity": "sha512-BQdRtxxoUNfSoZxqeBGOyuT9nEYSn18xZHwGMb0mMVpn2NBcYbnyKY4BK2LIHRgw33CBGlUmE+KMaNvyTpLLtQ=="
+ "version": "0.76.8",
+ "resolved": "https://registry.npmjs.org/ob1/-/ob1-0.76.8.tgz",
+ "integrity": "sha512-dlBkJJV5M/msj9KYA9upc+nUWVwuOFFTbu28X6kZeGwcuW+JxaHSBZ70SYQnk5M+j5JbNLR6yKHmgW4M5E7X5g=="
},
"object-assign": {
"version": "4.1.1"
@@ -75785,6 +76926,14 @@
"scheduler": "^0.22.0"
}
},
+ "react-error-boundary": {
+ "version": "4.0.11",
+ "resolved": "https://registry.npmjs.org/react-error-boundary/-/react-error-boundary-4.0.11.tgz",
+ "integrity": "sha512-U13ul67aP5DOSPNSCWQ/eO0AQEYzEFkVljULQIjMV0KlffTAhxuDoBKdO0pb/JZ8mDhMKFZ9NZi0BmLGUiNphw==",
+ "requires": {
+ "@babel/runtime": "^7.12.5"
+ }
+ },
"react-freeze": {
"version": "1.0.3",
"requires": {}
@@ -75809,20 +76958,20 @@
}
},
"react-native": {
- "version": "0.72.3",
- "resolved": "https://registry.npmjs.org/react-native/-/react-native-0.72.3.tgz",
- "integrity": "sha512-QqISi+JVmCssNP2FlQ4MWhlc4O/I00MRE1/GClvyZ8h/6kdsyk/sOirkYdZqX3+DrJfI3q+OnyMnsyaXIQ/5tQ==",
+ "version": "0.72.4",
+ "resolved": "https://registry.npmjs.org/react-native/-/react-native-0.72.4.tgz",
+ "integrity": "sha512-+vrObi0wZR+NeqL09KihAAdVlQ9IdplwznJWtYrjnQ4UbCW6rkzZJebRsugwUneSOKNFaHFEo1uKU89HsgtYBg==",
"requires": {
"@jest/create-cache-key-function": "^29.2.1",
- "@react-native-community/cli": "11.3.5",
- "@react-native-community/cli-platform-android": "11.3.5",
- "@react-native-community/cli-platform-ios": "11.3.5",
+ "@react-native-community/cli": "11.3.6",
+ "@react-native-community/cli-platform-android": "11.3.6",
+ "@react-native-community/cli-platform-ios": "11.3.6",
"@react-native/assets-registry": "^0.72.0",
"@react-native/codegen": "^0.72.6",
"@react-native/gradle-plugin": "^0.72.11",
"@react-native/js-polyfills": "^0.72.1",
"@react-native/normalize-colors": "^0.72.0",
- "@react-native/virtualized-lists": "^0.72.6",
+ "@react-native/virtualized-lists": "^0.72.8",
"abort-controller": "^3.0.0",
"anser": "^1.4.9",
"base64-js": "^1.1.2",
@@ -75833,8 +76982,8 @@
"jest-environment-node": "^29.2.1",
"jsc-android": "^250231.0.0",
"memoize-one": "^5.0.0",
- "metro-runtime": "0.76.7",
- "metro-source-map": "0.76.7",
+ "metro-runtime": "0.76.8",
+ "metro-source-map": "0.76.8",
"mkdirp": "^0.5.1",
"nullthrows": "^1.1.1",
"pretty-format": "^26.5.2",
@@ -76060,9 +77209,9 @@
}
},
"react-native-google-places-autocomplete": {
- "version": "git+ssh://git@github.com/Expensify/react-native-google-places-autocomplete.git#ee87343c3e827ff7818abc71b6bb04fcc1f120e0",
- "integrity": "sha512-OJWCz4Epj1p8tyNImWNykAqpd/X1MkNCFPY0dSbgiTJGbW4J5T4bC0PIUQ+ExjxWpWjcFaielTLdoSz0HfeIpw==",
- "from": "react-native-google-places-autocomplete@git+https://github.com/Expensify/react-native-google-places-autocomplete.git#ee87343c3e827ff7818abc71b6bb04fcc1f120e0",
+ "version": "git+ssh://git@github.com/Expensify/react-native-google-places-autocomplete.git#cef3ac29d9501091453136e1219e24c4ec9f9d76",
+ "integrity": "sha512-2z3ED8jOXasPTzBqvPwpG10LQsBArTRsYszmoz+TfqbgZrSBmP3c8rhaC//lx6Pvfs2r+KYWqJUrLf4mbCrjZw==",
+ "from": "react-native-google-places-autocomplete@git+https://github.com/Expensify/react-native-google-places-autocomplete.git#cef3ac29d9501091453136e1219e24c4ec9f9d76",
"requires": {
"lodash.debounce": "^4.0.8",
"prop-types": "^15.7.2",
@@ -76116,9 +77265,9 @@
}
},
"react-native-onyx": {
- "version": "1.0.76",
- "resolved": "https://registry.npmjs.org/react-native-onyx/-/react-native-onyx-1.0.76.tgz",
- "integrity": "sha512-mcMlYQCo1B/kom+4hu7CQKKLwvPFjQAJsVIzV2s9aa8XKNlcnYiJbfuM6RSJ1fFmSIeud4Y66rhv4/oWUkSl5A==",
+ "version": "1.0.84",
+ "resolved": "https://registry.npmjs.org/react-native-onyx/-/react-native-onyx-1.0.84.tgz",
+ "integrity": "sha512-qQ+o+qS5ucZLbKbG5kI0UsC42N4h1Pprg/1D7PqjDeVanS3iUv33rT4fbrHuar77g0DSTA1/M8bC2WmYrShS9A==",
"requires": {
"ascii-table": "0.0.9",
"fast-equals": "^4.0.3",
@@ -76141,7 +77290,9 @@
}
},
"react-native-performance": {
- "version": "4.0.0",
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/react-native-performance/-/react-native-performance-5.1.0.tgz",
+ "integrity": "sha512-rq/YBf0/GptSOM/Lj64/1yRq8uN2YE0psFB16wFbYBbTcIEp/0rrgN2HyS5lhvfBOFgKoDRWQ53jHSCb+QJ5eA==",
"requires": {}
},
"react-native-performance-flipper-reporter": {
@@ -76238,7 +77389,9 @@
}
},
"react-native-svg": {
- "version": "13.9.0",
+ "version": "13.13.0",
+ "resolved": "https://registry.npmjs.org/react-native-svg/-/react-native-svg-13.13.0.tgz",
+ "integrity": "sha512-L8y8uEiMG0Tr++Nb2+24wlMuv18+bmq/CMoFFtTUlEqVvGCoK2ea8WamPl/9bV8gjL+Rngg5NqEBvKS23sbYoA==",
"requires": {
"css-select": "^5.1.0",
"css-tree": "^1.1.3"
@@ -78848,11 +80001,6 @@
"version": "2.0.15",
"dev": true
},
- "tabbable": {
- "version": "6.2.0",
- "resolved": "https://registry.npmjs.org/tabbable/-/tabbable-6.2.0.tgz",
- "integrity": "sha512-Cat63mxsVJlzYvN51JmVXIgNoUokrIaT2zLclCXjRd8boZ0004U4KCs/sToJ75C6sdlByWxpYnb5Boif1VSFew=="
- },
"table": {
"version": "6.8.1",
"resolved": "https://registry.npmjs.org/table/-/table-6.8.1.tgz",
@@ -79079,7 +80227,9 @@
"dev": true
},
"throat": {
- "version": "5.0.0"
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/throat/-/throat-5.0.0.tgz",
+ "integrity": "sha512-fcwX4mndzpLQKBS1DVYhGAcYaYt7vsHNIvQV+WXMvnow5cgjPphq5CaayLaGsjRdSCKZFNGt7/GYAuXaNOiYCA=="
},
"throttle-debounce": {
"version": "3.0.1",
@@ -79983,7 +81133,9 @@
"version": "1.2.8"
},
"vlq": {
- "version": "1.0.1"
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/vlq/-/vlq-1.0.1.tgz",
+ "integrity": "sha512-gQpnTgkubC6hQgdIcRdYGDSDc+SaujOdyesZQMv6JlfQee/9Mp0Qhnys6WxDWvQnL5WZdT7o2Ul187aSt0Rq+w=="
},
"vm-browserify": {
"version": "1.1.2"
diff --git a/package.json b/package.json
index 221c3fa7cbf2..a181de2bce16 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "new.expensify",
- "version": "1.3.70-5",
+ "version": "1.3.72-9",
"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.",
@@ -94,7 +94,6 @@
"domhandler": "^4.3.0",
"expensify-common": "git+ssh://git@github.com/Expensify/expensify-common.git#35bff866a8d345b460ea6256f0a0f0a8a7f81086",
"fbjs": "^3.0.2",
- "focus-trap-react": "^10.2.1",
"htmlparser2": "^7.2.0",
"idb-keyval": "^6.2.1",
"jest-when": "^3.5.2",
@@ -113,7 +112,8 @@
"react-content-loader": "^6.1.0",
"react-dom": "18.1.0",
"react-map-gl": "^7.1.3",
- "react-native": "0.72.3",
+ "react-error-boundary": "^4.0.11",
+ "react-native": "0.72.4",
"react-native-blob-util": "^0.17.3",
"react-native-collapsible": "^1.6.0",
"react-native-config": "^1.4.5",
@@ -123,7 +123,7 @@
"react-native-fast-image": "^8.6.3",
"react-native-fs": "^2.20.0",
"react-native-gesture-handler": "2.12.0",
- "react-native-google-places-autocomplete": "git+https://github.com/Expensify/react-native-google-places-autocomplete.git#ee87343c3e827ff7818abc71b6bb04fcc1f120e0",
+ "react-native-google-places-autocomplete": "git+https://github.com/Expensify/react-native-google-places-autocomplete.git#cef3ac29d9501091453136e1219e24c4ec9f9d76",
"react-native-haptic-feedback": "^1.13.0",
"react-native-image-pan-zoom": "^2.1.12",
"react-native-image-picker": "^5.1.0",
@@ -132,10 +132,10 @@
"react-native-linear-gradient": "^2.8.1",
"react-native-localize": "^2.2.6",
"react-native-modal": "^13.0.0",
- "react-native-onyx": "1.0.76",
+ "react-native-onyx": "1.0.84",
"react-native-pager-view": "^6.2.0",
"react-native-pdf": "^6.7.1",
- "react-native-performance": "^4.0.0",
+ "react-native-performance": "^5.1.0",
"react-native-permissions": "^3.0.1",
"react-native-picker-select": "git+https://github.com/Expensify/react-native-picker-select.git#eae05855286dc699954415cc1d629bfd8e8e47e2",
"react-native-plaid-link-sdk": "^10.0.0",
@@ -145,7 +145,7 @@
"react-native-render-html": "6.3.1",
"react-native-safe-area-context": "4.4.1",
"react-native-screens": "3.21.0",
- "react-native-svg": "^13.9.0",
+ "react-native-svg": "^13.13.0",
"react-native-tab-view": "^3.5.2",
"react-native-url-polyfill": "^2.0.0",
"react-native-view-shot": "^3.6.0",
@@ -181,7 +181,7 @@
"@octokit/plugin-paginate-rest": "3.1.0",
"@octokit/plugin-throttling": "4.1.0",
"@react-native-community/eslint-config": "3.0.0",
- "@react-native/metro-config": "^0.72.9",
+ "@react-native/metro-config": "^0.72.11",
"@react-navigation/devtools": "^6.0.10",
"@storybook/addon-a11y": "^6.5.9",
"@storybook/addon-essentials": "^7.0.0",
@@ -232,7 +232,7 @@
"electron-builder": "24.6.4",
"eslint": "^7.6.0",
"eslint-config-airbnb-typescript": "^17.1.0",
- "eslint-config-expensify": "^2.0.38",
+ "eslint-config-expensify": "^2.0.39",
"eslint-config-prettier": "^8.8.0",
"eslint-plugin-jest": "^24.1.0",
"eslint-plugin-jsdoc": "^46.2.6",
@@ -246,7 +246,7 @@
"jest-circus": "29.4.1",
"jest-cli": "29.4.1",
"jest-environment-jsdom": "^29.4.1",
- "metro-react-native-babel-preset": "0.76.7",
+ "metro-react-native-babel-preset": "0.76.8",
"mock-fs": "^4.13.0",
"onchange": "^7.1.0",
"portfinder": "^1.0.28",
diff --git a/patches/react-native+0.72.3+001+initial.patch b/patches/react-native+0.72.4+001+initial.patch
similarity index 100%
rename from patches/react-native+0.72.3+001+initial.patch
rename to patches/react-native+0.72.4+001+initial.patch
diff --git a/patches/react-native+0.72.3+002+NumberOfLines.patch b/patches/react-native+0.72.4+002+NumberOfLines.patch
similarity index 97%
rename from patches/react-native+0.72.3+002+NumberOfLines.patch
rename to patches/react-native+0.72.4+002+NumberOfLines.patch
index 7d773297cb5f..75422f84708e 100644
--- a/patches/react-native+0.72.3+002+NumberOfLines.patch
+++ b/patches/react-native+0.72.4+002+NumberOfLines.patch
@@ -30,10 +30,10 @@ index 6f69329..d531bee 100644
maxLength: true,
autoCapitalize: true,
diff --git a/node_modules/react-native/Libraries/Components/TextInput/TextInput.d.ts b/node_modules/react-native/Libraries/Components/TextInput/TextInput.d.ts
-index ff029fb..0835135 100644
+index 8badb2a..b19f197 100644
--- a/node_modules/react-native/Libraries/Components/TextInput/TextInput.d.ts
+++ b/node_modules/react-native/Libraries/Components/TextInput/TextInput.d.ts
-@@ -338,12 +338,6 @@ export interface TextInputAndroidProps {
+@@ -347,12 +347,6 @@ export interface TextInputAndroidProps {
*/
inlineImagePadding?: number | undefined;
@@ -46,7 +46,7 @@ index ff029fb..0835135 100644
/**
* Sets the return key to the label. Use it instead of `returnKeyType`.
* @platform android
-@@ -654,11 +648,29 @@ export interface TextInputProps
+@@ -663,11 +657,30 @@ export interface TextInputProps
*/
maxLength?: number | undefined;
@@ -72,6 +72,7 @@ index ff029fb..0835135 100644
+ * Use it with multiline set to true to be able to fill the lines.
+ */
+ rows?: number | undefined;
++
+
/**
* Callback that is called when the text input is blurred
@@ -147,10 +148,10 @@ index 7ed4579..b1d994e 100644
* If `true`, the text input obscures the text entered so that sensitive text
* like passwords stay secure. The default value is `false`. Does not work with 'multiline={true}'.
diff --git a/node_modules/react-native/Libraries/Components/TextInput/TextInput.js b/node_modules/react-native/Libraries/Components/TextInput/TextInput.js
-index df89097..3b223ec 100644
+index 2127191..542fc06 100644
--- a/node_modules/react-native/Libraries/Components/TextInput/TextInput.js
+++ b/node_modules/react-native/Libraries/Components/TextInput/TextInput.js
-@@ -387,7 +387,6 @@ type AndroidProps = $ReadOnly<{|
+@@ -390,7 +390,6 @@ type AndroidProps = $ReadOnly<{|
/**
* Sets the number of lines for a `TextInput`. Use it with multiline set to
* `true` to be able to fill the lines.
@@ -158,7 +159,7 @@ index df89097..3b223ec 100644
*/
numberOfLines?: ?number,
-@@ -400,10 +399,14 @@ type AndroidProps = $ReadOnly<{|
+@@ -403,10 +402,14 @@ type AndroidProps = $ReadOnly<{|
/**
* Sets the number of rows for a `TextInput`. Use it with multiline set to
* `true` to be able to fill the lines.
@@ -174,7 +175,7 @@ index df89097..3b223ec 100644
/**
* When `false`, it will prevent the soft keyboard from showing when the field is focused.
* Defaults to `true`.
-@@ -1066,6 +1069,9 @@ function InternalTextInput(props: Props): React.Node {
+@@ -1069,6 +1072,9 @@ function InternalTextInput(props: Props): React.Node {
accessibilityState,
id,
tabIndex,
@@ -184,7 +185,7 @@ index df89097..3b223ec 100644
selection: propsSelection,
...otherProps
} = props;
-@@ -1422,6 +1428,8 @@ function InternalTextInput(props: Props): React.Node {
+@@ -1427,6 +1433,8 @@ function InternalTextInput(props: Props): React.Node {
focusable={tabIndex !== undefined ? !tabIndex : focusable}
mostRecentEventCount={mostRecentEventCount}
nativeID={id ?? props.nativeID}
@@ -193,7 +194,7 @@ index df89097..3b223ec 100644
onBlur={_onBlur}
onKeyPressSync={props.unstable_onKeyPressSync}
onChange={_onChange}
-@@ -1477,6 +1485,7 @@ function InternalTextInput(props: Props): React.Node {
+@@ -1482,6 +1490,7 @@ function InternalTextInput(props: Props): React.Node {
mostRecentEventCount={mostRecentEventCount}
nativeID={id ?? props.nativeID}
numberOfLines={props.rows ?? props.numberOfLines}
@@ -549,7 +550,7 @@ index 190bc27..c2bcdc1 100644
: mEllipsizeLocation;
setEllipsize(ellipsizeLocation);
diff --git a/node_modules/react-native/ReactAndroid/src/main/java/com/facebook/react/views/text/TextLayoutManager.java b/node_modules/react-native/ReactAndroid/src/main/java/com/facebook/react/views/text/TextLayoutManager.java
-index 561a2d0..017be13 100644
+index 561a2d0..9409cfc 100644
--- a/node_modules/react-native/ReactAndroid/src/main/java/com/facebook/react/views/text/TextLayoutManager.java
+++ b/node_modules/react-native/ReactAndroid/src/main/java/com/facebook/react/views/text/TextLayoutManager.java
@@ -18,6 +18,7 @@ import android.text.SpannableStringBuilder;
@@ -568,7 +569,7 @@ index 561a2d0..017be13 100644
private static final LruCache sSpannableCache =
new LruCache<>(spannableCacheSize);
private static final ConcurrentHashMap sTagToSpannableCache =
-@@ -385,6 +387,47 @@ public class TextLayoutManager {
+@@ -385,6 +387,48 @@ public class TextLayoutManager {
? paragraphAttributes.getInt(MAXIMUM_NUMBER_OF_LINES_KEY)
: UNSET;
@@ -612,12 +613,13 @@ index 561a2d0..017be13 100644
+ if (numberOfLines != UNSET && numberOfLines != 0) {
+ maximumNumberOfLines = numberOfLines;
+ }
++
+
int calculatedLineCount =
maximumNumberOfLines == UNSET || maximumNumberOfLines == 0
? layout.getLineCount()
diff --git a/node_modules/react-native/ReactAndroid/src/main/java/com/facebook/react/views/text/TextLayoutManagerMapBuffer.java b/node_modules/react-native/ReactAndroid/src/main/java/com/facebook/react/views/text/TextLayoutManagerMapBuffer.java
-index 0d118f0..f29f069 100644
+index 0d118f0..0ae44b7 100644
--- a/node_modules/react-native/ReactAndroid/src/main/java/com/facebook/react/views/text/TextLayoutManagerMapBuffer.java
+++ b/node_modules/react-native/ReactAndroid/src/main/java/com/facebook/react/views/text/TextLayoutManagerMapBuffer.java
@@ -18,6 +18,7 @@ import android.text.SpannableStringBuilder;
@@ -636,7 +638,7 @@ index 0d118f0..f29f069 100644
private static final boolean ENABLE_MEASURE_LOGGING = ReactBuildConfig.DEBUG && false;
-@@ -399,6 +401,46 @@ public class TextLayoutManagerMapBuffer {
+@@ -399,6 +401,47 @@ public class TextLayoutManagerMapBuffer {
? paragraphAttributes.getInt(PA_KEY_MAX_NUMBER_OF_LINES)
: UNSET;
@@ -679,15 +681,16 @@ index 0d118f0..f29f069 100644
+ if (numberOfLines != UNSET && numberOfLines != 0) {
+ maximumNumberOfLines = numberOfLines;
+ }
++
+
int calculatedLineCount =
maximumNumberOfLines == UNSET || maximumNumberOfLines == 0
? layout.getLineCount()
diff --git a/node_modules/react-native/ReactAndroid/src/main/java/com/facebook/react/views/textinput/ReactEditText.java b/node_modules/react-native/ReactAndroid/src/main/java/com/facebook/react/views/textinput/ReactEditText.java
-index 1b5e0f4..67d0b73 100644
+index ced37be..ef2f321 100644
--- a/node_modules/react-native/ReactAndroid/src/main/java/com/facebook/react/views/textinput/ReactEditText.java
+++ b/node_modules/react-native/ReactAndroid/src/main/java/com/facebook/react/views/textinput/ReactEditText.java
-@@ -483,7 +483,13 @@ public class ReactEditText extends AppCompatEditText
+@@ -548,7 +548,13 @@ public class ReactEditText extends AppCompatEditText
* href='https://android.googlesource.com/platform/frameworks/base/+/jb-release/core/java/android/widget/TextView.java'>TextView.java}
*/
if (isMultiline()) {
@@ -807,10 +810,10 @@ index f5f87c6..b7d1e90 100644
attributes.ellipsizeMode,
attributes.textBreakStrategy,
diff --git a/node_modules/react-native/ReactCommon/react/renderer/attributedstring/conversions.h b/node_modules/react-native/ReactCommon/react/renderer/attributedstring/conversions.h
-index 8687b89..26379f4 100644
+index 8687b89..eab75f4 100644
--- a/node_modules/react-native/ReactCommon/react/renderer/attributedstring/conversions.h
+++ b/node_modules/react-native/ReactCommon/react/renderer/attributedstring/conversions.h
-@@ -835,12 +835,18 @@ inline ParagraphAttributes convertRawProp(
+@@ -835,10 +835,16 @@ inline ParagraphAttributes convertRawProp(
ParagraphAttributes const &defaultParagraphAttributes) {
auto paragraphAttributes = ParagraphAttributes{};
@@ -819,19 +822,15 @@ index 8687b89..26379f4 100644
context,
rawProps,
"numberOfLines",
-- sourceParagraphAttributes.maximumNumberOfLines,
-- defaultParagraphAttributes.maximumNumberOfLines);
+ sourceParagraphAttributes.numberOfLines,
+ defaultParagraphAttributes.numberOfLines);
+ paragraphAttributes.maximumNumberOfLines = convertRawProp(
-+ context,
-+ rawProps,
-+ "maximumNumberOfLines",
-+ sourceParagraphAttributes.maximumNumberOfLines,
-+ defaultParagraphAttributes.maximumNumberOfLines);
++ context,
++ rawProps,
++ "maximumNumberOfLines",
+ sourceParagraphAttributes.maximumNumberOfLines,
+ defaultParagraphAttributes.maximumNumberOfLines);
paragraphAttributes.ellipsizeMode = convertRawProp(
- context,
- rawProps,
@@ -913,6 +919,7 @@ inline std::string toString(AttributedString::Range const &range) {
inline folly::dynamic toDynamic(
const ParagraphAttributes ¶graphAttributes) {
@@ -853,7 +852,7 @@ index 8687b89..26379f4 100644
PA_KEY_HYPHENATION_FREQUENCY,
toString(paragraphAttributes.android_hyphenationFrequency));
+ builder.putInt(
-+ PA_KEY_NUMBER_OF_LINES, paragraphAttributes.numberOfLines);
++ PA_KEY_NUMBER_OF_LINES, paragraphAttributes.numberOfLines);
return builder.build();
}
@@ -914,15 +913,15 @@ index ba39ebb..ead28e3 100644
std::string textBreakStrategy{};
SharedColor underlineColorAndroid{};
diff --git a/node_modules/react-native/ReactCommon/react/renderer/textlayoutmanager/platform/ios/react/renderer/textlayoutmanager/RCTTextLayoutManager.mm b/node_modules/react-native/ReactCommon/react/renderer/textlayoutmanager/platform/ios/react/renderer/textlayoutmanager/RCTTextLayoutManager.mm
-index 368c334..2a98ad0 100644
+index 368c334..a1bb33e 100644
--- a/node_modules/react-native/ReactCommon/react/renderer/textlayoutmanager/platform/ios/react/renderer/textlayoutmanager/RCTTextLayoutManager.mm
+++ b/node_modules/react-native/ReactCommon/react/renderer/textlayoutmanager/platform/ios/react/renderer/textlayoutmanager/RCTTextLayoutManager.mm
-@@ -244,26 +244,50 @@ - (void)getRectWithAttributedString:(AttributedString)attributedString
+@@ -244,26 +244,51 @@ - (void)getRectWithAttributedString:(AttributedString)attributedString
#pragma mark - Private
-- (NSTextStorage *)_textStorageForNSAttributesString:(NSAttributedString *)attributedString
-+- (NSTextStorage *)_textStorageForNSAttributesString:(NSAttributedString *)inputAttributedString
+++- (NSTextStorage *)_textStorageForNSAttributesString:(NSAttributedString *)inputAttributedString
paragraphAttributes:(ParagraphAttributes)paragraphAttributes
size:(CGSize)size
{
@@ -950,7 +949,6 @@ index 368c334..2a98ad0 100644
- ? RCTNSLineBreakModeFromEllipsizeMode(paragraphAttributes.ellipsizeMode)
- : NSLineBreakByClipping;
- textContainer.maximumNumberOfLines = paragraphAttributes.maximumNumberOfLines;
-+
+ [attributedString insertAttributedString:[[NSAttributedString alloc] initWithString:newLines attributes:attributesOfFirstCharacter] atIndex:0];
+ }
+
@@ -959,7 +957,7 @@ index 368c334..2a98ad0 100644
NSLayoutManager *layoutManager = [NSLayoutManager new];
layoutManager.usesFontLeading = NO;
[layoutManager addTextContainer:textContainer];
--
+
- NSTextStorage *textStorage = [[NSTextStorage alloc] initWithAttributedString:attributedString];
+ NSTextStorage *textStorage = [NSTextStorage new];
@@ -973,6 +971,7 @@ index 368c334..2a98ad0 100644
+ textContainer.maximumNumberOfLines = paragraphAttributes.maximumNumberOfLines;
+
+ [textStorage replaceCharactersInRange:(NSRange){0, textStorage.length} withAttributedString:attributedString];
++
+
if (paragraphAttributes.adjustsFontSizeToFit) {
CGFloat minimumFontSize = !isnan(paragraphAttributes.minimumFontSize) ? paragraphAttributes.minimumFontSize : 4.0;
diff --git a/patches/react-native+0.72.3+003+VerticalScrollBarPosition.patch b/patches/react-native+0.72.4+003+VerticalScrollBarPosition.patch
similarity index 76%
rename from patches/react-native+0.72.3+003+VerticalScrollBarPosition.patch
rename to patches/react-native+0.72.4+003+VerticalScrollBarPosition.patch
index 1cf8f6de54fb..e6ed0d4f79a3 100644
--- a/patches/react-native+0.72.3+003+VerticalScrollBarPosition.patch
+++ b/patches/react-native+0.72.4+003+VerticalScrollBarPosition.patch
@@ -1,12 +1,11 @@
diff --git a/node_modules/react-native/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactScrollViewManager.java b/node_modules/react-native/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactScrollViewManager.java
-index 46e0ccf..53293a4 100644
+index 33658e7..31c20c0 100644
--- a/node_modules/react-native/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactScrollViewManager.java
+++ b/node_modules/react-native/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactScrollViewManager.java
-@@ -379,4 +379,15 @@ public class ReactScrollViewManager extends ViewGroupManager
- public void setScrollEventThrottle(ReactScrollView view, int scrollEventThrottle) {
+@@ -381,6 +381,17 @@ public class ReactScrollViewManager extends ViewGroupManager
view.setScrollEventThrottle(scrollEventThrottle);
}
-+
+
+ @ReactProp(name = "verticalScrollbarPosition")
+ public void setVerticalScrollbarPosition(ReactScrollView view, String position) {
+ if ("right".equals(position)) {
@@ -17,4 +16,7 @@ index 46e0ccf..53293a4 100644
+ view.setVerticalScrollbarPosition(View.SCROLLBAR_POSITION_DEFAULT);
+ }
+ }
- }
++
+ @ReactProp(name = "isInvertedVirtualizedList")
+ public void setIsInvertedVirtualizedList(ReactScrollView view, boolean applyFix) {
+ // Usually when inverting the scroll view we are using scaleY: -1 on the list
diff --git a/patches/react-native+0.72.3+004+ModalKeyboardFlashing.patch b/patches/react-native+0.72.4+004+ModalKeyboardFlashing.patch
similarity index 100%
rename from patches/react-native+0.72.3+004+ModalKeyboardFlashing.patch
rename to patches/react-native+0.72.4+004+ModalKeyboardFlashing.patch
diff --git a/patches/react-native-image-picker+5.1.0.patch b/patches/react-native-image-picker+5.1.0.patch
new file mode 100644
index 000000000000..0defc430e669
--- /dev/null
+++ b/patches/react-native-image-picker+5.1.0.patch
@@ -0,0 +1,133 @@
+diff --git a/node_modules/react-native-image-picker/android/src/main/java/com/imagepicker/ImagePickerModuleImpl.java b/node_modules/react-native-image-picker/android/src/main/java/com/imagepicker/ImagePickerModuleImpl.java
+index 89b69a8..d86ab1e 100644
+--- a/node_modules/react-native-image-picker/android/src/main/java/com/imagepicker/ImagePickerModuleImpl.java
++++ b/node_modules/react-native-image-picker/android/src/main/java/com/imagepicker/ImagePickerModuleImpl.java
+@@ -29,6 +29,120 @@ public class ImagePickerModuleImpl implements ActivityEventListener {
+ public static final int REQUEST_LAUNCH_VIDEO_CAPTURE = 13002;
+ public static final int REQUEST_LAUNCH_LIBRARY = 13003;
+
++ // Prevent svg images from being selected as they are not supported (Image component does not support them)
++ // and also because iOS does not allow them to be selected (for consistency).
++ // Since, we can't exclude a mime type, we instead allow all image mime types except 'image/svg+xml'.
++ // Image mime types are generated by merging the Android image mime type support and the IANA media-types lists.
++ // https://android.googlesource.com/platform/external/mime-support/+/main/mime.types#636
++ // https://www.iana.org/assignments/media-types/media-types.xhtml#image
++ private static final String[] ALLOWED_IMAGE_MIME_TYPES = {
++ "image/aces",
++ "image/apng",
++ "image/avci",
++ "image/avcs",
++ "image/avif",
++ "image/bmp",
++ "image/cgm",
++ "image/dicom-rle",
++ "image/dpx",
++ "image/emf",
++ "image/example",
++ "image/fits",
++ "image/g3fax",
++ "image/gif",
++ "image/heic-sequence",
++ "image/heic",
++ "image/heif-sequence",
++ "image/heif",
++ "image/hej2k",
++ "image/hsj2",
++ "image/ief",
++ "image/j2c",
++ "image/jls",
++ "image/jp2",
++ "image/jpeg",
++ "image/jph",
++ "image/jphc",
++ "image/jpm",
++ "image/jpx",
++ "image/jxr",
++ "image/jxrA",
++ "image/jxrS",
++ "image/jxs",
++ "image/jxsc",
++ "image/jxsi",
++ "image/jxss",
++ "image/ktx",
++ "image/ktx2",
++ "image/naplps",
++ "image/pcx",
++ "image/png",
++ "image/prs.btif",
++ "image/prs.pti",
++ "image/pwg-raster",
++ // "image/svg+xml",
++ "image/t38",
++ "image/tiff-fx",
++ "image/tiff",
++ "image/vnd.adobe.photoshop",
++ "image/vnd.airzip.accelerator.azv",
++ "image/vnd.cns.inf2",
++ "image/vnd.dece.graphic",
++ "image/vnd.djvu",
++ "image/vnd.dvb.subtitle",
++ "image/vnd.dwg",
++ "image/vnd.dxf",
++ "image/vnd.fastbidsheet",
++ "image/vnd.fpx",
++ "image/vnd.fst",
++ "image/vnd.fujixerox.edmics-mmr",
++ "image/vnd.fujixerox.edmics-rlc",
++ "image/vnd.globalgraphics.pgb",
++ "image/vnd.microsoft.icon",
++ "image/vnd.mix",
++ "image/vnd.mozilla.apng",
++ "image/vnd.ms-modi",
++ "image/vnd.net-fpx",
++ "image/vnd.pco.b16",
++ "image/vnd.radiance",
++ "image/vnd.sealed.png",
++ "image/vnd.sealedmedia.softseal.gif",
++ "image/vnd.sealedmedia.softseal.jpg",
++ "image/vnd.svf",
++ "image/vnd.tencent.tap",
++ "image/vnd.valve.source.texture",
++ "image/vnd.wap.wbmp",
++ "image/vnd.xiff",
++ "image/vnd.zbrush.pcx",
++ "image/webp",
++ "image/wmf",
++ "image/x-canon-cr2",
++ "image/x-canon-crw",
++ "image/x-cmu-raster",
++ "image/x-coreldraw",
++ "image/x-coreldrawpattern",
++ "image/x-coreldrawtemplate",
++ "image/x-corelphotopaint",
++ "image/x-emf",
++ "image/x-epson-erf",
++ "image/x-icon",
++ "image/x-jg",
++ "image/x-jng",
++ "image/x-ms-bmp",
++ "image/x-nikon-nef",
++ "image/x-olympus-orf",
++ "image/x-photoshop",
++ "image/x-portable-anymap",
++ "image/x-portable-bitmap",
++ "image/x-portable-graymap",
++ "image/x-portable-pixmap",
++ "image/x-rgb",
++ "image/x-wmf",
++ "image/x-xbitmap",
++ "image/x-xpixmap",
++ "image/x-xwindowdump",
++ };
++
+ private Uri fileUri;
+
+ private ReactApplicationContext reactContext;
+@@ -148,6 +262,7 @@ public class ImagePickerModuleImpl implements ActivityEventListener {
+
+ if (isPhoto) {
+ libraryIntent.setType("image/*");
++ libraryIntent.putExtra(Intent.EXTRA_MIME_TYPES, this.ALLOWED_IMAGE_MIME_TYPES);
+ } else if (isVideo) {
+ libraryIntent.setType("video/*");
+ } else if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU) {
diff --git a/src/CONST.ts b/src/CONST.ts
index 93576e0ccf9d..eed1b98ae551 100755
--- a/src/CONST.ts
+++ b/src/CONST.ts
@@ -757,6 +757,9 @@ const CONST = {
// 6 numeric digits
VALIDATE_CODE_REGEX_STRING: /^\d{6}$/,
+ // 8 alphanumeric characters
+ RECOVERY_CODE_REGEX_STRING: /^[a-zA-Z0-9]{8}$/,
+
// The server has a WAF (Web Application Firewall) which will strip out HTML/XML tags using this regex pattern.
// It's copied here so that the same regex pattern can be used in form validations to be consistent with the server.
VALIDATE_FOR_HTML_TAG_REGEX: /<([^>\s]+)(?:[^>]*?)>/g,
@@ -792,6 +795,10 @@ const CONST = {
INVISIBLE_CODEPOINTS: ['fe0f', '200d', '2066'],
+ UNICODE: {
+ LTR: '\u2066',
+ },
+
TOOLTIP_MAX_LINES: 3,
LOGIN_TYPE: {
@@ -802,6 +809,8 @@ const CONST = {
MAGIC_CODE_LENGTH: 6,
MAGIC_CODE_EMPTY_CHAR: ' ',
+ RECOVERY_CODE_LENGTH: 8,
+
KEYBOARD_TYPE: {
PHONE_PAD: 'phone-pad',
NUMBER_PAD: 'number-pad',
@@ -1026,7 +1035,6 @@ const CONST = {
},
PAYMENT_METHODS: {
- PAYPAL: 'payPalMe',
DEBIT_CARD: 'debitCard',
BANK_ACCOUNT: 'bankAccount',
},
@@ -1042,7 +1050,6 @@ const CONST = {
PAYMENT_TYPE: {
ELSEWHERE: 'Elsewhere',
EXPENSIFY: 'Expensify',
- PAYPAL_ME: 'PayPal.me',
VBBA: 'ACH',
},
MONEY_REQUEST_TYPE: {
@@ -1156,6 +1163,7 @@ const CONST = {
},
AVATAR_SIZE: {
+ XLARGE: 'xlarge',
LARGE: 'large',
MEDIUM: 'medium',
DEFAULT: 'default',
@@ -1170,6 +1178,7 @@ const CONST = {
SMALL_NORMAL: 'small-normal',
},
EXPENSIFY_CARD: {
+ BANK: 'Expensify Card',
FRAUD_TYPES: {
DOMAIN: 'domain',
INDIVIDUAL: 'individal',
@@ -1200,7 +1209,6 @@ const CONST = {
CARD_NUMBER: /^[0-9]{15,16}$/,
CARD_SECURITY_CODE: /^[0-9]{3,4}$/,
CARD_EXPIRATION_DATE: /^(0[1-9]|1[0-2])([^0-9])?([0-9]{4}|([0-9]{2}))$/,
- PAYPAL_ME_USERNAME: /^[a-zA-Z0-9]{1,20}$/,
ROOM_NAME: /^#[a-z0-9à-ÿ-]{1,80}$/,
// eslint-disable-next-line max-len, no-misleading-character-class
@@ -1304,9 +1312,9 @@ const CONST = {
},
// Auth limit is 60k for the column but we store edits and other metadata along the html so let's use a lower limit to accommodate for it.
- MAX_COMMENT_LENGTH: 15000,
+ MAX_COMMENT_LENGTH: 10000,
- // Furthermore, applying markup is very resource-consuming, so let's set a slightly lower limit for that
+ // Use the same value as MAX_COMMENT_LENGTH to ensure the entire comment is parsed. Note that applying markup is very resource-consuming.
MAX_MARKUP_LENGTH: 10000,
MAX_THREAD_REPLIES_PREVIEW: 99,
@@ -1350,6 +1358,8 @@ const CONST = {
DATE: 'date',
DESCRIPTION: 'description',
MERCHANT: 'merchant',
+ CATEGORY: 'category',
+ RECEIPT: 'receipt',
},
FOOTER: {
EXPENSE_MANAGEMENT_URL: `${USE_EXPENSIFY_URL}/expense-management`,
@@ -2531,32 +2541,6 @@ const CONST = {
SEARCH_ISSUES: 'https://github.com/Expensify/App/issues',
},
- PAYPAL_SUPPORTED_CURRENCIES: [
- 'AUD',
- 'BRL',
- 'CAD',
- 'CZK',
- 'DKK',
- 'EUR',
- 'HKD',
- 'HUF',
- 'ILS',
- 'JPY',
- 'MYR',
- 'MXN',
- 'TWD',
- 'NZD',
- 'NOK',
- 'PHP',
- 'PLN',
- 'GBP',
- 'RUB',
- 'SGD',
- 'SEK',
- 'CHF',
- 'THB',
- 'USD',
- ],
CONCIERGE_TRAVEL_URL: 'https://community.expensify.com/discussion/7066/introducing-concierge-travel',
SCREEN_READER_STATES: {
ALL: 'all',
@@ -2656,6 +2640,7 @@ const CONST = {
INDENTS: ' ',
PARENT_CHILD_SEPARATOR: ': ',
CATEGORY_LIST_THRESHOLD: 8,
+ TAG_LIST_THRESHOLD: 8,
DEMO_PAGES: {
SAASTR: 'SaaStrDemoSetup',
SBE: 'SbeDemoSetup',
@@ -2671,6 +2656,15 @@ const CONST = {
HTTPS: 'https',
PUSHER: 'pusher',
},
+ EVENTS: {
+ SCROLLING: 'scrolling',
+ },
+ HORIZONTAL_SPACER: {
+ DEFAULT_BORDER_BOTTOM_WIDTH: 1,
+ DEFAULT_MARGIN_VERTICAL: 8,
+ HIDDEN_MARGIN_VERTICAL: 0,
+ HIDDEN_BORDER_BOTTOM_WIDTH: 0,
+ },
} as const;
export default CONST;
diff --git a/src/Expensify.js b/src/Expensify.js
index 1086bd32cff9..9e6ae1ff27b4 100644
--- a/src/Expensify.js
+++ b/src/Expensify.js
@@ -30,7 +30,6 @@ import KeyboardShortcutsModal from './components/KeyboardShortcutsModal';
import AppleAuthWrapper from './components/SignInButtons/AppleAuthWrapper';
import EmojiPicker from './components/EmojiPicker/EmojiPicker';
import * as EmojiPickerAction from './libs/actions/EmojiPickerAction';
-import DownloadAppModal from './components/DownloadAppModal';
import DeeplinkWrapper from './components/DeeplinkWrapper';
// This lib needs to be imported, but it has nothing to export since all it contains is an Onyx connection
@@ -100,7 +99,9 @@ function Expensify(props) {
const [hasAttemptedToOpenPublicRoom, setAttemptedToOpenPublicRoom] = useState(false);
useEffect(() => {
- if (props.isCheckingPublicRoom) return;
+ if (props.isCheckingPublicRoom) {
+ return;
+ }
setAttemptedToOpenPublicRoom(true);
}, [props.isCheckingPublicRoom]);
@@ -193,7 +194,6 @@ function Expensify(props) {
{shouldInit && (
<>
-
diff --git a/src/ONYXKEYS.ts b/src/ONYXKEYS.ts
index 80afc4d5ffee..05256f2b806c 100755
--- a/src/ONYXKEYS.ts
+++ b/src/ONYXKEYS.ts
@@ -1,4 +1,5 @@
import {ValueOf} from 'type-fest';
+import {OnyxUpdate} from 'react-native-onyx';
import DeepValueOf from './types/utils/DeepValueOf';
import * as OnyxTypes from './types/onyx';
import CONST from './CONST';
@@ -87,13 +88,7 @@ const ONYXKEYS = {
SESSION: 'session',
BETAS: 'betas',
- /** Denotes if the Download App Banner has been dismissed */
- SHOW_DOWNLOAD_APP_BANNER: 'showDownloadAppBanner',
-
/** NVP keys
- * Contains the user's payPalMe data */
- PAYPAL: 'paypal',
-
/** Contains the user preference for the LHN priority mode */
NVP_PRIORITY_MODE: 'nvp_priorityMode',
@@ -175,6 +170,9 @@ const ONYXKEYS = {
/** Is report data loading? */
IS_LOADING_REPORT_DATA: 'isLoadingReportData',
+ /** Is report data loading? */
+ IS_LOADING_APP: 'isLoadingApp',
+
/** Is Keyboard shortcuts modal open? */
IS_SHORTCUTS_MODAL_OPEN: 'isShortcutsModalOpen',
@@ -284,7 +282,6 @@ const ONYXKEYS = {
MONEY_REQUEST_AMOUNT_FORM: 'moneyRequestAmountForm',
MONEY_REQUEST_DATE_FORM: 'moneyRequestCreatedForm',
NEW_CONTACT_METHOD_FORM: 'newContactMethodForm',
- PAYPAL_FORM: 'payPalForm',
WAYPOINT_FORM: 'waypointForm',
WAYPOINT_FORM_DRAFT: 'waypointFormDraft',
SETTINGS_STATUS_SET_FORM: 'settingsStatusSetForm',
@@ -307,9 +304,8 @@ type OnyxValues = {
[ONYXKEYS.ACTIVE_CLIENTS]: string[];
[ONYXKEYS.DEVICE_ID]: string;
[ONYXKEYS.IS_SIDEBAR_LOADED]: boolean;
- [ONYXKEYS.SHOW_DOWNLOAD_APP_BANNER]: boolean;
[ONYXKEYS.PERSISTED_REQUESTS]: OnyxTypes.Request[];
- [ONYXKEYS.QUEUED_ONYX_UPDATES]: OnyxTypes.QueuedOnyxUpdates;
+ [ONYXKEYS.QUEUED_ONYX_UPDATES]: OnyxUpdate[];
[ONYXKEYS.CURRENT_DATE]: string;
[ONYXKEYS.CREDENTIALS]: OnyxTypes.Credentials;
[ONYXKEYS.IOU]: OnyxTypes.IOU;
@@ -328,7 +324,6 @@ type OnyxValues = {
[ONYXKEYS.LOGIN_LIST]: OnyxTypes.Login;
[ONYXKEYS.SESSION]: OnyxTypes.Session;
[ONYXKEYS.BETAS]: OnyxTypes.Beta[];
- [ONYXKEYS.PAYPAL]: OnyxTypes.Paypal;
[ONYXKEYS.NVP_PRIORITY_MODE]: ValueOf;
[ONYXKEYS.NVP_BLOCKED_FROM_CONCIERGE]: OnyxTypes.BlockedFromConcierge;
[ONYXKEYS.NVP_PRIVATE_PUSH_NOTIFICATION_ID]: string;
@@ -367,7 +362,6 @@ type OnyxValues = {
[ONYXKEYS.LAST_OPENED_PUBLIC_ROOM_ID]: string;
[ONYXKEYS.PREFERRED_THEME]: ValueOf;
[ONYXKEYS.IS_USING_MEMORY_ONLY_KEYS]: boolean;
- [ONYXKEYS.RECEIPT_MODAL]: OnyxTypes.ReceiptModal;
[ONYXKEYS.MAPBOX_ACCESS_TOKEN]: OnyxTypes.MapboxAccessToken;
[ONYXKEYS.ONYX_UPDATES_FROM_SERVER]: OnyxTypes.OnyxUpdatesFromServer;
[ONYXKEYS.ONYX_UPDATES_LAST_UPDATE_ID_APPLIED_TO_CLIENT]: number;
@@ -420,7 +414,6 @@ type OnyxValues = {
[ONYXKEYS.FORMS.MONEY_REQUEST_DATE_FORM]: OnyxTypes.Form;
[ONYXKEYS.FORMS.MONEY_REQUEST_DATE_FORM]: OnyxTypes.Form;
[ONYXKEYS.FORMS.NEW_CONTACT_METHOD_FORM]: OnyxTypes.Form;
- [ONYXKEYS.FORMS.PAYPAL_FORM]: OnyxTypes.Form;
[ONYXKEYS.FORMS.WAYPOINT_FORM]: OnyxTypes.Form;
[ONYXKEYS.FORMS.WAYPOINT_FORM_DRAFT]: OnyxTypes.Form;
[ONYXKEYS.FORMS.SETTINGS_STATUS_SET_FORM]: OnyxTypes.Form;
diff --git a/src/ROUTES.ts b/src/ROUTES.ts
index 9459708c893b..2c37116db395 100644
--- a/src/ROUTES.ts
+++ b/src/ROUTES.ts
@@ -48,7 +48,6 @@ export default {
SETTINGS_ABOUT: 'settings/about',
SETTINGS_APP_DOWNLOAD_LINKS: 'settings/about/app-download-links',
SETTINGS_WALLET: 'settings/wallet',
- SETTINGS_ADD_PAYPAL_ME: 'settings/wallet/add-paypal-me',
SETTINGS_ADD_DEBIT_CARD: 'settings/wallet/add-debit-card',
SETTINGS_ADD_BANK_ACCOUNT: 'settings/wallet/add-bank-account',
SETTINGS_ENABLE_PAYMENTS: 'settings/wallet/enable-payments',
diff --git a/src/SCREENS.ts b/src/SCREENS.ts
index 47935e117e99..eb125a43c239 100644
--- a/src/SCREENS.ts
+++ b/src/SCREENS.ts
@@ -12,8 +12,14 @@ export default {
VALIDATE_LOGIN: 'ValidateLogin',
CONCIERGE: 'Concierge',
SETTINGS: {
+ ROOT: 'Settings_Root',
PREFERENCES: 'Settings_Preferences',
WORKSPACES: 'Settings_Workspaces',
+ SECURITY: 'Settings_Security',
+ STATUS: 'Settings_Status',
+ },
+ SAVE_THE_WORLD: {
+ ROOT: 'SaveTheWorld_Root',
},
SIGN_IN_WITH_APPLE_DESKTOP: 'AppleSignInDesktop',
SIGN_IN_WITH_GOOGLE_DESKTOP: 'GoogleSignInDesktop',
diff --git a/src/components/AddPaymentMethodMenu.js b/src/components/AddPaymentMethodMenu.js
index 7f1544a758f4..399247a35676 100644
--- a/src/components/AddPaymentMethodMenu.js
+++ b/src/components/AddPaymentMethodMenu.js
@@ -10,7 +10,6 @@ import withWindowDimensions from './withWindowDimensions';
import Permissions from '../libs/Permissions';
import PopoverMenu from './PopoverMenu';
import refPropTypes from './refPropTypes';
-import paypalMeDataPropTypes from './paypalMeDataPropTypes';
const propTypes = {
/** Should the component be visible? */
@@ -25,12 +24,6 @@ const propTypes = {
vertical: PropTypes.number,
}),
- /** Account details for PayPal.Me */
- payPalMeData: paypalMeDataPropTypes,
-
- /** Should we show the Paypal option */
- shouldShowPaypal: PropTypes.bool,
-
/** List of betas available to current user */
betas: PropTypes.arrayOf(PropTypes.string),
@@ -42,8 +35,6 @@ const propTypes = {
const defaultProps = {
anchorPosition: {},
- payPalMeData: {},
- shouldShowPaypal: true,
betas: [],
anchorRef: () => {},
};
@@ -73,15 +64,6 @@ function AddPaymentMethodMenu(props) {
},
]
: []),
- ...(props.shouldShowPaypal && !props.payPalMeData.description
- ? [
- {
- text: props.translate('common.payPalMe'),
- icon: Expensicons.PayPal,
- onSelected: () => props.onItemSelected(CONST.PAYMENT_METHODS.PAYPAL),
- },
- ]
- : []),
]}
withoutOverlay
/>
@@ -96,9 +78,6 @@ export default compose(
withWindowDimensions,
withLocalize,
withOnyx({
- payPalMeData: {
- key: ONYXKEYS.PAYPAL,
- },
betas: {
key: ONYXKEYS.BETAS,
},
diff --git a/src/components/AddPlaidBankAccount.js b/src/components/AddPlaidBankAccount.js
index e2843ba7fae8..dbe7e46ff6aa 100644
--- a/src/components/AddPlaidBankAccount.js
+++ b/src/components/AddPlaidBankAccount.js
@@ -1,7 +1,6 @@
import _ from 'underscore';
-import React, {useEffect, useRef, useCallback, useMemo} from 'react';
+import React, {useEffect, useRef, useCallback} from 'react';
import {ActivityIndicator, View} from 'react-native';
-import {useIsFocused} from '@react-navigation/native';
import PropTypes from 'prop-types';
import {withOnyx} from 'react-native-onyx';
import lodashGet from 'lodash/get';
@@ -39,9 +38,6 @@ const propTypes = {
/** Fired when the user exits the Plaid flow */
onExitPlaid: PropTypes.func,
- /** Fired when the screen is blurred */
- onBlurPlaid: PropTypes.func,
-
/** Fired when the user selects an account */
onSelect: PropTypes.func,
@@ -65,7 +61,6 @@ const defaultProps = {
selectedPlaidAccountID: '',
plaidLinkToken: '',
onExitPlaid: () => {},
- onBlurPlaid: () => {},
onSelect: () => {},
text: '',
receivedRedirectURI: null,
@@ -80,7 +75,6 @@ function AddPlaidBankAccount({
selectedPlaidAccountID,
plaidLinkToken,
onExitPlaid,
- onBlurPlaid,
onSelect,
text,
receivedRedirectURI,
@@ -94,7 +88,6 @@ function AddPlaidBankAccount({
const {translate} = useLocalize();
const {isOffline} = useNetwork();
- const isFocused = useIsFocused();
/**
* @returns {String}
@@ -109,11 +102,6 @@ function AddPlaidBankAccount({
}
};
- /**
- * @returns {Array}
- */
- const plaidBankAccounts = useMemo(() => lodashGet(plaidData, 'bankAccounts') || [], [plaidData]);
-
/**
* @returns {Boolean}
* I'm using useCallback so the useEffect which uses this function doesn't run on every render.
@@ -163,13 +151,6 @@ function AddPlaidBankAccount({
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
- useEffect(() => {
- if (isFocused || plaidBankAccounts.length) {
- return;
- }
- onBlurPlaid();
- }, [isFocused, onBlurPlaid, plaidBankAccounts.length]);
-
useEffect(() => {
// If we are coming back from offline and we haven't authenticated with Plaid yet, we need to re-run our call to kick off Plaid
// previousNetworkState.current also makes sure that this doesn't run on the first render.
@@ -179,6 +160,7 @@ function AddPlaidBankAccount({
previousNetworkState.current = isOffline;
}, [allowDebit, bankAccountID, isAuthenticatedWithPlaid, isOffline]);
+ const plaidBankAccounts = lodashGet(plaidData, 'bankAccounts') || [];
const token = getPlaidLinkToken();
const options = _.map(plaidBankAccounts, (account) => ({
value: account.plaidAccountID,
diff --git a/src/components/AddressSearch/index.js b/src/components/AddressSearch/index.js
index 1697dddba805..1b4200572664 100644
--- a/src/components/AddressSearch/index.js
+++ b/src/components/AddressSearch/index.js
@@ -34,7 +34,7 @@ const propTypes = {
onBlur: PropTypes.func,
/** Error text to display */
- errorText: PropTypes.string,
+ errorText: PropTypes.oneOfType([PropTypes.string, PropTypes.arrayOf(PropTypes.oneOfType([PropTypes.string, PropTypes.object]))]),
/** Hint text to display */
hint: PropTypes.string,
@@ -300,7 +300,7 @@ function AddressSearch(props) {
query={query}
requestUrl={{
useOnPlatform: 'all',
- url: ApiUtils.getCommandURL({command: 'Proxy_GooglePlaces&proxyUrl='}),
+ url: props.network.isOffline ? null : ApiUtils.getCommandURL({command: 'Proxy_GooglePlaces&proxyUrl='}),
}}
textInputProps={{
InputComp: TextInput,
diff --git a/src/components/AttachmentModal.js b/src/components/AttachmentModal.js
index bbb0662132d2..946b5e2ddec9 100755
--- a/src/components/AttachmentModal.js
+++ b/src/components/AttachmentModal.js
@@ -1,4 +1,4 @@
-import React, {useState, useCallback} from 'react';
+import React, {useState, useCallback, useRef} from 'react';
import PropTypes from 'prop-types';
import {View, Animated, Keyboard} from 'react-native';
import Str from 'expensify-common/lib/str';
@@ -25,6 +25,10 @@ import HeaderGap from './HeaderGap';
import SafeAreaConsumer from './SafeAreaConsumer';
import addEncryptedAuthTokenToURL from '../libs/addEncryptedAuthTokenToURL';
import reportPropTypes from '../pages/reportPropTypes';
+import * as Expensicons from './Icon/Expensicons';
+import useWindowDimensions from '../hooks/useWindowDimensions';
+import Navigation from '../libs/Navigation/Navigation';
+import ROUTES from '../ROUTES';
import useNativeDriver from '../libs/useNativeDriver';
/**
@@ -94,6 +98,7 @@ const defaultProps = {
};
function AttachmentModal(props) {
+ const onModalHideCallbackRef = useRef(null);
const [isModalOpen, setIsModalOpen] = useState(props.defaultOpen);
const [shouldLoadAttachment, setShouldLoadAttachment] = useState(false);
const [isAttachmentInvalid, setIsAttachmentInvalid] = useState(false);
@@ -106,6 +111,8 @@ function AttachmentModal(props) {
const [isConfirmButtonDisabled, setIsConfirmButtonDisabled] = useState(false);
const [confirmButtonFadeAnimation] = useState(new Animated.Value(1));
const [shouldShowDownloadButton, setShouldShowDownloadButton] = React.useState(true);
+ const {windowWidth} = useWindowDimensions();
+
const [file, setFile] = useState(
props.originalFileName
? {
@@ -331,6 +338,10 @@ function AttachmentModal(props) {
}}
onModalHide={(e) => {
props.onModalHide(e);
+ if (onModalHideCallbackRef.current) {
+ onModalHideCallbackRef.current();
+ }
+
setShouldLoadAttachment(false);
}}
propagateSwipe
@@ -339,12 +350,30 @@ function AttachmentModal(props) {
downloadAttachment(source)}
shouldShowCloseButton={!props.isSmallScreenWidth}
shouldShowBackButton={props.isSmallScreenWidth}
onBackButtonPress={closeModal}
onCloseButtonPress={closeModal}
+ shouldShowThreeDotsButton={isAttachmentReceipt}
+ threeDotsAnchorPosition={styles.threeDotsPopoverOffsetAttachmentModal(windowWidth)}
+ threeDotsMenuItems={[
+ {
+ icon: Expensicons.Camera,
+ text: props.translate('common.replace'),
+ onSelected: () => {
+ onModalHideCallbackRef.current = () => Navigation.navigate(ROUTES.getEditRequestRoute(props.report.reportID, CONST.EDIT_REQUEST_FIELD.RECEIPT));
+ closeModal();
+ },
+ },
+ {
+ icon: Expensicons.Download,
+ text: props.translate('common.download'),
+ onSelected: () => downloadAttachment(source),
+ },
+ ]}
+ shouldOverlay
/>
{!_.isEmpty(props.report) ? (
diff --git a/src/components/Attachments/AttachmentCarousel/AttachmentCarouselCellRenderer.js b/src/components/Attachments/AttachmentCarousel/AttachmentCarouselCellRenderer.js
new file mode 100644
index 000000000000..2c698d5c8a61
--- /dev/null
+++ b/src/components/Attachments/AttachmentCarousel/AttachmentCarouselCellRenderer.js
@@ -0,0 +1,34 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import {View, PixelRatio} from 'react-native';
+import useWindowDimensions from '../../../hooks/useWindowDimensions';
+import styles from '../../../styles/styles';
+
+const propTypes = {
+ /** Cell Container styles */
+ style: PropTypes.oneOfType([PropTypes.arrayOf(PropTypes.object), PropTypes.object]),
+};
+
+const defaultProps = {
+ style: [],
+};
+
+function AttachmentCarouselCellRenderer(props) {
+ const {windowWidth, isSmallScreenWidth} = useWindowDimensions();
+ const modalStyles = styles.centeredModalStyles(isSmallScreenWidth, true);
+ const style = [props.style, styles.h100, {width: PixelRatio.roundToNearestPixel(windowWidth - (modalStyles.marginHorizontal + modalStyles.borderWidth) * 2)}];
+
+ return (
+
+ );
+}
+
+AttachmentCarouselCellRenderer.propTypes = propTypes;
+AttachmentCarouselCellRenderer.defaultProps = defaultProps;
+AttachmentCarouselCellRenderer.displayName = 'AttachmentCarouselCellRenderer';
+
+export default React.memo(AttachmentCarouselCellRenderer);
diff --git a/src/components/Attachments/AttachmentCarousel/Pager/AttachmentCarouselPage.js b/src/components/Attachments/AttachmentCarousel/Pager/AttachmentCarouselPage.js
index b1a844e4172d..adee75cb4fa9 100644
--- a/src/components/Attachments/AttachmentCarousel/Pager/AttachmentCarouselPage.js
+++ b/src/components/Attachments/AttachmentCarousel/Pager/AttachmentCarouselPage.js
@@ -41,8 +41,11 @@ function AttachmentCarouselPage({source, isAuthTokenRequired, isActive: initialI
// to prevent the image transformer from flashing while still rendering
// Instead, we show the fallback image while the image transformer is loading the image
useEffect(() => {
- if (initialIsActive) setTimeout(() => setIsActive(true), 1);
- else setIsActive(false);
+ if (initialIsActive) {
+ setTimeout(() => setIsActive(true), 1);
+ } else {
+ setIsActive(false);
+ }
}, [initialIsActive]);
const [initialActivePageLoad, setInitialActivePageLoad] = useState(isActive);
@@ -51,8 +54,11 @@ function AttachmentCarouselPage({source, isAuthTokenRequired, isActive: initialI
// We delay hiding the fallback image while image transformer is still rendering
useEffect(() => {
- if (isImageLoading) setShowFallback(true);
- else setTimeout(() => setShowFallback(false), 100);
+ if (isImageLoading) {
+ setShowFallback(true);
+ } else {
+ setTimeout(() => setShowFallback(false), 100);
+ }
}, [isImageLoading]);
return (
@@ -127,7 +133,9 @@ function AttachmentCarouselPage({source, isAuthTokenRequired, isActive: initialI
const scaledImageHeight = imageHeight * minImageScale;
// Don't update the dimensions if they are already set
- if (dimensions?.scaledImageWidth === scaledImageWidth && dimensions?.scaledImageHeight === scaledImageHeight) return;
+ if (dimensions?.scaledImageWidth === scaledImageWidth && dimensions?.scaledImageHeight === scaledImageHeight) {
+ return;
+ }
cachedDimensions.set(source, {
...dimensions,
diff --git a/src/components/Attachments/AttachmentCarousel/Pager/ImageTransformer.js b/src/components/Attachments/AttachmentCarousel/Pager/ImageTransformer.js
index 4475df168df2..b1c2864a05f6 100644
--- a/src/components/Attachments/AttachmentCarousel/Pager/ImageTransformer.js
+++ b/src/components/Attachments/AttachmentCarousel/Pager/ImageTransformer.js
@@ -306,7 +306,9 @@ function ImageTransformer({imageWidth, imageHeight, imageScaleX, imageScaleY, sc
stopAnimation();
})
.onFinalize((evt, success) => {
- if (!success || !onTap) return;
+ if (!success || !onTap) {
+ return;
+ }
runOnJS(onTap)();
});
@@ -432,7 +434,9 @@ function ImageTransformer({imageWidth, imageHeight, imageScaleX, imageScaleY, sc
const pinchGesture = Gesture.Pinch()
.onTouchesDown((evt, state) => {
// we don't want to activate pinch gesture when we are scrolling pager
- if (!isScrolling.value) return;
+ if (!isScrolling.value) {
+ return;
+ }
state.fail();
})
diff --git a/src/components/Attachments/AttachmentCarousel/extractAttachmentsFromReport.js b/src/components/Attachments/AttachmentCarousel/extractAttachmentsFromReport.js
index d5da25c89576..8a623a44709f 100644
--- a/src/components/Attachments/AttachmentCarousel/extractAttachmentsFromReport.js
+++ b/src/components/Attachments/AttachmentCarousel/extractAttachmentsFromReport.js
@@ -59,6 +59,7 @@ function extractAttachmentsFromReport(report, reportActions) {
isAuthTokenRequired: true,
file: {name: transaction.filename},
isReceipt: true,
+ transactionID,
});
return;
}
diff --git a/src/components/Attachments/AttachmentCarousel/index.js b/src/components/Attachments/AttachmentCarousel/index.js
index 5c731a0ccfee..00b603cdd7d9 100644
--- a/src/components/Attachments/AttachmentCarousel/index.js
+++ b/src/components/Attachments/AttachmentCarousel/index.js
@@ -3,6 +3,7 @@ import {View, FlatList, PixelRatio, Keyboard} from 'react-native';
import {withOnyx} from 'react-native-onyx';
import _ from 'underscore';
import styles from '../../../styles/styles';
+import AttachmentCarouselCellRenderer from './AttachmentCarouselCellRenderer';
import CarouselActions from './CarouselActions';
import withWindowDimensions from '../../withWindowDimensions';
import CarouselButtons from './CarouselButtons';
@@ -12,13 +13,13 @@ import ONYXKEYS from '../../../ONYXKEYS';
import withLocalize from '../../withLocalize';
import compose from '../../../libs/compose';
import useCarouselArrows from './useCarouselArrows';
-import useWindowDimensions from '../../../hooks/useWindowDimensions';
import CarouselItem from './CarouselItem';
import Navigation from '../../../libs/Navigation/Navigation';
import BlockingView from '../../BlockingViews/BlockingView';
import * as Illustrations from '../../Icon/Illustrations';
import variables from '../../../styles/variables';
import * as DeviceCapabilities from '../../../libs/DeviceCapabilities';
+import * as ReportActionsUtils from '../../../libs/ReportActionsUtils';
const viewabilityConfig = {
// To facilitate paging through the attachments, we want to consider an item "viewable" when it is
@@ -29,7 +30,6 @@ const viewabilityConfig = {
function AttachmentCarousel({report, reportActions, source, onNavigate, setDownloadButtonVisibility, translate}) {
const scrollRef = useRef(null);
- const {windowWidth, isSmallScreenWidth} = useWindowDimensions();
const canUseTouchScreen = DeviceCapabilities.canUseTouchScreen();
const [containerWidth, setContainerWidth] = useState(0);
@@ -38,13 +38,25 @@ function AttachmentCarousel({report, reportActions, source, onNavigate, setDownl
const [activeSource, setActiveSource] = useState(source);
const [shouldShowArrows, setShouldShowArrows, autoHideArrows, cancelAutoHideArrows] = useCarouselArrows();
+ const compareImage = useCallback(
+ (attachment) => {
+ if (attachment.isReceipt) {
+ const action = ReportActionsUtils.getParentReportAction(report);
+ const transactionID = _.get(action, ['originalMessage', 'IOUTransactionID']);
+ return attachment.transactionID === transactionID;
+ }
+ return attachment.source === source;
+ },
+ [source, report],
+ );
+
useEffect(() => {
const attachmentsFromReport = extractAttachmentsFromReport(report, reportActions);
- const initialPage = _.findIndex(attachmentsFromReport, (a) => a.source === source);
+ const initialPage = _.findIndex(attachmentsFromReport, compareImage);
// Dismiss the modal when deleting an attachment during its display in preview.
- if (initialPage === -1 && _.find(attachments, (a) => a.source === source)) {
+ if (initialPage === -1 && _.find(attachments, compareImage)) {
Navigation.dismissModal();
} else {
setPage(initialPage);
@@ -54,10 +66,12 @@ function AttachmentCarousel({report, reportActions, source, onNavigate, setDownl
setDownloadButtonVisibility(initialPage !== -1);
// Update the parent modal's state with the source and name from the mapped attachments
- if (!_.isUndefined(attachmentsFromReport[initialPage])) onNavigate(attachmentsFromReport[initialPage]);
+ if (!_.isUndefined(attachmentsFromReport[initialPage])) {
+ onNavigate(attachmentsFromReport[initialPage]);
+ }
}
// eslint-disable-next-line react-hooks/exhaustive-deps
- }, [report, reportActions, source]);
+ }, [reportActions, compareImage]);
/**
* Updates the page state when the user navigates between attachments
@@ -117,29 +131,6 @@ function AttachmentCarousel({report, reportActions, source, onNavigate, setDownl
[containerWidth],
);
- /**
- * Defines how a container for a single attachment should be rendered
- * @param {Object} cellRendererProps
- * @returns {JSX.Element}
- */
- const renderCell = useCallback(
- (cellProps) => {
- // Use window width instead of layout width to address the issue in https://github.com/Expensify/App/issues/17760
- // considering horizontal margin and border width in centered modal
- const modalStyles = styles.centeredModalStyles(isSmallScreenWidth, true);
- const style = [cellProps.style, styles.h100, {width: PixelRatio.roundToNearestPixel(windowWidth - (modalStyles.marginHorizontal + modalStyles.borderWidth) * 2)}];
-
- return (
-
- );
- },
- [isSmallScreenWidth, windowWidth],
- );
-
/**
* Defines how a single attachment should be rendered
* @param {Object} item
@@ -211,7 +202,7 @@ function AttachmentCarousel({report, reportActions, source, onNavigate, setDownl
windowSize={5}
maxToRenderPerBatch={3}
data={attachments}
- CellRendererComponent={renderCell}
+ CellRendererComponent={AttachmentCarouselCellRenderer}
renderItem={renderItem}
getItemLayout={getItemLayout}
keyExtractor={(item) => item.source}
diff --git a/src/components/Attachments/AttachmentCarousel/index.native.js b/src/components/Attachments/AttachmentCarousel/index.native.js
index 95cda7c2f5c9..bd12020341be 100644
--- a/src/components/Attachments/AttachmentCarousel/index.native.js
+++ b/src/components/Attachments/AttachmentCarousel/index.native.js
@@ -16,6 +16,7 @@ import * as Illustrations from '../../Icon/Illustrations';
import variables from '../../../styles/variables';
import compose from '../../../libs/compose';
import withLocalize from '../../withLocalize';
+import * as ReportActionsUtils from '../../../libs/ReportActionsUtils';
function AttachmentCarousel({report, reportActions, source, onNavigate, onClose, setDownloadButtonVisibility, translate}) {
const pagerRef = useRef(null);
@@ -27,13 +28,25 @@ function AttachmentCarousel({report, reportActions, source, onNavigate, onClose,
const [isPinchGestureRunning, setIsPinchGestureRunning] = useState(true);
const [shouldShowArrows, setShouldShowArrows, autoHideArrows, cancelAutoHideArrows] = useCarouselArrows();
+ const compareImage = useCallback(
+ (attachment) => {
+ if (attachment.isReceipt) {
+ const action = ReportActionsUtils.getParentReportAction(report);
+ const transactionID = _.get(action, ['originalMessage', 'IOUTransactionID']);
+ return attachment.transactionID === transactionID;
+ }
+ return attachment.source === source;
+ },
+ [source, report],
+ );
+
useEffect(() => {
const attachmentsFromReport = extractAttachmentsFromReport(report, reportActions);
- const initialPage = _.findIndex(attachmentsFromReport, (a) => a.source === source);
+ const initialPage = _.findIndex(attachmentsFromReport, compareImage);
// Dismiss the modal when deleting an attachment during its display in preview.
- if (initialPage === -1 && _.find(attachments, (a) => a.source === source)) {
+ if (initialPage === -1 && _.find(attachments, compareImage)) {
Navigation.dismissModal();
} else {
setPage(initialPage);
@@ -43,10 +56,12 @@ function AttachmentCarousel({report, reportActions, source, onNavigate, onClose,
setDownloadButtonVisibility(initialPage !== -1);
// Update the parent modal's state with the source and name from the mapped attachments
- if (!_.isUndefined(attachmentsFromReport[initialPage])) onNavigate(attachmentsFromReport[initialPage]);
+ if (!_.isUndefined(attachmentsFromReport[initialPage])) {
+ onNavigate(attachmentsFromReport[initialPage]);
+ }
}
// eslint-disable-next-line react-hooks/exhaustive-deps
- }, [report, reportActions, source]);
+ }, [reportActions, compareImage]);
/**
* Updates the page state when the user navigates between attachments
@@ -135,7 +150,9 @@ function AttachmentCarousel({report, reportActions, source, onNavigate, onClose,
onPageSelected={({nativeEvent: {position: newPage}}) => updatePage(newPage)}
onPinchGestureChange={(newIsPinchGestureRunning) => {
setIsPinchGestureRunning(newIsPinchGestureRunning);
- if (!newIsPinchGestureRunning && !shouldShowArrows) setShouldShowArrows(true);
+ if (!newIsPinchGestureRunning && !shouldShowArrows) {
+ setShouldShowArrows(true);
+ }
}}
onSwipeDown={onClose}
containerWidth={containerDimensions.width}
diff --git a/src/components/Attachments/AttachmentView/AttachmentViewPdf/index.native.js b/src/components/Attachments/AttachmentView/AttachmentViewPdf/index.native.js
index 0767b2b68985..fdf151c4d5d0 100644
--- a/src/components/Attachments/AttachmentView/AttachmentViewPdf/index.native.js
+++ b/src/components/Attachments/AttachmentView/AttachmentViewPdf/index.native.js
@@ -25,7 +25,9 @@ function AttachmentViewPdf({file, encryptedSourceUrl, isFocused, isUsedInCarouse
attachmentCarouselPagerContext.onPinchGestureChange(!shouldPagerScroll);
- if (attachmentCarouselPagerContext.shouldPagerScroll.value === shouldPagerScroll) return;
+ if (attachmentCarouselPagerContext.shouldPagerScroll.value === shouldPagerScroll) {
+ return;
+ }
attachmentCarouselPagerContext.shouldPagerScroll.value = shouldPagerScroll;
}
diff --git a/src/components/AvatarWithImagePicker.js b/src/components/AvatarWithImagePicker.js
index aa50d6db574b..4cc70f00d1ae 100644
--- a/src/components/AvatarWithImagePicker.js
+++ b/src/components/AvatarWithImagePicker.js
@@ -21,8 +21,11 @@ import stylePropTypes from '../styles/stylePropTypes';
import * as FileUtils from '../libs/fileDownload/FileUtils';
import getImageResolution from '../libs/fileDownload/getImageResolution';
import PressableWithoutFeedback from './Pressable/PressableWithoutFeedback';
+import AttachmentModal from './AttachmentModal';
import DotIndicatorMessage from './DotIndicatorMessage';
import * as Browser from '../libs/Browser';
+import withNavigationFocus, {withNavigationFocusPropTypes} from './withNavigationFocus';
+import compose from '../libs/compose';
const propTypes = {
/** Avatar source to display */
@@ -79,7 +82,17 @@ const propTypes = {
// eslint-disable-next-line react/forbid-prop-types
errors: PropTypes.object,
+ /** Title for avatar preview modal */
+ headerTitle: PropTypes.string,
+
+ /** Avatar source for avatar preview modal */
+ previewSource: PropTypes.oneOfType([PropTypes.string, PropTypes.func]),
+
+ /** File name of the avatar */
+ originalFileName: PropTypes.string,
+
...withLocalizePropTypes,
+ ...withNavigationFocusPropTypes,
};
const defaultProps = {
@@ -98,6 +111,9 @@ const defaultProps = {
onErrorClose: () => {},
pendingAction: null,
errors: null,
+ headerTitle: '',
+ previewSource: '',
+ originalFileName: '',
};
class AvatarWithImagePicker extends React.Component {
@@ -129,6 +145,9 @@ class AvatarWithImagePicker extends React.Component {
}
componentDidUpdate(prevProps) {
+ if (!prevProps.isFocused && this.props.isFocused) {
+ this.setError(null, {});
+ }
if (!prevProps.isUploading && this.props.isUploading) {
this.animation.start();
} else if (prevProps.isUploading && !this.props.isUploading) {
@@ -273,58 +292,72 @@ class AvatarWithImagePicker extends React.Component {
-
- {({openPicker}) => {
- const menuItems = [
- {
- icon: Expensicons.Upload,
- text: this.props.translate('avatarWithImagePicker.uploadPhoto'),
- onSelected: () => {
- if (Browser.isSafari()) {
- return;
- }
- openPicker({
- onPicked: this.showAvatarCropModal,
+
+ {({show}) => (
+
+ {({openPicker}) => {
+ const menuItems = [
+ {
+ icon: Expensicons.Upload,
+ text: this.props.translate('avatarWithImagePicker.uploadPhoto'),
+ onSelected: () => {
+ if (Browser.isSafari()) {
+ return;
+ }
+ openPicker({
+ onPicked: this.showAvatarCropModal,
+ });
+ },
+ },
+ ];
+
+ // If current avatar isn't a default avatar, allow Remove Photo option
+ if (!this.props.isUsingDefaultAvatar) {
+ menuItems.push({
+ icon: Expensicons.Trashcan,
+ text: this.props.translate('avatarWithImagePicker.removePhoto'),
+ onSelected: () => {
+ this.setError(null, {});
+ this.props.onImageRemoved();
+ },
+ });
+
+ menuItems.push({
+ icon: Expensicons.Eye,
+ text: this.props.translate('avatarWithImagePicker.viewPhoto'),
+ onSelected: () => show(),
});
- },
- },
- ];
-
- // If current avatar isn't a default avatar, allow Remove Photo option
- if (!this.props.isUsingDefaultAvatar) {
- menuItems.push({
- icon: Expensicons.Trashcan,
- text: this.props.translate('avatarWithImagePicker.removePhoto'),
- onSelected: () => {
- this.setError(null, {});
- this.props.onImageRemoved();
- },
- });
- }
- return (
- this.setState({isMenuVisible: false})}
- onItemSelected={(item, index) => {
- this.setState({isMenuVisible: false});
- // In order for the file picker to open dynamically, the click
- // function must be called from within a event handler that was initiated
- // by the user on Safari.
- if (index === 0 && Browser.isSafari()) {
- openPicker({
- onPicked: this.showAvatarCropModal,
- });
- }
- }}
- menuItems={menuItems}
- anchorPosition={this.props.anchorPosition}
- withoutOverlay
- anchorRef={this.anchorRef}
- anchorAlignment={this.props.anchorAlignment}
- />
- );
- }}
-
+ }
+ return (
+ this.setState({isMenuVisible: false})}
+ onItemSelected={(item, index) => {
+ this.setState({isMenuVisible: false});
+ // In order for the file picker to open dynamically, the click
+ // function must be called from within a event handler that was initiated
+ // by the user on Safari.
+ if (index === 0 && Browser.isSafari()) {
+ openPicker({
+ onPicked: this.showAvatarCropModal,
+ });
+ }
+ }}
+ menuItems={menuItems}
+ anchorPosition={this.props.anchorPosition}
+ withoutOverlay
+ anchorRef={this.anchorRef}
+ anchorAlignment={this.props.anchorAlignment}
+ />
+ );
+ }}
+
+ )}
+
{this.state.validationError && (
Navigation.goBack(ROUTES.HOME),
shouldShowLink: true,
shouldShowBackButton: true,
onLinkPress: () => Navigation.dismissModal(),
diff --git a/src/components/Button/index.js b/src/components/Button/index.js
index bfde528a4750..c16860344837 100644
--- a/src/components/Button/index.js
+++ b/src/components/Button/index.js
@@ -218,6 +218,7 @@ class Button extends Component {
this.props.icon && styles.textAlignLeft,
...this.props.textStyles,
]}
+ dataSet={{[CONST.SELECTION_SCRAPER_HIDDEN_ELEMENT]: true}}
>
{this.props.text}
diff --git a/src/components/ButtonWithDropdownMenu.js b/src/components/ButtonWithDropdownMenu.js
index 62eeb3030619..54d6c0deac5a 100644
--- a/src/components/ButtonWithDropdownMenu.js
+++ b/src/components/ButtonWithDropdownMenu.js
@@ -32,7 +32,7 @@ const propTypes = {
style: PropTypes.oneOfType([PropTypes.arrayOf(PropTypes.object), PropTypes.object]),
/** Menu options to display */
- /** e.g. [{text: 'Pay with Expensify', icon: Wallet}, {text: 'PayPal', icon: PayPal}] */
+ /** e.g. [{text: 'Pay with Expensify', icon: Wallet}] */
options: PropTypes.arrayOf(
PropTypes.shape({
value: PropTypes.string.isRequired,
diff --git a/src/components/CategoryPicker/categoryPickerPropTypes.js b/src/components/CategoryPicker/categoryPickerPropTypes.js
index b8e24c199a73..6f2800a5d98f 100644
--- a/src/components/CategoryPicker/categoryPickerPropTypes.js
+++ b/src/components/CategoryPicker/categoryPickerPropTypes.js
@@ -2,14 +2,11 @@ import PropTypes from 'prop-types';
import categoryPropTypes from '../categoryPropTypes';
const propTypes = {
- /** The report ID of the IOU */
- reportID: PropTypes.string.isRequired,
-
/** The policyID we are getting categories for */
policyID: PropTypes.string,
- /** The type of IOU report, i.e. bill, request, send */
- iouType: PropTypes.string.isRequired,
+ /** The selected category of an expense */
+ selectedCategory: PropTypes.string,
/* Onyx Props */
/** Collection of categories attached to a policy */
@@ -19,18 +16,15 @@ const propTypes = {
/** Collection of recently used categories attached to a policy */
policyRecentlyUsedCategories: PropTypes.arrayOf(PropTypes.string),
- /* Onyx Props */
- /** Holds data related to Money Request view state, rather than the underlying Money Request data. */
- iou: PropTypes.shape({
- category: PropTypes.string.isRequired,
- }),
+ /** Callback to fire when a category is pressed */
+ onSubmit: PropTypes.func.isRequired,
};
const defaultProps = {
policyID: '',
+ selectedCategory: '',
policyCategories: {},
policyRecentlyUsedCategories: [],
- iou: {},
};
export {propTypes, defaultProps};
diff --git a/src/components/CategoryPicker/index.js b/src/components/CategoryPicker/index.js
index 91c7e82e7887..90f72f183815 100644
--- a/src/components/CategoryPicker/index.js
+++ b/src/components/CategoryPicker/index.js
@@ -5,34 +5,31 @@ import lodashGet from 'lodash/get';
import ONYXKEYS from '../../ONYXKEYS';
import {propTypes, defaultProps} from './categoryPickerPropTypes';
import styles from '../../styles/styles';
-import Navigation from '../../libs/Navigation/Navigation';
-import ROUTES from '../../ROUTES';
import CONST from '../../CONST';
-import * as IOU from '../../libs/actions/IOU';
import * as OptionsListUtils from '../../libs/OptionsListUtils';
import OptionsSelector from '../OptionsSelector';
import useLocalize from '../../hooks/useLocalize';
-function CategoryPicker({policyCategories, reportID, iouType, iou, policyRecentlyUsedCategories}) {
+function CategoryPicker({selectedCategory, policyCategories, policyRecentlyUsedCategories, onSubmit}) {
const {translate} = useLocalize();
const [searchValue, setSearchValue] = useState('');
- const policyCategoriesCount = _.size(policyCategories);
+ const policyCategoriesCount = OptionsListUtils.getEnabledCategoriesCount(_.values(policyCategories));
const isCategoriesCountBelowThreshold = policyCategoriesCount < CONST.CATEGORY_LIST_THRESHOLD;
const selectedOptions = useMemo(() => {
- if (!iou.category) {
+ if (!selectedCategory) {
return [];
}
return [
{
- name: iou.category,
+ name: selectedCategory,
enabled: true,
accountID: null,
},
];
- }, [iou.category]);
+ }, [selectedCategory]);
const initialFocusedIndex = useMemo(() => {
if (isCategoriesCountBelowThreshold && selectedOptions.length > 0) {
@@ -46,27 +43,13 @@ function CategoryPicker({policyCategories, reportID, iouType, iou, policyRecentl
}, [policyCategories, selectedOptions, isCategoriesCountBelowThreshold]);
const sections = useMemo(
- () => OptionsListUtils.getNewChatOptions({}, {}, [], searchValue, selectedOptions, [], false, false, true, policyCategories, policyRecentlyUsedCategories, false).categoryOptions,
+ () => OptionsListUtils.getFilteredOptions({}, {}, [], searchValue, selectedOptions, [], false, false, true, policyCategories, policyRecentlyUsedCategories, false).categoryOptions,
[policyCategories, policyRecentlyUsedCategories, searchValue, selectedOptions],
);
const headerMessage = OptionsListUtils.getHeaderMessage(lodashGet(sections, '[0].data.length', 0) > 0, false, searchValue);
const shouldShowTextInput = !isCategoriesCountBelowThreshold;
- const navigateBack = () => {
- Navigation.goBack(ROUTES.getMoneyRequestConfirmationRoute(iouType, reportID));
- };
-
- const updateCategory = (category) => {
- if (category.searchText === iou.category) {
- IOU.resetMoneyRequestCategory();
- } else {
- IOU.setMoneyRequestCategory(category.searchText);
- }
-
- navigateBack();
- };
-
return (
);
}
@@ -97,7 +80,4 @@ export default withOnyx({
policyRecentlyUsedCategories: {
key: ({policyID}) => `${ONYXKEYS.COLLECTION.POLICY_RECENTLY_USED_CATEGORIES}${policyID}`,
},
- iou: {
- key: ONYXKEYS.IOU,
- },
})(CategoryPicker);
diff --git a/src/components/CollapsibleSection/index.js b/src/components/CollapsibleSection/index.js
index e9c3a90a7b30..7009d1905e1d 100644
--- a/src/components/CollapsibleSection/index.js
+++ b/src/components/CollapsibleSection/index.js
@@ -51,6 +51,7 @@ class CollapsibleSection extends React.Component {
{this.props.title}
diff --git a/src/components/Composer/index.android.js b/src/components/Composer/index.android.js
index d0805cbcc7c3..1132efa9e50e 100644
--- a/src/components/Composer/index.android.js
+++ b/src/components/Composer/index.android.js
@@ -97,7 +97,9 @@ function Composer({shouldClear, onClear, isDisabled, maxLines, forwardedRef, isC
* @return {Number}
*/
const maxNumberOfLines = useMemo(() => {
- if (isComposerFullSize) return 1000000;
+ if (isComposerFullSize) {
+ return 1000000;
+ }
return maxLines;
}, [isComposerFullSize, maxLines]);
diff --git a/src/components/Composer/index.ios.js b/src/components/Composer/index.ios.js
index c0a3859e6d01..0b2c93f6639e 100644
--- a/src/components/Composer/index.ios.js
+++ b/src/components/Composer/index.ios.js
@@ -97,7 +97,9 @@ function Composer({shouldClear, onClear, isDisabled, maxLines, forwardedRef, isC
* @return {Number}
*/
const maxNumberOfLines = useMemo(() => {
- if (isComposerFullSize) return undefined;
+ if (isComposerFullSize) {
+ return;
+ }
return maxLines;
}, [isComposerFullSize, maxLines]);
diff --git a/src/components/ConfirmContent.js b/src/components/ConfirmContent.js
index 9a72d4e7d584..ab3e23d6b1c1 100644
--- a/src/components/ConfirmContent.js
+++ b/src/components/ConfirmContent.js
@@ -100,8 +100,8 @@ function ConfirmContent(props) {
diff --git a/src/components/ConfirmedRoute.js b/src/components/ConfirmedRoute.js
index 4bcdc4738a3c..dab30e60ca55 100644
--- a/src/components/ConfirmedRoute.js
+++ b/src/components/ConfirmedRoute.js
@@ -1,9 +1,9 @@
import React, {useEffect} from 'react';
import PropTypes from 'prop-types';
-import {View} from 'react-native';
import {withOnyx} from 'react-native-onyx';
import lodashGet from 'lodash/get';
+import lodashIsNil from 'lodash/isNil';
import _ from 'underscore';
import ONYXKEYS from '../ONYXKEYS';
import CONST from '../CONST';
@@ -13,9 +13,8 @@ import * as Expensicons from './Icon/Expensicons';
import theme from '../styles/themes/default';
import styles from '../styles/styles';
import transactionPropTypes from './transactionPropTypes';
-import BlockingView from './BlockingViews/BlockingView';
+import PendingMapView from './MapView/PendingMapView';
import useNetwork from '../hooks/useNetwork';
-import useLocalize from '../hooks/useLocalize';
import DistanceMapView from './DistanceMapView';
const propTypes = {
@@ -44,7 +43,7 @@ const getWaypointMarkers = (waypoints) => {
const lastWaypointIndex = numberOfWaypoints - 1;
return _.filter(
_.map(waypoints, (waypoint, key) => {
- if (!waypoint || waypoint.lng === undefined || waypoint.lat === undefined) {
+ if (!waypoint || lodashIsNil(waypoint.lat) || lodashIsNil(waypoint.lng)) {
return;
}
@@ -76,7 +75,6 @@ const getWaypointMarkers = (waypoints) => {
function ConfirmedRoute({mapboxAccessToken, transaction}) {
const {isOffline} = useNetwork();
- const {translate} = useLocalize();
const {route0: route} = transaction.routes || {};
const waypoints = lodashGet(transaction, 'comment.waypoints', {});
const coordinates = lodashGet(route, 'geometry.coordinates', []);
@@ -104,14 +102,7 @@ function ConfirmedRoute({mapboxAccessToken, transaction}) {
styleURL={CONST.MAPBOX.STYLE_URL}
/>
) : (
-
-
-
+
)}
>
);
diff --git a/src/components/CurrentUserPersonalDetailsSkeletonView/index.js b/src/components/CurrentUserPersonalDetailsSkeletonView/index.js
index 6e6c46e971c0..cc305a628820 100644
--- a/src/components/CurrentUserPersonalDetailsSkeletonView/index.js
+++ b/src/components/CurrentUserPersonalDetailsSkeletonView/index.js
@@ -1,5 +1,6 @@
import React from 'react';
import PropTypes from 'prop-types';
+import _ from 'underscore';
import SkeletonViewContentLoader from 'react-content-loader/native';
import {Circle, Rect} from 'react-native-svg';
import {View} from 'react-native';
@@ -12,14 +13,26 @@ import styles from '../../styles/styles';
const propTypes = {
/** Whether to animate the skeleton view */
shouldAnimate: PropTypes.bool,
+
+ /** The size of the avatar */
+ avatarSize: PropTypes.oneOf(_.values(CONST.AVATAR_SIZE)),
+
+ /** Background color of the skeleton view */
+ backgroundColor: PropTypes.string,
+
+ /** Foreground color of the skeleton view */
+ foregroundColor: PropTypes.string,
};
const defaultProps = {
shouldAnimate: true,
+ avatarSize: CONST.AVATAR_SIZE.LARGE,
+ backgroundColor: themeColors.highlightBG,
+ foregroundColor: themeColors.border,
};
function CurrentUserPersonalDetailsSkeletonView(props) {
- const avatarPlaceholderSize = StyleUtils.getAvatarSize(CONST.AVATAR_SIZE.LARGE);
+ const avatarPlaceholderSize = StyleUtils.getAvatarSize(props.avatarSize);
const avatarPlaceholderRadius = avatarPlaceholderSize / 2;
const spaceBetweenAvatarAndHeadline = styles.mb3.marginBottom + styles.mt1.marginTop + (variables.lineHeightXXLarge - variables.fontSizeXLarge) / 2;
const headlineSize = variables.fontSizeXLarge;
@@ -29,8 +42,8 @@ function CurrentUserPersonalDetailsSkeletonView(props) {
1;
+ const previousValidatedWaypoints = usePrevious(validatedWaypoints);
+ const haveValidatedWaypointsChanged = !_.isEqual(previousValidatedWaypoints, validatedWaypoints);
+ const isRouteAbsentWithoutErrors = !hasRoute && !hasRouteError;
+ const shouldFetchRoute = (isRouteAbsentWithoutErrors || haveValidatedWaypointsChanged) && !isLoadingRoute && _.size(validatedWaypoints) > 1;
const waypointMarkers = useMemo(
() =>
_.filter(
_.map(waypoints, (waypoint, key) => {
- if (!waypoint || !lodashHas(waypoint, 'lat') || !lodashHas(waypoint, 'lng') || lodashIsNull(waypoint.lat) || lodashIsNull(waypoint.lng)) {
+ if (!waypoint || lodashIsNil(waypoint.lat) || lodashIsNil(waypoint.lng)) {
return;
}
@@ -188,7 +189,7 @@ function DistanceRequest({iou, iouType, report, transaction, mapboxAccessToken,
useEffect(updateGradientVisibility, [scrollContainerHeight, scrollContentHeight]);
const navigateBack = () => {
- Navigation.goBack(isEditing ? ROUTES.getMoneyRequestConfirmationRoute(iouType, reportID) : null);
+ Navigation.goBack(isEditing ? ROUTES.getMoneyRequestConfirmationRoute(iouType, reportID) : ROUTES.HOME);
};
const navigateToNextPage = () => {
@@ -260,7 +261,10 @@ function DistanceRequest({iou, iouType, report, transaction, mapboxAccessToken,
diff --git a/src/components/DownloadAppModal.js b/src/components/DownloadAppModal.js
deleted file mode 100644
index c96c6b3d28c0..000000000000
--- a/src/components/DownloadAppModal.js
+++ /dev/null
@@ -1,79 +0,0 @@
-import React, {useState} from 'react';
-import PropTypes from 'prop-types';
-import {withOnyx} from 'react-native-onyx';
-import ONYXKEYS from '../ONYXKEYS';
-import styles from '../styles/styles';
-import CONST from '../CONST';
-import AppIcon from '../../assets/images/expensify-app-icon.svg';
-import useLocalize from '../hooks/useLocalize';
-import * as Link from '../libs/actions/Link';
-import * as Browser from '../libs/Browser';
-import getOperatingSystem from '../libs/getOperatingSystem';
-import setShowDownloadAppModal from '../libs/actions/DownloadAppModal';
-import ConfirmModal from './ConfirmModal';
-
-const propTypes = {
- /** ONYX PROP to hide banner for a user that has dismissed it */
- // eslint-disable-next-line react/forbid-prop-types
- showDownloadAppBanner: PropTypes.bool,
-
- /** Whether the user is logged in */
- isAuthenticated: PropTypes.bool.isRequired,
-};
-
-const defaultProps = {
- showDownloadAppBanner: true,
-};
-
-function DownloadAppModal({isAuthenticated, showDownloadAppBanner}) {
- const [shouldShowBanner, setShouldShowBanner] = useState(Browser.isMobile() && isAuthenticated && showDownloadAppBanner);
-
- const {translate} = useLocalize();
-
- const handleCloseBanner = () => {
- setShowDownloadAppModal(false);
- setShouldShowBanner(false);
- };
-
- let link = '';
-
- if (getOperatingSystem() === CONST.OS.IOS) {
- link = CONST.APP_DOWNLOAD_LINKS.IOS;
- } else if (getOperatingSystem() === CONST.OS.ANDROID) {
- link = CONST.APP_DOWNLOAD_LINKS.ANDROID;
- }
-
- const handleOpenAppStore = () => {
- setShowDownloadAppModal(false);
- setShouldShowBanner(false);
- Link.openExternalLink(link, true);
- };
-
- return (
-
- );
-}
-
-DownloadAppModal.displayName = 'DownloadAppModal';
-DownloadAppModal.propTypes = propTypes;
-DownloadAppModal.defaultProps = defaultProps;
-
-export default withOnyx({
- showDownloadAppBanner: {
- key: ONYXKEYS.SHOW_DOWNLOAD_APP_BANNER,
- },
-})(DownloadAppModal);
diff --git a/src/components/EmojiPicker/EmojiPickerMenu/index.js b/src/components/EmojiPicker/EmojiPickerMenu/index.js
index d3268ebc54b0..27fd199a3895 100755
--- a/src/components/EmojiPicker/EmojiPickerMenu/index.js
+++ b/src/components/EmojiPicker/EmojiPickerMenu/index.js
@@ -104,7 +104,9 @@ class EmojiPickerMenu extends Component {
}
componentDidUpdate(prevProps) {
- if (prevProps.frequentlyUsedEmojis === this.props.frequentlyUsedEmojis) return;
+ if (prevProps.frequentlyUsedEmojis === this.props.frequentlyUsedEmojis) {
+ return;
+ }
const {filteredEmojis, headerEmojis, headerRowIndices} = this.getEmojisAndHeaderRowIndices();
this.emojis = filteredEmojis;
diff --git a/src/components/ErrorBoundary/BaseErrorBoundary.js b/src/components/ErrorBoundary/BaseErrorBoundary.js
index e479b04f7ade..d626442e81dd 100644
--- a/src/components/ErrorBoundary/BaseErrorBoundary.js
+++ b/src/components/ErrorBoundary/BaseErrorBoundary.js
@@ -1,5 +1,6 @@
import React from 'react';
import PropTypes from 'prop-types';
+import {ErrorBoundary} from 'react-error-boundary';
import BootSplash from '../../libs/BootSplash';
import GenericErrorPage from '../../pages/ErrorPage/GenericErrorPage';
@@ -22,40 +23,27 @@ const defaultProps = {
* This component captures an error in the child component tree and logs it to the server
* It can be used to wrap the entire app as well as to wrap specific parts for more granularity
* @see {@link https://reactjs.org/docs/error-boundaries.html#where-to-place-error-boundaries}
+ * @return {React.Component}
*/
-class BaseErrorBoundary extends React.Component {
- constructor(props) {
- super(props);
- this.state = {hasError: false};
- this.clearError = this.clearError.bind(this);
- }
-
- static getDerivedStateFromError() {
- // Update state so the next render will show the fallback UI.
- return {hasError: true};
- }
-
- componentDidCatch(error, errorInfo) {
- this.props.logError(this.props.errorMessage, error, JSON.stringify(errorInfo));
-
+function BaseErrorBoundary({logError, errorMessage, children}) {
+ const catchError = (error, errorInfo) => {
+ logError(errorMessage, error, JSON.stringify(errorInfo));
// We hide the splash screen since the error might happened during app init
BootSplash.hide();
- }
-
- clearError() {
- this.setState({hasError: false});
- }
-
- render() {
- if (this.state.hasError) {
- return ;
- }
-
- return this.props.children;
- }
+ };
+
+ return (
+ }
+ onError={catchError}
+ >
+ {children}
+
+ );
}
BaseErrorBoundary.propTypes = propTypes;
BaseErrorBoundary.defaultProps = defaultProps;
+BaseErrorBoundary.displayName = 'BaseErrorBoundary';
export default BaseErrorBoundary;
diff --git a/src/components/ExceededCommentLength.js b/src/components/ExceededCommentLength.js
index 2aa50779e10f..7c9ec4d2db25 100644
--- a/src/components/ExceededCommentLength.js
+++ b/src/components/ExceededCommentLength.js
@@ -4,6 +4,7 @@ import {debounce} from 'lodash';
import {withOnyx} from 'react-native-onyx';
import CONST from '../CONST';
import * as ReportUtils from '../libs/ReportUtils';
+import useLocalize from '../hooks/useLocalize';
import Text from './Text';
import styles from '../styles/styles';
import ONYXKEYS from '../ONYXKEYS';
@@ -25,6 +26,7 @@ const defaultProps = {
};
function ExceededCommentLength(props) {
+ const {numberFormat, translate} = useLocalize();
const [commentLength, setCommentLength] = useState(0);
const updateCommentLength = useMemo(
() =>
@@ -44,7 +46,14 @@ function ExceededCommentLength(props) {
return null;
}
- return {`${commentLength}/${CONST.MAX_COMMENT_LENGTH}`};
+ return (
+
+ {translate('composer.commentExceededMaxLength', {formattedMaxLength: numberFormat(CONST.MAX_COMMENT_LENGTH)})}
+
+ );
}
ExceededCommentLength.propTypes = propTypes;
diff --git a/src/components/FocusTrapView/index.js b/src/components/FocusTrapView/index.js
deleted file mode 100644
index 2dcab7b9d998..000000000000
--- a/src/components/FocusTrapView/index.js
+++ /dev/null
@@ -1,75 +0,0 @@
-/*
- * The FocusTrap is only used on web and desktop
- */
-import React, {useEffect, useRef} from 'react';
-import FocusTrap from 'focus-trap-react';
-import {View} from 'react-native';
-import {PropTypes} from 'prop-types';
-import {useIsFocused} from '@react-navigation/native';
-
-const propTypes = {
- /** Children to wrap with FocusTrap */
- children: PropTypes.node.isRequired,
-
- /** Whether to enable the FocusTrap */
- enabled: PropTypes.bool,
-
- /**
- * Whether to disable auto focus
- * It is used when the component inside the FocusTrap have their own auto focus logic
- */
- shouldEnableAutoFocus: PropTypes.bool,
-};
-
-const defaultProps = {
- enabled: true,
- shouldEnableAutoFocus: false,
-};
-
-function FocusTrapView({enabled, shouldEnableAutoFocus, ...props}) {
- const isFocused = useIsFocused();
-
- /**
- * Focus trap always needs a focusable element.
- * In case that we don't have any focusable elements in the modal,
- * the FocusTrap will use fallback View element using this ref.
- */
- const ref = useRef(null);
-
- /**
- * We have to set the 'tabindex' attribute to 0 to make the View focusable.
- * Currently, it is not possible to set this through props.
- * After the upgrade of 'react-native-web' to version 0.19 we can use 'tabIndex={0}' prop instead.
- */
- useEffect(() => {
- if (!ref.current) {
- return;
- }
- ref.current.setAttribute('tabindex', '0');
- }, []);
-
- return enabled ? (
- shouldEnableAutoFocus && ref.current,
- fallbackFocus: () => ref.current,
- clickOutsideDeactivates: true,
- }}
- >
-
-
- ) : (
- props.children
- );
-}
-
-FocusTrapView.displayName = 'FocusTrapView';
-FocusTrapView.propTypes = propTypes;
-FocusTrapView.defaultProps = defaultProps;
-
-export default FocusTrapView;
diff --git a/src/components/FocusTrapView/index.native.js b/src/components/FocusTrapView/index.native.js
deleted file mode 100644
index 5720601f5a2b..000000000000
--- a/src/components/FocusTrapView/index.native.js
+++ /dev/null
@@ -1,11 +0,0 @@
-/*
- * The FocusTrap is only used on web and desktop
- */
-
-function FocusTrapView({children}) {
- return children;
-}
-
-FocusTrapView.displayName = 'FocusTrapView';
-
-export default FocusTrapView;
diff --git a/src/components/HTMLEngineProvider/BaseHTMLEngineProvider.js b/src/components/HTMLEngineProvider/BaseHTMLEngineProvider.js
index 5417f7af6820..4b61d55ae228 100755
--- a/src/components/HTMLEngineProvider/BaseHTMLEngineProvider.js
+++ b/src/components/HTMLEngineProvider/BaseHTMLEngineProvider.js
@@ -6,6 +6,7 @@ import htmlRenderers from './HTMLRenderers';
import * as HTMLEngineUtils from './htmlEngineUtils';
import styles from '../../styles/styles';
import fontFamily from '../../styles/fontFamily';
+import convertToLTR from '../../libs/convertToLTR';
const propTypes = {
/** Whether text elements should be selectable */
@@ -71,6 +72,10 @@ function BaseHTMLEngineProvider(props) {
enableCSSInlineProcessing={false}
systemFonts={_.values(fontFamily)}
fallbackFonts={fallbackFonts}
+ domVisitors={{
+ // eslint-disable-next-line no-param-reassign
+ onText: (text) => (text.data = convertToLTR(text.data)),
+ }}
>
- {/* Native devices do not support margin between nested text */}
+
{' '}
- {props.translate('reportActionCompose.edited')}
+
+ {props.translate('reportActionCompose.edited')}
+
);
}
diff --git a/src/components/HTMLEngineProvider/HTMLRenderers/MentionUserRenderer.js b/src/components/HTMLEngineProvider/HTMLRenderers/MentionUserRenderer.js
index 7f7753dfea44..947a15ac6efb 100644
--- a/src/components/HTMLEngineProvider/HTMLRenderers/MentionUserRenderer.js
+++ b/src/components/HTMLEngineProvider/HTMLRenderers/MentionUserRenderer.js
@@ -11,6 +11,7 @@ import personalDetailsPropType from '../../../pages/personalDetailsPropType';
import * as StyleUtils from '../../../styles/StyleUtils';
import * as PersonalDetailsUtils from '../../../libs/PersonalDetailsUtils';
import TextLink from '../../TextLink';
+import CONST from '../../../CONST';
const propTypes = {
...htmlRendererPropTypes,
@@ -31,8 +32,8 @@ const showUserDetails = (email) => Navigation.navigate(ROUTES.getDetailsRoute(em
function MentionUserRenderer(props) {
const defaultRendererProps = _.omit(props, ['TDefaultRenderer', 'style']);
- // We need to remove the leading @ from data as it is not part of the login
- const loginWithoutLeadingAt = props.tnode.data ? props.tnode.data.slice(1) : '';
+ // We need to remove the LTR unicode and leading @ from data as it is not part of the login
+ const loginWithoutLeadingAt = props.tnode.data ? props.tnode.data.replace(CONST.UNICODE.LTR, '').slice(1) : '';
const accountID = _.first(PersonalDetailsUtils.getAccountIDsByLogins([loginWithoutLeadingAt]));
diff --git a/src/components/HTMLEngineProvider/applyStrikethrough/index.js b/src/components/HTMLEngineProvider/applyStrikethrough/index.js
deleted file mode 100644
index b754a37dbde9..000000000000
--- a/src/components/HTMLEngineProvider/applyStrikethrough/index.js
+++ /dev/null
@@ -1,5 +0,0 @@
-function applyStrikethrough(html) {
- return html;
-}
-
-export default applyStrikethrough;
diff --git a/src/components/HTMLEngineProvider/applyStrikethrough/index.native.js b/src/components/HTMLEngineProvider/applyStrikethrough/index.native.js
deleted file mode 100644
index 266e4eb20f62..000000000000
--- a/src/components/HTMLEngineProvider/applyStrikethrough/index.native.js
+++ /dev/null
@@ -1,8 +0,0 @@
-function applyStrikethrough(html, isPendingDelete) {
- if (isPendingDelete) {
- return `${html}`;
- }
- return html;
-}
-
-export default applyStrikethrough;
diff --git a/src/components/StaticHeaderPageLayout.js b/src/components/HeaderPageLayout.js
similarity index 53%
rename from src/components/StaticHeaderPageLayout.js
rename to src/components/HeaderPageLayout.js
index f97e42329942..bec1e52b1cad 100644
--- a/src/components/StaticHeaderPageLayout.js
+++ b/src/components/HeaderPageLayout.js
@@ -10,6 +10,8 @@ import themeColors from '../styles/themes/default';
import * as StyleUtils from '../styles/StyleUtils';
import useWindowDimensions from '../hooks/useWindowDimensions';
import FixedFooter from './FixedFooter';
+import useNetwork from '../hooks/useNetwork';
+import * as Browser from '../libs/Browser';
const propTypes = {
...headerWithBackButtonPropTypes,
@@ -22,16 +24,26 @@ const propTypes = {
/** A fixed footer to display at the bottom of the page. */
footer: PropTypes.node,
+
+ /** The image to display in the upper half of the screen. */
+ header: PropTypes.node,
+
+ /** Style to apply to the header image container */
+ // eslint-disable-next-line react/forbid-prop-types
+ headerContainerStyles: PropTypes.arrayOf(PropTypes.object),
};
const defaultProps = {
backgroundColor: themeColors.appBG,
+ header: null,
+ headerContainerStyles: [],
footer: null,
};
-function StaticHeaderPageLayout({backgroundColor, children, image: Image, footer, imageContainerStyle, style, ...propsToPassToHeader}) {
- const {windowHeight} = useWindowDimensions();
-
+function HeaderPageLayout({backgroundColor, children, footer, headerContainerStyles, style, headerContent, ...propsToPassToHeader}) {
+ const {windowHeight, isSmallScreenWidth} = useWindowDimensions();
+ const {isOffline} = useNetwork();
+ const appBGColor = StyleUtils.getBackgroundColorStyle(themeColors.appBG);
const {titleColor, iconFill} = useMemo(() => {
const isColorfulBackground = backgroundColor !== themeColors.appBG;
return {
@@ -45,7 +57,7 @@ function StaticHeaderPageLayout({backgroundColor, children, image: Image, footer
style={[StyleUtils.getBackgroundColorStyle(backgroundColor)]}
shouldEnablePickerAvoiding={false}
includeSafeAreaPaddingBottom={false}
- offlineIndicatorStyle={[StyleUtils.getBackgroundColorStyle(themeColors.appBG)]}
+ offlineIndicatorStyle={[appBGColor]}
>
{({safeAreaPaddingBottomStyle}) => (
<>
@@ -55,27 +67,24 @@ function StaticHeaderPageLayout({backgroundColor, children, image: Image, footer
titleColor={titleColor}
iconFill={iconFill}
/>
-
+
+ {/** Safari on ios/mac has a bug where overscrolling the page scrollview shows green background color. This is a workaround to fix that. https://github.com/Expensify/App/issues/23422 */}
+ {Browser.isSafari() && (
+
+
+
+
+ )}
-
-
-
+ {!Browser.isSafari() && }
+
+ {headerContent}
- {children}
+ {children}
{!_.isNull(footer) && {footer}}
@@ -85,8 +94,8 @@ function StaticHeaderPageLayout({backgroundColor, children, image: Image, footer
);
}
-StaticHeaderPageLayout.propTypes = propTypes;
-StaticHeaderPageLayout.defaultProps = defaultProps;
-StaticHeaderPageLayout.displayName = 'StaticHeaderPageLayout';
+HeaderPageLayout.propTypes = propTypes;
+HeaderPageLayout.defaultProps = defaultProps;
+HeaderPageLayout.displayName = 'HeaderPageLayout';
-export default StaticHeaderPageLayout;
+export default HeaderPageLayout;
diff --git a/src/components/HeaderWithBackButton/index.js b/src/components/HeaderWithBackButton/index.js
index bbf905cc1ac2..aab54612e206 100755
--- a/src/components/HeaderWithBackButton/index.js
+++ b/src/components/HeaderWithBackButton/index.js
@@ -22,7 +22,7 @@ import useKeyboardState from '../../hooks/useKeyboardState';
function HeaderWithBackButton({
iconFill = undefined,
guidesCallTaskID = '',
- onBackButtonPress = () => Navigation.goBack(),
+ onBackButtonPress = () => Navigation.goBack(ROUTES.HOME),
onCloseButtonPress = () => Navigation.dismissModal(),
onDownloadButtonPress = () => {},
onThreeDotsButtonPress = () => {},
@@ -47,6 +47,7 @@ function HeaderWithBackButton({
},
threeDotsMenuItems = [],
children = null,
+ shouldOverlay = false,
}) {
const [isDownloadButtonActive, temporarilyDisableDownloadButton] = useThrottledButtonState();
const {translate} = useLocalize();
@@ -137,6 +138,7 @@ function HeaderWithBackButton({
menuItems={threeDotsMenuItems}
onIconPress={onThreeDotsButtonPress}
anchorPosition={threeDotsAnchorPosition}
+ shouldOverlay={shouldOverlay}
/>
)}
{shouldShowCloseButton && (
diff --git a/src/components/Hoverable/hoverablePropTypes.js b/src/components/Hoverable/hoverablePropTypes.js
index 9fb2e3bc7306..d483a06d6aaf 100644
--- a/src/components/Hoverable/hoverablePropTypes.js
+++ b/src/components/Hoverable/hoverablePropTypes.js
@@ -12,12 +12,16 @@ const propTypes = {
/** Function that executes when the mouse leaves the children. */
onHoverOut: PropTypes.func,
+
+ /** Decides whether to handle the scroll behaviour to show hover once the scroll ends */
+ shouldHandleScroll: PropTypes.bool,
};
const defaultProps = {
disabled: false,
onHoverIn: () => {},
onHoverOut: () => {},
+ shouldHandleScroll: false,
};
export {propTypes, defaultProps};
diff --git a/src/components/Hoverable/index.js b/src/components/Hoverable/index.js
index 0b560703a069..38ea64952a2c 100644
--- a/src/components/Hoverable/index.js
+++ b/src/components/Hoverable/index.js
@@ -1,7 +1,9 @@
import _ from 'underscore';
import React, {Component} from 'react';
+import {DeviceEventEmitter} from 'react-native';
import {propTypes, defaultProps} from './hoverablePropTypes';
import * as DeviceCapabilities from '../../libs/DeviceCapabilities';
+import CONST from '../../CONST';
/**
* It is necessary to create a Hoverable component instead of relying solely on Pressable support for hover state,
@@ -19,12 +21,36 @@ class Hoverable extends Component {
isHovered: false,
};
+ this.isHoveredRef = false;
+ this.isScrollingRef = false;
this.wrapperView = null;
}
componentDidMount() {
document.addEventListener('visibilitychange', this.handleVisibilityChange);
document.addEventListener('mouseover', this.checkHover);
+
+ /**
+ * Only add the scrolling listener if the shouldHandleScroll prop is true
+ * and the scrollingListener is not already set.
+ */
+ if (!this.scrollingListener && this.props.shouldHandleScroll) {
+ this.scrollingListener = DeviceEventEmitter.addListener(CONST.EVENTS.SCROLLING, (scrolling) => {
+ /**
+ * If user has stopped scrolling and the isHoveredRef is true, then we should update the hover state.
+ */
+ if (!scrolling && this.isHoveredRef) {
+ this.setState({isHovered: this.isHoveredRef}, this.props.onHoverIn);
+ } else if (scrolling && this.isHoveredRef) {
+ /**
+ * If the user has started scrolling and the isHoveredRef is true, then we should set the hover state to false.
+ * This is to hide the existing hover and reaction bar.
+ */
+ this.setState({isHovered: false}, this.props.onHoverOut);
+ }
+ this.isScrollingRef = scrolling;
+ });
+ }
}
componentDidUpdate(prevProps) {
@@ -40,6 +66,9 @@ class Hoverable extends Component {
componentWillUnmount() {
document.removeEventListener('visibilitychange', this.handleVisibilityChange);
document.removeEventListener('mouseover', this.checkHover);
+ if (this.scrollingListener) {
+ this.scrollingListener.remove();
+ }
}
/**
@@ -52,6 +81,19 @@ class Hoverable extends Component {
return;
}
+ /**
+ * Capture whther or not the user is hovering over the component.
+ * We will use this to determine if we should update the hover state when the user has stopped scrolling.
+ */
+ this.isHoveredRef = isHovered;
+
+ /**
+ * If the isScrollingRef is true, then the user is scrolling and we should not update the hover state.
+ */
+ if (this.isScrollingRef && this.props.shouldHandleScroll && !this.state.isHovered) {
+ return;
+ }
+
if (isHovered !== this.state.isHovered) {
this.setState({isHovered}, isHovered ? this.props.onHoverIn : this.props.onHoverOut);
}
diff --git a/src/components/Icon/Expensicons.js b/src/components/Icon/Expensicons.js
index 42db57068edc..a0c8b72d755a 100644
--- a/src/components/Icon/Expensicons.js
+++ b/src/components/Icon/Expensicons.js
@@ -82,7 +82,6 @@ import NewWorkspace from '../../../assets/images/new-workspace.svg';
import Offline from '../../../assets/images/offline.svg';
import OfflineCloud from '../../../assets/images/offline-cloud.svg';
import Paperclip from '../../../assets/images/paperclip.svg';
-import PayPal from '../../../assets/images/paypal.svg';
import Paycheck from '../../../assets/images/paycheck.svg';
import Pencil from '../../../assets/images/pencil.svg';
import Phone from '../../../assets/images/phone.svg';
@@ -216,7 +215,6 @@ export {
Offline,
OfflineCloud,
Paperclip,
- PayPal,
Paycheck,
Pencil,
Phone,
diff --git a/src/components/IllustratedHeaderPageLayout.js b/src/components/IllustratedHeaderPageLayout.js
index 92a9c8b8552b..ac916117094b 100644
--- a/src/components/IllustratedHeaderPageLayout.js
+++ b/src/components/IllustratedHeaderPageLayout.js
@@ -1,18 +1,10 @@
-import _ from 'underscore';
import React from 'react';
import PropTypes from 'prop-types';
-import {ScrollView, View} from 'react-native';
import Lottie from 'lottie-react-native';
import headerWithBackButtonPropTypes from './HeaderWithBackButton/headerWithBackButtonPropTypes';
-import HeaderWithBackButton from './HeaderWithBackButton';
-import ScreenWrapper from './ScreenWrapper';
import styles from '../styles/styles';
import themeColors from '../styles/themes/default';
-import * as StyleUtils from '../styles/StyleUtils';
-import useWindowDimensions from '../hooks/useWindowDimensions';
-import FixedFooter from './FixedFooter';
-import useNetwork from '../hooks/useNetwork';
-import * as Browser from '../libs/Browser';
+import HeaderPageLayout from './HeaderPageLayout';
const propTypes = {
...headerWithBackButtonPropTypes,
@@ -40,54 +32,28 @@ const defaultProps = {
};
function IllustratedHeaderPageLayout({backgroundColor, children, illustration, footer, overlayContent, ...propsToPassToHeader}) {
- const {isOffline} = useNetwork();
- const {isSmallScreenWidth, windowHeight} = useWindowDimensions();
- const appBGColor = StyleUtils.getBackgroundColorStyle(themeColors.appBG);
-
return (
-
- {({safeAreaPaddingBottomStyle}) => (
+
-
-
- {/* Safari on ios/mac has a bug where overscrolling the page scrollview shows green the background color. This is a workaround to fix that. https://github.com/Expensify/App/issues/23422 */}
- {Browser.isSafari() && (
-
-
-
-
- )}
-
- {!Browser.isSafari() && }
-
-
- {overlayContent && overlayContent()}
-
- {children}
-
- {!_.isNull(footer) && {footer}}
-
+ {overlayContent && overlayContent()}
>
- )}
-
+ }
+ headerContainerStyles={[styles.justifyContentCenter, styles.w100]}
+ footer={footer}
+ // eslint-disable-next-line react/jsx-props-no-spreading
+ {...propsToPassToHeader}
+ >
+ {children}
+
);
}
diff --git a/src/components/ImageView/index.js b/src/components/ImageView/index.js
index c92bd7738253..e0dce180043b 100644
--- a/src/components/ImageView/index.js
+++ b/src/components/ImageView/index.js
@@ -87,7 +87,9 @@ function ImageView({isAuthTokenRequired, url, fileName}) {
};
const imageLoadingStart = () => {
- if (!isLoading) return;
+ if (!isLoading) {
+ return;
+ }
setIsLoading(true);
setZoomScale(0);
setIsZoomed(false);
@@ -141,11 +143,16 @@ function ImageView({isAuthTokenRequired, url, fileName}) {
*/
const onContainerPress = (e) => {
if (!isZoomed && !isDragging) {
- const {offsetX, offsetY} = e.nativeEvent;
- // Dividing clicked positions by the zoom scale to get coordinates
- // so that once we zoom we will scroll to the clicked location.
- const delta = getScrollOffset(offsetX / zoomScale, offsetY / zoomScale);
- setZoomDelta(delta);
+ if (e.nativeEvent) {
+ const {offsetX, offsetY} = e.nativeEvent;
+
+ // Dividing clicked positions by the zoom scale to get coordinates
+ // so that once we zoom we will scroll to the clicked location.
+ const delta = getScrollOffset(offsetX / zoomScale, offsetY / zoomScale);
+ setZoomDelta(delta);
+ } else {
+ setZoomDelta({offsetX: 0, offsetY: 0});
+ }
}
if (isZoomed && isDragging && isMouseDown) {
@@ -225,14 +232,14 @@ function ImageView({isAuthTokenRequired, url, fileName}) {
source={{uri: url}}
isAuthTokenRequired={isAuthTokenRequired}
// Hide image until finished loading to prevent showing preview with wrong dimensions.
- style={isLoading ? undefined : [styles.w100, styles.h100]}
+ style={isLoading || zoomScale === 0 ? undefined : [styles.w100, styles.h100]}
// When Image dimensions are lower than the container boundary(zoomscale <= 1), use `contain` to render the image with natural dimensions.
// Both `center` and `contain` keeps the image centered on both x and y axis.
resizeMode={zoomScale > 1 ? Image.resizeMode.center : Image.resizeMode.contain}
onLoadStart={imageLoadingStart}
onLoad={imageLoad}
/>
- {isLoading && }
+ {(isLoading || zoomScale === 0) && }
);
}
diff --git a/src/components/InvertedFlatList/BaseInvertedFlatList.js b/src/components/InvertedFlatList/BaseInvertedFlatList.js
index d3fcda0ea5fd..0bfffb733052 100644
--- a/src/components/InvertedFlatList/BaseInvertedFlatList.js
+++ b/src/components/InvertedFlatList/BaseInvertedFlatList.js
@@ -136,8 +136,6 @@ class BaseInvertedFlatList extends Component {
// Native platforms do not need to measure items and work fine without this.
// Web requires that items be measured or else crazy things happen when scrolling.
getItemLayout={this.props.shouldMeasureItems ? this.getItemLayout : undefined}
- // We keep this property very low so that chat switching remains fast
- maxToRenderPerBatch={1}
windowSize={15}
// Commenting the line below as it breaks the unread indicator test
diff --git a/src/components/InvertedFlatList/index.js b/src/components/InvertedFlatList/index.js
index 923a17210af7..d46cd5801605 100644
--- a/src/components/InvertedFlatList/index.js
+++ b/src/components/InvertedFlatList/index.js
@@ -1,9 +1,10 @@
-import React, {forwardRef} from 'react';
+import React, {forwardRef, useEffect, useRef} from 'react';
import PropTypes from 'prop-types';
-import {FlatList, StyleSheet} from 'react-native';
+import {DeviceEventEmitter, FlatList, StyleSheet} from 'react-native';
import _ from 'underscore';
import BaseInvertedFlatList from './BaseInvertedFlatList';
import styles from '../../styles/styles';
+import CONST from '../../CONST';
const propTypes = {
/** Passed via forwardRef so we can access the FlatList ref */
@@ -14,43 +15,121 @@ const propTypes = {
/** Any additional styles to apply */
// eslint-disable-next-line react/forbid-prop-types
contentContainerStyle: PropTypes.any,
+
+ /** Same as for FlatList */
+ onScroll: PropTypes.func,
};
// This is adapted from https://codesandbox.io/s/react-native-dsyse
// It's a HACK alert since FlatList has inverted scrolling on web
-class InvertedFlatList extends React.Component {
- constructor(props) {
- super(props);
+function InvertedFlatList(props) {
+ const {innerRef, contentContainerStyle} = props;
+ const listRef = React.createRef();
- this.list = undefined;
- }
+ const lastScrollEvent = useRef(null);
+ const scrollEndTimeout = useRef(null);
+ const updateInProgress = useRef(false);
+ const eventHandler = useRef(null);
- componentDidMount() {
- if (!_.isFunction(this.props.innerRef)) {
+ useEffect(() => {
+ if (!_.isFunction(innerRef)) {
// eslint-disable-next-line no-param-reassign
- this.props.innerRef.current = this.list;
+ innerRef.current = listRef.current;
} else {
- this.props.innerRef(this.list);
+ innerRef(listRef);
+ }
+
+ return () => {
+ if (scrollEndTimeout.current) {
+ clearTimeout(scrollEndTimeout.current);
+ }
+
+ if (eventHandler.current) {
+ eventHandler.current.remove();
+ }
+ };
+ }, [innerRef, listRef]);
+
+ /**
+ * Emits when the scrolling is in progress. Also,
+ * invokes the onScroll callback function from props.
+ *
+ * @param {Event} event - The onScroll event from the FlatList
+ */
+ const onScroll = (event) => {
+ props.onScroll(event);
+
+ if (!updateInProgress.current) {
+ updateInProgress.current = true;
+ eventHandler.current = DeviceEventEmitter.emit(CONST.EVENTS.SCROLLING, true);
+ }
+ };
+
+ /**
+ * Emits when the scrolling has ended.
+ */
+ const onScrollEnd = () => {
+ eventHandler.current = DeviceEventEmitter.emit(CONST.EVENTS.SCROLLING, false);
+ updateInProgress.current = false;
+ };
+
+ /**
+ * Decides whether the scrolling has ended or not. If it has ended,
+ * then it calls the onScrollEnd function. Otherwise, it calls the
+ * onScroll function and pass the event to it.
+ *
+ * This is a temporary work around, since react-native-web doesn't
+ * support onScrollBeginDrag and onScrollEndDrag props for FlatList.
+ * More info:
+ * https://github.com/necolas/react-native-web/pull/1305
+ *
+ * This workaround is taken from below and refactored to fit our needs:
+ * https://github.com/necolas/react-native-web/issues/1021#issuecomment-984151185
+ *
+ * @param {Event} event - The onScroll event from the FlatList
+ */
+ const handleScroll = (event) => {
+ onScroll(event);
+ const timestamp = Date.now();
+
+ if (scrollEndTimeout.current) {
+ clearTimeout(scrollEndTimeout.current);
}
- }
-
- render() {
- return (
- (this.list = el)}
- shouldMeasureItems
- contentContainerStyle={StyleSheet.compose(this.props.contentContainerStyle, styles.justifyContentEnd)}
- />
- );
- }
+
+ if (lastScrollEvent.current) {
+ scrollEndTimeout.current = setTimeout(() => {
+ if (lastScrollEvent.current !== timestamp) {
+ return;
+ }
+ // Scroll has ended
+ lastScrollEvent.current = null;
+ onScrollEnd();
+ }, 250);
+ }
+
+ lastScrollEvent.current = timestamp;
+ };
+
+ return (
+
+ );
}
InvertedFlatList.propTypes = propTypes;
InvertedFlatList.defaultProps = {
contentContainerStyle: {},
+ onScroll: () => {},
};
export default forwardRef((props, ref) => (
diff --git a/src/components/KYCWall/BaseKYCWall.js b/src/components/KYCWall/BaseKYCWall.js
index 4684901e47b1..ebf115c5de59 100644
--- a/src/components/KYCWall/BaseKYCWall.js
+++ b/src/components/KYCWall/BaseKYCWall.js
@@ -142,7 +142,6 @@ class KYCWall extends React.Component {
vertical: this.state.anchorPositionVertical,
horizontal: this.state.anchorPositionHorizontal,
}}
- shouldShowPaypal={false}
onItemSelected={(item) => {
this.setState({shouldShowAddPaymentMenu: false});
if (item === CONST.PAYMENT_METHODS.BANK_ACCOUNT) {
diff --git a/src/components/LHNOptionsList/OptionRowLHNData.js b/src/components/LHNOptionsList/OptionRowLHNData.js
index 2c51d6332946..4eebdd387fab 100644
--- a/src/components/LHNOptionsList/OptionRowLHNData.js
+++ b/src/components/LHNOptionsList/OptionRowLHNData.js
@@ -195,6 +195,7 @@ export default React.memo(
key: ONYXKEYS.NVP_PREFERRED_LOCALE,
},
}),
+ // eslint-disable-next-line rulesdir/no-multiple-onyx-in-file
withOnyx({
parentReportActions: {
key: ({fullReport}) => `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${fullReport.parentReportID}`,
@@ -209,6 +210,7 @@ export default React.memo(
// However, performance overhead of this is minimized by using memos inside the component.
receiptTransactions: {key: ONYXKEYS.COLLECTION.TRANSACTION},
}),
+ // eslint-disable-next-line rulesdir/no-multiple-onyx-in-file
withOnyx({
transaction: {
key: ({fullReport, parentReportActions}) =>
diff --git a/src/components/MagicCodeInput.js b/src/components/MagicCodeInput.js
index b21a275a6597..454aacc8a03b 100644
--- a/src/components/MagicCodeInput.js
+++ b/src/components/MagicCodeInput.js
@@ -258,7 +258,13 @@ function MagicCodeInput(props) {
{/* Hide the input above the text. Cannot set opacity to 0 as it would break pasting on iOS Safari. */}
(inputRefs.current[index] = ref)}
+ ref={(ref) => {
+ inputRefs.current[index] = ref;
+ // Setting attribute type to "search" to prevent Password Manager from appearing in Mobile Chrome
+ if (ref && ref.setAttribute) {
+ ref.setAttribute('type', 'search');
+ }
+ }}
autoFocus={index === 0 && props.autoFocus}
inputMode="numeric"
textContentType="oneTimeCode"
diff --git a/src/components/MapView/MapView.tsx b/src/components/MapView/MapView.tsx
index 7a2248ffafb9..d9f51e111a43 100644
--- a/src/components/MapView/MapView.tsx
+++ b/src/components/MapView/MapView.tsx
@@ -54,7 +54,9 @@ const MapView = forwardRef(({accessToken, style, ma
}, [accessToken]);
const setMapIdle = (e: MapState) => {
- if (e.gestures.isGestureActive) return;
+ if (e.gestures.isGestureActive) {
+ return;
+ }
setIsIdle(true);
if (onMapReady) {
onMapReady();
diff --git a/src/components/MapView/MapView.web.tsx b/src/components/MapView/MapView.web.tsx
index d1b26df8b00e..ce13cee10f54 100644
--- a/src/components/MapView/MapView.web.tsx
+++ b/src/components/MapView/MapView.web.tsx
@@ -44,6 +44,21 @@ const MapView = forwardRef(
map.fitBounds([northEast, southWest], {padding: mapPadding});
}, [waypoints, mapRef, mapPadding]);
+ useEffect(() => {
+ if (!mapRef) {
+ return;
+ }
+
+ const resizeObserver = new ResizeObserver(() => {
+ mapRef.resize();
+ });
+ resizeObserver.observe(mapRef.getContainer());
+
+ return () => {
+ resizeObserver?.disconnect();
+ };
+ }, [mapRef]);
+
useImperativeHandle(
ref,
() => ({
diff --git a/src/components/MapView/MapViewTypes.ts b/src/components/MapView/MapViewTypes.ts
index de32528d077e..dc56cb4642c4 100644
--- a/src/components/MapView/MapViewTypes.ts
+++ b/src/components/MapView/MapViewTypes.ts
@@ -27,6 +27,14 @@ type DirectionProps = {
coordinates: Array<[number, number]>;
};
+type PendingMapViewProps = {
+ /** Title message below the icon */
+ title?: string;
+
+ /** Subtitle message below the title */
+ subtitle?: string;
+};
+
// Initial state of the map
type InitialState = {
// Coordinate on which to center the map
@@ -55,4 +63,4 @@ type MapViewHandle = {
fitBounds: (ne: [number, number], sw: [number, number], paddingConfig?: number | number[], animationDuration?: number) => void;
};
-export type {DirectionStyle, WayPoint, MapViewProps, DirectionProps, MapViewHandle};
+export type {DirectionStyle, WayPoint, MapViewProps, DirectionProps, PendingMapViewProps, MapViewHandle};
diff --git a/src/components/MapView/PendingMapView.tsx b/src/components/MapView/PendingMapView.tsx
new file mode 100644
index 000000000000..6a35d2a9c369
--- /dev/null
+++ b/src/components/MapView/PendingMapView.tsx
@@ -0,0 +1,36 @@
+import React from 'react';
+import {View} from 'react-native';
+import _ from 'lodash';
+import variables from '../../styles/variables';
+import styles from '../../styles/styles';
+import Icon from '../Icon';
+import {PendingMapViewProps} from './MapViewTypes';
+import BlockingView from '../BlockingViews/BlockingView';
+import * as Expensicons from '../Icon/Expensicons';
+
+function PendingMapView({title = '', subtitle = ''}: PendingMapViewProps) {
+ const hasTextContent = !_.isEmpty(title) || !_.isEmpty(subtitle);
+
+ return (
+
+ {hasTextContent ? (
+
+ ) : (
+
+
+
+ )}
+
+ );
+}
+
+export default PendingMapView;
diff --git a/src/components/MapView/responder/index.android.ts b/src/components/MapView/responder/index.android.ts
new file mode 100644
index 000000000000..a0fce71d8ef5
--- /dev/null
+++ b/src/components/MapView/responder/index.android.ts
@@ -0,0 +1,8 @@
+import {PanResponder} from 'react-native';
+
+const responder = PanResponder.create({
+ onStartShouldSetPanResponder: () => true,
+ onPanResponderTerminationRequest: () => false,
+});
+
+export default responder;
diff --git a/src/components/MapView/responder/index.ts b/src/components/MapView/responder/index.ts
index a0fce71d8ef5..422d6c1b4963 100644
--- a/src/components/MapView/responder/index.ts
+++ b/src/components/MapView/responder/index.ts
@@ -1,7 +1,7 @@
import {PanResponder} from 'react-native';
const responder = PanResponder.create({
- onStartShouldSetPanResponder: () => true,
+ onMoveShouldSetPanResponder: () => true,
onPanResponderTerminationRequest: () => false,
});
diff --git a/src/components/MenuItem.js b/src/components/MenuItem.js
index 4f654df2a7b2..bb4eeb7a18ac 100644
--- a/src/components/MenuItem.js
+++ b/src/components/MenuItem.js
@@ -1,6 +1,7 @@
import _ from 'underscore';
-import React from 'react';
+import React, {useEffect, useMemo} from 'react';
import {View} from 'react-native';
+import ExpensiMark from 'expensify-common/lib/ExpensiMark';
import Text from './Text';
import styles from '../styles/styles';
import themeColors from '../styles/themes/default';
@@ -24,6 +25,10 @@ import * as Session from '../libs/actions/Session';
import Hoverable from './Hoverable';
import useWindowDimensions from '../hooks/useWindowDimensions';
import RenderHTML from './RenderHTML';
+import getPlatform from '../libs/getPlatform';
+
+const platform = getPlatform();
+const isNative = platform === CONST.PLATFORM.IOS || platform === CONST.PLATFORM.ANDROID;
const propTypes = menuItemPropTypes;
@@ -34,6 +39,7 @@ const defaultProps = {
shouldShowBasicTitle: false,
shouldShowDescriptionOnTop: false,
shouldShowHeaderTitle: false,
+ shouldParseTitle: false,
wrapperStyle: [],
style: styles.popoverMenuItem,
titleStyle: {},
@@ -79,6 +85,7 @@ const defaultProps = {
const MenuItem = React.forwardRef((props, ref) => {
const {isSmallScreenWidth} = useWindowDimensions();
+ const [html, setHtml] = React.useState('');
const isDeleted = _.contains(props.style, styles.offlineFeedback.deleted);
const descriptionVerticalMargin = props.shouldShowDescriptionOnTop ? styles.mb1 : styles.mt1;
@@ -106,6 +113,28 @@ const MenuItem = React.forwardRef((props, ref) => {
const fallbackAvatarSize = props.viewMode === CONST.OPTION_MODE.COMPACT ? CONST.AVATAR_SIZE.SMALL : CONST.AVATAR_SIZE.DEFAULT;
+ const titleRef = React.useRef('');
+ useEffect(() => {
+ if (!props.title || (titleRef.current.length && titleRef.current === props.title) || !props.shouldParseTitle) {
+ return;
+ }
+ const parser = new ExpensiMark();
+ setHtml(parser.replace(convertToLTR(props.title)));
+ titleRef.current = props.title;
+ }, [props.title, props.shouldParseTitle]);
+
+ const getProcessedTitle = useMemo(() => {
+ if (props.shouldRenderAsHTML) {
+ return convertToLTR(props.title);
+ }
+
+ if (props.shouldParseTitle) {
+ return html;
+ }
+
+ return '';
+ }, [props.title, props.shouldRenderAsHTML, props.shouldParseTitle, html]);
+
return (
{(isHovered) => (
@@ -222,12 +251,20 @@ const MenuItem = React.forwardRef((props, ref) => {
)}
- {Boolean(props.title) && Boolean(props.shouldRenderAsHTML) && }
-
- {Boolean(props.title) && !props.shouldRenderAsHTML && (
+ {Boolean(props.title) &&
+ (Boolean(props.shouldRenderAsHTML) || (Boolean(props.shouldParseTitle) && Boolean(html.length))) &&
+ (isNative ? (
+
+ ) : (
+
+
+
+ ))}
+ {!props.shouldRenderAsHTML && !html.length && Boolean(props.title) && (
{convertToLTR(props.title)}
diff --git a/src/components/Modal/BaseModal.js b/src/components/Modal/BaseModal.js
index fc64d8f38243..051c4ba3f80a 100644
--- a/src/components/Modal/BaseModal.js
+++ b/src/components/Modal/BaseModal.js
@@ -1,4 +1,4 @@
-import React, {forwardRef, useCallback, useEffect, useMemo} from 'react';
+import React, {forwardRef, useCallback, useEffect, useMemo, useRef} from 'react';
import {View} from 'react-native';
import PropTypes from 'prop-types';
import ReactNativeModal from 'react-native-modal';
@@ -14,6 +14,7 @@ import variables from '../../styles/variables';
import CONST from '../../CONST';
import ComposerFocusManager from '../../libs/ComposerFocusManager';
import useNativeDriver from '../../libs/useNativeDriver';
+import usePrevious from '../../hooks/usePrevious';
const propTypes = {
...modalPropTypes,
@@ -55,6 +56,9 @@ function BaseModal({
const safeAreaInsets = useSafeAreaInsets();
+ const isVisibleRef = useRef(isVisible);
+ const wasVisible = usePrevious(isVisible);
+
/**
* Hides modal
* @param {Boolean} [callHideCallback=true] Should we call the onModalHide callback
@@ -76,20 +80,25 @@ function BaseModal({
);
useEffect(() => {
- Modal.willAlertModalBecomeVisible(isVisible);
-
- // To handle closing any modal already visible when this modal is mounted, i.e. PopoverReportActionContextMenu
- Modal.setCloseModal(isVisible ? onClose : null);
- }, [isVisible, onClose]);
+ isVisibleRef.current = isVisible;
+ if (isVisible) {
+ Modal.willAlertModalBecomeVisible(true);
+ // To handle closing any modal already visible when this modal is mounted, i.e. PopoverReportActionContextMenu
+ Modal.setCloseModal(onClose);
+ } else if (wasVisible && !isVisible) {
+ Modal.willAlertModalBecomeVisible(false);
+ Modal.setCloseModal(null);
+ }
+ }, [isVisible, wasVisible, onClose]);
useEffect(
() => () => {
// Only trigger onClose and setModalVisibility if the modal is unmounting while visible.
- if (isVisible) {
- hideModal(true);
- Modal.willAlertModalBecomeVisible(false);
+ if (!isVisibleRef.current) {
+ return;
}
-
+ hideModal(true);
+ Modal.willAlertModalBecomeVisible(false);
// To prevent closing any modal already unmounted when this modal still remains as visible state
Modal.setCloseModal(null);
},
diff --git a/src/components/MoneyReportHeader.js b/src/components/MoneyReportHeader.js
index 06a7f0b57b8e..3ccd61d32961 100644
--- a/src/components/MoneyReportHeader.js
+++ b/src/components/MoneyReportHeader.js
@@ -79,7 +79,6 @@ function MoneyReportHeader({session, personalDetails, policy, chatReport, report
return isManager && !isApproved && !isSettled;
}, [policyType, isManager, isApproved, isSettled]);
const bankAccountRoute = ReportUtils.getBankAccountRoute(chatReport);
- const shouldShowPaypal = Boolean(lodashGet(personalDetails, [moneyRequestReport.managerID, 'payPalMeAddress']));
const formattedAmount = CurrencyUtils.convertToDisplayString(reportTotal, moneyRequestReport.currency);
return (
@@ -99,11 +98,10 @@ function MoneyReportHeader({session, personalDetails, policy, chatReport, report
IOU.payMoneyRequest(paymentType, chatReport, moneyRequestReport)}
- enablePaymentsRoute={ROUTES.BANK_ACCOUNT_NEW}
+ enablePaymentsRoute={ROUTES.ENABLE_PAYMENTS}
addBankAccountRoute={bankAccountRoute}
shouldShowPaymentOptions
style={[styles.pv2]}
@@ -128,11 +126,10 @@ function MoneyReportHeader({session, personalDetails, policy, chatReport, report
IOU.payMoneyRequest(paymentType, chatReport, moneyRequestReport)}
- enablePaymentsRoute={ROUTES.BANK_ACCOUNT_NEW}
+ enablePaymentsRoute={ROUTES.ENABLE_PAYMENTS}
addBankAccountRoute={bankAccountRoute}
shouldShowPaymentOptions
formattedAmount={formattedAmount}
diff --git a/src/components/MoneyRequestConfirmationList.js b/src/components/MoneyRequestConfirmationList.js
index da98d324681e..13471407914f 100755
--- a/src/components/MoneyRequestConfirmationList.js
+++ b/src/components/MoneyRequestConfirmationList.js
@@ -5,6 +5,7 @@ import {format} from 'date-fns';
import _ from 'underscore';
import {View} from 'react-native';
import lodashGet from 'lodash/get';
+import Text from './Text';
import styles from '../styles/styles';
import * as ReportUtils from '../libs/ReportUtils';
import * as OptionsListUtils from '../libs/OptionsListUtils';
@@ -30,11 +31,13 @@ import Image from './Image';
import useLocalize from '../hooks/useLocalize';
import * as ReceiptUtils from '../libs/ReceiptUtils';
import categoryPropTypes from './categoryPropTypes';
+import Switch from './Switch';
import tagPropTypes from './tagPropTypes';
import ConfirmedRoute from './ConfirmedRoute';
import transactionPropTypes from './transactionPropTypes';
import DistanceRequestUtils from '../libs/DistanceRequestUtils';
import * as IOU from '../libs/actions/IOU';
+import * as TransactionUtils from '../libs/TransactionUtils';
const propTypes = {
/** Callback to inform parent modal of success */
@@ -191,18 +194,32 @@ function MoneyRequestConfirmationList(props) {
const {unit, rate, currency} = props.mileageRate;
const distance = lodashGet(transaction, 'routes.route0.distance', 0);
const shouldCalculateDistanceAmount = props.isDistanceRequest && props.iouAmount === 0;
- const shouldCategoryBeEditable = !_.isEmpty(props.policyCategories) && Permissions.canUseCategories(props.betas);
+
+ // A flag for verifying that the current report is a sub-report of a workspace chat
+ const isPolicyExpenseChat = useMemo(() => ReportUtils.isPolicyExpenseChat(ReportUtils.getRootParentReport(ReportUtils.getReport(props.reportID))), [props.reportID]);
+
+ // A flag for showing the categories field
+ const shouldShowCategories = isPolicyExpenseChat && Permissions.canUseCategories(props.betas) && OptionsListUtils.hasEnabledOptions(_.values(props.policyCategories));
// Fetches the first tag list of the policy
const tagListKey = _.first(_.keys(props.policyTags));
const tagList = lodashGet(props.policyTags, [tagListKey, 'tags'], []);
const tagListName = lodashGet(props.policyTags, [tagListKey, 'name'], '');
const canUseTags = Permissions.canUseTags(props.betas);
+ // A flag for showing the tags field
+ const shouldShowTags = isPolicyExpenseChat && canUseTags && _.any(tagList, (tag) => tag.enabled);
- const formattedAmount = CurrencyUtils.convertToDisplayString(
- shouldCalculateDistanceAmount ? DistanceRequestUtils.getDistanceRequestAmount(distance, unit, rate) : props.iouAmount,
- props.isDistanceRequest ? currency : props.iouCurrencyCode,
- );
+ // A flag for showing the billable field
+ const shouldShowBillable = canUseTags && !lodashGet(props.policy, 'disabledFields.defaultBillable', true);
+
+ const hasRoute = TransactionUtils.hasRoute(transaction);
+ const isDistanceRequestWithoutRoute = props.isDistanceRequest && !hasRoute;
+ const formattedAmount = isDistanceRequestWithoutRoute
+ ? translate('common.tbd')
+ : CurrencyUtils.convertToDisplayString(
+ shouldCalculateDistanceAmount ? DistanceRequestUtils.getDistanceRequestAmount(distance, unit, rate) : props.iouAmount,
+ props.isDistanceRequest ? currency : props.iouCurrencyCode,
+ );
useEffect(() => {
if (!shouldCalculateDistanceAmount) {
@@ -230,7 +247,7 @@ function MoneyRequestConfirmationList(props) {
const splitOrRequestOptions = useMemo(() => {
let text;
- if (props.receiptPath) {
+ if (props.receiptPath || isDistanceRequestWithoutRoute) {
text = translate('iou.request');
} else {
const translationKey = props.hasMultipleParticipants ? 'iou.splitAmount' : 'iou.requestAmount';
@@ -242,7 +259,7 @@ function MoneyRequestConfirmationList(props) {
value: props.hasMultipleParticipants ? CONST.IOU.MONEY_REQUEST_TYPE.SPLIT : CONST.IOU.MONEY_REQUEST_TYPE.REQUEST,
},
];
- }, [props.hasMultipleParticipants, props.receiptPath, translate, formattedAmount]);
+ }, [props.hasMultipleParticipants, props.receiptPath, translate, formattedAmount, isDistanceRequestWithoutRoute]);
const selectedParticipants = useMemo(() => _.filter(props.selectedParticipants, (participant) => participant.selected), [props.selectedParticipants]);
const payeePersonalDetails = useMemo(() => props.payeePersonalDetails || props.currentUserPersonalDetails, [props.payeePersonalDetails, props.currentUserPersonalDetails]);
@@ -321,9 +338,9 @@ function MoneyRequestConfirmationList(props) {
if (!props.isDistanceRequest) {
return;
}
- const distanceMerchant = DistanceRequestUtils.getDistanceMerchant(distance, unit, rate, currency, translate);
+ const distanceMerchant = DistanceRequestUtils.getDistanceMerchant(hasRoute, distance, unit, rate, currency, translate);
IOU.setMoneyRequestMerchant(distanceMerchant);
- }, [distance, unit, rate, currency, translate, props.isDistanceRequest]);
+ }, [hasRoute, distance, unit, rate, currency, translate, props.isDistanceRequest]);
/**
* @param {Object} option
@@ -385,13 +402,11 @@ function MoneyRequestConfirmationList(props) {
const shouldShowSettlementButton = props.iouType === CONST.IOU.MONEY_REQUEST_TYPE.SEND;
const shouldDisableButton = selectedParticipants.length === 0;
- const recipient = props.selectedParticipants[0] || {};
return shouldShowSettlementButton ? (
);
- }, [confirm, props.selectedParticipants, props.bankAccountRoute, props.iouCurrencyCode, props.iouType, props.isReadOnly, props.policyID, selectedParticipants, splitOrRequestOptions]);
+ }, [confirm, props.bankAccountRoute, props.iouCurrencyCode, props.iouType, props.isReadOnly, props.policyID, selectedParticipants, splitOrRequestOptions]);
return (
Navigation.navigate(ROUTES.getMoneyRequestDescriptionRoute(props.iouType, props.reportID))}
@@ -510,7 +527,7 @@ function MoneyRequestConfirmationList(props) {
disabled={didConfirm || props.isReadOnly || !isTypeRequest}
/>
)}
- {shouldCategoryBeEditable && (
+ {shouldShowCategories && (
)}
- {canUseTags && !!tagList && (
+ {shouldShowTags && (
)}
+ {shouldShowBillable && (
+
+ {translate('common.billable')}
+
+
+ )}
>
)}
@@ -561,5 +588,8 @@ export default compose(
transaction: {
key: ({transactionID}) => `${ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`,
},
+ policy: {
+ key: ({policyID}) => `${ONYXKEYS.COLLECTION.POLICY}${policyID}`,
+ },
}),
)(MoneyRequestConfirmationList);
diff --git a/src/components/MoneyRequestHeader.js b/src/components/MoneyRequestHeader.js
index bdd7365b7893..7f1e58912128 100644
--- a/src/components/MoneyRequestHeader.js
+++ b/src/components/MoneyRequestHeader.js
@@ -9,8 +9,6 @@ import * as ReportUtils from '../libs/ReportUtils';
import * as Expensicons from './Icon/Expensicons';
import participantPropTypes from './participantPropTypes';
import styles from '../styles/styles';
-import withWindowDimensions, {windowDimensionsPropTypes} from './withWindowDimensions';
-import compose from '../libs/compose';
import Navigation from '../libs/Navigation/Navigation';
import ROUTES from '../ROUTES';
import ONYXKEYS from '../ONYXKEYS';
@@ -20,6 +18,8 @@ import useLocalize from '../hooks/useLocalize';
import MoneyRequestHeaderStatusBar from './MoneyRequestHeaderStatusBar';
import * as TransactionUtils from '../libs/TransactionUtils';
import reportActionPropTypes from '../pages/home/report/reportActionPropTypes';
+import transactionPropTypes from './transactionPropTypes';
+import useWindowDimensions from '../hooks/useWindowDimensions';
const propTypes = {
/** The report currently being looked at */
@@ -34,7 +34,7 @@ const propTypes = {
/** Personal details so we can get the ones for the report participants */
personalDetails: PropTypes.objectOf(participantPropTypes).isRequired,
- /** Onyx Props */
+ /* Onyx Props */
/** Session info for the currently logged in user. */
session: PropTypes.shape({
/** Currently logged in user email */
@@ -47,13 +47,8 @@ const propTypes = {
/** The report action the transaction is tied to from the parent report */
parentReportAction: PropTypes.shape(reportActionPropTypes),
- /** The transaction from the parent report action */
- transaction: PropTypes.shape({
- /** The ID of the transaction */
- transactionID: PropTypes.string,
- }),
-
- ...windowDimensionsPropTypes,
+ /** All the data for the transaction */
+ transaction: transactionPropTypes,
};
const defaultProps = {
@@ -65,24 +60,22 @@ const defaultProps = {
transaction: {},
};
-function MoneyRequestHeader(props) {
+function MoneyRequestHeader({session, parentReport, report, parentReportAction, transaction, policy, personalDetails}) {
const {translate} = useLocalize();
const [isDeleteModalVisible, setIsDeleteModalVisible] = useState(false);
- const moneyRequestReport = props.parentReport;
+ const moneyRequestReport = parentReport;
const isSettled = ReportUtils.isSettled(moneyRequestReport.reportID);
+ const {isSmallScreenWidth, windowWidth} = useWindowDimensions();
// Only the requestor can take delete the request, admins can only edit it.
- const isActionOwner = props.parentReportAction.actorAccountID === lodashGet(props.session, 'accountID', null);
- const report = props.report;
- report.ownerAccountID = lodashGet(props, ['parentReport', 'ownerAccountID'], null);
- report.ownerEmail = lodashGet(props, ['parentReport', 'ownerEmail'], '');
+ const isActionOwner = lodashGet(parentReportAction, 'actorAccountID') === lodashGet(session, 'accountID', null);
const deleteTransaction = useCallback(() => {
- IOU.deleteMoneyRequest(props.parentReportAction.originalMessage.IOUTransactionID, props.parentReportAction, true);
+ IOU.deleteMoneyRequest(lodashGet(parentReportAction, 'originalMessage.IOUTransactionID'), parentReportAction, true);
setIsDeleteModalVisible(false);
- }, [props.parentReportAction, setIsDeleteModalVisible]);
+ }, [parentReportAction, setIsDeleteModalVisible]);
- const isScanning = TransactionUtils.hasReceipt(props.transaction) && TransactionUtils.isReceiptBeingScanned(props.transaction);
+ const isScanning = TransactionUtils.hasReceipt(transaction) && TransactionUtils.isReceiptBeingScanned(transaction);
return (
<>
@@ -94,15 +87,19 @@ function MoneyRequestHeader(props) {
threeDotsMenuItems={[
{
icon: Expensicons.Trashcan,
- text: translate('reportActionContextMenu.deleteAction', {action: props.parentReportAction}),
+ text: translate('reportActionContextMenu.deleteAction', {action: parentReportAction}),
onSelected: () => setIsDeleteModalVisible(true),
},
]}
- threeDotsAnchorPosition={styles.threeDotsPopoverOffsetNoCloseButton(props.windowWidth)}
- report={report}
- policy={props.policy}
- personalDetails={props.personalDetails}
- shouldShowBackButton={props.isSmallScreenWidth}
+ threeDotsAnchorPosition={styles.threeDotsPopoverOffsetNoCloseButton(windowWidth)}
+ report={{
+ ...report,
+ ownerAccountID: lodashGet(parentReport, 'ownerAccountID', null),
+ ownerEmail: lodashGet(parentReport, 'ownerEmail', null),
+ }}
+ policy={policy}
+ personalDetails={personalDetails}
+ shouldShowBackButton={isSmallScreenWidth}
onBackButtonPress={() => Navigation.goBack(ROUTES.HOME, false, true)}
/>
{isScanning && }
@@ -125,23 +122,19 @@ MoneyRequestHeader.displayName = 'MoneyRequestHeader';
MoneyRequestHeader.propTypes = propTypes;
MoneyRequestHeader.defaultProps = defaultProps;
-export default compose(
- withWindowDimensions,
- withOnyx({
- session: {
- key: ONYXKEYS.SESSION,
- },
- parentReport: {
- key: ({report}) => `${ONYXKEYS.COLLECTION.REPORT}${report.parentReportID}`,
- },
- parentReportAction: {
- key: ({report}) => `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${(report.parentReportID, report.parentReportActionID)}`,
- selector: (reportActions, props) => props && props.parentReport && reportActions && reportActions[props.parentReport.parentReportActionID],
- canEvict: false,
- },
- transaction: {
- key: ({report, parentReportActions}) =>
- `${ONYXKEYS.COLLECTION.TRANSACTION}${lodashGet(parentReportActions, [report.parentReportActionID, 'originalMessage', 'IOUTransactionID'], '')}`,
- },
- }),
-)(MoneyRequestHeader);
+export default withOnyx({
+ session: {
+ key: ONYXKEYS.SESSION,
+ },
+ parentReport: {
+ key: ({report}) => `${ONYXKEYS.COLLECTION.REPORT}${report.parentReportID}`,
+ },
+ parentReportAction: {
+ key: ({report}) => `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${(report.parentReportID, report.parentReportActionID)}`,
+ selector: (reportActions, props) => props && props.parentReport && reportActions && reportActions[props.parentReport.parentReportActionID],
+ canEvict: false,
+ },
+ transaction: {
+ key: ({parentReportAction}) => `${ONYXKEYS.COLLECTION.TRANSACTION}${lodashGet(parentReportAction, 'originalMessage.IOUTransactionID', 0)}`,
+ },
+})(MoneyRequestHeader);
diff --git a/src/components/MultipleAvatars.js b/src/components/MultipleAvatars.js
index 4c6ba1307fb7..916646b5619a 100644
--- a/src/components/MultipleAvatars.js
+++ b/src/components/MultipleAvatars.js
@@ -221,6 +221,7 @@ function MultipleAvatars(props) {
{`+${avatars.length - props.maxAvatarsInRow}`}
@@ -278,6 +279,7 @@ function MultipleAvatars(props) {
{`+${props.icons.length - 1}`}
diff --git a/src/components/NewDatePicker/CalendarPicker/index.js b/src/components/NewDatePicker/CalendarPicker/index.js
index fe0c36d32e41..1e1ef3c3fad3 100644
--- a/src/components/NewDatePicker/CalendarPicker/index.js
+++ b/src/components/NewDatePicker/CalendarPicker/index.js
@@ -130,7 +130,10 @@ class CalendarPicker extends React.PureComponent {
return (
-
+ this.setState({isYearPickerVisible: true})}
style={[styles.alignItemsCenter, styles.flexRow, styles.flex1, styles.justifyContentStart]}
@@ -186,6 +189,7 @@ class CalendarPicker extends React.PureComponent {
{dayOfWeek[0]}
@@ -212,6 +216,7 @@ class CalendarPicker extends React.PureComponent {
accessibilityLabel={day ? day.toString() : undefined}
focusable={Boolean(day)}
accessible={Boolean(day)}
+ dataSet={{[CONST.SELECTION_SCRAPER_HIDDEN_ELEMENT]: true}}
>
{({hovered, pressed}) => (
e === null);
const hasErrorMessages = !_.isEmpty(errorMessages);
- const isOfflinePendingAction = props.network.isOffline && props.pendingAction;
+ const isOfflinePendingAction = isOffline && props.pendingAction;
const isUpdateOrDeleteError = hasErrors && (props.pendingAction === CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE || props.pendingAction === CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE);
const isAddError = hasErrors && props.pendingAction === CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD;
const needsOpacity = !props.shouldDisableOpacity && ((isOfflinePendingAction && !isUpdateOrDeleteError) || isAddError);
- const needsStrikeThrough = props.network.isOffline && props.pendingAction === CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE;
- const hideChildren = props.shouldHideOnDelete && !props.network.isOffline && props.pendingAction === CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE && !hasErrors;
+ const needsStrikeThrough = !props.shouldDisableStrikeThrough && isOffline && props.pendingAction === CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE;
+ const hideChildren = props.shouldHideOnDelete && !isOffline && props.pendingAction === CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE && !hasErrors;
let children = props.children;
// Apply strikethrough to children if needed, but skip it if we are not going to render them
@@ -126,12 +130,12 @@ function OfflineWithFeedback(props) {
messages={errorMessages}
type="error"
/>
-
+
@@ -146,4 +150,4 @@ OfflineWithFeedback.propTypes = propTypes;
OfflineWithFeedback.defaultProps = defaultProps;
OfflineWithFeedback.displayName = 'OfflineWithFeedback';
-export default compose(withLocalize, withNetwork())(OfflineWithFeedback);
+export default OfflineWithFeedback;
diff --git a/src/components/OptionsList/BaseOptionsList.js b/src/components/OptionsList/BaseOptionsList.js
index 9b6875ccf8c2..5a40c28a86c9 100644
--- a/src/components/OptionsList/BaseOptionsList.js
+++ b/src/components/OptionsList/BaseOptionsList.js
@@ -174,7 +174,11 @@ function BaseOptionsList({
const renderItem = ({item, index, section}) => {
const isItemDisabled = isDisabled || section.isDisabled || !!item.isDisabled;
const isSelected = _.some(selectedOptions, (option) => {
- if (option.accountID === item.accountID) {
+ if (option.accountID && option.accountID === item.accountID) {
+ return true;
+ }
+
+ if (option.reportID && option.reportID === item.reportID) {
return true;
}
diff --git a/src/components/Popover/index.js b/src/components/Popover/index.js
index bfd02ba1d3e3..a886fbbd0c6b 100644
--- a/src/components/Popover/index.js
+++ b/src/components/Popover/index.js
@@ -11,23 +11,41 @@ import PopoverWithoutOverlay from '../PopoverWithoutOverlay';
* On small screen widths, it uses BottomDocked modal type, and a Popover type on wide screen widths.
*/
function Popover(props) {
- if (!props.fullscreen && !props.isSmallScreenWidth) {
+ const {isVisible, onClose, isSmallScreenWidth, fullscreen, animationInTiming, onLayout, animationOutTiming, disableAnimation, withoutOverlay, anchorPosition} = props;
+
+ // Not adding this inside the PopoverProvider
+ // because this is an issue on smaller screens as well.
+ React.useEffect(() => {
+ const listener = () => {
+ if (!isVisible) {
+ return;
+ }
+
+ onClose();
+ };
+ window.addEventListener('popstate', listener);
+ return () => {
+ window.removeEventListener('popstate', listener);
+ };
+ }, [onClose, isVisible]);
+
+ if (!fullscreen && !isSmallScreenWidth) {
return createPortal(
,
document.body,
);
}
- if (props.withoutOverlay && !props.isSmallScreenWidth) {
+ if (withoutOverlay && !isSmallScreenWidth) {
// eslint-disable-next-line react/jsx-props-no-spreading
return createPortal(, document.body);
}
@@ -36,12 +54,12 @@ function Popover(props) {
);
}
diff --git a/src/components/PopoverWithMeasuredContent.js b/src/components/PopoverWithMeasuredContent.js
index 492807274d1e..6b71b4a59055 100644
--- a/src/components/PopoverWithMeasuredContent.js
+++ b/src/components/PopoverWithMeasuredContent.js
@@ -149,7 +149,7 @@ function PopoverWithMeasuredContent(props) {
but we can't measure its dimensions without first rendering it.
*/
{props.children}
diff --git a/src/components/Pressable/GenericPressable/index.js b/src/components/Pressable/GenericPressable/index.js
index 65fca85bc324..774ac3ac5092 100644
--- a/src/components/Pressable/GenericPressable/index.js
+++ b/src/components/Pressable/GenericPressable/index.js
@@ -15,7 +15,7 @@ const WebGenericPressable = forwardRef((props, ref) => (
aria-labelledby={props.accessibilityLabelledBy}
aria-valuenow={props.accessibilityValue}
nativeID={props.nativeID}
- dataSet={{tag: 'pressable', ...(props.noDragArea && {dragArea: false})}}
+ dataSet={{tag: 'pressable', ...(props.noDragArea && {dragArea: false}), ...props.dataSet}}
/>
));
diff --git a/src/components/Pressable/PressableWithFeedback.js b/src/components/Pressable/PressableWithFeedback.js
index 7eb0ee7286c9..40be99823ceb 100644
--- a/src/components/Pressable/PressableWithFeedback.js
+++ b/src/components/Pressable/PressableWithFeedback.js
@@ -58,19 +58,27 @@ const PressableWithFeedback = forwardRef((props, ref) => {
isExecuting={isExecuting}
onHoverIn={() => {
setIsHovered(true);
- if (props.onHoverIn) props.onHoverIn();
+ if (props.onHoverIn) {
+ props.onHoverIn();
+ }
}}
onHoverOut={() => {
setIsHovered(false);
- if (props.onHoverOut) props.onHoverOut();
+ if (props.onHoverOut) {
+ props.onHoverOut();
+ }
}}
onPressIn={() => {
setIsPressed(true);
- if (props.onPressIn) props.onPressIn();
+ if (props.onPressIn) {
+ props.onPressIn();
+ }
}}
onPressOut={() => {
setIsPressed(false);
- if (props.onPressOut) props.onPressOut();
+ if (props.onPressOut) {
+ props.onPressOut();
+ }
}}
onPress={(e) => {
singleExecution(() => props.onPress(e))();
diff --git a/src/components/PressableWithSecondaryInteraction/index.js b/src/components/PressableWithSecondaryInteraction/index.js
index 3f9039489b7c..d84a3f282e97 100644
--- a/src/components/PressableWithSecondaryInteraction/index.js
+++ b/src/components/PressableWithSecondaryInteraction/index.js
@@ -47,7 +47,7 @@ function PressableWithSecondaryInteraction({
if (forwardedRef) {
if (_.isFunction(forwardedRef)) {
- forwardedRef(pressableRef);
+ forwardedRef(pressableRef.current);
} else if (_.isObject(forwardedRef)) {
// eslint-disable-next-line no-param-reassign
forwardedRef.current = pressableRef.current;
diff --git a/src/components/QRShare/QRShareWithDownload/index.js b/src/components/QRShare/QRShareWithDownload/index.js
index 310122b96d40..665115823357 100644
--- a/src/components/QRShare/QRShareWithDownload/index.js
+++ b/src/components/QRShare/QRShareWithDownload/index.js
@@ -17,7 +17,9 @@ class QRShareWithDownload extends Component {
return new Promise((resolve, reject) => {
// eslint-disable-next-line es/no-optional-chaining
const svg = this.qrShareRef.current?.getSvg();
- if (svg == null) return reject();
+ if (svg == null) {
+ return reject();
+ }
svg.toDataURL((dataURL) => resolve(fileDownload(dataURL, getQrCodeFileName(this.props.title))));
});
diff --git a/src/components/Reactions/AddReactionBubble.js b/src/components/Reactions/AddReactionBubble.js
index 922be96084d8..0315c63aabf1 100644
--- a/src/components/Reactions/AddReactionBubble.js
+++ b/src/components/Reactions/AddReactionBubble.js
@@ -94,6 +94,7 @@ function AddReactionBubble(props) {
accessibilityRole={CONST.ACCESSIBILITY_ROLE.BUTTON}
// disable dimming
pressDimmingValue={1}
+ dataSet={{[CONST.SELECTION_SCRAPER_HIDDEN_ELEMENT]: true}}
>
{({hovered, pressed}) => (
<>
diff --git a/src/components/Reactions/EmojiReactionBubble.js b/src/components/Reactions/EmojiReactionBubble.js
index bb37735d6920..818bc8f33309 100644
--- a/src/components/Reactions/EmojiReactionBubble.js
+++ b/src/components/Reactions/EmojiReactionBubble.js
@@ -60,6 +60,7 @@ function EmojiReactionBubble(props) {
styles.emojiReactionBubble,
StyleUtils.getEmojiReactionBubbleStyle(hovered || pressed, props.hasUserReacted, props.isContextMenu),
props.shouldBlockReactions && styles.cursorDisabled,
+ styles.userSelectNone,
]}
onPress={() => {
if (props.shouldBlockReactions) {
@@ -83,9 +84,10 @@ function EmojiReactionBubble(props) {
}}
accessibilityRole={CONST.ACCESSIBILITY_ROLE.BUTTON}
accessibilityLabel={props.emojiCodes.join('')}
+ dataSet={{[CONST.SELECTION_SCRAPER_HIDDEN_ELEMENT]: true}}
>
- {props.emojiCodes.join('')}
- {props.count > 0 && {props.count}}
+ {props.emojiCodes.join('')}
+ {props.count > 0 && {props.count}}
);
}
diff --git a/src/components/Reactions/MiniQuickEmojiReactions.js b/src/components/Reactions/MiniQuickEmojiReactions.js
index 82f83cb1e961..a22a2967cefe 100644
--- a/src/components/Reactions/MiniQuickEmojiReactions.js
+++ b/src/components/Reactions/MiniQuickEmojiReactions.js
@@ -80,7 +80,12 @@ function MiniQuickEmojiReactions(props) {
tooltipText={`:${EmojiUtils.getLocalizedEmojiName(emoji.name, props.preferredLocale)}:`}
onPress={Session.checkIfActionIsAllowed(() => props.onEmojiSelected(emoji, props.emojiReactions))}
>
- {EmojiUtils.getPreferredEmojiCode(emoji, props.preferredSkinTone)}
+
+ {EmojiUtils.getPreferredEmojiCode(emoji, props.preferredSkinTone)}
+
))}
props.onEmojiSelected(emoji, props.emojiReactions)}
reportAction={props.reportAction}
/>
diff --git a/src/components/ReportActionItem/MoneyReportView.js b/src/components/ReportActionItem/MoneyReportView.js
index 68eecf11d5bf..f593d947f96c 100644
--- a/src/components/ReportActionItem/MoneyReportView.js
+++ b/src/components/ReportActionItem/MoneyReportView.js
@@ -15,6 +15,7 @@ import variables from '../../styles/variables';
import * as CurrencyUtils from '../../libs/CurrencyUtils';
import EmptyStateBackgroundImage from '../../../assets/images/empty-state_background-fade.png';
import useLocalize from '../../hooks/useLocalize';
+import SpacerView from '../SpacerView';
const propTypes = {
/** The report currently being looked at */
@@ -66,7 +67,10 @@ function MoneyReportView(props) {
- {props.shouldShowHorizontalRule && }
+
);
}
diff --git a/src/components/ReportActionItem/MoneyRequestPreview.js b/src/components/ReportActionItem/MoneyRequestPreview.js
index a5a499169cbd..2afc6240f85d 100644
--- a/src/components/ReportActionItem/MoneyRequestPreview.js
+++ b/src/components/ReportActionItem/MoneyRequestPreview.js
@@ -163,24 +163,21 @@ function MoneyRequestPreview(props) {
const isScanning = hasReceipt && TransactionUtils.isReceiptBeingScanned(props.transaction);
const hasFieldErrors = TransactionUtils.hasMissingSmartscanFields(props.transaction);
const isDistanceRequest = TransactionUtils.isDistanceRequest(props.transaction);
+ const isSettled = ReportUtils.isSettled(props.iouReport);
// Show the merchant for IOUs and expenses only if they are custom or not related to scanning smartscan
const shouldShowMerchant =
!_.isEmpty(requestMerchant) && !props.isBillSplit && requestMerchant !== CONST.TRANSACTION.PARTIAL_TRANSACTION_MERCHANT && requestMerchant !== CONST.TRANSACTION.DEFAULT_MERCHANT;
const shouldShowDescription = !_.isEmpty(description) && !shouldShowMerchant;
- const receiptImages = hasReceipt ? [ReceiptUtils.getThumbnailAndImageURIs(props.transaction.receipt.source, props.transaction.filename || props.transaction.receiptFilename || '')] : [];
+ const receiptImages = hasReceipt ? [ReceiptUtils.getThumbnailAndImageURIs(props.transaction.receipt.source, props.transaction.filename || '')] : [];
const getSettledMessage = () => {
switch (lodashGet(props.action, 'originalMessage.paymentType', '')) {
- case CONST.IOU.PAYMENT_TYPE.PAYPAL_ME:
- return props.translate('iou.settledPaypalMe');
- case CONST.IOU.PAYMENT_TYPE.ELSEWHERE:
- return props.translate('iou.settledElsewhere');
case CONST.IOU.PAYMENT_TYPE.EXPENSIFY:
return props.translate('iou.settledExpensify');
default:
- return '';
+ return props.translate('iou.settledElsewhere');
}
};
@@ -235,7 +232,7 @@ function MoneyRequestPreview(props) {
errorRowStyles={[styles.mbn1]}
needsOffscreenAlphaCompositing
>
-
+
{hasReceipt && (
{getPreviewHeaderText()}
- {Boolean(getSettledMessage()) && (
+ {isSettled && (
<>
{requestMerchant}
)}
-
+
{!isCurrentUserManager && props.shouldShowPendingConversionMessage && (
- {props.translate('iou.pendingConversionMessage')}
+ {props.translate('iou.pendingConversionMessage')}
)}
- {shouldShowDescription && {description}}
+ {shouldShowDescription && {description}}
{props.isBillSplit && !_.isEmpty(participantAccountIDs) && (
-
+
{props.translate('iou.amountEach', {
amount: CurrencyUtils.convertToDisplayString(
IOUUtils.calculateAmount(isPolicyExpenseChat ? 1 : participantAccountIDs.length - 1, requestAmount, requestCurrency),
@@ -342,6 +339,7 @@ function MoneyRequestPreview(props) {
onLongPress={showContextMenu}
accessibilityLabel={props.isBillSplit ? props.translate('iou.split') : props.translate('iou.cash')}
accessibilityHint={CurrencyUtils.convertToDisplayString(requestAmount, requestCurrency)}
+ style={[styles.moneyRequestPreviewBox, ...props.containerStyles]}
>
{childContainer}
diff --git a/src/components/ReportActionItem/MoneyRequestView.js b/src/components/ReportActionItem/MoneyRequestView.js
index a9264812b99d..178cab75a0c2 100644
--- a/src/components/ReportActionItem/MoneyRequestView.js
+++ b/src/components/ReportActionItem/MoneyRequestView.js
@@ -1,7 +1,8 @@
-import React from 'react';
+import React, {useMemo} from 'react';
import {View} from 'react-native';
import {withOnyx} from 'react-native-onyx';
import lodashGet from 'lodash/get';
+import lodashValues from 'lodash/values';
import PropTypes from 'prop-types';
import reportPropTypes from '../../pages/reportPropTypes';
import ONYXKEYS from '../../ONYXKEYS';
@@ -9,9 +10,11 @@ import ROUTES from '../../ROUTES';
import Navigation from '../../libs/Navigation/Navigation';
import withCurrentUserPersonalDetails, {withCurrentUserPersonalDetailsPropTypes} from '../withCurrentUserPersonalDetails';
import compose from '../../libs/compose';
+import Permissions from '../../libs/Permissions';
import MenuItemWithTopDescription from '../MenuItemWithTopDescription';
import styles from '../../styles/styles';
import * as ReportUtils from '../../libs/ReportUtils';
+import * as OptionsListUtils from '../../libs/OptionsListUtils';
import * as ReportActionsUtils from '../../libs/ReportActionsUtils';
import * as StyleUtils from '../../styles/StyleUtils';
import CONST from '../../CONST';
@@ -27,25 +30,36 @@ import Image from '../Image';
import ReportActionItemImage from './ReportActionItemImage';
import * as TransactionUtils from '../../libs/TransactionUtils';
import OfflineWithFeedback from '../OfflineWithFeedback';
+import categoryPropTypes from '../categoryPropTypes';
+import SpacerView from '../SpacerView';
const propTypes = {
/** The report currently being looked at */
report: reportPropTypes.isRequired,
+ /** Whether we should display the horizontal rule below the component */
+ shouldShowHorizontalRule: PropTypes.bool.isRequired,
+
+ /* Onyx Props */
+ /** List of betas available to current user */
+ betas: PropTypes.arrayOf(PropTypes.string),
+
/** The expense report or iou report (only will have a value if this is a transaction thread) */
parentReport: iouReportPropTypes,
+ /** Collection of categories attached to a policy */
+ policyCategories: PropTypes.objectOf(categoryPropTypes),
+
/** The transaction associated with the transactionThread */
transaction: transactionPropTypes,
- /** Whether we should display the horizontal rule below the component */
- shouldShowHorizontalRule: PropTypes.bool.isRequired,
-
...withCurrentUserPersonalDetailsPropTypes,
};
const defaultProps = {
+ betas: [],
parentReport: {},
+ policyCategories: {},
transaction: {
amount: 0,
currency: CONST.CURRENCY.USD,
@@ -53,7 +67,7 @@ const defaultProps = {
},
};
-function MoneyRequestView({report, parentReport, shouldShowHorizontalRule, transaction}) {
+function MoneyRequestView({betas, report, parentReport, policyCategories, shouldShowHorizontalRule, transaction}) {
const {isSmallScreenWidth} = useWindowDimensions();
const {translate} = useLocalize();
@@ -65,6 +79,7 @@ function MoneyRequestView({report, parentReport, shouldShowHorizontalRule, trans
currency: transactionCurrency,
comment: transactionDescription,
merchant: transactionMerchant,
+ category: transactionCategory,
} = ReportUtils.getTransactionDetails(transaction);
const isEmptyMerchant =
transactionMerchant === '' || transactionMerchant === CONST.TRANSACTION.UNKNOWN_MERCHANT || transactionMerchant === CONST.TRANSACTION.PARTIAL_TRANSACTION_MERCHANT;
@@ -72,6 +87,10 @@ function MoneyRequestView({report, parentReport, shouldShowHorizontalRule, trans
const isSettled = ReportUtils.isSettled(moneyRequestReport.reportID);
const canEdit = ReportUtils.canEditMoneyRequest(parentReportAction);
+ // A flag for verifying that the current report is a sub-report of a workspace chat
+ const isPolicyExpenseChat = useMemo(() => ReportUtils.isPolicyExpenseChat(ReportUtils.getRootParentReport(report)), [report]);
+ // A flag for showing categories
+ const shouldShowCategory = isPolicyExpenseChat && Permissions.canUseCategories(betas) && (transactionCategory || OptionsListUtils.hasEnabledOptions(lodashValues(policyCategories)));
let description = `${translate('iou.amount')} • ${translate('iou.cash')}`;
if (isSettled) {
@@ -91,7 +110,7 @@ function MoneyRequestView({report, parentReport, shouldShowHorizontalRule, trans
let hasErrors = false;
if (hasReceipt) {
receiptURIs = ReceiptUtils.getThumbnailAndImageURIs(transaction.receipt.source, transaction.filename);
- hasErrors = TransactionUtils.hasMissingSmartscanFields(transaction);
+ hasErrors = canEdit && TransactionUtils.hasMissingSmartscanFields(transaction);
}
const isDistanceRequest = TransactionUtils.isDistanceRequest(transaction);
@@ -105,6 +124,7 @@ function MoneyRequestView({report, parentReport, shouldShowHorizontalRule, trans
style={[StyleUtils.getReportWelcomeBackgroundImageStyle(true)]}
/>
+
{hasReceipt && (
Navigation.navigate(ROUTES.getEditRequestRoute(report.reportID, CONST.EDIT_REQUEST_FIELD.DESCRIPTION))}
+ wrapperStyle={[styles.pv2, styles.taskDescriptionMenuItem]}
+ numberOfLinesTitle={0}
/>
@@ -165,7 +188,22 @@ function MoneyRequestView({report, parentReport, shouldShowHorizontalRule, trans
subtitleTextStyle={styles.textLabelError}
/>
- {shouldShowHorizontalRule && }
+ {shouldShowCategory && (
+
+ Navigation.navigate(ROUTES.getEditRequestRoute(report.reportID, CONST.EDIT_REQUEST_FIELD.CATEGORY))}
+ />
+
+ )}
+
);
}
@@ -177,12 +215,18 @@ MoneyRequestView.displayName = 'MoneyRequestView';
export default compose(
withCurrentUserPersonalDetails,
withOnyx({
+ betas: {
+ key: ONYXKEYS.BETAS,
+ },
parentReport: {
key: ({report}) => `${ONYXKEYS.COLLECTION.REPORT}${report.parentReportID}`,
},
policy: {
key: ({report}) => `${ONYXKEYS.COLLECTION.POLICY}${report.policyID}`,
},
+ policyCategories: {
+ key: ({report}) => `${ONYXKEYS.COLLECTION.POLICY_CATEGORIES}${report.policyID}`,
+ },
session: {
key: ONYXKEYS.SESSION,
},
diff --git a/src/components/ReportActionItem/ReportPreview.js b/src/components/ReportActionItem/ReportPreview.js
index 1f60dddef6ec..1350c62bda88 100644
--- a/src/components/ReportActionItem/ReportPreview.js
+++ b/src/components/ReportActionItem/ReportPreview.js
@@ -120,9 +120,7 @@ function ReportPreview(props) {
const isScanning = hasReceipts && ReportUtils.areAllRequestsBeingSmartScanned(props.iouReportID, props.action);
const hasErrors = hasReceipts && ReportUtils.hasMissingSmartscanFields(props.iouReportID);
const lastThreeTransactionsWithReceipts = ReportUtils.getReportPreviewDisplayTransactions(props.action);
- const lastThreeReceipts = _.map(lastThreeTransactionsWithReceipts, ({receipt, filename, receiptFilename}) =>
- ReceiptUtils.getThumbnailAndImageURIs(receipt.source, filename || receiptFilename || ''),
- );
+ const lastThreeReceipts = _.map(lastThreeTransactionsWithReceipts, ({receipt, filename}) => ReceiptUtils.getThumbnailAndImageURIs(receipt.source, filename || ''));
const hasOnlyOneReceiptRequest = numberOfRequests === 1 && hasReceipts;
const previewSubtitle = hasOnlyOneReceiptRequest
@@ -177,7 +175,7 @@ function ReportPreview(props) {
onPressIn={() => DeviceCapabilities.canUseTouchScreen() && ControlSelection.block()}
onPressOut={() => ControlSelection.unblock()}
onLongPress={(event) => showContextMenuForReport(event, props.contextMenuAnchor, props.chatReportID, props.action, props.checkIfContextMenuActive)}
- style={[styles.flexRow, styles.justifyContentBetween]}
+ style={[styles.flexRow, styles.justifyContentBetween, styles.reportPreviewBox]}
accessibilityRole="button"
accessibilityLabel={props.translate('iou.viewDetails')}
>
@@ -229,7 +227,7 @@ function ReportPreview(props) {
chatReportID={props.chatReportID}
iouReport={props.iouReport}
onPress={(paymentType) => IOU.payMoneyRequest(paymentType, props.chatReport, props.iouReport)}
- enablePaymentsRoute={ROUTES.BANK_ACCOUNT_NEW}
+ enablePaymentsRoute={ROUTES.ENABLE_PAYMENTS}
addBankAccountRoute={bankAccountRoute}
style={[styles.requestPreviewBox]}
/>
diff --git a/src/components/ReportActionItem/TaskAction.js b/src/components/ReportActionItem/TaskAction.js
index c0b60ad996db..9d32fa7a1e77 100644
--- a/src/components/ReportActionItem/TaskAction.js
+++ b/src/components/ReportActionItem/TaskAction.js
@@ -1,13 +1,10 @@
import React from 'react';
import {View} from 'react-native';
import PropTypes from 'prop-types';
-import {withOnyx} from 'react-native-onyx';
import withLocalize, {withLocalizePropTypes} from '../withLocalize';
-import compose from '../../libs/compose';
-import ONYXKEYS from '../../ONYXKEYS';
import Text from '../Text';
import styles from '../../styles/styles';
-import CONST from '../../CONST';
+import * as Task from '../../libs/actions/Task';
const propTypes = {
/** Name of the reportAction action */
@@ -17,60 +14,20 @@ const propTypes = {
// eslint-disable-next-line react/no-unused-prop-types -- This is used in the withOnyx HOC
taskReportID: PropTypes.string.isRequired,
- /* Onyx Props */
- taskReport: PropTypes.shape({
- /** Title of the task */
- reportName: PropTypes.string,
-
- /** AccountID of the manager in this iou report */
- managerID: PropTypes.number,
-
- /** AccountID of the creator of this iou report */
- ownerAccountID: PropTypes.number,
- }),
-
...withLocalizePropTypes,
};
-const defaultProps = {
- taskReport: {},
-};
function TaskAction(props) {
- const taskReportName = props.taskReport.reportName || '';
-
- let taskStatusText = '';
- switch (props.actionName) {
- case CONST.REPORT.ACTIONS.TYPE.TASKCOMPLETED:
- taskStatusText = props.translate('task.messages.completed');
- break;
- case CONST.REPORT.ACTIONS.TYPE.TASKCANCELLED:
- taskStatusText = props.translate('task.messages.canceled');
- break;
- case CONST.REPORT.ACTIONS.TYPE.TASKREOPENED:
- taskStatusText = props.translate('task.messages.reopened');
- break;
- default:
- taskStatusText = props.translate('task.task');
- }
-
return (
<>
- {`${taskStatusText} ${taskReportName}`}
+ {Task.getTaskReportActionMessage(props.actionName, props.taskReportID, false)}
>
);
}
TaskAction.propTypes = propTypes;
-TaskAction.defaultProps = defaultProps;
TaskAction.displayName = 'TaskAction';
-export default compose(
- withLocalize,
- withOnyx({
- taskReport: {
- key: ({taskReportID}) => `${ONYXKEYS.COLLECTION.REPORT}${taskReportID}`,
- },
- }),
-)(TaskAction);
+export default withLocalize(TaskAction);
diff --git a/src/components/ReportActionItem/TaskPreview.js b/src/components/ReportActionItem/TaskPreview.js
index ca4103624440..d0181e3d736a 100644
--- a/src/components/ReportActionItem/TaskPreview.js
+++ b/src/components/ReportActionItem/TaskPreview.js
@@ -72,6 +72,11 @@ function TaskPreview(props) {
const assigneeDisplayName = lodashGet(props.personalDetailsList, [taskAssigneeAccountID, 'displayName'], '');
const taskAssignee = assigneeLogin || assigneeDisplayName;
const htmlForTaskPreview = taskAssignee ? `@${taskAssignee} ${taskTitle}` : `${taskTitle}`;
+ const isDeletedParentAction = ReportUtils.isCanceledTaskReport(props.taskReport, props.action);
+
+ if (isDeletedParentAction) {
+ return ${props.translate('parentReportAction.deletedTask')}`} />;
+ }
return (
diff --git a/src/components/ReportActionItem/TaskView.js b/src/components/ReportActionItem/TaskView.js
index 965c3120d51b..ae77a18b980f 100644
--- a/src/components/ReportActionItem/TaskView.js
+++ b/src/components/ReportActionItem/TaskView.js
@@ -27,6 +27,7 @@ import getButtonState from '../../libs/getButtonState';
import PressableWithSecondaryInteraction from '../PressableWithSecondaryInteraction';
import * as Session from '../../libs/actions/Session';
import * as Expensicons from '../Icon/Expensicons';
+import SpacerView from '../SpacerView';
const propTypes = {
/** The report currently being looked at */
@@ -50,7 +51,8 @@ function TaskView(props) {
const isOpen = ReportUtils.isOpenTaskReport(props.report);
const isCanceled = ReportUtils.isCanceledTaskReport(props.report);
const canModifyTask = Task.canModifyTask(props.report, props.currentUserPersonalDetails.accountID);
- const disableState = !canModifyTask || !isOpen;
+ const disableState = !canModifyTask || isCanceled;
+ const isDisableInteractive = !canModifyTask || !isOpen;
return (
(
{
+ if (isDisableInteractive) {
+ return;
+ }
if (e && e.type === 'click') {
e.currentTarget.blur();
}
Navigation.navigate(ROUTES.getTaskReportTitleRoute(props.report.reportID));
})}
- style={({pressed}) => [styles.ph5, styles.pv2, StyleUtils.getButtonBackgroundColorStyle(getButtonState(hovered, pressed, false, disableState), true)]}
+ style={({pressed}) => [
+ styles.ph5,
+ styles.pv2,
+ StyleUtils.getButtonBackgroundColorStyle(getButtonState(hovered, pressed, false, disableState, !isDisableInteractive), true),
+ isDisableInteractive && !disableState && styles.cursorDefault,
+ ]}
ref={props.forwardedRef}
disabled={disableState}
accessibilityLabel={taskTitle || props.translate('task.task')}
@@ -119,6 +129,7 @@ function TaskView(props) {
Navigation.navigate(ROUTES.getTaskReportDescriptionRoute(props.report.reportID))}
@@ -127,6 +138,7 @@ function TaskView(props) {
wrapperStyle={[styles.pv2, styles.taskDescriptionMenuItem]}
shouldGreyOutWhenDisabled={false}
numberOfLinesTitle={0}
+ interactive={!isDisableInteractive}
/>
{props.report.managerID ? (
@@ -144,6 +156,7 @@ function TaskView(props) {
wrapperStyle={[styles.pv2]}
isSmallAvatarSubscriptMenu
shouldGreyOutWhenDisabled={false}
+ interactive={!isDisableInteractive}
/>
) : (
@@ -154,10 +167,14 @@ function TaskView(props) {
disabled={disableState}
wrapperStyle={[styles.pv2]}
shouldGreyOutWhenDisabled={false}
+ interactive={!isDisableInteractive}
/>
)}
- {props.shouldShowHorizontalRule && }
+
);
}
diff --git a/src/components/ReportActionsSkeletonView/SkeletonViewLines.js b/src/components/ReportActionsSkeletonView/SkeletonViewLines.js
index ddaa46e0b731..e4432ceb2309 100644
--- a/src/components/ReportActionsSkeletonView/SkeletonViewLines.js
+++ b/src/components/ReportActionsSkeletonView/SkeletonViewLines.js
@@ -31,20 +31,20 @@ function SkeletonViewLines(props) {
r="20"
/>
{props.numberOfRows > 1 && (
2 && (
-
-
- {this.props.environment === CONST.ENVIRONMENT.DEV && }
- {this.props.environment === CONST.ENVIRONMENT.DEV && }
- {
- // If props.children is a function, call it to provide the insets to the children.
- _.isFunction(this.props.children)
- ? this.props.children({
- insets,
- safeAreaPaddingBottomStyle,
- didScreenTransitionEnd: this.state.didScreenTransitionEnd,
- })
- : this.props.children
- }
- {this.props.isSmallScreenWidth && this.props.shouldShowOfflineIndicator && }
-
+
+ {this.props.environment === CONST.ENVIRONMENT.DEV && }
+ {this.props.environment === CONST.ENVIRONMENT.DEV && }
+ {
+ // If props.children is a function, call it to provide the insets to the children.
+ _.isFunction(this.props.children)
+ ? this.props.children({
+ insets,
+ safeAreaPaddingBottomStyle,
+ didScreenTransitionEnd: this.state.didScreenTransitionEnd,
+ })
+ : this.props.children
+ }
+ {this.props.isSmallScreenWidth && this.props.shouldShowOfflineIndicator && }
diff --git a/src/components/ScreenWrapper/propTypes.js b/src/components/ScreenWrapper/propTypes.js
index c3538b3c026d..83033d9e97b7 100644
--- a/src/components/ScreenWrapper/propTypes.js
+++ b/src/components/ScreenWrapper/propTypes.js
@@ -48,12 +48,6 @@ const propTypes = {
/** Styles for the offline indicator */
offlineIndicatorStyle: stylePropTypes,
-
- /** Whether to disable the focus trap */
- shouldDisableFocusTrap: PropTypes.bool,
-
- /** Whether to disable auto focus of the focus trap */
- shouldEnableAutoFocus: PropTypes.bool,
};
const defaultProps = {
@@ -69,8 +63,6 @@ const defaultProps = {
shouldShowOfflineIndicator: true,
offlineIndicatorStyle: [],
headerGapStyles: [],
- shouldDisableFocusTrap: false,
- shouldEnableAutoFocus: false,
};
export {propTypes, defaultProps};
diff --git a/src/components/SelectionList/BaseSelectionList.js b/src/components/SelectionList/BaseSelectionList.js
index 2a1bca24ab74..e57f00e1849c 100644
--- a/src/components/SelectionList/BaseSelectionList.js
+++ b/src/components/SelectionList/BaseSelectionList.js
@@ -348,6 +348,7 @@ function BaseSelectionList({
accessibilityRole="button"
accessibilityState={{checked: flattenedSections.allSelected}}
disabled={flattenedSections.allOptions.length === flattenedSections.disabledOptionsIndexes.length}
+ dataSet={{[CONST.SELECTION_SCRAPER_HIDDEN_ELEMENT]: true}}
>
diff --git a/src/components/SelectionList/UserListItem.js b/src/components/SelectionList/UserListItem.js
index a3c25f09af3b..014e0cf879a5 100644
--- a/src/components/SelectionList/UserListItem.js
+++ b/src/components/SelectionList/UserListItem.js
@@ -63,6 +63,7 @@ function UserListItem({item, isFocused = false, showTooltip, onSelectRow, onDism
hoverDimmingValue={1}
hoverStyle={styles.hoveredComponentBG}
focusStyle={styles.hoveredComponentBG}
+ dataSet={{[CONST.SELECTION_SCRAPER_HIDDEN_ELEMENT]: true}}
>
(method.value === paymentMethod ? 0 : 1));
}
return buttonOptions;
- }, [betas, currency, formattedAmount, iouReport, nvp_lastPaymentMethod, policyID, shouldShowPaymentOptions, shouldShowPaypal, translate]);
+ }, [betas, currency, formattedAmount, iouReport, nvp_lastPaymentMethod, policyID, shouldShowPaymentOptions, translate]);
const selectPaymentType = (event, iouPaymentType, triggerKYCFlow) => {
if (iouPaymentType === CONST.IOU.PAYMENT_TYPE.EXPENSIFY || iouPaymentType === CONST.IOU.PAYMENT_TYPE.VBBA) {
diff --git a/src/components/SignInButtons/AppleSignIn/index.android.js b/src/components/SignInButtons/AppleSignIn/index.android.js
index 83a99683b178..48d2bf3cc861 100644
--- a/src/components/SignInButtons/AppleSignIn/index.android.js
+++ b/src/components/SignInButtons/AppleSignIn/index.android.js
@@ -39,7 +39,9 @@ function AppleSignIn() {
appleSignInRequest()
.then((token) => Session.beginAppleSignIn(token))
.catch((e) => {
- if (e.message === appleAuthAndroid.Error.SIGNIN_CANCELLED) return null;
+ if (e.message === appleAuthAndroid.Error.SIGNIN_CANCELLED) {
+ return null;
+ }
Log.alert('[Apple Sign In] Apple authentication failed', e);
});
};
diff --git a/src/components/SignInButtons/AppleSignIn/index.desktop.js b/src/components/SignInButtons/AppleSignIn/index.desktop.js
index 425e88ddf930..52ff3ef710b0 100644
--- a/src/components/SignInButtons/AppleSignIn/index.desktop.js
+++ b/src/components/SignInButtons/AppleSignIn/index.desktop.js
@@ -14,7 +14,7 @@ const appleSignInWebRouteForDesktopFlow = `${CONFIG.EXPENSIFY.NEW_EXPENSIFY_URL}
*/
function AppleSignIn() {
return (
-
+ {
window.open(appleSignInWebRouteForDesktopFlow);
diff --git a/src/components/SignInButtons/AppleSignIn/index.ios.js b/src/components/SignInButtons/AppleSignIn/index.ios.js
index 681eebb298c5..0c9a8c9e8211 100644
--- a/src/components/SignInButtons/AppleSignIn/index.ios.js
+++ b/src/components/SignInButtons/AppleSignIn/index.ios.js
@@ -37,7 +37,9 @@ function AppleSignIn() {
appleSignInRequest()
.then((token) => Session.beginAppleSignIn(token))
.catch((e) => {
- if (e.code === appleAuth.Error.CANCELED) return null;
+ if (e.code === appleAuth.Error.CANCELED) {
+ return null;
+ }
Log.alert('[Apple Sign In] Apple authentication failed', e);
});
};
diff --git a/src/components/SignInButtons/AppleSignIn/index.website.js b/src/components/SignInButtons/AppleSignIn/index.website.js
index 41c8f2afd4d5..7046de5068b1 100644
--- a/src/components/SignInButtons/AppleSignIn/index.website.js
+++ b/src/components/SignInButtons/AppleSignIn/index.website.js
@@ -55,7 +55,9 @@ const successListener = (event) => {
};
const failureListener = (event) => {
- if (!event.detail || event.detail.error === 'popup_closed_by_user') return null;
+ if (!event.detail || event.detail.error === 'popup_closed_by_user') {
+ return null;
+ }
Log.warn(`Apple sign-in failed: ${event.detail}`);
};
@@ -126,7 +128,9 @@ const SingletonAppleSignInButtonWithFocus = withNavigationFocus(SingletonAppleSi
function AppleSignIn({isDesktopFlow}) {
const [scriptLoaded, setScriptLoaded] = useState(false);
useEffect(() => {
- if (window.appleAuthScriptLoaded) return;
+ if (window.appleAuthScriptLoaded) {
+ return;
+ }
const localeCode = getUserLanguage();
const script = document.createElement('script');
diff --git a/src/components/SignInButtons/GoogleSignIn/index.desktop.js b/src/components/SignInButtons/GoogleSignIn/index.desktop.js
index bdba2052d664..95a78f34614b 100644
--- a/src/components/SignInButtons/GoogleSignIn/index.desktop.js
+++ b/src/components/SignInButtons/GoogleSignIn/index.desktop.js
@@ -17,7 +17,7 @@ const googleSignInWebRouteForDesktopFlow = `${CONFIG.EXPENSIFY.NEW_EXPENSIFY_URL
*/
function GoogleSignIn() {
return (
-
+ {
window.open(googleSignInWebRouteForDesktopFlow);
diff --git a/src/components/SpacerView.js b/src/components/SpacerView.js
new file mode 100644
index 000000000000..3b81bbfa0dc5
--- /dev/null
+++ b/src/components/SpacerView.js
@@ -0,0 +1,49 @@
+import React from 'react';
+import Animated, {useAnimatedStyle, useSharedValue, withTiming} from 'react-native-reanimated';
+import PropTypes from 'prop-types';
+import * as StyleUtils from '../styles/StyleUtils';
+import stylePropTypes from '../styles/stylePropTypes';
+import CONST from '../CONST';
+
+const propTypes = {
+ /**
+ * Should we show the spacer
+ */
+ shouldShow: PropTypes.bool.isRequired,
+
+ /**
+ * Array of style objects
+ * @default []
+ */
+ style: stylePropTypes,
+};
+
+const defaultProps = {
+ style: [],
+};
+
+function SpacerView({shouldShow = true, style = []}) {
+ const marginVertical = useSharedValue(CONST.HORIZONTAL_SPACER.DEFAULT_MARGIN_VERTICAL);
+ const borderBottomWidth = useSharedValue(CONST.HORIZONTAL_SPACER.DEFAULT_BORDER_BOTTOM_WIDTH);
+ const animatedStyles = useAnimatedStyle(() => ({
+ marginVertical: marginVertical.value,
+ borderBottomWidth: borderBottomWidth.value,
+ }));
+
+ React.useEffect(() => {
+ const duration = CONST.ANIMATED_TRANSITION;
+ const values = {
+ marginVertical: shouldShow ? CONST.HORIZONTAL_SPACER.DEFAULT_MARGIN_VERTICAL : CONST.HORIZONTAL_SPACER.HIDDEN_MARGIN_VERTICAL,
+ borderBottomWidth: shouldShow ? CONST.HORIZONTAL_SPACER.DEFAULT_BORDER_BOTTOM_WIDTH : CONST.HORIZONTAL_SPACER.HIDDEN_BORDER_BOTTOM_WIDTH,
+ };
+ marginVertical.value = withTiming(values.marginVertical, {duration});
+ borderBottomWidth.value = withTiming(values.borderBottomWidth, {duration});
+ }, [shouldShow, borderBottomWidth, marginVertical]);
+
+ return ;
+}
+
+SpacerView.displayName = 'SpacerView';
+SpacerView.propTypes = propTypes;
+SpacerView.defaultProps = defaultProps;
+export default SpacerView;
diff --git a/src/components/TagPicker/index.js b/src/components/TagPicker/index.js
index 25021bd817d7..c46ca1b57b22 100644
--- a/src/components/TagPicker/index.js
+++ b/src/components/TagPicker/index.js
@@ -1,62 +1,58 @@
-import React, {useMemo} from 'react';
+import React, {useMemo, useState} from 'react';
import _ from 'underscore';
import lodashGet from 'lodash/get';
import {withOnyx} from 'react-native-onyx';
+import CONST from '../../CONST';
import ONYXKEYS from '../../ONYXKEYS';
import styles from '../../styles/styles';
-import Navigation from '../../libs/Navigation/Navigation';
-import ROUTES from '../../ROUTES';
import useLocalize from '../../hooks/useLocalize';
import * as OptionsListUtils from '../../libs/OptionsListUtils';
import OptionsSelector from '../OptionsSelector';
import {propTypes, defaultProps} from './tagPickerPropTypes';
-function TagPicker({policyTags, reportID, tag, iouType, iou}) {
+function TagPicker({selectedTag, tag, policyTags, policyRecentlyUsedTags, onSubmit}) {
const {translate} = useLocalize();
+ const [searchValue, setSearchValue] = useState('');
+
+ const policyRecentlyUsedTagsList = lodashGet(policyRecentlyUsedTags, tag, []);
+ const policyTagList = lodashGet(policyTags, [tag, 'tags'], {});
+ const policyTagsCount = _.size(_.filter(policyTagList, (policyTag) => policyTag.enabled));
+ const isTagsCountBelowThreshold = policyTagsCount < CONST.TAG_LIST_THRESHOLD;
+
+ const shouldShowTextInput = !isTagsCountBelowThreshold;
const selectedOptions = useMemo(() => {
- if (!iou.tag) {
+ if (!selectedTag) {
return [];
}
return [
{
- name: iou.tag,
+ name: selectedTag,
enabled: true,
+ accountID: null,
},
];
- }, [iou.tag]);
-
- // Only shows one section, which will be the default behavior if there are
- // less than 8 policy tags
- // TODO: support sections with search
- const sections = useMemo(() => {
- const tagList = _.chain(lodashGet(policyTags, [tag, 'tags'], {}))
- .values()
- .map((t) => ({
- text: t.name,
- keyForList: t.name,
- tooltipText: t.name,
- }))
- .value();
+ }, [selectedTag]);
- return [
- {
- data: tagList,
- },
- ];
- }, [policyTags, tag]);
+ const initialFocusedIndex = useMemo(() => {
+ if (isTagsCountBelowThreshold && selectedOptions.length > 0) {
+ return _.chain(policyTagList)
+ .values()
+ .findIndex((policyTag) => policyTag.name === selectedOptions[0].name, true)
+ .value();
+ }
- const headerMessage = OptionsListUtils.getHeaderMessage(lodashGet(sections, '[0].data.length', 0) > 0, false, '');
+ return 0;
+ }, [policyTagList, selectedOptions, isTagsCountBelowThreshold]);
- const navigateBack = () => {
- Navigation.goBack(ROUTES.getMoneyRequestConfirmationRoute(iouType, reportID));
- };
+ const sections = useMemo(
+ () =>
+ OptionsListUtils.getFilteredOptions({}, {}, [], searchValue, selectedOptions, [], false, false, false, {}, [], true, policyTagList, policyRecentlyUsedTagsList, false).tagOptions,
+ [searchValue, selectedOptions, policyTagList, policyRecentlyUsedTagsList],
+ );
- const updateTag = () => {
- // TODO: add logic to save the selected tag
- navigateBack();
- };
+ const headerMessage = OptionsListUtils.getHeaderMessage(lodashGet(sections, '[0].data.length', 0) > 0, false, '');
return (
);
}
@@ -84,7 +84,4 @@ export default withOnyx({
policyRecentlyUsedTags: {
key: ({policyID}) => `${ONYXKEYS.COLLECTION.POLICY_RECENTLY_USED_TAGS}${policyID}`,
},
- iou: {
- key: ONYXKEYS.IOU,
- },
})(TagPicker);
diff --git a/src/components/TagPicker/tagPickerPropTypes.js b/src/components/TagPicker/tagPickerPropTypes.js
index ad57a0409f15..a5d94605a76a 100644
--- a/src/components/TagPicker/tagPickerPropTypes.js
+++ b/src/components/TagPicker/tagPickerPropTypes.js
@@ -1,22 +1,18 @@
import PropTypes from 'prop-types';
import tagPropTypes from '../tagPropTypes';
-import {iouPropTypes, iouDefaultProps} from '../../pages/iou/propTypes';
const propTypes = {
- /** The report ID of the IOU */
- reportID: PropTypes.string.isRequired,
-
/** The policyID we are getting tags for */
policyID: PropTypes.string.isRequired,
+ /** The selected tag of the money request */
+ selectedTag: PropTypes.string.isRequired,
+
/** The name of tag list we are getting tags for */
tag: PropTypes.string.isRequired,
- /** The type of IOU report, i.e. bill, request, send */
- iouType: PropTypes.string.isRequired,
-
/** Callback to submit the selected tag */
- onSubmit: PropTypes.func,
+ onSubmit: PropTypes.func.isRequired,
/* Onyx Props */
/** Collection of tags attached to a policy */
@@ -29,15 +25,11 @@ const propTypes = {
/** List of recently used tags */
policyRecentlyUsedTags: PropTypes.objectOf(PropTypes.arrayOf(PropTypes.string)),
-
- /** Holds data related to Money Request view state, rather than the underlying Money Request data. */
- iou: iouPropTypes,
};
const defaultProps = {
policyTags: {},
policyRecentlyUsedTags: {},
- iou: iouDefaultProps,
};
export {propTypes, defaultProps};
diff --git a/src/components/TextInput/BaseTextInput.js b/src/components/TextInput/BaseTextInput.js
index a85e896b07d5..ec22b09dde04 100644
--- a/src/components/TextInput/BaseTextInput.js
+++ b/src/components/TextInput/BaseTextInput.js
@@ -306,6 +306,7 @@ function BaseTextInput(props) {
pointerEvents="none"
selectable={false}
style={[styles.textInputPrefix, !hasLabel && styles.pv0]}
+ dataSet={{[CONST.SELECTION_SCRAPER_HIDDEN_ELEMENT]: true}}
>
{props.prefixCharacter}
diff --git a/src/components/TextInput/index.js b/src/components/TextInput/index.js
index 48a92f081200..6cefe04e71a1 100644
--- a/src/components/TextInput/index.js
+++ b/src/components/TextInput/index.js
@@ -30,7 +30,9 @@ function TextInput(props) {
});
return () => {
- if (!removeVisibilityListenerRef.current) return;
+ if (!removeVisibilityListenerRef.current) {
+ return;
+ }
removeVisibilityListenerRef.current();
};
// eslint-disable-next-line react-hooks/exhaustive-deps
diff --git a/src/components/ThreeDotsMenu/index.js b/src/components/ThreeDotsMenu/index.js
index b5637a4f3879..f0cee6fdea2f 100644
--- a/src/components/ThreeDotsMenu/index.js
+++ b/src/components/ThreeDotsMenu/index.js
@@ -45,6 +45,9 @@ const propTypes = {
horizontal: PropTypes.oneOf(_.values(CONST.MODAL.ANCHOR_ORIGIN_HORIZONTAL)),
vertical: PropTypes.oneOf(_.values(CONST.MODAL.ANCHOR_ORIGIN_VERTICAL)),
}),
+
+ /** Whether the popover menu should overlay the current view */
+ shouldOverlay: PropTypes.bool,
};
const defaultProps = {
@@ -57,9 +60,10 @@ const defaultProps = {
horizontal: CONST.MODAL.ANCHOR_ORIGIN_HORIZONTAL.LEFT,
vertical: CONST.MODAL.ANCHOR_ORIGIN_VERTICAL.TOP, // we assume that popover menu opens below the button, anchor is at TOP
},
+ shouldOverlay: false,
};
-function ThreeDotsMenu({iconTooltip, icon, iconFill, iconStyles, onIconPress, menuItems, anchorPosition, anchorAlignment}) {
+function ThreeDotsMenu({iconTooltip, icon, iconFill, iconStyles, onIconPress, menuItems, anchorPosition, anchorAlignment, shouldOverlay}) {
const [isPopupMenuVisible, setPopupMenuVisible] = useState(false);
const buttonRef = useRef(null);
const {translate} = useLocalize();
@@ -106,7 +110,7 @@ function ThreeDotsMenu({iconTooltip, icon, iconFill, iconStyles, onIconPress, me
anchorAlignment={anchorAlignment}
onItemSelected={hidePopoverMenu}
menuItems={menuItems}
- withoutOverlay
+ withoutOverlay={!shouldOverlay}
anchorRef={buttonRef}
/>
>
diff --git a/src/components/Tooltip/index.js b/src/components/Tooltip/index.js
index 398df07649cf..f60982f52dd4 100644
--- a/src/components/Tooltip/index.js
+++ b/src/components/Tooltip/index.js
@@ -154,6 +154,7 @@ function Tooltip(props) {
{children}
diff --git a/src/components/Tooltip/tooltipPropTypes.js b/src/components/Tooltip/tooltipPropTypes.js
index af18c4cfa412..2ddf8120d58c 100644
--- a/src/components/Tooltip/tooltipPropTypes.js
+++ b/src/components/Tooltip/tooltipPropTypes.js
@@ -28,6 +28,9 @@ const propTypes = {
/** Unique key of renderTooltipContent to rerender the tooltip when one of the key changes */
renderTooltipContentKey: PropTypes.arrayOf(PropTypes.string),
+
+ /** passes this down to Hoverable component to decide whether to handle the scroll behaviour to show hover once the scroll ends */
+ shouldHandleScroll: PropTypes.bool,
};
const defaultProps = {
@@ -38,6 +41,7 @@ const defaultProps = {
numberOfLines: CONST.TOOLTIP_MAX_LINES,
renderTooltipContent: undefined,
renderTooltipContentKey: [],
+ shouldHandleScroll: false,
};
export {propTypes, defaultProps};
diff --git a/src/components/UserDetailsTooltip/index.web.js b/src/components/UserDetailsTooltip/index.web.js
index 5fdae15184ac..1a78459d30a6 100644
--- a/src/components/UserDetailsTooltip/index.web.js
+++ b/src/components/UserDetailsTooltip/index.web.js
@@ -66,6 +66,7 @@ function UserDetailsTooltip(props) {
shiftHorizontal={props.shiftHorizontal}
renderTooltipContent={renderTooltipContent}
renderTooltipContentKey={[userDisplayName, userLogin]}
+ shouldHandleScroll
>
{props.children}
diff --git a/src/components/optionPropTypes.js b/src/components/optionPropTypes.js
index bb1a7a073b61..709298036f07 100644
--- a/src/components/optionPropTypes.js
+++ b/src/components/optionPropTypes.js
@@ -68,6 +68,4 @@ export default PropTypes.shape({
brickRoadIndicator: PropTypes.oneOf([CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR, '']),
phoneNumber: PropTypes.string,
-
- payPalMeAddress: PropTypes.string,
});
diff --git a/src/components/paypalMeDataPropTypes.js b/src/components/paypalMeDataPropTypes.js
deleted file mode 100644
index 5498186748a5..000000000000
--- a/src/components/paypalMeDataPropTypes.js
+++ /dev/null
@@ -1,15 +0,0 @@
-import PropTypes from 'prop-types';
-
-export default PropTypes.shape({
- /** This is always 'PayPal.me' */
- title: PropTypes.string,
-
- /** The paypalMe address */
- description: PropTypes.string,
-
- /** This is always 'payPalMe' */
- methodID: PropTypes.string,
-
- /** This is always 'payPalMe' */
- accountType: PropTypes.string,
-});
diff --git a/src/components/withNavigationFallback.js b/src/components/withNavigationFallback.js
index bc4ea5dd3fad..e82946c9e049 100644
--- a/src/components/withNavigationFallback.js
+++ b/src/components/withNavigationFallback.js
@@ -1,39 +1,30 @@
-import React, {Component} from 'react';
+import React, {forwardRef, useContext, useMemo} from 'react';
import {NavigationContext} from '@react-navigation/core';
import getComponentDisplayName from '../libs/getComponentDisplayName';
import refPropTypes from './refPropTypes';
export default function (WrappedComponent) {
- class WithNavigationFallback extends Component {
- render() {
- if (!this.context) {
- return (
- true,
- addListener: () => () => {},
- removeListener: () => () => {},
- }}
- >
-
-
- );
- }
+ function WithNavigationFallback(props) {
+ const context = useContext(NavigationContext);
- return (
+ const navigationContextValue = useMemo(() => ({isFocused: () => true, addListener: () => () => {}, removeListener: () => () => {}}), []);
+
+ return context ? (
+
+ ) : (
+
- );
- }
+
+ );
}
- WithNavigationFallback.contextType = NavigationContext;
WithNavigationFallback.displayName = `WithNavigationFocusWithFallback(${getComponentDisplayName(WrappedComponent)})`;
WithNavigationFallback.propTypes = {
forwardedRef: refPropTypes,
@@ -41,7 +32,8 @@ export default function (WrappedComponent) {
WithNavigationFallback.defaultProps = {
forwardedRef: undefined,
};
- return React.forwardRef((props, ref) => (
+
+ return forwardRef((props, ref) => (
{
const onDimensionChange = (newDimensions) => {
const {window} = newDimensions;
-
setWindowDimension({
windowHeight: window.height,
windowWidth: window.width,
});
};
- const dimensionsEventListener = Dimensions.addEventListener('change', onDimensionChange);
+ const onDimensionChangeDebounce = lodashDebounce(onDimensionChange, 300);
+
+ const dimensionsEventListener = Dimensions.addEventListener('change', onDimensionChangeDebounce);
return () => {
if (!dimensionsEventListener) {
diff --git a/src/hooks/usePrivatePersonalDetails.js b/src/hooks/usePrivatePersonalDetails.js
index 37eb63dcd0fd..14c1e42e629a 100644
--- a/src/hooks/usePrivatePersonalDetails.js
+++ b/src/hooks/usePrivatePersonalDetails.js
@@ -1,4 +1,5 @@
import {useEffect, useContext} from 'react';
+import _ from 'underscore';
import * as PersonalDetails from '../libs/actions/PersonalDetails';
import {NetworkContext} from '../components/OnyxProvider';
@@ -9,7 +10,8 @@ export default function usePrivatePersonalDetails() {
const {isOffline} = useContext(NetworkContext);
useEffect(() => {
- if (isOffline || Boolean(PersonalDetails.getPrivatePersonalDetails())) {
+ const personalDetails = PersonalDetails.getPrivatePersonalDetails();
+ if (isOffline || (Boolean(personalDetails) && !_.isUndefined(personalDetails.isLoading))) {
return;
}
PersonalDetails.openPersonalDetailsPage();
diff --git a/src/hooks/useWindowDimensions/index.js b/src/hooks/useWindowDimensions/index.js
index 86ff7ce85d3d..1a1f7eed5a67 100644
--- a/src/hooks/useWindowDimensions/index.js
+++ b/src/hooks/useWindowDimensions/index.js
@@ -1,5 +1,5 @@
// eslint-disable-next-line no-restricted-imports
-import {useWindowDimensions} from 'react-native';
+import {Dimensions, useWindowDimensions} from 'react-native';
import variables from '../../styles/variables';
/**
@@ -8,7 +8,9 @@ import variables from '../../styles/variables';
*/
export default function () {
const {width: windowWidth, height: windowHeight} = useWindowDimensions();
- const isExtraSmallScreenHeight = windowHeight <= variables.extraSmallMobileResponsiveHeightBreakpoint;
+ // When the soft keyboard opens on mWeb, the window height changes. Use static screen height instead to get real screenHeight.
+ const screenHeight = Dimensions.get('screen').height;
+ const isExtraSmallScreenHeight = screenHeight <= variables.extraSmallMobileResponsiveHeightBreakpoint;
const isSmallScreenWidth = windowWidth <= variables.mobileResponsiveWidthBreakpoint;
const isMediumScreenWidth = windowWidth > variables.mobileResponsiveWidthBreakpoint && windowWidth <= variables.tabletResponsiveWidthBreakpoint;
const isLargeScreenWidth = windowWidth > variables.tabletResponsiveWidthBreakpoint;
diff --git a/src/languages/en.ts b/src/languages/en.ts
index 9c44b4356a31..f7c028d2a106 100755
--- a/src/languages/en.ts
+++ b/src/languages/en.ts
@@ -25,7 +25,6 @@ import type {
ReportArchiveReasonsPolicyDeletedParams,
RequestCountParams,
SettleExpensifyCardParams,
- SettlePaypalMeParams,
RequestAmountParams,
SplitAmountParams,
AmountEachParams,
@@ -37,7 +36,6 @@ import type {
WaitingOnBankAccountParams,
SettledAfterAddedBankAccountParams,
PaidElsewhereWithAmountParams,
- PaidUsingPaypalWithAmountParams,
PaidWithExpensifyWithAmountParams,
ThreadRequestReportNameParams,
ThreadSentMoneyReportNameParams,
@@ -71,6 +69,8 @@ import type {
SetTheRequestParams,
UpdatedTheRequestParams,
RemovedTheRequestParams,
+ FormattedMaxLengthParams,
+ RequestedAmountMessageParams,
TagSelectionParams,
TranslationBase,
} from './types';
@@ -206,7 +206,6 @@ export default {
done: 'Done',
more: 'More',
debitCard: 'Debit card',
- payPalMe: 'PayPal.me',
bankAccount: 'Bank account',
join: 'Join',
decline: 'Decline',
@@ -244,6 +243,7 @@ export default {
showMore: 'Show more',
merchant: 'Merchant',
category: 'Category',
+ billable: 'Billable',
tag: 'Tag',
receipt: 'Receipt',
replace: 'Replace',
@@ -254,6 +254,7 @@ export default {
kilometers: 'kilometers',
recent: 'Recent',
all: 'All',
+ tbd: 'TBD',
},
anonymousReportFooter: {
logoTagline: 'Join the discussion.',
@@ -282,6 +283,7 @@ export default {
composer: {
noExtensionFoundForMimeType: 'No extension found for mime type',
problemGettingImageYouPasted: 'There was a problem getting the image you pasted',
+ commentExceededMaxLength: ({formattedMaxLength}: FormattedMaxLengthParams) => `The maximum comment length is ${formattedMaxLength} characters.`,
},
baseUpdateAppModal: {
updateApp: 'Update app',
@@ -344,11 +346,6 @@ export default {
`It's always great to see a new face around here! Please enter the magic code sent to ${login}. It should arrive within a minute or two.`,
welcomeEnterMagicCode: ({login}: WelcomeEnterMagicCodeParams) => `Please enter the magic code sent to ${login}. It should arrive within a minute or two.`,
},
- DownloadAppModal: {
- downloadTheApp: 'Download the app',
- keepTheConversationGoing: 'Keep the conversation going in New Expensify, download the app for an enhanced experience.',
- noThanks: 'No thanks',
- },
login: {
hero: {
header: 'Split bills, request payments, and chat with friends.',
@@ -518,11 +515,10 @@ export default {
deleteConfirmation: 'Are you sure that you want to delete this request?',
settledExpensify: 'Paid',
settledElsewhere: 'Paid elsewhere',
- settledPaypalMe: 'Paid using Paypal.me',
settleExpensify: ({formattedAmount}: SettleExpensifyCardParams) => `Pay ${formattedAmount} with Expensify`,
payElsewhere: 'Pay elsewhere',
- settlePaypalMe: ({formattedAmount}: SettlePaypalMeParams) => `Pay ${formattedAmount} with PayPal.me`,
requestAmount: ({amount}: RequestAmountParams) => `request ${amount}`,
+ requestedAmount: ({formattedAmount, comment}: RequestedAmountMessageParams) => `requested ${formattedAmount}${comment ? ` for ${comment}` : ''}`,
splitAmount: ({amount}: SplitAmountParams) => `split ${amount}`,
amountEach: ({amount}: AmountEachParams) => `${amount} each`,
payerOwesAmount: ({payer, amount}: PayerOwesAmountParams) => `${payer} owes ${amount}`,
@@ -534,9 +530,8 @@ export default {
waitingOnBankAccount: ({submitterDisplayName}: WaitingOnBankAccountParams) => `started settling up, payment is held until ${submitterDisplayName} adds a bank account`,
settledAfterAddedBankAccount: ({submitterDisplayName, amount}: SettledAfterAddedBankAccountParams) =>
`${submitterDisplayName} added a bank account. The ${amount} payment has been made.`,
- paidElsewhereWithAmount: ({amount}: PaidElsewhereWithAmountParams) => `paid ${amount} elsewhere`,
- paidUsingPaypalWithAmount: ({amount}: PaidUsingPaypalWithAmountParams) => `paid ${amount} using Paypal.me`,
- paidWithExpensifyWithAmount: ({amount}: PaidWithExpensifyWithAmountParams) => `paid ${amount} with Expensify`,
+ paidElsewhereWithAmount: ({payer, amount}: PaidElsewhereWithAmountParams) => `${payer} paid ${amount} elsewhere`,
+ paidWithExpensifyWithAmount: ({payer, amount}: PaidWithExpensifyWithAmountParams) => `${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',
@@ -548,6 +543,7 @@ export default {
threadSentMoneyReportName: ({formattedAmount, comment}: ThreadSentMoneyReportNameParams) => `${formattedAmount} sent${comment ? ` for ${comment}` : ''}`,
tagSelection: ({tagName}: TagSelectionParams) => `Select a ${tagName} to add additional organization to your money`,
error: {
+ invalidAmount: 'Please enter a valid amount before continuing.',
invalidSplit: 'Split amounts do not equal total amount',
other: 'Unexpected error, please try again later',
genericCreateFailureMessage: 'Unexpected error requesting money, please try again later',
@@ -573,6 +569,8 @@ export default {
uploadPhoto: 'Upload photo',
removePhoto: 'Remove photo',
editImage: 'Edit photo',
+ viewPhoto: 'View photo',
+ imageUploadFailed: 'Image upload failed',
deleteWorkspaceError: 'Sorry, there was an unexpected problem deleting your workspace avatar.',
sizeExceeded: ({maxUploadSizeInMB}: SizeExceededParams) => `The selected image exceeds the maximum upload size of ${maxUploadSizeInMB}MB.`,
resolutionConstraints: ({minHeightInPx, minWidthInPx, maxHeightInPx, maxWidthInPx}: ResolutionConstraintsParams) =>
@@ -592,6 +590,7 @@ export default {
online: 'Online',
offline: 'Offline',
syncing: 'Syncing',
+ profileAvatar: 'Profile avatar',
},
loungeAccessPage: {
loungeAccess: 'Lounge access',
@@ -733,6 +732,7 @@ export default {
keepCodesSafe: 'Keep these recovery codes safe!',
codesLoseAccess:
'If you lose access to your authenticator app and don’t have these codes, you will lose access to your account. \n\nNote: Setting up two-factor authentication will log you out of all other active sessions.',
+ errorStepCodes: 'Please copy or download codes before continuing.',
stepVerify: 'Verify',
scanCode: 'Scan the QR code using your',
authenticatorApp: 'authenticator app',
@@ -744,6 +744,15 @@ export default {
copy: 'Copy',
disable: 'Disable',
},
+ recoveryCodeForm: {
+ error: {
+ pleaseFillRecoveryCode: 'Please enter your recovery code',
+ incorrectRecoveryCode: 'Incorrect recovery code. Please try again.',
+ },
+ useRecoveryCode: 'Use recovery code',
+ recoveryCode: 'Recovery code',
+ use2fa: 'Use two-factor authentication code',
+ },
twoFactorAuthForm: {
error: {
pleaseFillTwoFactorAuth: 'Please enter your two-factor authentication code',
@@ -760,18 +769,7 @@ export default {
sharedNoteMessage: 'Keep notes about this chat here. Expensify employees and other users on the team.expensify.com domain can view these notes.',
notesUnavailable: 'No notes found for the user',
composerLabel: 'Notes',
- },
- addPayPalMePage: {
- enterYourUsernameToGetPaidViaPayPal: 'Get paid back via PayPal.',
- payPalMe: 'PayPal.me/',
- yourPayPalUsername: 'Your PayPal username',
- addPayPalAccount: 'Add PayPal account',
- growlMessageOnSave: 'Your PayPal username was successfully added',
- updatePaypalAccount: 'Save PayPal account',
- growlMessageOnUpdate: 'Your PayPal username was successfully saved',
- formatError: 'Invalid PayPal.me username',
- checkListOf: 'Check the list of ',
- supportedCurrencies: 'supported currencies',
+ myNote: 'My note',
},
addDebitCardPage: {
addADebitCard: 'Add a debit card',
@@ -802,7 +800,6 @@ export default {
setDefaultSuccess: 'Default payment method set!',
deleteAccount: 'Delete account',
deleteConfirmation: 'Are you sure that you want to delete this account?',
- deletePayPalSuccess: 'PayPal.me successfully deleted',
error: {
notOwnerOfBankAccount: 'There was an error setting this bank account as your default payment method.',
invalidBankAccount: 'This bank account is temporarily suspended.',
@@ -909,6 +906,7 @@ export default {
validateCodeForm: {
magicCodeNotReceived: "Didn't receive a magic code?",
enterAuthenticatorCode: 'Please enter your authenticator code',
+ enterRecoveryCode: 'Please enter your recovery code',
requiredWhen2FAEnabled: 'Required when 2FA is enabled',
requestNewCode: 'Request a new code in ',
requestNewCodeAfterErrorOccurred: 'Request a new code',
@@ -1321,6 +1319,7 @@ export default {
memberNotFound: 'Member not found. To invite a new member to the workspace, please use the Invite button above.',
notAuthorized: `You do not have access to this page. Are you trying to join the workspace? Please reach out to the owner of this workspace so they can add you as a member! Something else? Reach out to ${CONST.EMAIL.CONCIERGE}`,
goToRoom: ({roomName}: GoToRoomParams) => `Go to ${roomName} room`,
+ workspaceAvatar: 'Workspace avatar',
},
emptyWorkspace: {
title: 'Create a new workspace',
@@ -1529,7 +1528,7 @@ export default {
completed: 'Completed',
messages: {
completed: 'completed task',
- canceled: 'canceled task',
+ canceled: 'deleted task',
reopened: 'reopened task',
error: 'You do not have the permission to do the requested action.',
},
@@ -1701,6 +1700,7 @@ export default {
parentReportAction: {
deletedMessage: '[Deleted message]',
deletedRequest: '[Deleted request]',
+ deletedTask: '[Deleted task]',
hiddenMessage: '[Hidden message]',
},
threads: {
diff --git a/src/languages/es.ts b/src/languages/es.ts
index a50b6821fc34..a68f33a33730 100644
--- a/src/languages/es.ts
+++ b/src/languages/es.ts
@@ -25,7 +25,6 @@ import type {
ReportArchiveReasonsPolicyDeletedParams,
RequestCountParams,
SettleExpensifyCardParams,
- SettlePaypalMeParams,
RequestAmountParams,
SplitAmountParams,
AmountEachParams,
@@ -37,7 +36,6 @@ import type {
WaitingOnBankAccountParams,
SettledAfterAddedBankAccountParams,
PaidElsewhereWithAmountParams,
- PaidUsingPaypalWithAmountParams,
PaidWithExpensifyWithAmountParams,
ThreadRequestReportNameParams,
ThreadSentMoneyReportNameParams,
@@ -71,6 +69,8 @@ import type {
SetTheRequestParams,
UpdatedTheRequestParams,
RemovedTheRequestParams,
+ FormattedMaxLengthParams,
+ RequestedAmountMessageParams,
TagSelectionParams,
EnglishTranslation,
} from './types';
@@ -196,7 +196,6 @@ export default {
done: 'Listo',
more: 'Más',
debitCard: 'Tarjeta de débito',
- payPalMe: 'PayPal.me',
bankAccount: 'Cuenta bancaria',
join: 'Unirse',
decline: 'Rechazar',
@@ -234,6 +233,7 @@ export default {
showMore: 'Mostrar más',
merchant: 'Comerciante',
category: 'Categoría',
+ billable: 'Facturable',
tag: 'Etiqueta',
receipt: 'Recibo',
replace: 'Sustituir',
@@ -244,6 +244,7 @@ export default {
kilometers: 'kilómetros',
recent: 'Reciente',
all: 'Todo',
+ tbd: 'Por determinar',
},
anonymousReportFooter: {
logoTagline: 'Únete a la discusión.',
@@ -272,6 +273,7 @@ export default {
composer: {
noExtensionFoundForMimeType: 'No se encontró una extension para este tipo de contenido',
problemGettingImageYouPasted: 'Ha ocurrido un problema al obtener la imagen que has pegado',
+ commentExceededMaxLength: ({formattedMaxLength}: FormattedMaxLengthParams) => `El comentario debe tener máximo ${formattedMaxLength} caracteres.`,
},
baseUpdateAppModal: {
updateApp: 'Actualizar app',
@@ -335,11 +337,6 @@ export default {
`¡Siempre es genial ver una cara nueva por aquí! Por favor ingresa el código mágico enviado a ${login}. Debería llegar en un par de minutos.`,
welcomeEnterMagicCode: ({login}: WelcomeEnterMagicCodeParams) => `Por favor, introduce el código mágico enviado a ${login}. Debería llegar en un par de minutos.`,
},
- DownloadAppModal: {
- downloadTheApp: 'Descarga la aplicación',
- keepTheConversationGoing: 'Mantén la conversación en New Expensify, descarga la aplicación para una experiencia mejorada.',
- noThanks: 'No, gracias',
- },
login: {
hero: {
header: 'Divida las facturas, solicite pagos y chatee con sus amigos.',
@@ -510,11 +507,10 @@ export default {
deleteConfirmation: '¿Estás seguro de que quieres eliminar este pedido?',
settledExpensify: 'Pagado',
settledElsewhere: 'Pagado de otra forma',
- settledPaypalMe: 'Pagado con PayPal.me',
settleExpensify: ({formattedAmount}: SettleExpensifyCardParams) => `Pagar ${formattedAmount} con Expensify`,
payElsewhere: 'Pagar de otra forma',
- settlePaypalMe: ({formattedAmount}: SettlePaypalMeParams) => `Pagar ${formattedAmount} con PayPal.me`,
requestAmount: ({amount}: RequestAmountParams) => `solicitar ${amount}`,
+ requestedAmount: ({formattedAmount, comment}: RequestedAmountMessageParams) => `solicité ${formattedAmount}${comment ? ` para ${comment}` : ''}`,
splitAmount: ({amount}: SplitAmountParams) => `dividir ${amount}`,
amountEach: ({amount}: AmountEachParams) => `${amount} cada uno`,
payerOwesAmount: ({payer, amount}: PayerOwesAmountParams) => `${payer} debe ${amount}`,
@@ -526,9 +522,8 @@ export default {
waitingOnBankAccount: ({submitterDisplayName}: WaitingOnBankAccountParams) => `inicio el pago, pero no se procesará hasta que ${submitterDisplayName} añada una cuenta bancaria`,
settledAfterAddedBankAccount: ({submitterDisplayName, amount}: SettledAfterAddedBankAccountParams) =>
`${submitterDisplayName} añadió una cuenta bancaria. El pago de ${amount} se ha realizado.`,
- paidElsewhereWithAmount: ({amount}: PaidElsewhereWithAmountParams) => `pagó ${amount} de otra forma`,
- paidUsingPaypalWithAmount: ({amount}: PaidUsingPaypalWithAmountParams) => `pagó ${amount} con PayPal.me`,
- paidWithExpensifyWithAmount: ({amount}: PaidWithExpensifyWithAmountParams) => `pagó ${amount} con Expensify`,
+ paidElsewhereWithAmount: ({payer, amount}: PaidElsewhereWithAmountParams) => `${payer} pagó ${amount} de otra forma`,
+ paidWithExpensifyWithAmount: ({payer, amount}: PaidWithExpensifyWithAmountParams) => `${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',
@@ -541,6 +536,7 @@ export default {
threadSentMoneyReportName: ({formattedAmount, comment}: ThreadSentMoneyReportNameParams) => `${formattedAmount} enviado${comment ? ` para ${comment}` : ''}`,
tagSelection: ({tagName}: TagSelectionParams) => `Seleccione una ${tagName} para organizar mejor tu dinero`,
error: {
+ invalidAmount: 'Por favor ingresa un monto válido antes de continuar.',
invalidSplit: 'La suma de las partes no equivale al monto total',
other: 'Error inesperado, por favor inténtalo más tarde',
genericCreateFailureMessage: 'Error inesperado solicitando dinero, Por favor, inténtalo más tarde',
@@ -566,6 +562,8 @@ export default {
uploadPhoto: 'Subir foto',
removePhoto: 'Eliminar foto',
editImage: 'Editar foto',
+ viewPhoto: 'Ver foto',
+ imageUploadFailed: 'Error al cargar la imagen',
deleteWorkspaceError: 'Lo sentimos, hubo un problema eliminando el avatar de su espacio de trabajo.',
sizeExceeded: ({maxUploadSizeInMB}: SizeExceededParams) => `La imagen supera el tamaño máximo de ${maxUploadSizeInMB}MB.`,
resolutionConstraints: ({minHeightInPx, minWidthInPx, maxHeightInPx, maxWidthInPx}: ResolutionConstraintsParams) =>
@@ -585,6 +583,7 @@ export default {
online: 'En línea',
offline: 'Desconectado',
syncing: 'Sincronizando',
+ profileAvatar: 'Perfil avatar',
},
loungeAccessPage: {
loungeAccess: 'Acceso a la sala vip',
@@ -728,6 +727,7 @@ export default {
keepCodesSafe: '¡Guarda los códigos de recuperación en un lugar seguro!',
codesLoseAccess:
'Si pierdes el acceso a tu aplicación de autenticación y no tienes estos códigos, perderás el acceso a tu cuenta. \n\nNota: Configurar la autenticación de dos factores cerrará la sesión de todas las demás sesiones activas.',
+ errorStepCodes: 'Copia o descarga los códigos antes de continuar.',
stepVerify: 'Verificar',
scanCode: 'Escanea el código QR usando tu',
authenticatorApp: 'aplicación de autenticación',
@@ -739,6 +739,15 @@ export default {
copy: 'Copiar',
disable: 'Deshabilitar',
},
+ recoveryCodeForm: {
+ error: {
+ pleaseFillRecoveryCode: 'Por favor, introduce tu código de recuperación',
+ incorrectRecoveryCode: 'Código de recuperación incorrecto. Por favor, inténtalo de nuevo',
+ },
+ useRecoveryCode: 'Usar código de recuperación',
+ recoveryCode: 'Código de recuperación',
+ use2fa: 'Usar autenticación de dos factores',
+ },
twoFactorAuthForm: {
error: {
pleaseFillTwoFactorAuth: 'Por favor, introduce tu código de autenticación de dos factores',
@@ -755,18 +764,7 @@ export default {
sharedNoteMessage: 'Guarda notas sobre este chat aquí. Los empleados de Expensify y otros usuarios del dominio team.expensify.com pueden ver estas notas.',
notesUnavailable: 'No se han encontrado notas para el usuario',
composerLabel: 'Notas',
- },
- addPayPalMePage: {
- enterYourUsernameToGetPaidViaPayPal: 'Recibe pagos vía PayPal.',
- payPalMe: 'PayPal.me/',
- yourPayPalUsername: 'Tu usuario de PayPal',
- addPayPalAccount: 'Añadir cuenta de PayPal',
- growlMessageOnSave: 'Tu nombre de usuario de PayPal se añadió correctamente',
- updatePaypalAccount: 'Guardar cuenta PayPal',
- growlMessageOnUpdate: 'Su nombre de usuario de PayPal se guardó con éxito',
- formatError: 'Usuario PayPal.me no válido',
- checkListOf: 'Consulta la lista de ',
- supportedCurrencies: 'monedas admitidas',
+ myNote: 'Mi notas',
},
addDebitCardPage: {
addADebitCard: 'Añadir una tarjeta de débito',
@@ -797,7 +795,6 @@ export default {
setDefaultSuccess: 'Método de pago configurado',
deleteAccount: 'Eliminar cuenta',
deleteConfirmation: '¿Estás seguro de que quieres eliminar esta cuenta?',
- deletePayPalSuccess: 'PayPal.me eliminada correctamente',
error: {
notOwnerOfBankAccount: 'Ha ocurrido un error al establecer esta cuenta bancaria como método de pago predeterminado.',
invalidBankAccount: 'Esta cuenta bancaria está temporalmente suspendida.',
@@ -905,6 +902,7 @@ export default {
validateCodeForm: {
magicCodeNotReceived: '¿No recibiste un código mágico?',
enterAuthenticatorCode: 'Por favor, introduce el código de autenticador',
+ enterRecoveryCode: 'Por favor, introduce tu código de recuperación',
requiredWhen2FAEnabled: 'Obligatorio cuando A2F está habilitado',
requestNewCode: 'Pedir un código nuevo en ',
requestNewCodeAfterErrorOccurred: 'Solicitar un nuevo código',
@@ -1340,6 +1338,7 @@ export default {
memberNotFound: 'Miembro no encontrado. Para invitar a un nuevo miembro al espacio de trabajo, por favor, utiliza el botón Invitar que está arriba.',
notAuthorized: `No tienes acceso a esta página. ¿Estás tratando de unirte al espacio de trabajo? Comunícate con el propietario de este espacio de trabajo para que pueda añadirte como miembro. ¿Necesitas algo más? Comunícate con ${CONST.EMAIL.CONCIERGE}`,
goToRoom: ({roomName}: GoToRoomParams) => `Ir a la sala ${roomName}`,
+ workspaceAvatar: 'Espacio de trabajo avatar',
},
emptyWorkspace: {
title: 'Crear un nuevo espacio de trabajo',
@@ -1551,7 +1550,7 @@ export default {
completed: 'Completada',
messages: {
completed: 'tarea completada',
- canceled: 'tarea cancelada',
+ canceled: 'tarea eliminado',
reopened: 'tarea reabrir',
error: 'No tiene permiso para realizar la acción solicitada.',
},
@@ -2183,6 +2182,7 @@ export default {
parentReportAction: {
deletedMessage: '[Mensaje eliminado]',
deletedRequest: '[Pedido eliminado]',
+ deletedTask: '[Tarea eliminado]',
hiddenMessage: '[Mensaje oculto]',
},
threads: {
diff --git a/src/languages/types.ts b/src/languages/types.ts
index 565b5933b989..70bf2e4cae3d 100644
--- a/src/languages/types.ts
+++ b/src/languages/types.ts
@@ -100,10 +100,10 @@ type SettleExpensifyCardParams = {
formattedAmount: string;
};
-type SettlePaypalMeParams = {formattedAmount: string};
-
type RequestAmountParams = {amount: number};
+type RequestedAmountMessageParams = {formattedAmount: string; comment: string};
+
type SplitAmountParams = {amount: number};
type AmountEachParams = {amount: number};
@@ -124,11 +124,9 @@ type WaitingOnBankAccountParams = {submitterDisplayName: string};
type SettledAfterAddedBankAccountParams = {submitterDisplayName: string; amount: string};
-type PaidElsewhereWithAmountParams = {amount: string};
+type PaidElsewhereWithAmountParams = {payer: string; amount: string};
-type PaidUsingPaypalWithAmountParams = {amount: string};
-
-type PaidWithExpensifyWithAmountParams = {amount: string};
+type PaidWithExpensifyWithAmountParams = {payer: string; amount: string};
type ThreadRequestReportNameParams = {formattedAmount: string; comment: string};
@@ -192,6 +190,8 @@ type RemovedTheRequestParams = {valueName: string; oldValueToDisplay: string};
type UpdatedTheRequestParams = {valueName: string; newValueToDisplay: string; oldValueToDisplay: string};
+type FormattedMaxLengthParams = {formattedMaxLength: string};
+
type TagSelectionParams = {tagName: string};
/* Translation Object types */
@@ -260,8 +260,8 @@ export type {
ReportArchiveReasonsPolicyDeletedParams,
RequestCountParams,
SettleExpensifyCardParams,
- SettlePaypalMeParams,
RequestAmountParams,
+ RequestedAmountMessageParams,
SplitAmountParams,
AmountEachParams,
PayerOwesAmountParams,
@@ -273,7 +273,6 @@ export type {
WaitingOnBankAccountParams,
SettledAfterAddedBankAccountParams,
PaidElsewhereWithAmountParams,
- PaidUsingPaypalWithAmountParams,
PaidWithExpensifyWithAmountParams,
ThreadRequestReportNameParams,
ThreadSentMoneyReportNameParams,
@@ -306,5 +305,6 @@ export type {
SetTheRequestParams,
UpdatedTheRequestParams,
RemovedTheRequestParams,
+ FormattedMaxLengthParams,
TagSelectionParams,
};
diff --git a/src/libs/AppStateMonitor/index.js b/src/libs/AppStateMonitor/index.ts
similarity index 71%
rename from src/libs/AppStateMonitor/index.js
rename to src/libs/AppStateMonitor/index.ts
index 12370382919e..5c206579944d 100644
--- a/src/libs/AppStateMonitor/index.js
+++ b/src/libs/AppStateMonitor/index.ts
@@ -1,20 +1,15 @@
-import {AppState} from 'react-native';
+import {AppState, AppStateStatus} from 'react-native';
import CONST from '../../CONST';
import shouldReportActivity from './shouldReportActivity';
-let appState = CONST.APP_STATE.ACTIVE;
+let appState: AppStateStatus = CONST.APP_STATE.ACTIVE;
/**
* Listener that will only fire the callback when the user has become active.
- *
- * @param {Function} callback
- * @returns {Function} to unsubscribe
+ * @returns callback to unsubscribe
*/
-function addBecameActiveListener(callback) {
- /**
- * @param {String} state
- */
- function appStateChangeCallback(state) {
+function addBecameActiveListener(callback: () => void): () => void {
+ function appStateChangeCallback(state: AppStateStatus) {
if (shouldReportActivity && (appState === CONST.APP_STATE.INACTIVE || appState === CONST.APP_STATE.BACKGROUND) && state === CONST.APP_STATE.ACTIVE) {
callback();
}
diff --git a/src/libs/AppStateMonitor/shouldReportActivity/index.native.js b/src/libs/AppStateMonitor/shouldReportActivity/index.native.js
deleted file mode 100644
index ff3177babdde..000000000000
--- a/src/libs/AppStateMonitor/shouldReportActivity/index.native.js
+++ /dev/null
@@ -1 +0,0 @@
-export default true;
diff --git a/src/libs/AppStateMonitor/shouldReportActivity/index.native.ts b/src/libs/AppStateMonitor/shouldReportActivity/index.native.ts
new file mode 100644
index 000000000000..0e5fdb57a597
--- /dev/null
+++ b/src/libs/AppStateMonitor/shouldReportActivity/index.native.ts
@@ -0,0 +1,5 @@
+import ShouldReportActivity from './types';
+
+const shouldReportActivity: ShouldReportActivity = true;
+
+export default shouldReportActivity;
diff --git a/src/libs/AppStateMonitor/shouldReportActivity/index.js b/src/libs/AppStateMonitor/shouldReportActivity/index.ts
similarity index 70%
rename from src/libs/AppStateMonitor/shouldReportActivity/index.js
rename to src/libs/AppStateMonitor/shouldReportActivity/index.ts
index 05939ab7eb8d..db326345714e 100644
--- a/src/libs/AppStateMonitor/shouldReportActivity/index.js
+++ b/src/libs/AppStateMonitor/shouldReportActivity/index.ts
@@ -1,4 +1,8 @@
+import ShouldReportActivity from './types';
+
// We only need to report when the app becomes active on native since web maintains most of it's network functions while
// in the "background" and the concept is not quite the same on mobile. We avoid setting this to true for web since
// the event would fire much more frequently than it does on native causing performance issues.
-export default false;
+const shouldReportActivity: ShouldReportActivity = false;
+
+export default shouldReportActivity;
diff --git a/src/libs/AppStateMonitor/shouldReportActivity/types.ts b/src/libs/AppStateMonitor/shouldReportActivity/types.ts
new file mode 100644
index 000000000000..2341c0d5cbf1
--- /dev/null
+++ b/src/libs/AppStateMonitor/shouldReportActivity/types.ts
@@ -0,0 +1,3 @@
+type ShouldReportActivity = boolean;
+
+export default ShouldReportActivity;
diff --git a/src/libs/BootSplash/index.js b/src/libs/BootSplash/index.js
index ff7ab5562b1f..c169f380a8eb 100644
--- a/src/libs/BootSplash/index.js
+++ b/src/libs/BootSplash/index.js
@@ -9,10 +9,14 @@ function hide() {
return document.fonts.ready.then(() => {
const splash = document.getElementById('splash');
- if (splash) splash.style.opacity = 0;
+ if (splash) {
+ splash.style.opacity = 0;
+ }
return resolveAfter(250).then(() => {
- if (!splash || !splash.parentNode) return;
+ if (!splash || !splash.parentNode) {
+ return;
+ }
splash.parentNode.removeChild(splash);
});
});
diff --git a/src/libs/CardUtils.ts b/src/libs/CardUtils.ts
index bbb938a666ac..beb0ea800091 100644
--- a/src/libs/CardUtils.ts
+++ b/src/libs/CardUtils.ts
@@ -1,3 +1,6 @@
+import {Card} from '../types/onyx';
+import CONST from '../CONST';
+
/**
* @returns string with a month in MM format
*/
@@ -15,4 +18,11 @@ function getYearFromExpirationDateString(expirationDateString: string) {
return cardYear.length === 2 ? `20${cardYear}` : cardYear;
}
-export {getMonthFromExpirationDateString, getYearFromExpirationDateString};
+function getCompanyCards(cardList: {string: Card}) {
+ if (!cardList) {
+ return [];
+ }
+ return Object.values(cardList).filter((card) => card.bank !== CONST.EXPENSIFY_CARD.BANK);
+}
+
+export {getMonthFromExpirationDateString, getYearFromExpirationDateString, getCompanyCards};
diff --git a/src/libs/ComposerUtils/getDraftComment.js b/src/libs/ComposerUtils/getDraftComment.js
index ddcb966bb2a7..854df1ac65ee 100644
--- a/src/libs/ComposerUtils/getDraftComment.js
+++ b/src/libs/ComposerUtils/getDraftComment.js
@@ -5,7 +5,9 @@ const draftCommentMap = {};
Onyx.connect({
key: ONYXKEYS.COLLECTION.REPORT_DRAFT_COMMENT,
callback: (value, key) => {
- if (!key) return;
+ if (!key) {
+ return;
+ }
const reportID = key.replace(ONYXKEYS.COLLECTION.REPORT_DRAFT_COMMENT, '');
draftCommentMap[reportID] = value;
diff --git a/src/libs/CurrencyUtils.js b/src/libs/CurrencyUtils.js
index 6cbb0db7661b..5cf0b22ef337 100644
--- a/src/libs/CurrencyUtils.js
+++ b/src/libs/CurrencyUtils.js
@@ -128,4 +128,25 @@ function convertToDisplayString(amountInCents, currency = CONST.CURRENCY.USD) {
});
}
-export {getCurrencyDecimals, getCurrencyUnit, getLocalizedCurrencySymbol, getCurrencySymbol, isCurrencySymbolLTR, convertToBackendAmount, convertToFrontendAmount, convertToDisplayString};
+/**
+ * Checks if passed currency code is a valid currency based on currency list
+ *
+ * @param {String} currencyCode
+ * @returns {Boolean}
+ */
+function isValidCurrencyCode(currencyCode) {
+ const currency = lodashGet(currencyList, currencyCode);
+ return Boolean(currency);
+}
+
+export {
+ getCurrencyDecimals,
+ getCurrencyUnit,
+ getLocalizedCurrencySymbol,
+ getCurrencySymbol,
+ isCurrencySymbolLTR,
+ convertToBackendAmount,
+ convertToFrontendAmount,
+ convertToDisplayString,
+ isValidCurrencyCode,
+};
diff --git a/src/libs/DateUtils.js b/src/libs/DateUtils.js
index b33a1b1b2a73..70c4277bdb5e 100644
--- a/src/libs/DateUtils.js
+++ b/src/libs/DateUtils.js
@@ -298,7 +298,9 @@ function getDateStringFromISOTimestamp(isoTimestamp) {
* @returns {String}
*/
function getStatusUntilDate(inputDate) {
- if (!inputDate) return '';
+ if (!inputDate) {
+ return '';
+ }
const {translateLocal} = Localize;
const input = new Date(inputDate);
diff --git a/src/libs/DistanceRequestUtils.js b/src/libs/DistanceRequestUtils.js
index 51e37530465d..34fa14163835 100644
--- a/src/libs/DistanceRequestUtils.js
+++ b/src/libs/DistanceRequestUtils.js
@@ -64,21 +64,34 @@ function convertDistanceUnit(distanceInMeters, unit) {
*
* @param {Number} distanceInMeters Distance traveled
* @param {'mi' | 'km'} unit Unit that should be used to display the distance
+ * @returns {String} The distance in requested units, rounded to 2 decimals
+ */
+const getRoundedDistanceInUnits = (distanceInMeters, unit) => {
+ const convertedDistance = convertDistanceUnit(distanceInMeters, unit);
+ return convertedDistance.toFixed(2);
+};
+
+/**
+ *
+ * @param {boolean} hasRoute Whether the route exists for the distance request
+ * @param {Number} distanceInMeters Distance traveled
+ * @param {'mi' | 'km'} unit Unit that should be used to display the distance
* @param {Number} rate Expensable amount allowed per unit
* @param {String} currency The currency associated with the rate
* @param {Function} translate Translate function
- * @returns {String} A string that describes the distance travled and the rate used for expense calculation
+ * @returns {String} A string that describes the distance traveled and the rate used for expense calculation
*/
-const getDistanceMerchant = (distanceInMeters, unit, rate, currency, translate) => {
- const convertedDistance = convertDistanceUnit(distanceInMeters, unit);
+const getDistanceMerchant = (hasRoute, distanceInMeters, unit, rate, currency, translate) => {
+ const distanceInUnits = hasRoute ? getRoundedDistanceInUnits(distanceInMeters, unit) : translate('common.tbd');
+
const distanceUnit = unit === CONST.CUSTOM_UNITS.DISTANCE_UNIT_MILES ? translate('common.miles') : translate('common.kilometers');
const singularDistanceUnit = unit === CONST.CUSTOM_UNITS.DISTANCE_UNIT_MILES ? translate('common.mile') : translate('common.kilometer');
- const roundedDistance = convertedDistance.toFixed(2);
- const unitString = roundedDistance === 1 ? singularDistanceUnit : distanceUnit;
+ const unitString = distanceInUnits === 1 ? singularDistanceUnit : distanceUnit;
+
const ratePerUnit = rate * 0.01;
const currencySymbol = CurrencyUtils.getCurrencySymbol(currency) || `${currency} `;
- return `${roundedDistance} ${unitString} @ ${currencySymbol}${ratePerUnit} / ${singularDistanceUnit}`;
+ return `${distanceInUnits} ${unitString} @ ${currencySymbol}${ratePerUnit} / ${singularDistanceUnit}`;
};
/**
diff --git a/src/libs/DomUtils/index.js b/src/libs/DomUtils/index.js
deleted file mode 100644
index ad636c6167fb..000000000000
--- a/src/libs/DomUtils/index.js
+++ /dev/null
@@ -1,12 +0,0 @@
-function blurActiveElement() {
- document.activeElement.blur();
-}
-
-function getActiveElement() {
- return document.activeElement;
-}
-
-export default {
- blurActiveElement,
- getActiveElement,
-};
diff --git a/src/libs/DomUtils/index.native.js b/src/libs/DomUtils/index.native.js
deleted file mode 100644
index 1d3ef14c954d..000000000000
--- a/src/libs/DomUtils/index.native.js
+++ /dev/null
@@ -1,10 +0,0 @@
-function blurActiveElement() {}
-
-function getActiveElement() {
- return undefined;
-}
-
-export default {
- blurActiveElement,
- getActiveElement,
-};
diff --git a/src/libs/DomUtils/index.native.ts b/src/libs/DomUtils/index.native.ts
new file mode 100644
index 000000000000..d3774baec208
--- /dev/null
+++ b/src/libs/DomUtils/index.native.ts
@@ -0,0 +1,10 @@
+import {BlurActiveElement, GetActiveElement} from './types';
+
+const blurActiveElement: BlurActiveElement = () => {};
+
+const getActiveElement: GetActiveElement = () => null;
+
+export default {
+ blurActiveElement,
+ getActiveElement,
+};
diff --git a/src/libs/DomUtils/index.ts b/src/libs/DomUtils/index.ts
new file mode 100644
index 000000000000..784a01bd7885
--- /dev/null
+++ b/src/libs/DomUtils/index.ts
@@ -0,0 +1,18 @@
+import {BlurActiveElement, GetActiveElement} from './types';
+
+const blurActiveElement: BlurActiveElement = () => {
+ const activeElement = document.activeElement as HTMLElement;
+
+ if (!activeElement?.blur) {
+ return;
+ }
+
+ activeElement.blur();
+};
+
+const getActiveElement: GetActiveElement = () => document.activeElement;
+
+export default {
+ blurActiveElement,
+ getActiveElement,
+};
diff --git a/src/libs/DomUtils/types.ts b/src/libs/DomUtils/types.ts
new file mode 100644
index 000000000000..8be7b3cddae5
--- /dev/null
+++ b/src/libs/DomUtils/types.ts
@@ -0,0 +1,4 @@
+type BlurActiveElement = () => void;
+type GetActiveElement = () => Element | null;
+
+export type {BlurActiveElement, GetActiveElement};
diff --git a/src/libs/E2E/apiMocks/openApp.js b/src/libs/E2E/apiMocks/openApp.js
index 745c1952f07d..d50f4462cfd9 100644
--- a/src/libs/E2E/apiMocks/openApp.js
+++ b/src/libs/E2E/apiMocks/openApp.js
@@ -1094,7 +1094,6 @@ export default () => ({
},
firstName: '',
lastName: '',
- payPalMeAddress: 'qwerty',
phoneNumber: '',
validated: true,
},
@@ -1110,7 +1109,6 @@ export default () => ({
},
firstName: '"Chat N',
lastName: 'Laz"',
- payPalMeAddress: '',
phoneNumber: '',
validated: true,
},
@@ -1126,7 +1124,6 @@ export default () => ({
},
firstName: '',
lastName: '',
- payPalMeAddress: '',
phoneNumber: '',
validated: true,
},
@@ -1142,7 +1139,6 @@ export default () => ({
},
firstName: '123',
lastName: 'Ios',
- payPalMeAddress: 'Wwerty',
phoneNumber: '',
validated: true,
},
@@ -1158,7 +1154,6 @@ export default () => ({
},
firstName: 'Qqq',
lastName: 'Qqq',
- payPalMeAddress: '',
phoneNumber: '',
validated: true,
},
@@ -1174,7 +1169,6 @@ export default () => ({
},
firstName: 'Main',
lastName: 'Ios🏴ios',
- payPalMeAddress: '',
phoneNumber: '',
validated: true,
},
@@ -1190,7 +1184,6 @@ export default () => ({
},
firstName: '0604',
lastName: 'Lsn',
- payPalMeAddress: '12345',
phoneNumber: '',
validated: true,
},
@@ -1206,7 +1199,6 @@ export default () => ({
},
firstName: '07 04 0704',
lastName: 'Lsn lsn',
- payPalMeAddress: '',
phoneNumber: '',
validated: true,
},
@@ -1222,7 +1214,6 @@ export default () => ({
},
firstName: 'Katya',
lastName: 'Becciv',
- payPalMeAddress: 'testing',
phoneNumber: '',
validated: true,
},
@@ -1238,7 +1229,6 @@ export default () => ({
},
firstName: 'Katie',
lastName: 'Becciv',
- payPalMeAddress: 'kbecciv',
phoneNumber: '',
validated: true,
},
@@ -1254,7 +1244,6 @@ export default () => ({
},
firstName: '11',
lastName: '11',
- payPalMeAddress: '',
phoneNumber: '',
validated: true,
},
@@ -1270,7 +1259,6 @@ export default () => ({
},
firstName: '"First"',
lastName: '',
- payPalMeAddress: '444555',
phoneNumber: '',
validated: true,
},
@@ -1286,7 +1274,6 @@ export default () => ({
},
firstName: 'bernardo',
lastName: 'utest',
- payPalMeAddress: '',
phoneNumber: '',
validated: false,
},
@@ -1302,7 +1289,6 @@ export default () => ({
},
firstName: 'Chat',
lastName: 'HT',
- payPalMeAddress: '111',
phoneNumber: '',
validated: true,
},
@@ -1318,7 +1304,6 @@ export default () => ({
},
firstName: '',
lastName: '',
- payPalMeAddress: '',
phoneNumber: '',
validated: true,
},
@@ -1334,7 +1319,6 @@ export default () => ({
},
firstName: '',
lastName: '',
- payPalMeAddress: '',
phoneNumber: '',
validated: true,
},
@@ -1350,7 +1334,6 @@ export default () => ({
},
firstName: '',
lastName: '',
- payPalMeAddress: '',
phoneNumber: '',
validated: true,
localCurrencyCode: 'USD',
@@ -1367,7 +1350,6 @@ export default () => ({
},
firstName: '',
lastName: '',
- payPalMeAddress: '',
phoneNumber: '',
validated: true,
},
@@ -1383,7 +1365,6 @@ export default () => ({
},
firstName: '',
lastName: '',
- payPalMeAddress: '',
phoneNumber: '',
validated: true,
},
@@ -1399,7 +1380,6 @@ export default () => ({
},
firstName: 'Applause',
lastName: 'Main Account',
- payPalMeAddress: 'ss',
phoneNumber: '',
validated: true,
},
@@ -1415,7 +1395,6 @@ export default () => ({
},
firstName: 'Christoph',
lastName: 'Pader',
- payPalMeAddress: '',
phoneNumber: '',
validated: true,
},
@@ -1431,7 +1410,6 @@ export default () => ({
},
firstName: 'Concierge',
lastName: '',
- payPalMeAddress: '',
phoneNumber: '',
validated: true,
},
@@ -1447,7 +1425,6 @@ export default () => ({
},
firstName: 'Chat S',
lastName: '',
- payPalMeAddress: '',
phoneNumber: '',
validated: true,
},
@@ -1463,7 +1440,6 @@ export default () => ({
},
firstName: 'Tayla',
lastName: 'Simmons',
- payPalMeAddress: '',
phoneNumber: '',
validated: true,
},
@@ -2162,7 +2138,6 @@ export default () => ({
cachedTotal: '($1,473.11)',
total: 147311,
stateNum: 1,
- submitterPayPalMeAddress: '',
hasOutstandingIOU: true,
},
report_4249286573496381: {
@@ -2175,7 +2150,6 @@ export default () => ({
cachedTotal: '($212.78)',
total: 21278,
stateNum: 1,
- submitterPayPalMeAddress: '',
hasOutstandingIOU: true,
},
},
diff --git a/src/libs/EmojiTrie.js b/src/libs/EmojiTrie.ts
similarity index 53%
rename from src/libs/EmojiTrie.js
rename to src/libs/EmojiTrie.ts
index 00e5fc1388e1..d0a53acf29c9 100644
--- a/src/libs/EmojiTrie.js
+++ b/src/libs/EmojiTrie.ts
@@ -1,29 +1,61 @@
-import _ from 'underscore';
+import React from 'react';
+import {SvgProps} from 'react-native-svg';
import emojis, {localeEmojis} from '../../assets/emojis';
import Trie from './Trie';
import Timing from './actions/Timing';
import CONST from '../CONST';
+type Emoji = {
+ code: string;
+ header?: boolean;
+ icon?: React.FC;
+ name?: string;
+ types?: string[];
+};
+
+type LocalizedEmoji = {
+ name?: string;
+ keywords: string[];
+};
+
+type LocalizedEmojis = Record;
+
+type Suggestion = {
+ code: string;
+ types?: string[];
+ name?: string;
+};
+
+type EmojiMetaData = {
+ suggestions?: Suggestion[];
+};
+
Timing.start(CONST.TIMING.TRIE_INITIALIZATION);
-const supportedLanguages = [CONST.LOCALES.DEFAULT, CONST.LOCALES.ES];
+const supportedLanguages = [CONST.LOCALES.DEFAULT, CONST.LOCALES.ES] as const;
+
+type SupportedLanguage = (typeof supportedLanguages)[number];
+
+type EmojiTrie = {
+ [key in SupportedLanguage]?: Trie;
+};
/**
*
- * @param {Trie} trie The Trie object.
- * @param {Array} keywords An array containing the keywords.
- * @param {Object} item An object containing the properties of the emoji.
- * @param {String} name The localized name of the emoji.
- * @param {Boolean} shouldPrependKeyword Prepend the keyword (instead of append) to the suggestions
+ * @param trie The Trie object.
+ * @param keywords An array containing the keywords.
+ * @param item An object containing the properties of the emoji.
+ * @param name The localized name of the emoji.
+ * @param shouldPrependKeyword Prepend the keyword (instead of append) to the suggestions
*/
-function addKeywordsToTrie(trie, keywords, item, name, shouldPrependKeyword = false) {
- _.forEach(keywords, (keyword) => {
+function addKeywordsToTrie(trie: Trie, keywords: string[], item: Emoji, name: string, shouldPrependKeyword = false) {
+ keywords.forEach((keyword) => {
const keywordNode = trie.search(keyword);
if (!keywordNode) {
trie.add(keyword, {suggestions: [{code: item.code, types: item.types, name}]});
} else {
const suggestion = {code: item.code, types: item.types, name};
- const suggestions = shouldPrependKeyword ? [suggestion, ...keywordNode.metaData.suggestions] : [...keywordNode.metaData.suggestions, suggestion];
+ const suggestions = shouldPrependKeyword ? [suggestion, ...(keywordNode.metaData.suggestions ?? [])] : [...(keywordNode.metaData.suggestions ?? []), suggestion];
trie.update(keyword, {
...keywordNode.metaData,
suggestions,
@@ -35,26 +67,27 @@ function addKeywordsToTrie(trie, keywords, item, name, shouldPrependKeyword = fa
/**
* Allows searching based on parts of the name. This turns 'white_large_square' into ['white_large_square', 'large_square', 'square'].
*
- * @param {String} name The emoji name
- * @returns {Array} An array containing the name parts
+ * @param name The emoji name
+ * @returns An array containing the name parts
*/
-function getNameParts(name) {
+function getNameParts(name: string): string[] {
const nameSplit = name.split('_');
- return _.map(nameSplit, (_namePart, index) => nameSplit.slice(index).join('_'));
+ return nameSplit.map((namePart, index) => nameSplit.slice(index).join('_'));
}
-function createTrie(lang = CONST.LOCALES.DEFAULT) {
+function createTrie(lang: SupportedLanguage = CONST.LOCALES.DEFAULT): Trie {
const trie = new Trie();
- const langEmojis = localeEmojis[lang];
+ const langEmojis: LocalizedEmojis = localeEmojis[lang];
+ const defaultLangEmojis: LocalizedEmojis = localeEmojis[CONST.LOCALES.DEFAULT];
const isDefaultLocale = lang === CONST.LOCALES.DEFAULT;
- _.forEach(emojis, (item) => {
- if (item.header) {
+ emojis.forEach((item: Emoji) => {
+ if (!item.name) {
return;
}
const englishName = item.name;
- const localeName = _.get(langEmojis, [item.code, 'name'], englishName);
+ const localeName = langEmojis?.[item.code]?.name ?? englishName;
const node = trie.search(localeName);
if (!node) {
@@ -67,7 +100,7 @@ function createTrie(lang = CONST.LOCALES.DEFAULT) {
addKeywordsToTrie(trie, nameParts, item, localeName);
// Add keywords for both the locale language and English to enable users to search using either language.
- const keywords = _.get(langEmojis, [item.code, 'keywords'], []).concat(isDefaultLocale ? [] : _.get(localeEmojis, [CONST.LOCALES.DEFAULT, item.code, 'keywords'], []));
+ const keywords = (langEmojis?.[item.code]?.keywords ?? []).concat(isDefaultLocale ? [] : defaultLangEmojis?.[item.code]?.keywords ?? []);
addKeywordsToTrie(trie, keywords, item, localeName);
/**
@@ -83,7 +116,7 @@ function createTrie(lang = CONST.LOCALES.DEFAULT) {
return trie;
}
-const emojiTrie = _.reduce(supportedLanguages, (prev, cur) => ({...prev, [cur]: createTrie(cur)}), {});
+const emojiTrie: EmojiTrie = supportedLanguages.reduce((prev, cur) => ({...prev, [cur]: createTrie(cur)}), {});
Timing.end(CONST.TIMING.TRIE_INITIALIZATION);
diff --git a/src/libs/FormHelper.js b/src/libs/FormHelper.js
deleted file mode 100644
index feab0f44acea..000000000000
--- a/src/libs/FormHelper.js
+++ /dev/null
@@ -1,50 +0,0 @@
-import _ from 'underscore';
-import lodashGet from 'lodash/get';
-import lodashUnset from 'lodash/unset';
-import lodashCloneDeep from 'lodash/cloneDeep';
-
-class FormHelper {
- constructor({errorPath, setErrors}) {
- this.errorPath = errorPath;
- this.setErrors = setErrors;
- this.getErrors = this.getErrors.bind(this);
- this.clearError = this.clearError.bind(this);
- this.clearErrors = this.clearErrors.bind(this);
- }
-
- /**
- * @param {Object} props
- * @returns {Object}
- */
- getErrors(props) {
- return lodashGet(props, this.errorPath, {});
- }
-
- /**
- * @param {Object} props
- * @param {String[]} paths
- */
- clearErrors(props, paths) {
- const errors = this.getErrors(props);
- const pathsWithErrors = _.filter(paths, (path) => lodashGet(errors, path, false));
- if (_.size(pathsWithErrors) === 0) {
- // No error found for this path
- return;
- }
-
- // Clear the existing errors
- const newErrors = lodashCloneDeep(errors);
- _.forEach(pathsWithErrors, (path) => lodashUnset(newErrors, path));
- this.setErrors(newErrors);
- }
-
- /**
- * @param {Object} props
- * @param {String} path
- */
- clearError(props, path) {
- this.clearErrors(props, [path]);
- }
-}
-
-export default FormHelper;
diff --git a/src/libs/Growl.ts b/src/libs/Growl.ts
index 99c728f0a210..33d7311973cb 100644
--- a/src/libs/Growl.ts
+++ b/src/libs/Growl.ts
@@ -12,7 +12,9 @@ const isReadyPromise = new Promise((resolve) => {
});
function setIsReady() {
- if (!resolveIsReadyPromise) return;
+ if (!resolveIsReadyPromise) {
+ return;
+ }
resolveIsReadyPromise();
}
@@ -21,7 +23,9 @@ function setIsReady() {
*/
function show(bodyText: string, type: string, duration: number = CONST.GROWL.DURATION) {
isReadyPromise.then(() => {
- if (!growlRef?.current?.show) return;
+ if (!growlRef?.current?.show) {
+ return;
+ }
growlRef.current.show(bodyText, type, duration);
});
}
diff --git a/src/libs/IOUUtils.js b/src/libs/IOUUtils.ts
similarity index 51%
rename from src/libs/IOUUtils.js
rename to src/libs/IOUUtils.ts
index 2042c6beda05..6f6024506985 100644
--- a/src/libs/IOUUtils.js
+++ b/src/libs/IOUUtils.ts
@@ -1,18 +1,17 @@
-import _ from 'underscore';
import CONST from '../CONST';
import * as TransactionUtils from './TransactionUtils';
import * as CurrencyUtils from './CurrencyUtils';
+import {Report, Transaction} from '../types/onyx';
/**
* Calculates the amount per user given a list of participants
*
- * @param {Number} numberOfParticipants - Number of participants in the chat. It should not include the current user.
- * @param {Number} total - IOU total amount in backend format (cents, no matter the currency)
- * @param {String} currency - This is used to know how many decimal places are valid to use when splitting the total
- * @param {Boolean} isDefaultUser - Whether we are calculating the amount for the current user
- * @returns {Number}
+ * @param numberOfParticipants - Number of participants in the chat. It should not include the current user.
+ * @param total - IOU total amount in backend format (cents, no matter the currency)
+ * @param currency - This is used to know how many decimal places are valid to use when splitting the total
+ * @param isDefaultUser - Whether we are calculating the amount for the current user
*/
-function calculateAmount(numberOfParticipants, total, currency, isDefaultUser = false) {
+function calculateAmount(numberOfParticipants: number, total: number, currency: string, isDefaultUser = false): number {
// Since the backend can maximum store 2 decimal places, any currency with more than 2 decimals
// has to be capped to 2 decimal places
const currencyUnit = Math.min(100, CurrencyUtils.getCurrencyUnit(currency));
@@ -34,35 +33,32 @@ function calculateAmount(numberOfParticipants, total, currency, isDefaultUser =
* 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 {Object} iouReport
- * @param {Number} actorAccountID
- * @param {Number} amount
- * @param {String} currency
- * @param {String} isDeleting - whether the user is deleting the request
- * @returns {Object}
+ * @param isDeleting - whether the user is deleting the request
*/
-function updateIOUOwnerAndTotal(iouReport, actorAccountID, amount, currency, isDeleting = false) {
+function updateIOUOwnerAndTotal(iouReport: Report, actorAccountID: number, amount: number, currency: string, isDeleting = false): Report {
if (currency !== iouReport.currency) {
return iouReport;
}
// Make a copy so we don't mutate the original object
- const iouReportUpdate = {...iouReport};
+ const iouReportUpdate: Report = {...iouReport};
- if (actorAccountID === iouReport.ownerAccountID) {
- iouReportUpdate.total += isDeleting ? -amount : amount;
- } else {
- iouReportUpdate.total += isDeleting ? amount : -amount;
- }
+ if (iouReportUpdate.total) {
+ if (actorAccountID === iouReport.ownerAccountID) {
+ iouReportUpdate.total += isDeleting ? -amount : amount;
+ } else {
+ iouReportUpdate.total += isDeleting ? amount : -amount;
+ }
- if (iouReportUpdate.total < 0) {
- // The total sign has changed and hence we need to flip the manager and owner of the report.
- iouReportUpdate.ownerAccountID = iouReport.managerID;
- iouReportUpdate.managerID = iouReport.ownerAccountID;
- iouReportUpdate.total = -iouReportUpdate.total;
- }
+ if (iouReportUpdate.total < 0) {
+ // The total sign has changed and hence we need to flip the manager and owner of the report.
+ iouReportUpdate.ownerAccountID = iouReport.managerID;
+ iouReportUpdate.managerID = iouReport.ownerAccountID;
+ iouReportUpdate.total = -iouReportUpdate.total;
+ }
- iouReportUpdate.hasOutstandingIOU = iouReportUpdate.total !== 0;
+ iouReportUpdate.hasOutstandingIOU = iouReportUpdate.total !== 0;
+ }
return iouReportUpdate;
}
@@ -70,23 +66,19 @@ function updateIOUOwnerAndTotal(iouReport, actorAccountID, amount, currency, isD
/**
* Returns whether or not an IOU report contains money requests in a different currency
* that are either created or cancelled offline, and thus haven't been converted to the report's currency yet
- *
- * @param {Object} iouReport
- * @returns {Boolean}
*/
-function isIOUReportPendingCurrencyConversion(iouReport) {
- const reportTransactions = TransactionUtils.getAllReportTransactions(iouReport.reportID);
- const pendingRequestsInDifferentCurrency = _.filter(reportTransactions, (transaction) => transaction.pendingAction && TransactionUtils.getCurrency(transaction) !== iouReport.currency);
+function isIOUReportPendingCurrencyConversion(iouReport: Report): boolean {
+ const reportTransactions: Transaction[] = TransactionUtils.getAllReportTransactions(iouReport.reportID);
+ const pendingRequestsInDifferentCurrency = reportTransactions.filter((transaction) => transaction.pendingAction && TransactionUtils.getCurrency(transaction) !== iouReport.currency);
return pendingRequestsInDifferentCurrency.length > 0;
}
/**
* Checks if the iou type is one of request, send, or split.
- * @param {String} iouType
- * @returns {Boolean}
*/
-function isValidMoneyRequestType(iouType) {
- return [CONST.IOU.MONEY_REQUEST_TYPE.REQUEST, CONST.IOU.MONEY_REQUEST_TYPE.SPLIT].includes(iouType);
+function isValidMoneyRequestType(iouType: string): boolean {
+ const moneyRequestType: string[] = [CONST.IOU.MONEY_REQUEST_TYPE.REQUEST, CONST.IOU.MONEY_REQUEST_TYPE.SPLIT];
+ return moneyRequestType.includes(iouType);
}
export {calculateAmount, updateIOUOwnerAndTotal, isIOUReportPendingCurrencyConversion, isValidMoneyRequestType};
diff --git a/src/libs/LocalePhoneNumber.js b/src/libs/LocalePhoneNumber.ts
similarity index 82%
rename from src/libs/LocalePhoneNumber.js
rename to src/libs/LocalePhoneNumber.ts
index e5c7cbfa45ba..962040aee049 100644
--- a/src/libs/LocalePhoneNumber.js
+++ b/src/libs/LocalePhoneNumber.ts
@@ -3,20 +3,17 @@ import Str from 'expensify-common/lib/str';
import {parsePhoneNumber} from 'awesome-phonenumber';
import ONYXKEYS from '../ONYXKEYS';
-let countryCodeByIP;
+let countryCodeByIP: number;
Onyx.connect({
key: ONYXKEYS.COUNTRY_CODE,
- callback: (val) => (countryCodeByIP = val || 1),
+ callback: (val) => (countryCodeByIP = val ?? 1),
});
/**
* Returns a locally converted phone number for numbers from the same region
* and an internationally converted phone number with the country code for numbers from other regions
- *
- * @param {String} number
- * @returns {String}
*/
-function formatPhoneNumber(number) {
+function formatPhoneNumber(number: string): string {
if (!number) {
return '';
}
@@ -26,7 +23,7 @@ function formatPhoneNumber(number) {
// return the string untouched if it's not a phone number
if (!parsedPhoneNumber.valid) {
- if (parsedPhoneNumber.number && parsedPhoneNumber.number.international) {
+ if (parsedPhoneNumber.number?.international) {
return parsedPhoneNumber.number.international;
}
return numberWithoutSMSDomain;
diff --git a/src/libs/Log.js b/src/libs/Log.ts
similarity index 70%
rename from src/libs/Log.js
rename to src/libs/Log.ts
index e51fb74aedd5..cf139eec2682 100644
--- a/src/libs/Log.js
+++ b/src/libs/Log.ts
@@ -1,45 +1,43 @@
// Making an exception to this rule here since we don't need an "action" for Log and Log should just be used directly. Creating a Log
// action would likely cause confusion about which one to use. But most other API methods should happen inside an action file.
/* eslint-disable rulesdir/no-api-in-views */
+import {Merge} from 'type-fest';
import Logger from 'expensify-common/lib/Logger';
import getPlatform from './getPlatform';
import pkg from '../../package.json';
import requireParameters from './requireParameters';
import * as Network from './Network';
-let timeout = null;
+let timeout: NodeJS.Timeout;
-/**
- * @param {Object} parameters
- * @param {String} parameters.expensifyCashAppVersion
- * @param {Object[]} parameters.logPacket
- * @returns {Promise}
- */
-function LogCommand(parameters) {
+type LogCommandParameters = {
+ expensifyCashAppVersion: string;
+ logPacket: string;
+};
+
+function LogCommand(parameters: LogCommandParameters): Promise<{requestID: string}> {
const commandName = 'Log';
requireParameters(['logPacket', 'expensifyCashAppVersion'], parameters, commandName);
// Note: We are forcing Log to run since it requires no authToken and should only be queued when we are offline.
// Non-cancellable request: during logout, when requests are cancelled, we don't want to cancel any remaining logs
- return Network.post(commandName, {...parameters, forceNetworkRequest: true, canCancel: false});
+ return Network.post(commandName, {...parameters, forceNetworkRequest: true, canCancel: false}) as Promise<{requestID: string}>;
}
+// eslint-disable-next-line
+type ServerLoggingCallbackOptions = {api_setCookie: boolean; logPacket: string};
+type RequestParams = Merge;
+
/**
* Network interface for logger.
- *
- * @param {Logger} logger
- * @param {Object} params
- * @param {Object} params.parameters
- * @param {String} params.message
- * @return {Promise}
*/
-function serverLoggingCallback(logger, params) {
- const requestParams = params;
+function serverLoggingCallback(logger: Logger, params: ServerLoggingCallbackOptions): Promise<{requestID: string}> {
+ const requestParams = params as RequestParams;
requestParams.shouldProcessImmediately = false;
requestParams.shouldRetry = false;
requestParams.expensifyCashAppVersion = `expensifyCash[${getPlatform()}]${pkg.version}`;
if (requestParams.parameters) {
- requestParams.parameters = JSON.stringify(params.parameters);
+ requestParams.parameters = JSON.stringify(requestParams.parameters);
}
clearTimeout(timeout);
timeout = setTimeout(() => logger.info('Flushing logs older than 10 minutes', true, {}, true), 10 * 60 * 1000);
diff --git a/src/libs/MoneyRequestUtils.js b/src/libs/MoneyRequestUtils.ts
similarity index 53%
rename from src/libs/MoneyRequestUtils.js
rename to src/libs/MoneyRequestUtils.ts
index e60eae0cdfe5..b8a6a3da303f 100644
--- a/src/libs/MoneyRequestUtils.js
+++ b/src/libs/MoneyRequestUtils.ts
@@ -1,47 +1,36 @@
-import lodashGet from 'lodash/get';
-import _ from 'underscore';
+import {ValueOf} from 'type-fest';
import CONST from '../CONST';
/**
* Strip comma from the amount
- *
- * @param {String} amount
- * @returns {String}
*/
-function stripCommaFromAmount(amount) {
+function stripCommaFromAmount(amount: string): string {
return amount.replace(/,/g, '');
}
/**
* Strip spaces from the amount
- *
- * @param {String} amount
- * @returns {String}
*/
-function stripSpacesFromAmount(amount) {
+function stripSpacesFromAmount(amount: string): string {
return amount.replace(/\s+/g, '');
}
/**
* Adds a leading zero to the amount if user entered just the decimal separator
*
- * @param {String} amount - Changed amount from user input
- * @returns {String}
+ * @param amount - Changed amount from user input
*/
-function addLeadingZero(amount) {
+function addLeadingZero(amount: string): string {
return amount === '.' ? '0.' : amount;
}
/**
* Calculate the length of the amount with leading zeroes
- *
- * @param {String} amount
- * @returns {Number}
*/
-function calculateAmountLength(amount) {
+function calculateAmountLength(amount: string): number {
const leadingZeroes = amount.match(/^0+/);
- const leadingZeroesLength = lodashGet(leadingZeroes, '[0].length', 0);
- const absAmount = parseFloat((stripCommaFromAmount(amount) * 100).toFixed(2)).toString();
+ const leadingZeroesLength = leadingZeroes?.[0]?.length ?? 0;
+ const absAmount = parseFloat((Number(stripCommaFromAmount(amount)) * 100).toFixed(2)).toString();
if (/\D/.test(absAmount)) {
return CONST.IOU.AMOUNT_MAX_LENGTH + 1;
@@ -52,11 +41,8 @@ function calculateAmountLength(amount) {
/**
* Check if amount is a decimal up to 3 digits
- *
- * @param {String} amount
- * @returns {Boolean}
*/
-function validateAmount(amount) {
+function validateAmount(amount: string): boolean {
const decimalNumberRegex = new RegExp(/^\d+(,\d+)*(\.\d{0,2})?$/, 'i');
return amount === '' || (decimalNumberRegex.test(amount) && calculateAmountLength(amount) <= CONST.IOU.AMOUNT_MAX_LENGTH);
}
@@ -64,13 +50,10 @@ function validateAmount(amount) {
/**
* Replaces each character by calling `convertFn`. If `convertFn` throws an error, then
* the original character will be preserved.
- *
- * @param {String} text
- * @param {Function} convertFn - `fromLocaleDigit` or `toLocaleDigit`
- * @returns {String}
*/
-function replaceAllDigits(text, convertFn) {
- return _.chain([...text])
+function replaceAllDigits(text: string, convertFn: (char: string) => string): string {
+ return text
+ .split('')
.map((char) => {
try {
return convertFn(char);
@@ -78,19 +61,21 @@ function replaceAllDigits(text, convertFn) {
return char;
}
})
- .join('')
- .value();
+ .join('');
}
/**
* Check if distance request or not
- *
- * @param {String} iouType - `send` | `split` | `request`
- * @param {String} selectedTab - `manual` | `scan` | `distance`
- * @returns {Boolean}
*/
-function isDistanceRequest(iouType, selectedTab) {
+function isDistanceRequest(iouType: ValueOf, selectedTab: ValueOf): boolean {
return iouType === CONST.IOU.MONEY_REQUEST_TYPE.REQUEST && selectedTab === CONST.TAB.DISTANCE;
}
-export {stripCommaFromAmount, stripSpacesFromAmount, addLeadingZero, validateAmount, replaceAllDigits, isDistanceRequest};
+/**
+ * Check if scan request or not
+ */
+function isScanRequest(selectedTab: ValueOf): boolean {
+ return selectedTab === CONST.TAB.SCAN;
+}
+
+export {stripCommaFromAmount, stripSpacesFromAmount, addLeadingZero, validateAmount, replaceAllDigits, isDistanceRequest, isScanRequest};
diff --git a/src/libs/Navigation/AppNavigator/AuthScreens.js b/src/libs/Navigation/AppNavigator/AuthScreens.js
index e0197805f09c..16d0e2225007 100644
--- a/src/libs/Navigation/AppNavigator/AuthScreens.js
+++ b/src/libs/Navigation/AppNavigator/AuthScreens.js
@@ -38,6 +38,8 @@ import DemoSetupPage from '../../../pages/DemoSetupPage';
let timezone;
let currentAccountID;
+let isLoadingApp;
+
Onyx.connect({
key: ONYXKEYS.SESSION,
callback: (val) => {
@@ -75,6 +77,13 @@ Onyx.connect({
},
});
+Onyx.connect({
+ key: ONYXKEYS.IS_LOADING_APP,
+ callback: (val) => {
+ isLoadingApp = val;
+ },
+});
+
const RootStack = createCustomStackNavigator();
// We want to delay the re-rendering for components(e.g. ReportActionCompose)
@@ -126,7 +135,13 @@ class AuthScreens extends React.Component {
componentDidMount() {
NetworkConnection.listenForReconnect();
- NetworkConnection.onReconnect(() => App.reconnectApp(this.props.lastUpdateIDAppliedToClient));
+ NetworkConnection.onReconnect(() => {
+ if (isLoadingApp) {
+ App.openApp();
+ } else {
+ App.reconnectApp(this.props.lastUpdateIDAppliedToClient);
+ }
+ });
PusherConnectionManager.init();
Pusher.init({
appKey: CONFIG.PUSHER.APP_KEY,
@@ -182,10 +197,10 @@ class AuthScreens extends React.Component {
chatShortcutConfig.shortcutKey,
() => {
Modal.close(() => {
- if (Navigation.isActiveRoute(ROUTES.NEW_CHAT)) {
+ if (Navigation.isActiveRoute(ROUTES.NEW)) {
return;
}
- Navigation.navigate(ROUTES.NEW_CHAT);
+ Navigation.navigate(ROUTES.NEW);
});
},
chatShortcutConfig.descriptionKey,
diff --git a/src/libs/Navigation/AppNavigator/ModalStackNavigators.js b/src/libs/Navigation/AppNavigator/ModalStackNavigators.js
index c5bb02354641..5c110264e034 100644
--- a/src/libs/Navigation/AppNavigator/ModalStackNavigators.js
+++ b/src/libs/Navigation/AppNavigator/ModalStackNavigators.js
@@ -334,7 +334,7 @@ const NewTeachersUniteNavigator = createModalStackNavigator([
const SaveTheWorldPage = require('../../../pages/TeachersUnite/SaveTheWorldPage').default;
return SaveTheWorldPage;
},
- name: 'SaveTheWorld_Root',
+ name: SCREENS.SAVE_THE_WORLD.ROOT,
},
{
getComponent: () => {
@@ -365,7 +365,7 @@ const SettingsModalStackNavigator = createModalStackNavigator([
const SettingsInitialPage = require('../../../pages/settings/InitialSettingsPage').default;
return SettingsInitialPage;
},
- name: 'Settings_Root',
+ name: SCREENS.SETTINGS.ROOT,
},
{
getComponent: () => {
@@ -506,7 +506,7 @@ const SettingsModalStackNavigator = createModalStackNavigator([
const SettingsSecurityPage = require('../../../pages/settings/Security/SecuritySettingsPage').default;
return SettingsSecurityPage;
},
- name: 'Settings_Security',
+ name: SCREENS.SETTINGS.SECURITY,
},
{
getComponent: () => {
@@ -550,13 +550,6 @@ const SettingsModalStackNavigator = createModalStackNavigator([
},
name: 'Settings_Wallet_Choose_Transfer_Account',
},
- {
- getComponent: () => {
- const SettingsAddPayPalMePage = require('../../../pages/settings/Wallet/AddPayPalMePage').default;
- return SettingsAddPayPalMePage;
- },
- name: 'Settings_Add_Paypal_Me',
- },
{
getComponent: () => {
const EnablePaymentsPage = require('../../../pages/EnablePayments/EnablePaymentsPage').default;
@@ -583,7 +576,7 @@ const SettingsModalStackNavigator = createModalStackNavigator([
const SettingsStatus = require('../../../pages/settings/Profile/CustomStatus/StatusPage').default;
return SettingsStatus;
},
- name: 'Settings_Status',
+ name: SCREENS.SETTINGS.STATUS,
},
{
getComponent: () => {
diff --git a/src/libs/Navigation/Navigation.js b/src/libs/Navigation/Navigation.js
index b574b4ffa205..1264ec777b28 100644
--- a/src/libs/Navigation/Navigation.js
+++ b/src/libs/Navigation/Navigation.js
@@ -97,7 +97,7 @@ function navigate(route = ROUTES.HOME, type) {
* @param {Bool} shouldEnforceFallback - Enforces navigation to fallback route
* @param {Bool} shouldPopToTop - Should we navigate to LHN on back press
*/
-function goBack(fallbackRoute = ROUTES.HOME, shouldEnforceFallback = false, shouldPopToTop = false) {
+function goBack(fallbackRoute, shouldEnforceFallback = false, shouldPopToTop = false) {
if (!canNavigate('goBack')) {
return;
}
diff --git a/src/libs/Navigation/NavigationRoot.js b/src/libs/Navigation/NavigationRoot.js
index d8cb96e2c6b3..4d50a1cd6a68 100644
--- a/src/libs/Navigation/NavigationRoot.js
+++ b/src/libs/Navigation/NavigationRoot.js
@@ -103,7 +103,8 @@ function NavigationRoot(props) {
prevStatusBarBackgroundColor.current = statusBarBackgroundColor.current;
statusBarBackgroundColor.current = currentScreenBackgroundColor;
- if (prevStatusBarBackgroundColor.current === statusBarBackgroundColor.current) {
+
+ if (currentScreenBackgroundColor === themeColors.appBG && prevStatusBarBackgroundColor.current === themeColors.appBG) {
return;
}
diff --git a/src/libs/Navigation/OnyxTabNavigator.js b/src/libs/Navigation/OnyxTabNavigator.js
index dc68021bf515..2782054497b0 100644
--- a/src/libs/Navigation/OnyxTabNavigator.js
+++ b/src/libs/Navigation/OnyxTabNavigator.js
@@ -33,6 +33,7 @@ function OnyxTabNavigator({id, selectedTab, children, ...rest}) {
id={id}
initialRouteName={selectedTab}
backBehavior="initialRoute"
+ keyboardDismissMode="none"
screenListeners={{
state: (event) => {
const state = event.data.state;
diff --git a/src/libs/Navigation/linkingConfig.js b/src/libs/Navigation/linkingConfig.js
index 14ee2b895831..f4420330fbd9 100644
--- a/src/libs/Navigation/linkingConfig.js
+++ b/src/libs/Navigation/linkingConfig.js
@@ -38,7 +38,7 @@ export default {
screens: {
Settings: {
screens: {
- Settings_Root: {
+ [SCREENS.SETTINGS.ROOT]: {
path: ROUTES.SETTINGS,
},
[SCREENS.SETTINGS.WORKSPACES]: {
@@ -65,7 +65,7 @@ export default {
path: ROUTES.SETTINGS_CLOSE,
exact: true,
},
- Settings_Security: {
+ [SCREENS.SETTINGS.SECURITY]: {
path: ROUTES.SETTINGS_SECURITY,
exact: true,
},
@@ -85,10 +85,6 @@ export default {
path: ROUTES.SETTINGS_WALLET_CHOOSE_TRANSFER_ACCOUNT,
exact: true,
},
- Settings_Add_Paypal_Me: {
- path: ROUTES.SETTINGS_ADD_PAYPAL_ME,
- exact: true,
- },
Settings_Add_Debit_Card: {
path: ROUTES.SETTINGS_ADD_DEBIT_CARD,
exact: true,
@@ -163,7 +159,7 @@ export default {
path: ROUTES.SETTINGS_SHARE_CODE,
exact: true,
},
- Settings_Status: {
+ [SCREENS.SETTINGS.STATUS]: {
path: ROUTES.SETTINGS_STATUS,
exact: true,
},
@@ -277,7 +273,7 @@ export default {
},
TeachersUnite: {
screens: {
- SaveTheWorld_Root: ROUTES.TEACHERS_UNITE,
+ [SCREENS.SAVE_THE_WORLD.ROOT]: ROUTES.TEACHERS_UNITE,
I_Know_A_Teacher: ROUTES.I_KNOW_A_TEACHER,
Intro_School_Principal: ROUTES.INTRO_SCHOOL_PRINCIPAL,
I_Am_A_Teacher: ROUTES.I_AM_A_TEACHER,
diff --git a/src/libs/Notification/PushNotification/ForegroundNotifications/index.android.js b/src/libs/Notification/PushNotification/ForegroundNotifications/index.android.js
new file mode 100644
index 000000000000..0afc8fe10490
--- /dev/null
+++ b/src/libs/Notification/PushNotification/ForegroundNotifications/index.android.js
@@ -0,0 +1,15 @@
+import Airship from '@ua/react-native-airship';
+import shouldShowPushNotification from '../shouldShowPushNotification';
+
+function configureForegroundNotifications() {
+ Airship.push.android.setForegroundDisplayPredicate((pushPayload) => Promise.resolve(shouldShowPushNotification(pushPayload)));
+}
+
+function disableForegroundNotifications() {
+ Airship.push.android.setForegroundDisplayPredicate(() => Promise.resolve(false));
+}
+
+export default {
+ configureForegroundNotifications,
+ disableForegroundNotifications,
+};
diff --git a/src/libs/Notification/PushNotification/configureForegroundNotifications/index.ios.js b/src/libs/Notification/PushNotification/ForegroundNotifications/index.ios.js
similarity index 78%
rename from src/libs/Notification/PushNotification/configureForegroundNotifications/index.ios.js
rename to src/libs/Notification/PushNotification/ForegroundNotifications/index.ios.js
index 88d94b4ee805..17ad1baaebe3 100644
--- a/src/libs/Notification/PushNotification/configureForegroundNotifications/index.ios.js
+++ b/src/libs/Notification/PushNotification/ForegroundNotifications/index.ios.js
@@ -1,7 +1,7 @@
import Airship, {iOS} from '@ua/react-native-airship';
import shouldShowPushNotification from '../shouldShowPushNotification';
-export default function configureForegroundNotifications() {
+function configureForegroundNotifications() {
// Set our default iOS foreground presentation to be loud with a banner
// More info here https://developer.apple.com/documentation/usernotifications/unusernotificationcenterdelegate/1649518-usernotificationcenter
Airship.push.iOS.setForegroundPresentationOptions([
@@ -15,3 +15,12 @@ export default function configureForegroundNotifications() {
// Returning null keeps the default presentation. Returning [] uses no presentation (hides the notification).
Airship.push.iOS.setForegroundPresentationOptionsCallback((pushPayload) => Promise.resolve(shouldShowPushNotification(pushPayload) ? null : []));
}
+
+function disableForegroundNotifications() {
+ Airship.push.iOS.setForegroundPresentationOptionsCallback(() => Promise.resolve([]));
+}
+
+export default {
+ configureForegroundNotifications,
+ disableForegroundNotifications,
+};
diff --git a/src/libs/Notification/PushNotification/configureForegroundNotifications/index.js b/src/libs/Notification/PushNotification/ForegroundNotifications/index.js
similarity index 52%
rename from src/libs/Notification/PushNotification/configureForegroundNotifications/index.js
rename to src/libs/Notification/PushNotification/ForegroundNotifications/index.js
index c6cb13a0b3b9..acb116f7bc43 100644
--- a/src/libs/Notification/PushNotification/configureForegroundNotifications/index.js
+++ b/src/libs/Notification/PushNotification/ForegroundNotifications/index.js
@@ -1,4 +1,7 @@
/**
* Configures notification handling while in the foreground on iOS and Android. This is a no-op on other platforms.
*/
-export default function () {}
+export default {
+ configureForegroundNotifications: () => {},
+ disableForegroundNotifications: () => {},
+};
diff --git a/src/libs/Notification/PushNotification/configureForegroundNotifications/index.android.js b/src/libs/Notification/PushNotification/configureForegroundNotifications/index.android.js
deleted file mode 100644
index 393072df3d12..000000000000
--- a/src/libs/Notification/PushNotification/configureForegroundNotifications/index.android.js
+++ /dev/null
@@ -1,6 +0,0 @@
-import Airship from '@ua/react-native-airship';
-import shouldShowPushNotification from '../shouldShowPushNotification';
-
-export default function configureForegroundNotifications() {
- Airship.push.android.setForegroundDisplayPredicate((pushPayload) => Promise.resolve(shouldShowPushNotification(pushPayload)));
-}
diff --git a/src/libs/Notification/PushNotification/index.native.js b/src/libs/Notification/PushNotification/index.native.js
index 299af69873f9..7192ee66a791 100644
--- a/src/libs/Notification/PushNotification/index.native.js
+++ b/src/libs/Notification/PushNotification/index.native.js
@@ -6,7 +6,7 @@ import Log from '../../Log';
import NotificationType from './NotificationType';
import * as PushNotification from '../../actions/PushNotification';
import ONYXKEYS from '../../../ONYXKEYS';
-import configureForegroundNotifications from './configureForegroundNotifications';
+import ForegroundNotifications from './ForegroundNotifications';
let isUserOptedInToPushNotifications = false;
Onyx.connect({
@@ -96,7 +96,7 @@ function init() {
// Keep track of which users have enabled push notifications via an NVP.
Airship.addListener(EventType.NotificationOptInStatus, refreshNotificationOptInStatus);
- configureForegroundNotifications();
+ ForegroundNotifications.configureForegroundNotifications();
}
/**
@@ -136,6 +136,7 @@ function deregister() {
Airship.contact.reset();
Airship.removeAllListeners(EventType.PushReceived);
Airship.removeAllListeners(EventType.NotificationResponse);
+ ForegroundNotifications.disableForegroundNotifications();
}
/**
diff --git a/src/libs/Notification/PushNotification/subscribeToReportCommentPushNotifications.js b/src/libs/Notification/PushNotification/subscribeToReportCommentPushNotifications.js
index a36fef610a39..8e16bb72f656 100644
--- a/src/libs/Notification/PushNotification/subscribeToReportCommentPushNotifications.js
+++ b/src/libs/Notification/PushNotification/subscribeToReportCommentPushNotifications.js
@@ -27,7 +27,7 @@ export default function subscribeToReportCommentPushNotifications() {
try {
// If a chat is visible other than the one we are trying to navigate to, then we need to navigate back
if (Navigation.getActiveRoute().slice(1, 2) === ROUTES.REPORT && !Navigation.isActiveRoute(`r/${reportID}`)) {
- Navigation.goBack();
+ Navigation.goBack(ROUTES.HOME);
}
Log.info('[PushNotification] onSelected() - Navigation is ready. Navigating...', false, {reportID, reportActionID});
diff --git a/src/libs/OptionsListUtils.js b/src/libs/OptionsListUtils.js
index 7629a1acc0a6..3bdf77745432 100644
--- a/src/libs/OptionsListUtils.js
+++ b/src/libs/OptionsListUtils.js
@@ -92,31 +92,30 @@ Onyx.connect({
});
/**
- * Get the options for a policy expense report.
+ * Get the option for a policy expense report.
* @param {Object} report
- * @returns {Array}
+ * @returns {Object}
*/
-function getPolicyExpenseReportOptions(report) {
+function getPolicyExpenseReportOption(report) {
const expenseReport = policyExpenseReports[`${ONYXKEYS.COLLECTION.REPORT}${report.reportID}`];
const policyExpenseChatAvatarSource = ReportUtils.getWorkspaceAvatar(expenseReport);
const reportName = ReportUtils.getReportName(expenseReport);
- return [
- {
- ...expenseReport,
- keyForList: expenseReport.policyID,
- text: reportName,
- alternateText: Localize.translateLocal('workspace.common.workspace'),
- icons: [
- {
- source: policyExpenseChatAvatarSource,
- name: reportName,
- type: CONST.ICON_TYPE_WORKSPACE,
- },
- ],
- selected: report.selected,
- isPolicyExpenseChat: true,
- },
- ];
+ return {
+ ...expenseReport,
+ keyForList: expenseReport.policyID,
+ text: reportName,
+ alternateText: Localize.translateLocal('workspace.common.workspace'),
+ icons: [
+ {
+ source: policyExpenseChatAvatarSource,
+ name: reportName,
+ type: CONST.ICON_TYPE_WORKSPACE,
+ },
+ ],
+ selected: report.selected,
+ isPolicyExpenseChat: true,
+ searchText: report.searchText,
+ };
}
/**
@@ -201,38 +200,35 @@ function isPersonalDetailsReady(personalDetails) {
}
/**
- * Get the participant options for a report.
- * @param {Array
- {props.translate('genericErrorPage.title')}
+ {translate('genericErrorPage.title')}
- {`${props.translate('genericErrorPage.body.helpTextConcierge')} `}
+ {`${translate('genericErrorPage.body.helpTextConcierge')} `}
diff --git a/src/pages/FlagCommentPage.js b/src/pages/FlagCommentPage.js
index 0b850e6c3849..1a9a2d8c8767 100644
--- a/src/pages/FlagCommentPage.js
+++ b/src/pages/FlagCommentPage.js
@@ -2,6 +2,7 @@ import React, {useCallback} from 'react';
import _ from 'underscore';
import {View, ScrollView} from 'react-native';
import PropTypes from 'prop-types';
+import {withOnyx} from 'react-native-onyx';
import reportPropTypes from './reportPropTypes';
import reportActionPropTypes from './home/report/reportActionPropTypes';
import withLocalize, {withLocalizePropTypes} from '../components/withLocalize';
@@ -20,6 +21,7 @@ import * as ReportActionsUtils from '../libs/ReportActionsUtils';
import * as Session from '../libs/actions/Session';
import FullPageNotFoundView from '../components/BlockingViews/FullPageNotFoundView';
import withReportAndReportActionOrNotFound from './home/report/withReportAndReportActionOrNotFound';
+import ONYXKEYS from '../ONYXKEYS';
const propTypes = {
/** Array of report actions for this report */
@@ -178,4 +180,13 @@ FlagCommentPage.propTypes = propTypes;
FlagCommentPage.defaultProps = defaultProps;
FlagCommentPage.displayName = 'FlagCommentPage';
-export default compose(withLocalize, withReportAndReportActionOrNotFound)(FlagCommentPage);
+export default compose(
+ withLocalize,
+ withReportAndReportActionOrNotFound,
+ withOnyx({
+ parentReportActions: {
+ key: ({report}) => `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${report.parentReportID || report.reportID}`,
+ canEvict: false,
+ },
+ }),
+)(FlagCommentPage);
diff --git a/src/pages/NewChatPage.js b/src/pages/NewChatPage.js
index e72cb9a3f79b..cb54aa8e5a7b 100755
--- a/src/pages/NewChatPage.js
+++ b/src/pages/NewChatPage.js
@@ -55,8 +55,9 @@ function NewChatPage({betas, isGroupChat, personalDetails, reports, translate})
const headerMessage = OptionsListUtils.getHeaderMessage(
filteredPersonalDetails.length + filteredRecentReports.length !== 0,
Boolean(filteredUserToInvite),
- searchTerm,
+ searchTerm.trim(),
maxParticipantsReached,
+ _.some(selectedOptions, (participant) => participant.searchText.toLowerCase().includes(searchTerm.trim().toLowerCase())),
);
const isOptionsDataReady = ReportUtils.isReportDataReady() && OptionsListUtils.isPersonalDetailsReady(personalDetails);
@@ -123,7 +124,7 @@ function NewChatPage({betas, isGroupChat, personalDetails, reports, translate})
recentReports,
personalDetails: newChatPersonalDetails,
userToInvite,
- } = OptionsListUtils.getNewChatOptions(reports, personalDetails, betas, searchTerm, newSelectedOptions, excludedGroupEmails);
+ } = OptionsListUtils.getFilteredOptions(reports, personalDetails, betas, searchTerm, newSelectedOptions, excludedGroupEmails);
setSelectedOptions(newSelectedOptions);
setFilteredRecentReports(recentReports);
@@ -158,7 +159,7 @@ function NewChatPage({betas, isGroupChat, personalDetails, reports, translate})
recentReports,
personalDetails: newChatPersonalDetails,
userToInvite,
- } = OptionsListUtils.getNewChatOptions(reports, personalDetails, betas, searchTerm, selectedOptions, isGroupChat ? excludedGroupEmails : []);
+ } = OptionsListUtils.getFilteredOptions(reports, personalDetails, betas, searchTerm, selectedOptions, isGroupChat ? excludedGroupEmails : []);
setFilteredRecentReports(recentReports);
setFilteredPersonalDetails(newChatPersonalDetails);
setFilteredUserToInvite(userToInvite);
diff --git a/src/pages/NewChatSelectorPage.js b/src/pages/NewChatSelectorPage.js
index 89a3fd1adc72..ce0bbda0d239 100755
--- a/src/pages/NewChatSelectorPage.js
+++ b/src/pages/NewChatSelectorPage.js
@@ -1,4 +1,5 @@
import React from 'react';
+import {withOnyx} from 'react-native-onyx';
import OnyxTabNavigator, {TopTab} from '../libs/Navigation/OnyxTabNavigator';
import TabSelector from '../components/TabSelector/TabSelector';
import Navigation from '../libs/Navigation/Navigation';
@@ -6,6 +7,7 @@ import Permissions from '../libs/Permissions';
import NewChatPage from './NewChatPage';
import WorkspaceNewRoomPage from './workspace/WorkspaceNewRoomPage';
import CONST from '../CONST';
+import ONYXKEYS from '../ONYXKEYS';
import withWindowDimensions, {windowDimensionsPropTypes} from '../components/withWindowDimensions';
import HeaderWithBackButton from '../components/HeaderWithBackButton';
import ScreenWrapper from '../components/ScreenWrapper';
@@ -66,4 +68,10 @@ NewChatSelectorPage.propTypes = propTypes;
NewChatSelectorPage.defaultProps = defaultProps;
NewChatSelectorPage.displayName = 'NewChatPage';
-export default compose(withLocalize, withWindowDimensions)(NewChatSelectorPage);
+export default compose(
+ withLocalize,
+ withWindowDimensions,
+ withOnyx({
+ betas: {key: ONYXKEYS.BETAS},
+ }),
+)(NewChatSelectorPage);
diff --git a/src/pages/PrivateNotes/PrivateNotesEditPage.js b/src/pages/PrivateNotes/PrivateNotesEditPage.js
index 4cada83941ac..206e9e74d91f 100644
--- a/src/pages/PrivateNotes/PrivateNotesEditPage.js
+++ b/src/pages/PrivateNotes/PrivateNotesEditPage.js
@@ -24,6 +24,7 @@ import * as Report from '../../libs/actions/Report';
import useLocalize from '../../hooks/useLocalize';
import OfflineWithFeedback from '../../components/OfflineWithFeedback';
import focusAndUpdateMultilineInputRange from '../../libs/focusAndUpdateMultilineInputRange';
+import ROUTES from '../../ROUTES';
const propTypes = {
/** All of the personal details for everyone */
@@ -72,7 +73,7 @@ function PrivateNotesEditPage({route, personalDetailsList, session, report}) {
Keyboard.dismiss();
// Take user back to the PrivateNotesView page
- Navigation.goBack();
+ Navigation.goBack(ROUTES.HOME);
};
return (
@@ -83,14 +84,12 @@ function PrivateNotesEditPage({route, personalDetailsList, session, report}) {
Navigation.goBack()}
>
Navigation.dismissModal()}
- onBackButtonPress={() => Navigation.goBack()}
/>
diff --git a/src/pages/PrivateNotes/PrivateNotesListPage.js b/src/pages/PrivateNotes/PrivateNotesListPage.js
index 5ea081a12f25..098bfd2a245b 100644
--- a/src/pages/PrivateNotes/PrivateNotesListPage.js
+++ b/src/pages/PrivateNotes/PrivateNotesListPage.js
@@ -107,26 +107,22 @@ function PrivateNotesListPage({report, personalDetailsList, network, session}) {
const privateNoteBrickRoadIndicator = (accountID) => (!_.isEmpty(lodashGet(report, ['privateNotes', accountID, 'errors'], '')) ? CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR : '');
return _.chain(lodashGet(report, 'privateNotes', {}))
.map((privateNote, accountID) => ({
- title: Number(lodashGet(session, 'accountID', null)) === Number(accountID) ? 'My note' : lodashGet(personalDetailsList, [accountID, 'login'], ''),
+ title: Number(lodashGet(session, 'accountID', null)) === Number(accountID) ? translate('privateNotes.myNote') : lodashGet(personalDetailsList, [accountID, 'login'], ''),
icon: UserUtils.getAvatar(lodashGet(personalDetailsList, [accountID, 'avatar'], UserUtils.getDefaultAvatar(accountID)), accountID),
iconType: CONST.ICON_TYPE_AVATAR,
action: () => Navigation.navigate(ROUTES.getPrivateNotesViewRoute(report.reportID, accountID)),
brickRoadIndicator: privateNoteBrickRoadIndicator(accountID),
}))
.value();
- }, [report, personalDetailsList, session]);
+ }, [report, personalDetailsList, session, translate]);
return (
- Navigation.goBack()}
- >
+ Navigation.dismissModal()}
- onBackButtonPress={() => Navigation.goBack()}
/>
{report.isLoadingPrivateNotes && _.isEmpty(lodashGet(report, 'privateNotes', {})) ? (
diff --git a/src/pages/PrivateNotes/PrivateNotesViewPage.js b/src/pages/PrivateNotes/PrivateNotesViewPage.js
index 86814ed4dc92..48f053f10f90 100644
--- a/src/pages/PrivateNotes/PrivateNotesViewPage.js
+++ b/src/pages/PrivateNotes/PrivateNotesViewPage.js
@@ -60,14 +60,12 @@ function PrivateNotesViewPage({route, personalDetailsList, session, report}) {
Navigation.goBack()}
>
Navigation.dismissModal()}
- onBackButtonPress={() => Navigation.goBack()}
/>
diff --git a/src/pages/ProfilePage.js b/src/pages/ProfilePage.js
index b306164a8ba0..b515da04b7be 100755
--- a/src/pages/ProfilePage.js
+++ b/src/pages/ProfilePage.js
@@ -139,12 +139,12 @@ function ProfilePage(props) {
const hasStatus = !!statusEmojiCode && Permissions.canUseCustomStatus(props.betas);
const statusContent = `${statusEmojiCode} ${statusText}`;
- const navigateBackTo = lodashGet(props.route, 'params.backTo', '');
+ const navigateBackTo = lodashGet(props.route, 'params.backTo', ROUTES.HOME);
const chatReportWithCurrentUser = !isCurrentUser && !Session.isAnonymousUser() ? ReportUtils.getChatByParticipants([accountID]) : 0;
return (
-
+ Navigation.goBack(navigateBackTo)}
diff --git a/src/pages/ReimbursementAccount/BankAccountPlaidStep.js b/src/pages/ReimbursementAccount/BankAccountPlaidStep.js
index 80c9257d367a..70cf43fcfdc2 100644
--- a/src/pages/ReimbursementAccount/BankAccountPlaidStep.js
+++ b/src/pages/ReimbursementAccount/BankAccountPlaidStep.js
@@ -1,4 +1,5 @@
-import React, {useCallback} from 'react';
+import React, {useCallback, useEffect} from 'react';
+import {useIsFocused} from '@react-navigation/native';
import PropTypes from 'prop-types';
import _ from 'underscore';
import lodashGet from 'lodash/get';
@@ -41,6 +42,7 @@ const defaultProps = {
function BankAccountPlaidStep(props) {
const {plaidData, receivedRedirectURI, plaidLinkOAuthToken, reimbursementAccount, reimbursementAccountDraft, onBackButtonPress, getDefaultStateForField, translate} = props;
+ const isFocused = useIsFocused();
const validate = useCallback((values) => {
const errorFields = {};
@@ -51,6 +53,14 @@ function BankAccountPlaidStep(props) {
return errorFields;
}, []);
+ useEffect(() => {
+ const plaidBankAccounts = lodashGet(plaidData, 'bankAccounts') || [];
+ if (isFocused || plaidBankAccounts.length) {
+ return;
+ }
+ BankAccounts.setBankAccountSubStep(null);
+ }, [isFocused, plaidData]);
+
const submit = useCallback(() => {
const selectedPlaidBankAccount = _.findWhere(lodashGet(plaidData, 'bankAccounts', []), {
plaidAccountID: lodashGet(reimbursementAccountDraft, 'plaidAccountID', ''),
@@ -103,7 +113,6 @@ function BankAccountPlaidStep(props) {
}}
plaidData={plaidData}
onExitPlaid={() => BankAccounts.setBankAccountSubStep(null)}
- onBlurPlaid={() => BankAccounts.setBankAccountSubStep(null)}
receivedRedirectURI={receivedRedirectURI}
plaidLinkOAuthToken={plaidLinkOAuthToken}
allowDebit
diff --git a/src/pages/ReimbursementAccount/ReimbursementAccountPage.js b/src/pages/ReimbursementAccount/ReimbursementAccountPage.js
index 3160ad590c50..cdb3aeebe924 100644
--- a/src/pages/ReimbursementAccount/ReimbursementAccountPage.js
+++ b/src/pages/ReimbursementAccount/ReimbursementAccountPage.js
@@ -284,7 +284,7 @@ class ReimbursementAccountPage extends React.Component {
const currentStep = achData.currentStep || CONST.BANK_ACCOUNT.STEP.BANK_ACCOUNT;
const subStep = achData.subStep;
const shouldShowOnfido = this.props.onfidoToken && !achData.isOnfidoSetupComplete;
- const backTo = lodashGet(this.props.route.params, 'backTo');
+ const backTo = lodashGet(this.props.route.params, 'backTo', ROUTES.HOME);
switch (currentStep) {
case CONST.BANK_ACCOUNT.STEP.BANK_ACCOUNT:
if (this.hasInProgressVBBA()) {
@@ -405,7 +405,7 @@ class ReimbursementAccountPage extends React.Component {
continue={this.continue}
policyName={policyName}
onBackButtonPress={() => {
- Navigation.goBack(lodashGet(this.props.route.params, 'backTo'));
+ Navigation.goBack(lodashGet(this.props.route.params, 'backTo', ROUTES.HOME));
}}
/>
);
diff --git a/src/pages/SearchPage.js b/src/pages/SearchPage.js
index 67d4ebb57876..2ee29380ff80 100755
--- a/src/pages/SearchPage.js
+++ b/src/pages/SearchPage.js
@@ -1,5 +1,5 @@
import _ from 'underscore';
-import React, {useCallback, useEffect, useState, useMemo} from 'react';
+import React, {Component} from 'react';
import {View} from 'react-native';
import PropTypes from 'prop-types';
import {withOnyx} from 'react-native-onyx';
@@ -9,15 +9,17 @@ import * as ReportUtils from '../libs/ReportUtils';
import ONYXKEYS from '../ONYXKEYS';
import styles from '../styles/styles';
import Navigation from '../libs/Navigation/Navigation';
+import withWindowDimensions, {windowDimensionsPropTypes} from '../components/withWindowDimensions';
import * as Report from '../libs/actions/Report';
import HeaderWithBackButton from '../components/HeaderWithBackButton';
import ScreenWrapper from '../components/ScreenWrapper';
import Timing from '../libs/actions/Timing';
import CONST from '../CONST';
+import withLocalize, {withLocalizePropTypes} from '../components/withLocalize';
+import compose from '../libs/compose';
import personalDetailsPropType from './personalDetailsPropType';
import reportPropTypes from './reportPropTypes';
import Performance from '../libs/Performance';
-import useLocalize from '../hooks/useLocalize';
const propTypes = {
/* Onyx Props */
@@ -30,6 +32,11 @@ const propTypes = {
/** All reports shared with the user */
reports: PropTypes.objectOf(reportPropTypes),
+
+ /** Window Dimensions Props */
+ ...windowDimensionsPropTypes,
+
+ ...withLocalizePropTypes,
};
const defaultProps = {
@@ -38,158 +45,172 @@ const defaultProps = {
reports: {},
};
-function SearchPage({betas, personalDetails, reports}) {
- // Data for initialization (runs only on the first render)
- const {
- recentReports: initialRecentReports,
- personalDetails: initialPersonalDetails,
- userToInvite: initialUserToInvite,
- // Ignoring the rule because in this case we need the data only initially
- // eslint-disable-next-line react-hooks/exhaustive-deps
- } = useMemo(() => OptionsListUtils.getSearchOptions(reports, personalDetails, '', betas), []);
-
- const [searchValue, setSearchValue] = useState('');
- const [searchOptions, setSearchOptions] = useState({
- recentReports: initialRecentReports,
- personalDetails: initialPersonalDetails,
- userToInvite: initialUserToInvite,
- });
-
- const {translate} = useLocalize();
-
- const updateOptions = useCallback(() => {
- const {
- recentReports: localRecentReports,
- personalDetails: localPersonalDetails,
- userToInvite: localUserToInvite,
- } = OptionsListUtils.getSearchOptions(reports, personalDetails, searchValue.trim(), betas);
-
- setSearchOptions({
- recentReports: localRecentReports,
- personalDetails: localPersonalDetails,
- userToInvite: localUserToInvite,
- });
- }, [reports, personalDetails, searchValue, betas]);
-
- const debouncedUpdateOptions = useMemo(() => _.debounce(updateOptions, 75), [updateOptions]);
+class SearchPage extends Component {
+ constructor(props) {
+ super(props);
- useEffect(() => {
Timing.start(CONST.TIMING.SEARCH_RENDER);
Performance.markStart(CONST.TIMING.SEARCH_RENDER);
- }, []);
- useEffect(() => {
- debouncedUpdateOptions();
- }, [searchValue, debouncedUpdateOptions]);
+ this.searchRendered = this.searchRendered.bind(this);
+ this.selectReport = this.selectReport.bind(this);
+ this.onChangeText = this.onChangeText.bind(this);
+ this.debouncedUpdateOptions = _.debounce(this.updateOptions.bind(this), 75);
+
+ const {recentReports, personalDetails, userToInvite} = OptionsListUtils.getSearchOptions(props.reports, props.personalDetails, '', props.betas);
+
+ this.state = {
+ searchValue: '',
+ recentReports,
+ personalDetails,
+ userToInvite,
+ };
+ }
+
+ componentDidUpdate(prevProps) {
+ if (_.isEqual(prevProps.reports, this.props.reports) && _.isEqual(prevProps.personalDetails, this.props.personalDetails)) {
+ return;
+ }
+ this.updateOptions();
+ }
+
+ onChangeText(searchValue = '') {
+ this.setState({searchValue}, this.debouncedUpdateOptions);
+ }
/**
* Returns the sections needed for the OptionsSelector
*
* @returns {Array}
*/
- const getSections = () => {
+ getSections() {
const sections = [];
let indexOffset = 0;
- if (searchOptions.recentReports.length > 0) {
+ if (this.state.recentReports.length > 0) {
sections.push({
- data: searchOptions.recentReports,
+ data: this.state.recentReports,
shouldShow: true,
indexOffset,
});
- indexOffset += searchOptions.recentReports.length;
+ indexOffset += this.state.recentReports.length;
}
- if (searchOptions.personalDetails.length > 0) {
+ if (this.state.personalDetails.length > 0) {
sections.push({
- data: searchOptions.personalDetails,
+ data: this.state.personalDetails,
shouldShow: true,
indexOffset,
});
- indexOffset += searchOptions.recentReports.length;
+ indexOffset += this.state.recentReports.length;
}
- if (searchOptions.userToInvite) {
+ if (this.state.userToInvite) {
sections.push({
- data: [searchOptions.userToInvite],
+ data: [this.state.userToInvite],
shouldShow: true,
indexOffset,
});
}
return sections;
- };
+ }
- const searchRendered = () => {
+ searchRendered() {
Timing.end(CONST.TIMING.SEARCH_RENDER);
Performance.markEnd(CONST.TIMING.SEARCH_RENDER);
- };
-
- const onChangeText = (value = '') => {
- setSearchValue(value);
- };
+ }
+
+ updateOptions() {
+ const {recentReports, personalDetails, userToInvite} = OptionsListUtils.getSearchOptions(
+ this.props.reports,
+ this.props.personalDetails,
+ this.state.searchValue.trim(),
+ this.props.betas,
+ );
+ this.setState({
+ userToInvite,
+ recentReports,
+ personalDetails,
+ });
+ }
/**
* Reset the search value and redirect to the selected report
*
* @param {Object} option
*/
- const selectReport = (option) => {
+ selectReport(option) {
if (!option) {
return;
}
+
if (option.reportID) {
- setSearchValue('');
- Navigation.dismissModal(option.reportID);
+ this.setState(
+ {
+ searchValue: '',
+ },
+ () => {
+ Navigation.dismissModal(option.reportID);
+ },
+ );
} else {
Report.navigateToAndOpenReport([option.login]);
}
- };
-
- const isOptionsDataReady = ReportUtils.isReportDataReady() && OptionsListUtils.isPersonalDetailsReady(personalDetails);
- const headerMessage = OptionsListUtils.getHeaderMessage(
- searchOptions.recentReports.length + searchOptions.personalDetails.length !== 0,
- Boolean(searchOptions.userToInvite),
- searchValue,
- );
- return (
-
- {({didScreenTransitionEnd, safeAreaPaddingBottomStyle}) => (
- <>
-
-
-
-
- >
- )}
-
- );
+ }
+
+ render() {
+ const sections = this.getSections();
+ const isOptionsDataReady = ReportUtils.isReportDataReady() && OptionsListUtils.isPersonalDetailsReady(this.props.personalDetails);
+ const headerMessage = OptionsListUtils.getHeaderMessage(
+ this.state.recentReports.length + this.state.personalDetails.length !== 0,
+ Boolean(this.state.userToInvite),
+ this.state.searchValue,
+ );
+
+ return (
+
+ {({didScreenTransitionEnd, safeAreaPaddingBottomStyle}) => (
+ <>
+
+
+
+
+ >
+ )}
+
+ );
+ }
}
SearchPage.propTypes = propTypes;
SearchPage.defaultProps = defaultProps;
-SearchPage.displayName = 'SearchPage';
-export default withOnyx({
- reports: {
- key: ONYXKEYS.COLLECTION.REPORT,
- },
- personalDetails: {
- key: ONYXKEYS.PERSONAL_DETAILS_LIST,
- },
- betas: {
- key: ONYXKEYS.BETAS,
- },
-})(SearchPage);
+
+export default compose(
+ withLocalize,
+ withWindowDimensions,
+ withOnyx({
+ reports: {
+ key: ONYXKEYS.COLLECTION.REPORT,
+ },
+ personalDetails: {
+ key: ONYXKEYS.PERSONAL_DETAILS_LIST,
+ },
+ betas: {
+ key: ONYXKEYS.BETAS,
+ },
+ }),
+)(SearchPage);
diff --git a/src/pages/ShareCodePage.js b/src/pages/ShareCodePage.js
index a81d02c60b36..a36149a5f4fa 100644
--- a/src/pages/ShareCodePage.js
+++ b/src/pages/ShareCodePage.js
@@ -42,8 +42,9 @@ class ShareCodePage extends React.Component {
render() {
const isReport = this.props.report != null && this.props.report.reportID != null;
- const subtitle = ReportUtils.getChatRoomSubtitle(this.props.report);
-
+ const title = isReport ? ReportUtils.getReportName(this.props.report) : this.props.currentUserPersonalDetails.displayName;
+ const formattedEmail = this.props.formatPhoneNumber(this.props.session.email);
+ const subtitle = isReport ? ReportUtils.getParentNavigationSubtitle(this.props.report).workspaceName || ReportUtils.getChatRoomSubtitle(this.props.report) : formattedEmail;
const urlWithTrailingSlash = Url.addTrailingForwardSlash(this.props.environmentURL);
const url = isReport
? `${urlWithTrailingSlash}${ROUTES.getReportRoute(this.props.report.reportID)}`
@@ -51,7 +52,6 @@ class ShareCodePage extends React.Component {
const platform = getPlatform();
const isNative = platform === CONST.PLATFORM.IOS || platform === CONST.PLATFORM.ANDROID;
- const formattedEmail = this.props.formatPhoneNumber(this.props.session.email);
return (
@@ -65,8 +65,8 @@ class ShareCodePage extends React.Component {
Navigation.goBack(ROUTES.HOME)}
illustration={LottieAnimations.SaveTheWorld}
>
diff --git a/src/pages/home/HeaderView.js b/src/pages/home/HeaderView.js
index 8d389a8c8581..477d063c1747 100644
--- a/src/pages/home/HeaderView.js
+++ b/src/pages/home/HeaderView.js
@@ -29,6 +29,7 @@ import reportActionPropTypes from './report/reportActionPropTypes';
import PressableWithoutFeedback from '../../components/Pressable/PressableWithoutFeedback';
import PinButton from '../../components/PinButton';
import TaskHeaderActionButton from '../../components/TaskHeaderActionButton';
+import * as ReportActionsUtils from '../../libs/ReportActionsUtils';
import ParentNavigationSubtitle from '../../components/ParentNavigationSubtitle';
const propTypes = {
@@ -93,12 +94,14 @@ function HeaderView(props) {
const isConcierge = ReportUtils.hasSingleParticipant(props.report) && _.contains(participants, CONST.ACCOUNT_ID.CONCIERGE);
const isAutomatedExpensifyAccount = ReportUtils.hasSingleParticipant(props.report) && ReportUtils.hasAutomatedExpensifyAccountIDs(participants);
const guideCalendarLink = lodashGet(props.account, 'guideCalendarLink');
+ const parentReportAction = ReportActionsUtils.getParentReportAction(props.report);
+ const isCanceledTaskReport = ReportUtils.isCanceledTaskReport(props.report, parentReportAction);
// We hide the button when we are chatting with an automated Expensify account since it's not possible to contact
// these users via alternative means. It is possible to request a call with Concierge so we leave the option for them.
const shouldShowCallButton = (isConcierge && guideCalendarLink) || (!isAutomatedExpensifyAccount && !isTaskReport);
const threeDotMenuItems = [];
- if (isTaskReport) {
+ if (isTaskReport && !isCanceledTaskReport) {
const canModifyTask = Task.canModifyTask(props.report, props.session.accountID);
if (ReportUtils.isOpenTaskReport(props.report) && canModifyTask) {
threeDotMenuItems.push({
diff --git a/src/pages/home/ReportScreen.js b/src/pages/home/ReportScreen.js
index 004087c22308..63e60a545de9 100644
--- a/src/pages/home/ReportScreen.js
+++ b/src/pages/home/ReportScreen.js
@@ -1,5 +1,6 @@
import React, {useRef, useState, useEffect, useMemo, useCallback} from 'react';
import {withOnyx} from 'react-native-onyx';
+import {useFocusEffect} from '@react-navigation/native';
import PropTypes from 'prop-types';
import {View} from 'react-native';
import lodashGet from 'lodash/get';
@@ -37,6 +38,7 @@ import ReportScreenContext from './ReportScreenContext';
import TaskHeaderActionButton from '../../components/TaskHeaderActionButton';
import DragAndDropProvider from '../../components/DragAndDrop/Provider';
import usePrevious from '../../hooks/usePrevious';
+import CONST from '../../CONST';
import withCurrentReportID, {withCurrentReportIDPropTypes, withCurrentReportIDDefaultProps} from '../../components/withCurrentReportID';
const propTypes = {
@@ -107,6 +109,15 @@ const defaultProps = {
...withCurrentReportIDDefaultProps,
};
+/**
+ *
+ * Function to check weather the report available in props is default
+ *
+ * @param {Object} report
+ * @returns {Boolean}
+ */
+const checkDefaultReport = (report) => report === defaultProps.report;
+
/**
* Get the currently viewed report ID as number
*
@@ -151,6 +162,8 @@ function ReportScreen({
// There are no reportActions at all to display and we are still in the process of loading the next set of actions.
const isLoadingInitialReportActions = _.isEmpty(reportActions) && report.isLoadingReportActions;
+ const isOptimisticDelete = lodashGet(report, 'statusNum') === CONST.REPORT.STATUS.CLOSED;
+
const shouldHideReport = !ReportUtils.canAccessReport(report, policies, betas);
const isLoading = !reportID || !isSidebarLoaded || _.isEmpty(personalDetails) || firstRenderRef.current;
@@ -163,6 +176,8 @@ function ReportScreen({
const isTopMostReportId = currentReportID === getReportID(route);
+ const isDefaultReport = checkDefaultReport(report);
+
let headerView = (
{
- const unsubscribeVisibilityListener = Visibility.onVisibilityChange(() => {
- const isTopMostReportID = Navigation.getTopmostReportId() === getReportID(route);
- // If the report is not fully visible (AKA on small screen devices and LHR is open) or the report is optimistic (AKA not yet created)
- // we don't need to call openReport
- if (!getIsReportFullyVisible(isTopMostReportID) || report.isOptimisticReport) {
- return;
- }
-
- Report.openReport(report.reportID);
- });
+ useFocusEffect(
+ useCallback(() => {
+ const unsubscribeVisibilityListener = Visibility.onVisibilityChange(() => {
+ const isTopMostReportID = Navigation.getTopmostReportId() === getReportID(route);
+ // If the report is not fully visible (AKA on small screen devices and LHR is open) or the report is optimistic (AKA not yet created)
+ // we don't need to call openReport
+ if (!getIsReportFullyVisible(isTopMostReportID) || report.isOptimisticReport) {
+ return;
+ }
+
+ Report.openReport(report.reportID);
+ });
+
+ return () => unsubscribeVisibilityListener();
+ // The effect should run only on the first focus to attach listener
+ // eslint-disable-next-line react-hooks/exhaustive-deps
+ }, []),
+ );
+ useEffect(() => {
fetchReportIfNeeded();
ComposerActions.setShouldShowComposeInput(true);
- return () => {
- if (!unsubscribeVisibilityListener) {
- return;
- }
- unsubscribeVisibilityListener();
- };
// I'm disabling the warning, as it expects to use exhaustive deps, even though we want this useEffect to run only on the first render.
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
@@ -289,6 +306,12 @@ function ReportScreen({
ComposerActions.setShouldShowComposeInput(true);
}, [route, report, errors, fetchReportIfNeeded, prevReport.reportID]);
+ // eslint-disable-next-line rulesdir/no-negated-variables
+ const shouldShowNotFoundPage = useMemo(
+ () => (!_.isEmpty(report) && !isDefaultReport && !report.reportID && !isOptimisticDelete && !report.isLoadingReportActions && !isLoading) || shouldHideReport,
+ [report, isLoading, shouldHideReport, isDefaultReport, isOptimisticDelete],
+ );
+
return (
{headerView}
- {ReportUtils.isTaskReport(report) && isSmallScreenWidth && ReportUtils.isOpenTaskReport(report) && (
+ {ReportUtils.isTaskReport(report) && isSmallScreenWidth && ReportUtils.isOpenTaskReport(report, parentReportAction) && (
diff --git a/src/pages/home/report/ContextMenu/BaseReportActionContextMenu.js b/src/pages/home/report/ContextMenu/BaseReportActionContextMenu.js
index 3f4a5fe3ac4c..6382af6a898e 100755
--- a/src/pages/home/report/ContextMenu/BaseReportActionContextMenu.js
+++ b/src/pages/home/report/ContextMenu/BaseReportActionContextMenu.js
@@ -49,7 +49,9 @@ function BaseReportActionContextMenu(props) {
const wrapperStyle = getReportActionContextMenuStyles(props.isMini, props.isSmallScreenWidth);
const reportAction = useMemo(() => {
- if (_.isEmpty(props.reportActions) || props.reportActionID === '0') return {};
+ if (_.isEmpty(props.reportActions) || props.reportActionID === '0') {
+ return {};
+ }
return props.reportActions[props.reportActionID] || {};
}, [props.reportActions, props.reportActionID]);
diff --git a/src/pages/home/report/ContextMenu/ContextMenuActions.js b/src/pages/home/report/ContextMenu/ContextMenuActions.js
index 556366a96792..173bda0e5221 100644
--- a/src/pages/home/report/ContextMenu/ContextMenuActions.js
+++ b/src/pages/home/report/ContextMenu/ContextMenuActions.js
@@ -21,6 +21,10 @@ import QuickEmojiReactions from '../../../../components/Reactions/QuickEmojiReac
import MiniQuickEmojiReactions from '../../../../components/Reactions/MiniQuickEmojiReactions';
import Navigation from '../../../../libs/Navigation/Navigation';
import ROUTES from '../../../../ROUTES';
+import * as Task from '../../../../libs/actions/Task';
+import * as Localize from '../../../../libs/Localize';
+import * as TransactionUtils from '../../../../libs/TransactionUtils';
+import * as CurrencyUtils from '../../../../libs/CurrencyUtils';
/**
* Gets the HTML version of the message in an action.
@@ -182,10 +186,11 @@ export default [
// the `text` and `icon`
onPress: (closePopover, {reportAction, selection}) => {
const isTaskAction = ReportActionsUtils.isTaskAction(reportAction);
+ const isCreateTaskAction = ReportActionsUtils.isCreatedTaskReportAction(reportAction);
const isReportPreviewAction = ReportActionsUtils.isReportPreviewAction(reportAction);
const message = _.last(lodashGet(reportAction, 'message', [{}]));
- const originalMessage = _.get(reportAction, 'originalMessage', {});
- const messageHtml = isTaskAction ? lodashGet(originalMessage, 'html', '') : lodashGet(message, 'html', '');
+ const reportID = lodashGet(reportAction, 'originalMessage.taskReportID', '').toString();
+ const messageHtml = isTaskAction || isCreateTaskAction ? Task.getTaskReportActionMessage(reportAction.actionName, reportID, isCreateTaskAction) : lodashGet(message, 'html', '');
const isAttachment = ReportActionsUtils.isReportActionAttachment(reportAction);
if (!isAttachment) {
@@ -197,6 +202,16 @@ export default [
} else if (ReportActionsUtils.isModifiedExpenseAction(reportAction)) {
const modifyExpenseMessage = ReportUtils.getModifiedExpenseMessage(reportAction);
Clipboard.setString(modifyExpenseMessage);
+ } else if (ReportActionsUtils.isMoneyRequestAction(reportAction)) {
+ const originalMessage = _.get(reportAction, 'originalMessage', {});
+ const transaction = TransactionUtils.getTransaction(originalMessage.IOUTransactionID);
+ const {amount, currency, comment} = ReportUtils.getTransactionDetails(transaction);
+ const formattedAmount = CurrencyUtils.convertToDisplayString(amount, currency);
+ const displaymessage = Localize.translateLocal('iou.requestedAmount', {
+ formattedAmount,
+ comment,
+ });
+ Clipboard.setString(displaymessage);
} else if (content) {
const parser = new ExpensiMark();
if (!Clipboard.canSetHtml()) {
diff --git a/src/pages/home/report/FloatingMessageCounter/index.js b/src/pages/home/report/FloatingMessageCounter/index.js
index c477b8137bea..a4e2d25d9490 100644
--- a/src/pages/home/report/FloatingMessageCounter/index.js
+++ b/src/pages/home/report/FloatingMessageCounter/index.js
@@ -1,6 +1,7 @@
import React, {useEffect, useMemo, useCallback} from 'react';
import {Animated, View} from 'react-native';
import PropTypes from 'prop-types';
+import CONST from '../../../../CONST';
import styles from '../../../../styles/styles';
import Button from '../../../../components/Button';
import Text from '../../../../components/Text';
@@ -76,6 +77,7 @@ function FloatingMessageCounter(props) {
{translate('newMessages')}
diff --git a/src/pages/home/report/ReportActionCompose/ComposerWithSuggestions.js b/src/pages/home/report/ReportActionCompose/ComposerWithSuggestions.js
index 3b5b181d2fcb..ccf7a0a51518 100644
--- a/src/pages/home/report/ReportActionCompose/ComposerWithSuggestions.js
+++ b/src/pages/home/report/ReportActionCompose/ComposerWithSuggestions.js
@@ -185,6 +185,11 @@ function ComposerWithSuggestions({
setIsCommentEmpty(!!newComment.match(/^(\s)*$/));
setValue(newComment);
if (commentValue !== newComment) {
+ // Ensure emoji suggestions are hidden even when the selection is not changed (so calculateEmojiSuggestion would not be called).
+ if (suggestionsRef.current) {
+ suggestionsRef.current.resetSuggestions();
+ }
+
const remainder = ComposerUtils.getCommonSuffixLength(commentRef.current, newComment);
setSelection({
start: newComment.length - remainder,
@@ -212,7 +217,7 @@ function ComposerWithSuggestions({
debouncedBroadcastUserIsTyping(reportID);
}
},
- [debouncedUpdateFrequentlyUsedEmojis, preferredLocale, preferredSkinTone, reportID, setIsCommentEmpty],
+ [debouncedUpdateFrequentlyUsedEmojis, preferredLocale, preferredSkinTone, reportID, setIsCommentEmpty, suggestionsRef],
);
/**
@@ -241,6 +246,11 @@ function ComposerWithSuggestions({
return '';
}
+ // Since we're submitting the form here which should clear the composer
+ // We don't really care about saving the draft the user was typing
+ // We need to make sure an empty draft gets saved instead
+ debouncedSaveReportComment.cancel();
+
updateComment('');
setTextInputShouldClear(true);
if (isComposerFullSize) {
@@ -305,7 +315,7 @@ function ComposerWithSuggestions({
const onSelectionChange = useCallback(
(e) => {
- if (suggestionsRef.current.onSelectionChange(e)) {
+ if (textInputRef.current && textInputRef.current.isFocused() && suggestionsRef.current.onSelectionChange(e)) {
return;
}
diff --git a/src/pages/home/report/ReportActionCompose/ReportActionCompose.js b/src/pages/home/report/ReportActionCompose/ReportActionCompose.js
index ddcd43cd8cd0..c5179290bf2c 100644
--- a/src/pages/home/report/ReportActionCompose/ReportActionCompose.js
+++ b/src/pages/home/report/ReportActionCompose/ReportActionCompose.js
@@ -30,7 +30,6 @@ import OfflineWithFeedback from '../../../../components/OfflineWithFeedback';
import SendButton from './SendButton';
import AttachmentPickerWithMenuItems from './AttachmentPickerWithMenuItems';
import ComposerWithSuggestions from './ComposerWithSuggestions';
-import debouncedSaveReportComment from '../../../../libs/ComposerUtils/debouncedSaveReportComment';
import reportActionPropTypes from '../reportActionPropTypes';
import useLocalize from '../../../../hooks/useLocalize';
import getModalState from '../../../../libs/getModalState';
@@ -220,10 +219,6 @@ function ReportActionCompose({
*/
const addAttachment = useCallback(
(file) => {
- // Since we're submitting the form here which should clear the composer
- // We don't really care about saving the draft the user was typing
- // We need to make sure an empty draft gets saved instead
- debouncedSaveReportComment.cancel();
const newComment = composerRef.current.prepareCommentAndResetComposer();
Report.addAttachment(reportID, file, newComment);
setTextInputShouldClear(false);
@@ -251,11 +246,6 @@ function ReportActionCompose({
e.preventDefault();
}
- // Since we're submitting the form here which should clear the composer
- // We don't really care about saving the draft the user was typing
- // We need to make sure an empty draft gets saved instead
- debouncedSaveReportComment.cancel();
-
const newComment = composerRef.current.prepareCommentAndResetComposer();
if (!newComment) {
return;
diff --git a/src/pages/home/report/ReportActionCompose/SuggestionEmoji.js b/src/pages/home/report/ReportActionCompose/SuggestionEmoji.js
index 687570af12e6..a760627e53cc 100644
--- a/src/pages/home/report/ReportActionCompose/SuggestionEmoji.js
+++ b/src/pages/home/report/ReportActionCompose/SuggestionEmoji.js
@@ -42,12 +42,6 @@ const propTypes = {
/** Callback when a emoji was inserted */
onInsertedEmoji: PropTypes.func.isRequired,
- /** The current selection */
- selection: PropTypes.shape({
- start: PropTypes.number.isRequired,
- end: PropTypes.number.isRequired,
- }).isRequired,
-
...SuggestionProps.baseProps,
};
diff --git a/src/pages/home/report/ReportActionCompose/SuggestionMention.js b/src/pages/home/report/ReportActionCompose/SuggestionMention.js
index 79b5d1d66e36..a76025b67b1e 100644
--- a/src/pages/home/report/ReportActionCompose/SuggestionMention.js
+++ b/src/pages/home/report/ReportActionCompose/SuggestionMention.js
@@ -1,4 +1,4 @@
-import React, {useState, useCallback, useRef, useImperativeHandle} from 'react';
+import React, {useState, useCallback, useRef, useImperativeHandle, useEffect} from 'react';
import PropTypes from 'prop-types';
import _ from 'underscore';
import {withOnyx} from 'react-native-onyx';
@@ -45,6 +45,7 @@ const defaultProps = {
function SuggestionMention({
value,
setValue,
+ selection,
setSelection,
isComposerFullSize,
personalDetails,
@@ -231,12 +232,9 @@ function SuggestionMention({
[getMentionOptions, personalDetails, resetSuggestions, setHighlightedMentionIndex, value],
);
- const onSelectionChange = useCallback(
- (e) => {
- calculateMentionSuggestion(e.nativeEvent.selection.end);
- },
- [calculateMentionSuggestion],
- );
+ useEffect(() => {
+ calculateMentionSuggestion(selection.end);
+ }, [selection, calculateMentionSuggestion]);
const updateShouldShowSuggestionMenuToFalse = useCallback(() => {
setSuggestionValues((prevState) => {
@@ -262,12 +260,11 @@ function SuggestionMention({
forwardedRef,
() => ({
resetSuggestions,
- onSelectionChange,
triggerHotkeyActions,
setShouldBlockSuggestionCalc,
updateShouldShowSuggestionMenuToFalse,
}),
- [onSelectionChange, resetSuggestions, setShouldBlockSuggestionCalc, triggerHotkeyActions, updateShouldShowSuggestionMenuToFalse],
+ [resetSuggestions, setShouldBlockSuggestionCalc, triggerHotkeyActions, updateShouldShowSuggestionMenuToFalse],
);
if (!isMentionSuggestionsMenuVisible) {
diff --git a/src/pages/home/report/ReportActionCompose/Suggestions.js b/src/pages/home/report/ReportActionCompose/Suggestions.js
index ed2ab9586d52..60cb9de4ccfb 100644
--- a/src/pages/home/report/ReportActionCompose/Suggestions.js
+++ b/src/pages/home/report/ReportActionCompose/Suggestions.js
@@ -66,8 +66,7 @@ function Suggestions({
const onSelectionChange = useCallback((e) => {
const emojiHandler = suggestionEmojiRef.current.onSelectionChange(e);
- const mentionHandler = suggestionMentionRef.current.onSelectionChange(e);
- return emojiHandler || mentionHandler;
+ return emojiHandler;
}, []);
const updateShouldShowSuggestionMenuToFalse = useCallback(() => {
@@ -102,6 +101,7 @@ function Suggestions({
value,
setValue,
setSelection,
+ selection,
isComposerFullSize,
updateComment,
composerHeight,
@@ -116,7 +116,6 @@ function Suggestions({
ref={suggestionEmojiRef}
// eslint-disable-next-line react/jsx-props-no-spreading
{...baseProps}
- selection={selection}
onInsertedEmoji={onInsertedEmoji}
resetKeyboardInput={resetKeyboardInput}
/>
diff --git a/src/pages/home/report/ReportActionCompose/suggestionProps.js b/src/pages/home/report/ReportActionCompose/suggestionProps.js
index 24cf51b018c4..12447929b980 100644
--- a/src/pages/home/report/ReportActionCompose/suggestionProps.js
+++ b/src/pages/home/report/ReportActionCompose/suggestionProps.js
@@ -7,6 +7,12 @@ const baseProps = {
/** Callback to update the current input value */
setValue: PropTypes.func.isRequired,
+ /** The current selection value */
+ selection: PropTypes.shape({
+ start: PropTypes.number.isRequired,
+ end: PropTypes.number.isRequired,
+ }).isRequired,
+
/** Callback to update the current selection */
setSelection: PropTypes.func.isRequired,
diff --git a/src/pages/home/report/ReportActionItem.js b/src/pages/home/report/ReportActionItem.js
index fae5c518bbfe..8cf8fd78371d 100644
--- a/src/pages/home/report/ReportActionItem.js
+++ b/src/pages/home/report/ReportActionItem.js
@@ -67,6 +67,7 @@ import * as BankAccounts from '../../../libs/actions/BankAccounts';
import usePrevious from '../../../hooks/usePrevious';
import ReportScreenContext from '../ReportScreenContext';
import Permissions from '../../../libs/Permissions';
+import RenderHTML from '../../../components/RenderHTML';
import ReportAttachmentsContext from './ReportAttachmentsContext';
const propTypes = {
@@ -520,6 +521,22 @@ function ReportActionItem(props) {
);
}
if (ReportUtils.isTaskReport(props.report)) {
+ if (ReportUtils.isCanceledTaskReport(props.report, parentReportAction)) {
+ return (
+ <>
+
+ ${props.translate('parentReportAction.deletedTask')}`} />
+
+
+ >
+ );
+ }
+
return (
-
+
{(hovered) => (
{props.shouldDisplayNewMarker && }
@@ -603,6 +623,7 @@ function ReportActionItem(props) {
errors={props.action.errors}
errorRowStyles={[styles.ml10, styles.mr2]}
needsOffscreenAlphaCompositing={ReportActionsUtils.isMoneyRequestAction(props.action)}
+ shouldDisableStrikeThrough
>
{isWhisper && (
diff --git a/src/pages/home/report/ReportActionItemFragment.js b/src/pages/home/report/ReportActionItemFragment.js
index d768fcacd5b7..1f61b44841bc 100644
--- a/src/pages/home/report/ReportActionItemFragment.js
+++ b/src/pages/home/report/ReportActionItemFragment.js
@@ -16,7 +16,6 @@ import compose from '../../../libs/compose';
import convertToLTR from '../../../libs/convertToLTR';
import {withNetwork} from '../../../components/OnyxProvider';
import CONST from '../../../CONST';
-import applyStrikethrough from '../../../components/HTMLEngineProvider/applyStrikethrough';
import editedLabelStyles from '../../../styles/editedLabelStyles';
import UserDetailsTooltip from '../../../components/UserDetailsTooltip';
import avatarPropTypes from '../../../components/avatarPropTypes';
@@ -112,6 +111,7 @@ function ReportActionItemFragment(props) {
);
}
const {html, text} = props.fragment;
+ const isPendingDelete = props.pendingAction === CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE && props.network.isOffline;
// Threaded messages display "[Deleted message]" instead of being hidden altogether.
// While offline we display the previous message with a strikethrough style. Once online we want to
@@ -128,34 +128,40 @@ function ReportActionItemFragment(props) {
// Only render HTML if we have html in the fragment
if (!differByLineBreaksOnly) {
- const isPendingDelete = props.pendingAction === CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE && props.network.isOffline;
const editedTag = props.fragment.isEdited ? `` : '';
- const htmlContent = applyStrikethrough(html + editedTag, isPendingDelete);
+ const htmlContent = isPendingDelete ? `${html}` : html;
- return ${htmlContent}` : `${htmlContent}`} />;
+ const htmlWithTag = editedTag ? `${htmlContent}${editedTag}` : htmlContent;
+
+ return ${htmlWithTag}` : `${htmlWithTag}`} />;
}
const containsOnlyEmojis = EmojiUtils.containsOnlyEmojis(text);
return (
-
- {convertToLTR(props.iouMessage || text)}
+
+
+ {convertToLTR(props.iouMessage || text)}
+
{Boolean(props.fragment.isEdited) && (
-
+ <>
{' '}
- {props.translate('reportActionCompose.edited')}
-
+
+ {props.translate('reportActionCompose.edited')}
+
+ >
)}
);
diff --git a/src/pages/home/report/ReportActionItemParentAction.js b/src/pages/home/report/ReportActionItemParentAction.js
index 68c5163643b5..8e1fb6aafdd4 100644
--- a/src/pages/home/report/ReportActionItemParentAction.js
+++ b/src/pages/home/report/ReportActionItemParentAction.js
@@ -1,5 +1,5 @@
import React from 'react';
-import {View} from 'react-native';
+import {View, Image} from 'react-native';
import lodashGet from 'lodash/get';
import {withOnyx} from 'react-native-onyx';
import PropTypes from 'prop-types';
@@ -15,6 +15,7 @@ import withLocalize from '../../../components/withLocalize';
import ReportActionItem from './ReportActionItem';
import reportActionPropTypes from './reportActionPropTypes';
import * as ReportActionsUtils from '../../../libs/ReportActionsUtils';
+import EmptyStateBackgroundImage from '../../../../assets/images/empty-state_background-fade.png';
const propTypes = {
/** Flag to show, hide the thread divider line */
@@ -60,6 +61,11 @@ function ReportActionItemParentAction(props) {
onClose={() => Report.navigateToConciergeChatAndDeleteReport(props.report.reportID)}
>
+
{parentReportAction && (
{`${numberOfRepliesText} ${replyText}`}
@@ -69,6 +70,7 @@ function ReportActionItemThread(props) {
selectable={false}
numberOfLines={1}
style={[styles.ml2, styles.textMicroSupporting, styles.flex1]}
+ dataSet={{[CONST.SELECTION_SCRAPER_HIDDEN_ELEMENT]: true}}
>
{timeStamp}
diff --git a/src/pages/home/report/ReportActionsList.js b/src/pages/home/report/ReportActionsList.js
index 7f897ee825fb..19270ff13f45 100644
--- a/src/pages/home/report/ReportActionsList.js
+++ b/src/pages/home/report/ReportActionsList.js
@@ -74,6 +74,12 @@ const MSG_VISIBLE_THRESHOLD = 250;
// the useRef value gets reset when the reportID changes, so we use a global variable to keep track
let prevReportID = null;
+// In the component we are subscribing to the arrival of new actions.
+// As there is the possibility that there are multiple instances of a ReportScreen
+// for the same report, we only ever want one subscription to be active, as
+// the subscriptions could otherwise be conflicting.
+const newActionUnsubscribeMap = {};
+
/**
* Create a unique key for each action in the FlatList.
* We use the reportActionID that is a string representation of a random 64-bit int, which should be
@@ -109,7 +115,7 @@ function ReportActionsList({
const {isOffline} = useNetwork();
const opacity = useSharedValue(0);
const userActiveSince = useRef(null);
- const currentUnreadMarker = useRef(null);
+ const [currentUnreadMarker, setCurrentUnreadMarker] = useState(null);
const scrollingVerticalOffset = useRef(0);
const readActionSkipped = useRef(false);
const reportActionSize = useRef(sortedReportActions.length);
@@ -152,12 +158,12 @@ function ReportActionsList({
}
}
- if (currentUnreadMarker.current || reportActionSize.current === sortedReportActions.length) {
+ if (currentUnreadMarker || reportActionSize.current === sortedReportActions.length) {
return;
}
reportActionSize.current = sortedReportActions.length;
- currentUnreadMarker.current = null;
+ setCurrentUnreadMarker(null);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [sortedReportActions.length, report.reportID]);
@@ -169,18 +175,56 @@ function ReportActionsList({
}
// Clearing the current unread marker so that it can be recalculated
- currentUnreadMarker.current = null;
+ setCurrentUnreadMarker(null);
setMessageManuallyMarked({read: true});
// We only care when a new lastReadTime is set in the report
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [report.lastReadTime]);
+ useEffect(() => {
+ // Why are we doing this, when in the cleanup of the useEffect we are already calling the unsubscribe function?
+ // Answer: On web, when navigating to another report screen, the previous report screen doesn't get unmounted,
+ // meaning that the cleanup might not get called. When we then open a report we had open already previosuly, a new
+ // ReportScreen will get created. Thus, we have to cancel the earlier subscription of the previous screen,
+ // because the two subscriptions could conflict!
+ // In case we return to the previous screen (e.g. by web back navigation) the useEffect for that screen would
+ // fire again, as the focus has changed and will set up the subscription correctly again.
+ const previousSubUnsubscribe = newActionUnsubscribeMap[report.reportID];
+ if (previousSubUnsubscribe) {
+ previousSubUnsubscribe();
+ }
+
+ // This callback is triggered when a new action arrives via Pusher and the event is emitted from Report.js. This allows us to maintain
+ // a single source of truth for the "new action" event instead of trying to derive that a new action has appeared from looking at props.
+ const unsubscribe = Report.subscribeToNewActionEvent(report.reportID, (isFromCurrentUser) => {
+ // If a new comment is added and it's from the current user scroll to the bottom otherwise leave the user positioned where
+ // they are now in the list.
+ if (!isFromCurrentUser) {
+ return;
+ }
+ reportScrollManager.scrollToBottom();
+ });
+
+ const cleanup = () => {
+ if (unsubscribe) {
+ unsubscribe();
+ }
+ Report.unsubscribeFromReportChannel(report.reportID);
+ };
+
+ newActionUnsubscribeMap[report.reportID] = cleanup;
+
+ return cleanup;
+
+ // eslint-disable-next-line react-hooks/exhaustive-deps
+ }, [report.reportID]);
+
/**
* Show/hide the new floating message counter when user is scrolling back/forth in the history of messages.
*/
const handleUnreadFloatingButton = () => {
- if (scrollingVerticalOffset.current > VERTICAL_OFFSET_THRESHOLD && !isFloatingMessageCounterVisible && !!currentUnreadMarker.current) {
+ if (scrollingVerticalOffset.current > VERTICAL_OFFSET_THRESHOLD && !isFloatingMessageCounterVisible && !!currentUnreadMarker) {
setIsFloatingMessageCounterVisible(true);
}
@@ -216,6 +260,15 @@ function ReportActionsList({
return Math.ceil(availableHeight / minimumReportActionHeight);
}, [windowHeight]);
+ /**
+ * Thread's divider line should hide when the first chat in the thread is marked as unread.
+ * This is so that it will not be conflicting with header's separator line.
+ */
+ const shouldHideThreadDividerLine = useMemo(
+ () => sortedReportActions.length > 1 && sortedReportActions[sortedReportActions.length - 2].reportActionID === currentUnreadMarker,
+ [sortedReportActions, currentUnreadMarker],
+ );
+
/**
* @param {Object} args
* @param {Number} args.index
@@ -225,7 +278,7 @@ function ReportActionsList({
({item: reportAction, index}) => {
let shouldDisplayNewMarker = false;
- if (!currentUnreadMarker.current) {
+ if (!currentUnreadMarker) {
const nextMessage = sortedReportActions[index + 1];
const isCurrentMessageUnread = isMessageUnread(reportAction, report.lastReadTime);
shouldDisplayNewMarker = isCurrentMessageUnread && !isMessageUnread(nextMessage, report.lastReadTime);
@@ -235,18 +288,17 @@ function ReportActionsList({
}
const canDisplayMarker = scrollingVerticalOffset.current < MSG_VISIBLE_THRESHOLD ? reportAction.created < userActiveSince.current : true;
- if (!currentUnreadMarker.current && shouldDisplayNewMarker && canDisplayMarker) {
- currentUnreadMarker.current = reportAction.reportActionID;
+ if (!currentUnreadMarker && shouldDisplayNewMarker && canDisplayMarker) {
+ setCurrentUnreadMarker(reportAction.reportActionID);
}
} else {
- shouldDisplayNewMarker = reportAction.reportActionID === currentUnreadMarker.current;
+ shouldDisplayNewMarker = reportAction.reportActionID === currentUnreadMarker;
}
const shouldDisplayParentAction =
reportAction.actionName === CONST.REPORT.ACTIONS.TYPE.CREATED &&
ReportUtils.isChatThread(report) &&
!ReportActionsUtils.isTransactionThread(ReportActionsUtils.getParentReportAction(report));
- const shouldHideThreadDividerLine = sortedReportActions.length > 1 && sortedReportActions[sortedReportActions.length - 2].reportActionID === currentUnreadMarker.current;
return shouldDisplayParentAction ? (
);
},
- [report, hasOutstandingIOU, sortedReportActions, mostRecentIOUReportActionID, messageManuallyMarked],
+ [report, hasOutstandingIOU, sortedReportActions, mostRecentIOUReportActionID, messageManuallyMarked, shouldHideThreadDividerLine, currentUnreadMarker],
);
// Native mobile does not render updates flatlist the changes even though component did update called.
// To notify there something changes we can use extraData prop to flatlist
- const extraData = [isSmallScreenWidth ? currentUnreadMarker.current : undefined, ReportUtils.isArchivedRoom(report)];
+ const extraData = [isSmallScreenWidth ? currentUnreadMarker : undefined, ReportUtils.isArchivedRoom(report)];
const hideComposer = ReportUtils.shouldDisableWriteActions(report);
const shouldShowReportRecipientLocalTime = ReportUtils.canShowReportRecipientLocalTime(personalDetailsList, report, currentUserPersonalDetails.accountID) && !isComposerFullSize;
return (
<>
{
- const reportIDs = SidebarUtils.getOrderedReportIDs(currentReportID, chatReports, betas, policies, priorityMode, allReportActions);
+ const reportIDs = SidebarUtils.getOrderedReportIDs(null, chatReports, betas, policies, priorityMode, allReportActions);
if (deepEqual(reportIDsRef.current, reportIDs)) {
return reportIDsRef.current;
}
// We need to update existing reports only once while loading because they are updated several times during loading and causes this regression: https://github.com/Expensify/App/issues/24596#issuecomment-1681679531
- if (!isLoading || !reportIDsRef.current || (_.isEmpty(reportIDsRef.current) && currentReportID)) {
+ if (!isLoading || !reportIDsRef.current) {
reportIDsRef.current = reportIDs;
}
return reportIDsRef.current || [];
- }, [allReportActions, betas, chatReports, currentReportID, policies, priorityMode, isLoading]);
+ }, [allReportActions, betas, chatReports, policies, priorityMode, isLoading]);
+
+ // We need to make sure the current report is in the list of reports, but we do not want
+ // to have to re-generate the list every time the currentReportID changes. To do that
+ // we first generate the list as if there was no current report, then here we check if
+ // the current report is missing from the list, which should very rarely happen. In this
+ // case we re-generate the list a 2nd time with the current report included.
+ const optionListItemsWithCurrentReport = useMemo(() => {
+ if (currentReportID && !_.contains(optionListItems, currentReportID)) {
+ return SidebarUtils.getOrderedReportIDs(currentReportID, chatReports, betas, policies, priorityMode, allReportActions);
+ }
+ return optionListItems;
+ }, [currentReportID, optionListItems, chatReports, betas, policies, priorityMode, allReportActions]);
const currentReportIDRef = useRef(currentReportID);
currentReportIDRef.current = currentReportID;
@@ -100,7 +112,7 @@ function SidebarLinksData({isFocused, allReportActions, betas, chatReports, curr
// Data props:
isActiveReport={isActiveReport}
isLoading={isLoading}
- optionListItems={optionListItems}
+ optionListItems={optionListItemsWithCurrentReport}
/>
);
diff --git a/src/pages/home/sidebar/SidebarScreen/BaseSidebarScreen.js b/src/pages/home/sidebar/SidebarScreen/BaseSidebarScreen.js
index 16cc3f2da458..3d54306b6248 100644
--- a/src/pages/home/sidebar/SidebarScreen/BaseSidebarScreen.js
+++ b/src/pages/home/sidebar/SidebarScreen/BaseSidebarScreen.js
@@ -34,7 +34,6 @@ function BaseSidebarScreen(props) {
includeSafeAreaPaddingBottom={false}
shouldEnableKeyboardAvoidingView={false}
style={[styles.sidebar, Browser.isMobile() ? styles.userSelectNone : {}]}
- shouldDisableFocusTrap
>
{({insets}) => (
<>
diff --git a/src/pages/home/sidebar/SidebarScreen/FloatingActionButtonAndPopover.js b/src/pages/home/sidebar/SidebarScreen/FloatingActionButtonAndPopover.js
index 418b7c89aa91..e9ede2c9a89a 100644
--- a/src/pages/home/sidebar/SidebarScreen/FloatingActionButtonAndPopover.js
+++ b/src/pages/home/sidebar/SidebarScreen/FloatingActionButtonAndPopover.js
@@ -5,7 +5,6 @@ import lodashGet from 'lodash/get';
import {View} from 'react-native';
import styles from '../../../../styles/styles';
import * as Expensicons from '../../../../components/Icon/Expensicons';
-import * as Browser from '../../../../libs/Browser';
import Navigation from '../../../../libs/Navigation/Navigation';
import ROUTES from '../../../../ROUTES';
import NAVIGATORS from '../../../../NAVIGATORS';
@@ -61,9 +60,6 @@ const propTypes = {
/** Indicated whether the report data is loading */
isLoading: PropTypes.bool,
- /** For first time users, whether the download app banner should show */
- shouldShowDownloadAppBanner: PropTypes.bool,
-
/** Forwarded ref to FloatingActionButtonAndPopover */
innerRef: PropTypes.oneOfType([PropTypes.func, PropTypes.object]),
};
@@ -74,7 +70,6 @@ const defaultProps = {
betas: [],
isLoading: false,
innerRef: null,
- shouldShowDownloadAppBanner: true,
};
/**
@@ -157,12 +152,9 @@ function FloatingActionButtonAndPopover(props) {
if (currentRoute && ![NAVIGATORS.CENTRAL_PANE_NAVIGATOR, SCREENS.HOME].includes(currentRoute.name)) {
return;
}
- // Avoid rendering the create menu for first-time users until they have dismissed the download app banner (mWeb only).
- if (props.shouldShowDownloadAppBanner && Browser.isMobile()) {
- return;
- }
Welcome.show({routes, showCreateMenu});
- }, [props.shouldShowDownloadAppBanner, props.navigation, showCreateMenu, props.demoInfo]);
+ // eslint-disable-next-line react-hooks/exhaustive-deps
+ }, []);
useEffect(() => {
if (!didScreenBecomeInactive()) {
@@ -207,11 +199,6 @@ function FloatingActionButtonAndPopover(props) {
text: props.translate('iou.requestMoney'),
onSelected: () => interceptAnonymousUser(() => IOU.startMoneyRequest(CONST.IOU.MONEY_REQUEST_TYPE.REQUEST)),
},
- {
- icon: Expensicons.Heart,
- text: props.translate('sidebarScreen.saveTheWorld'),
- onSelected: () => interceptAnonymousUser(() => Navigation.navigate(ROUTES.TEACHERS_UNITE)),
- },
...(Permissions.canUseTasks(props.betas)
? [
{
@@ -274,9 +261,6 @@ export default compose(
isLoading: {
key: ONYXKEYS.IS_LOADING_REPORT_DATA,
},
- shouldShowDownloadAppBanner: {
- key: ONYXKEYS.SHOW_DOWNLOAD_APP_BANNER,
- },
}),
)(
forwardRef((props, ref) => (
diff --git a/src/pages/home/sidebar/SidebarScreen/index.js b/src/pages/home/sidebar/SidebarScreen/index.js
index 705d9d1e2d08..08888352ddff 100755
--- a/src/pages/home/sidebar/SidebarScreen/index.js
+++ b/src/pages/home/sidebar/SidebarScreen/index.js
@@ -23,18 +23,28 @@ function SidebarScreen(props) {
}, []),
);
+ /**
+ * Method to hide popover when dragover.
+ */
+ const hidePopoverOnDragOver = useCallback(() => {
+ if (!popoverModal.current) {
+ return;
+ }
+ popoverModal.current.hideCreateMenu();
+ }, []);
+
/**
* Method create event listener
*/
const createDragoverListener = () => {
- document.addEventListener('dragover', () => popoverModal.current.hideCreateMenu());
+ document.addEventListener('dragover', hidePopoverOnDragOver);
};
/**
* Method remove event listener.
*/
const removeDragoverListener = () => {
- document.removeEventListener('dragover', () => popoverModal.current.hideCreateMenu());
+ document.removeEventListener('dragover', hidePopoverOnDragOver);
};
return (
diff --git a/src/pages/iou/IOUCurrencySelection.js b/src/pages/iou/IOUCurrencySelection.js
index 80f4a5c17e23..1238d8934f75 100644
--- a/src/pages/iou/IOUCurrencySelection.js
+++ b/src/pages/iou/IOUCurrencySelection.js
@@ -83,7 +83,7 @@ function IOUCurrencySelection(props) {
// Navigating to "backTo" will result in forward navigation instead, causing disruption to the currency selection.
// To prevent any negative experience, we have made the decision to simply close the currency selection page.
if (_.isEmpty(backTo) || props.navigation.getState().routes.length === 1) {
- Navigation.goBack();
+ Navigation.goBack(ROUTES.HOME);
} else {
Navigation.navigate(`${props.route.params.backTo}?currency=${option.currencyCode}`);
}
@@ -104,7 +104,7 @@ function IOUCurrencySelection(props) {
};
});
- const searchRegex = new RegExp(Str.escapeForRegExp(searchValue), 'i');
+ const searchRegex = new RegExp(Str.escapeForRegExp(searchValue.trim()), 'i');
const filteredCurrencies = _.filter(currencyOptions, (currencyOption) => searchRegex.test(currencyOption.text));
const isEmpty = searchValue.trim() && !filteredCurrencies.length;
diff --git a/src/pages/iou/MoneyRequestCategoryPage.js b/src/pages/iou/MoneyRequestCategoryPage.js
index 80b88a762609..92934a505a04 100644
--- a/src/pages/iou/MoneyRequestCategoryPage.js
+++ b/src/pages/iou/MoneyRequestCategoryPage.js
@@ -10,6 +10,8 @@ import HeaderWithBackButton from '../../components/HeaderWithBackButton';
import CategoryPicker from '../../components/CategoryPicker';
import ONYXKEYS from '../../ONYXKEYS';
import reportPropTypes from '../reportPropTypes';
+import * as IOU from '../../libs/actions/IOU';
+import {iouPropTypes, iouDefaultProps} from './propTypes';
const propTypes = {
/** Navigation route context info provided by react navigation */
@@ -24,15 +26,20 @@ const propTypes = {
}),
}).isRequired,
+ /* Onyx Props */
/** The report currently being used */
report: reportPropTypes,
+
+ /** Holds data related to Money Request view state, rather than the underlying Money Request data. */
+ iou: iouPropTypes,
};
const defaultProps = {
report: {},
+ iou: iouDefaultProps,
};
-function MoneyRequestCategoryPage({route, report}) {
+function MoneyRequestCategoryPage({route, report, iou}) {
const {translate} = useLocalize();
const reportID = lodashGet(route, 'params.reportID', '');
@@ -42,6 +49,16 @@ function MoneyRequestCategoryPage({route, report}) {
Navigation.goBack(ROUTES.getMoneyRequestConfirmationRoute(iouType, reportID));
};
+ const updateCategory = (category) => {
+ if (category.searchText === iou.category) {
+ IOU.resetMoneyRequestCategory();
+ } else {
+ IOU.setMoneyRequestCategory(category.searchText);
+ }
+
+ Navigation.goBack(ROUTES.getMoneyRequestConfirmationRoute(iouType, reportID));
+ };
+
return (
);
@@ -69,4 +86,7 @@ export default withOnyx({
report: {
key: ({route}) => `${ONYXKEYS.COLLECTION.REPORT}${lodashGet(route, 'params.reportID', '')}`,
},
+ iou: {
+ key: ONYXKEYS.IOU,
+ },
})(MoneyRequestCategoryPage);
diff --git a/src/pages/iou/MoneyRequestTagPage.js b/src/pages/iou/MoneyRequestTagPage.js
index a1795d50df8a..60be6035431c 100644
--- a/src/pages/iou/MoneyRequestTagPage.js
+++ b/src/pages/iou/MoneyRequestTagPage.js
@@ -5,6 +5,7 @@ import lodashGet from 'lodash/get';
import {withOnyx} from 'react-native-onyx';
import compose from '../../libs/compose';
import ROUTES from '../../ROUTES';
+import * as IOU from '../../libs/actions/IOU';
import Navigation from '../../libs/Navigation/Navigation';
import useLocalize from '../../hooks/useLocalize';
import ScreenWrapper from '../../components/ScreenWrapper';
@@ -15,6 +16,7 @@ import tagPropTypes from '../../components/tagPropTypes';
import ONYXKEYS from '../../ONYXKEYS';
import reportPropTypes from '../reportPropTypes';
import styles from '../../styles/styles';
+import {iouPropTypes, iouDefaultProps} from './propTypes';
const propTypes = {
/** Navigation route context info provided by react navigation */
@@ -40,14 +42,18 @@ const propTypes = {
tags: PropTypes.objectOf(tagPropTypes),
}),
),
+
+ /** Holds data related to Money Request view state, rather than the underlying Money Request data. */
+ iou: iouPropTypes,
};
const defaultProps = {
report: {},
policyTags: {},
+ iou: iouDefaultProps,
};
-function MoneyRequestTagPage({route, report, policyTags}) {
+function MoneyRequestTagPage({route, report, policyTags, iou}) {
const {translate} = useLocalize();
const iouType = lodashGet(route, 'params.iouType', '');
@@ -55,10 +61,19 @@ function MoneyRequestTagPage({route, report, policyTags}) {
// Fetches the first tag list of the policy
const tagListKey = _.first(_.keys(policyTags));
const tagList = lodashGet(policyTags, tagListKey, {});
- const tagListName = lodashGet(tagList, 'name', '');
+ const tagListName = lodashGet(tagList, 'name', translate('common.tag'));
const navigateBack = () => {
- Navigation.goBack(ROUTES.getMoneyRequestConfirmationRoute(iouType, lodashGet(report, 'reportID', '')));
+ Navigation.goBack(ROUTES.getMoneyRequestConfirmationRoute(iouType, report.reportID));
+ };
+
+ const updateTag = (selectedTag) => {
+ if (selectedTag.searchText === iou.tag) {
+ IOU.resetMoneyRequestTag();
+ } else {
+ IOU.setMoneyRequestTag(selectedTag.searchText);
+ }
+ navigateBack();
};
return (
@@ -67,15 +82,15 @@ function MoneyRequestTagPage({route, report, policyTags}) {
shouldEnableMaxHeight
>
- {translate('iou.tagSelection', {tagListName} || translate('common.tag'))}
+ {translate('iou.tagSelection', {tagName: tagListName})}
);
@@ -87,16 +102,14 @@ MoneyRequestTagPage.defaultProps = defaultProps;
export default compose(
withOnyx({
+ report: {
+ key: ({route}) => `${ONYXKEYS.COLLECTION.REPORT}${lodashGet(route, 'params.reportID')}`,
+ },
iou: {
key: ONYXKEYS.IOU,
},
}),
- withOnyx({
- report: {
- // Fetch report ID from IOU participants if no report ID is set in route
- key: ({route, iou}) => `${ONYXKEYS.COLLECTION.REPORT}${lodashGet(route, 'params.reportID', '') || lodashGet(iou, 'participants.0.reportID', '')}`,
- },
- }),
+ // eslint-disable-next-line rulesdir/no-multiple-onyx-in-file
withOnyx({
policyTags: {
key: ({report}) => `${ONYXKEYS.COLLECTION.POLICY_TAGS}${report ? report.policyID : '0'}`,
diff --git a/src/pages/iou/ReceiptSelector/index.js b/src/pages/iou/ReceiptSelector/index.js
index 3f5672c3d33d..eb6e2328afd2 100644
--- a/src/pages/iou/ReceiptSelector/index.js
+++ b/src/pages/iou/ReceiptSelector/index.js
@@ -1,6 +1,7 @@
import {View, Text, PixelRatio} from 'react-native';
import React, {useContext, useState} from 'react';
import lodashGet from 'lodash/get';
+import _ from 'underscore';
import PropTypes from 'prop-types';
import {withOnyx} from 'react-native-onyx';
import * as IOU from '../../../libs/actions/IOU';
@@ -15,21 +16,14 @@ import ReceiptDropUI from '../ReceiptDropUI';
import AttachmentPicker from '../../../components/AttachmentPicker';
import ConfirmModal from '../../../components/ConfirmModal';
import ONYXKEYS from '../../../ONYXKEYS';
-import Receipt from '../../../libs/actions/Receipt';
import useWindowDimensions from '../../../hooks/useWindowDimensions';
import useLocalize from '../../../hooks/useLocalize';
import {DragAndDropContext} from '../../../components/DragAndDrop/Provider';
-import * as ReceiptUtils from '../../../libs/ReceiptUtils';
import {iouPropTypes, iouDefaultProps} from '../propTypes';
+import * as FileUtils from '../../../libs/fileDownload/FileUtils';
+import Navigation from '../../../libs/Navigation/Navigation';
const propTypes = {
- /** Information shown to the user when a receipt is not valid */
- receiptModal: PropTypes.shape({
- isAttachmentInvalid: PropTypes.bool,
- attachmentInvalidReasonTitle: PropTypes.string,
- attachmentInvalidReason: PropTypes.string,
- }),
-
/** The report on which the request is initiated on */
report: reportPropTypes,
@@ -47,29 +41,64 @@ const propTypes = {
/** Holds data related to Money Request view state, rather than the underlying Money Request data. */
iou: iouPropTypes,
+
+ /** The id of the transaction we're editing */
+ transactionID: PropTypes.string,
};
const defaultProps = {
- receiptModal: {
- isAttachmentInvalid: false,
- attachmentInvalidReasonTitle: '',
- attachmentInvalidReason: '',
- },
report: {},
iou: iouDefaultProps,
+ transactionID: '',
};
function ReceiptSelector(props) {
const reportID = lodashGet(props.route, 'params.reportID', '');
const iouType = lodashGet(props.route, 'params.iouType', '');
- const isAttachmentInvalid = lodashGet(props.receiptModal, 'isAttachmentInvalid', false);
- const attachmentInvalidReasonTitle = lodashGet(props.receiptModal, 'attachmentInvalidReasonTitle', '');
- const attachmentInvalidReason = lodashGet(props.receiptModal, 'attachmentInvalidReason', '');
+ const [isAttachmentInvalid, setIsAttachmentInvalid] = useState(false);
+ const [attachmentInvalidReasonTitle, setAttachmentInvalidReasonTitle] = useState('');
+ const [attachmentInvalidReason, setAttachmentValidReason] = useState('');
const [receiptImageTopPosition, setReceiptImageTopPosition] = useState(0);
const {isSmallScreenWidth} = useWindowDimensions();
const {translate} = useLocalize();
const {isDraggingOver} = useContext(DragAndDropContext);
+ const hideReciptModal = () => {
+ setIsAttachmentInvalid(false);
+ };
+
+ /**
+ * Sets the upload receipt error modal content when an invalid receipt is uploaded
+ * @param {*} isInvalid
+ * @param {*} title
+ * @param {*} reason
+ */
+ const setUploadReceiptError = (isInvalid, title, reason) => {
+ setIsAttachmentInvalid(isInvalid);
+ setAttachmentInvalidReasonTitle(title);
+ setAttachmentValidReason(reason);
+ };
+
+ function validateReceipt(file) {
+ const {fileExtension} = FileUtils.splitExtensionFromFileName(lodashGet(file, 'name', ''));
+ if (_.contains(CONST.API_ATTACHMENT_VALIDATIONS.UNALLOWED_EXTENSIONS, fileExtension.toLowerCase())) {
+ setUploadReceiptError(true, 'attachmentPicker.wrongFileType', 'attachmentPicker.notAllowedExtension');
+ return false;
+ }
+
+ if (lodashGet(file, 'size', 0) > CONST.API_ATTACHMENT_VALIDATIONS.MAX_SIZE) {
+ setUploadReceiptError(true, 'attachmentPicker.attachmentTooLarge', 'attachmentPicker.sizeExceeded');
+ return false;
+ }
+
+ if (lodashGet(file, 'size', 0) < CONST.API_ATTACHMENT_VALIDATIONS.MIN_SIZE) {
+ setUploadReceiptError(true, 'attachmentPicker.attachmentTooSmall', 'attachmentPicker.sizeNotMet');
+ return false;
+ }
+
+ return true;
+ }
+
/**
* Sets the Receipt objects and navigates the user to the next page
* @param {Object} file
@@ -77,12 +106,19 @@ function ReceiptSelector(props) {
* @param {Object} report
*/
const setReceiptAndNavigate = (file, iou, report) => {
- if (!ReceiptUtils.validateReceipt(file)) {
+ if (!validateReceipt(file)) {
return;
}
const filePath = URL.createObjectURL(file);
IOU.setMoneyRequestReceipt(filePath, file.name);
+
+ if (props.transactionID) {
+ IOU.replaceReceipt(props.transactionID, file, filePath);
+ Navigation.dismissModal();
+ return;
+ }
+
IOU.navigateToNextPage(iou, iouType, reportID, report);
};
@@ -142,8 +178,8 @@ function ReceiptSelector(props) {
/>
`${ONYXKEYS.COLLECTION.REPORT}${lodashGet(route, 'params.reportID', '')}`,
},
diff --git a/src/pages/iou/ReceiptSelector/index.native.js b/src/pages/iou/ReceiptSelector/index.native.js
index 4ff32d940c9f..36f883ad08e5 100644
--- a/src/pages/iou/ReceiptSelector/index.native.js
+++ b/src/pages/iou/ReceiptSelector/index.native.js
@@ -23,6 +23,8 @@ import Log from '../../../libs/Log';
import * as CameraPermission from './CameraPermission';
import {iouPropTypes, iouDefaultProps} from '../propTypes';
import NavigationAwareCamera from './NavigationAwareCamera';
+import Navigation from '../../../libs/Navigation/Navigation';
+import * as FileUtils from '../../../libs/fileDownload/FileUtils';
const propTypes = {
/** React Navigation route */
@@ -42,11 +44,15 @@ const propTypes = {
/** Holds data related to Money Request view state, rather than the underlying Money Request data. */
iou: iouPropTypes,
+
+ /** The id of the transaction we're editing */
+ transactionID: PropTypes.string,
};
const defaultProps = {
report: {},
iou: iouDefaultProps,
+ transactionID: '',
};
/**
@@ -74,7 +80,7 @@ function getImagePickerOptions(type) {
};
}
-function ReceiptSelector(props) {
+function ReceiptSelector({route, report, iou, transactionID}) {
const devices = useCameraDevices('wide-angle-camera');
const device = devices.back;
@@ -84,9 +90,9 @@ function ReceiptSelector(props) {
const isAndroidBlockedPermissionRef = useRef(false);
const appState = useRef(AppState.currentState);
- const iouType = lodashGet(props.route, 'params.iouType', '');
- const reportID = lodashGet(props.route, 'params.reportID', '');
- const pageIndex = lodashGet(props.route, 'params.pageIndex', 1);
+ const iouType = lodashGet(route, 'params.iouType', '');
+ const reportID = lodashGet(route, 'params.reportID', '');
+ const pageIndex = lodashGet(route, 'params.pageIndex', 1);
const {translate} = useLocalize();
@@ -195,14 +201,25 @@ function ReceiptSelector(props) {
flash: flash ? 'on' : 'off',
})
.then((photo) => {
- IOU.setMoneyRequestReceipt(`file://${photo.path}`, photo.path);
- IOU.navigateToNextPage(props.iou, iouType, reportID, props.report);
+ const filePath = `file://${photo.path}`;
+ IOU.setMoneyRequestReceipt(filePath, photo.path);
+
+ if (transactionID) {
+ FileUtils.readFileAsync(filePath, photo.path).then((receipt) => {
+ IOU.replaceReceipt(transactionID, receipt, filePath);
+ });
+
+ Navigation.dismissModal();
+ return;
+ }
+
+ IOU.navigateToNextPage(iou, iouType, reportID, report);
})
.catch((error) => {
showCameraAlert();
Log.warn('Error taking photo', error);
});
- }, [flash, iouType, props.iou, props.report, reportID, translate]);
+ }, [flash, iouType, iou, report, reportID, translate, transactionID]);
CameraPermission.getCameraPermissionStatus().then((permissionStatus) => {
setPermissions(permissionStatus);
@@ -260,8 +277,18 @@ function ReceiptSelector(props) {
onPress={() => {
showImagePicker(launchImageLibrary)
.then((receiptImage) => {
- IOU.setMoneyRequestReceipt(receiptImage[0].uri, receiptImage[0].fileName);
- IOU.navigateToNextPage(props.iou, iouType, reportID, props.report);
+ const filePath = receiptImage[0].uri;
+ IOU.setMoneyRequestReceipt(filePath, receiptImage[0].fileName);
+
+ if (transactionID) {
+ FileUtils.readFileAsync(filePath, receiptImage[0].fileName).then((receipt) => {
+ IOU.replaceReceipt(transactionID, receipt, filePath);
+ });
+ Navigation.dismissModal();
+ return;
+ }
+
+ IOU.navigateToNextPage(iou, iouType, reportID, report);
})
.catch(() => {
Log.info('User did not select an image from gallery');
diff --git a/src/pages/iou/SplitBillDetailsPage.js b/src/pages/iou/SplitBillDetailsPage.js
index b11b16fa3c1b..0107b041abf4 100644
--- a/src/pages/iou/SplitBillDetailsPage.js
+++ b/src/pages/iou/SplitBillDetailsPage.js
@@ -61,14 +61,11 @@ function SplitBillDetailsPage(props) {
let participants;
if (ReportUtils.isPolicyExpenseChat(props.report)) {
participants = [
- ...OptionsListUtils.getParticipantsOptions([{accountID: participantAccountIDs[0], selected: true}], props.personalDetails),
- ...OptionsListUtils.getPolicyExpenseReportOptions({...props.report, selected: true}),
+ OptionsListUtils.getParticipantsOption({accountID: participantAccountIDs[0], selected: true}, props.personalDetails),
+ OptionsListUtils.getPolicyExpenseReportOption({...props.report, selected: true}),
];
} else {
- participants = OptionsListUtils.getParticipantsOptions(
- _.map(participantAccountIDs, (accountID) => ({accountID, selected: true})),
- props.personalDetails,
- );
+ participants = _.map(participantAccountIDs, (accountID) => OptionsListUtils.getParticipantsOption({accountID, selected: true}, props.personalDetails));
}
const payeePersonalDetails = props.personalDetails[reportAction.actorAccountID];
const participantsExcludingPayee = _.filter(participants, (participant) => participant.accountID !== reportAction.actorAccountID);
diff --git a/src/pages/iou/WaypointEditor.js b/src/pages/iou/WaypointEditor.js
index 68e85a34746d..e34730acccea 100644
--- a/src/pages/iou/WaypointEditor.js
+++ b/src/pages/iou/WaypointEditor.js
@@ -23,6 +23,7 @@ import * as Transaction from '../../libs/actions/Transaction';
import * as ValidationUtils from '../../libs/ValidationUtils';
import ROUTES from '../../ROUTES';
import transactionPropTypes from '../../components/transactionPropTypes';
+import * as ErrorUtils from '../../libs/ErrorUtils';
const propTypes = {
/** The transactionID of the IOU */
@@ -104,18 +105,28 @@ function WaypointEditor({transactionID, route: {params: {iouType = '', waypointI
const errors = {};
const waypointValue = values[`waypoint${waypointIndex}`] || '';
if (isOffline && waypointValue !== '' && !ValidationUtils.isValidAddress(waypointValue)) {
- errors[`waypoint${waypointIndex}`] = 'bankAccount.error.address';
+ ErrorUtils.addErrorMessage(errors, `waypoint${waypointIndex}`, 'bankAccount.error.address');
}
// If the user is online and they are trying to save a value without using the autocomplete, show an error message instructing them to use a selected address instead.
// That enables us to save the address with coordinates when it is selected
if (!isOffline && waypointValue !== '' && waypointAddress !== waypointValue) {
- errors[`waypoint${waypointIndex}`] = 'distance.errors.selectSuggestedAddress';
+ ErrorUtils.addErrorMessage(errors, `waypoint${waypointIndex}`, 'distance.errors.selectSuggestedAddress');
}
return errors;
};
+ const saveWaypoint = (waypoint) => {
+ if (parsedWaypointIndex < _.size(allWaypoints)) {
+ Transaction.saveWaypoint(transactionID, waypointIndex, waypoint);
+ } else {
+ const finishWaypoint = lodashGet(allWaypoints, `waypoint${_.size(allWaypoints) - 1}`, {});
+ Transaction.saveWaypoint(transactionID, waypointIndex, finishWaypoint);
+ Transaction.saveWaypoint(transactionID, waypointIndex - 1, waypoint);
+ }
+ };
+
const onSubmit = (values) => {
const waypointValue = values[`waypoint${waypointIndex}`] || '';
@@ -132,8 +143,7 @@ function WaypointEditor({transactionID, route: {params: {iouType = '', waypointI
lng: null,
address: waypointValue,
};
-
- Transaction.saveWaypoint(transactionID, waypointIndex, waypoint);
+ saveWaypoint(waypoint);
}
// Other flows will be handled by selecting a waypoint with selectWaypoint as this is mainly for the offline flow
@@ -152,8 +162,7 @@ function WaypointEditor({transactionID, route: {params: {iouType = '', waypointI
lng: values.lng,
address: values.address,
};
-
- Transaction.saveWaypoint(transactionID, waypointIndex, waypoint);
+ saveWaypoint(waypoint);
Navigation.goBack(ROUTES.getMoneyRequestDistanceTabRoute(iouType));
};
@@ -163,7 +172,7 @@ function WaypointEditor({transactionID, route: {params: {iouType = '', waypointI
onEntryTransitionEnd={() => textInput.current && textInput.current.focus()}
shouldEnableMaxHeight
>
- waypointCount - 1) && isFocused}>
+ waypointCount) && isFocused}>
(textInput.current = e)}
- hint={!isOffline ? translate('distance.errors.selectSuggestedAddress') : ''}
+ hint={!isOffline ? 'distance.errors.selectSuggestedAddress' : ''}
containerStyles={[styles.mt4]}
label={translate('distance.address')}
defaultValue={waypointAddress}
diff --git a/src/pages/iou/propTypes/index.js b/src/pages/iou/propTypes/index.js
index 5ecd00d11876..586f8424a2c2 100644
--- a/src/pages/iou/propTypes/index.js
+++ b/src/pages/iou/propTypes/index.js
@@ -18,6 +18,9 @@ const iouPropTypes = PropTypes.shape({
/** The merchant name */
merchant: PropTypes.string,
+ /** The category name */
+ category: PropTypes.string,
+
/** The tag */
tag: PropTypes.string,
@@ -37,6 +40,7 @@ const iouDefaultProps = {
currency: CONST.CURRENCY.USD,
comment: '',
merchant: '',
+ category: '',
tag: '',
created: '',
participants: [],
diff --git a/src/pages/iou/steps/MoneyRequestAmountForm.js b/src/pages/iou/steps/MoneyRequestAmountForm.js
index 1ea0b002b235..e08fd5bde881 100644
--- a/src/pages/iou/steps/MoneyRequestAmountForm.js
+++ b/src/pages/iou/steps/MoneyRequestAmountForm.js
@@ -12,6 +12,7 @@ import * as DeviceCapabilities from '../../../libs/DeviceCapabilities';
import TextInputWithCurrencySymbol from '../../../components/TextInputWithCurrencySymbol';
import useLocalize from '../../../hooks/useLocalize';
import CONST from '../../../CONST';
+import FormHelpMessage from '../../../components/FormHelpMessage';
import refPropTypes from '../../../components/refPropTypes';
import getOperatingSystem from '../../../libs/getOperatingSystem';
import * as Browser from '../../../libs/Browser';
@@ -57,6 +58,8 @@ const getNewSelection = (oldSelection, prevLength, newLength) => {
return {start: cursorPosition, end: cursorPosition};
};
+const isAmountValid = (amount) => !amount.length || parseFloat(amount) < 0.01;
+
const AMOUNT_VIEW_ID = 'amountView';
const NUM_PAD_CONTAINER_VIEW_ID = 'numPadContainerView';
const NUM_PAD_VIEW_ID = 'numPadView';
@@ -70,6 +73,9 @@ function MoneyRequestAmountForm({amount, currency, isEditing, forwardedRef, onCu
const selectedAmountAsString = amount ? CurrencyUtils.convertToFrontendAmount(amount).toString() : '';
const [currentAmount, setCurrentAmount] = useState(selectedAmountAsString);
+ const [isInvalidAmount, setIsInvalidAmount] = useState(isAmountValid(selectedAmountAsString));
+ const [firstPress, setFirstPress] = useState(false);
+ const [formError, setFormError] = useState('');
const [shouldUpdateSelection, setShouldUpdateSelection] = useState(true);
const [selection, setSelection] = useState({
@@ -127,6 +133,9 @@ function MoneyRequestAmountForm({amount, currency, isEditing, forwardedRef, onCu
setSelection((prevSelection) => ({...prevSelection}));
return;
}
+ const checkInvalidAmount = isAmountValid(newAmountWithoutSpaces);
+ setIsInvalidAmount(checkInvalidAmount);
+ setFormError(checkInvalidAmount ? 'iou.error.invalidAmount' : '');
setCurrentAmount((prevAmount) => {
const strippedAmount = MoneyRequestUtils.stripCommaFromAmount(newAmountWithoutSpaces);
const isForwardDelete = prevAmount.length > strippedAmount.length && forwardDeletePressedRef.current;
@@ -177,8 +186,13 @@ function MoneyRequestAmountForm({amount, currency, isEditing, forwardedRef, onCu
* Submit amount and navigate to a proper page
*/
const submitAndNavigateToNextPage = useCallback(() => {
+ if (isInvalidAmount) {
+ setFirstPress(true);
+ setFormError('iou.error.invalidAmount');
+ return;
+ }
onSubmitButtonPress(currentAmount);
- }, [onSubmitButtonPress, currentAmount]);
+ }, [onSubmitButtonPress, currentAmount, isInvalidAmount]);
/**
* Input handler to check for a forward-delete key (or keyboard shortcut) press.
@@ -231,9 +245,16 @@ function MoneyRequestAmountForm({amount, currency, isEditing, forwardedRef, onCu
onKeyPress={textInputKeyPress}
/>
+ {!_.isEmpty(formError) && firstPress && (
+
+ )}
onMouseDown(event, [NUM_PAD_CONTAINER_VIEW_ID, NUM_PAD_VIEW_ID])}
- style={[styles.w100, styles.justifyContentEnd, styles.pageWrapper]}
+ style={[styles.w100, styles.justifyContentEnd, styles.pageWrapper, styles.pt0]}
nativeID={NUM_PAD_CONTAINER_VIEW_ID}
>
{DeviceCapabilities.canUseTouchScreen() ? (
@@ -249,7 +270,6 @@ function MoneyRequestAmountForm({amount, currency, isEditing, forwardedRef, onCu
style={[styles.w100, styles.mt5]}
onPress={submitAndNavigateToNextPage}
pressOnEnter
- isDisabled={!currentAmount.length || parseFloat(currentAmount) < 0.01}
text={buttonText}
/>
diff --git a/src/pages/iou/steps/MoneyRequestConfirmPage.js b/src/pages/iou/steps/MoneyRequestConfirmPage.js
index 38a5b9c82c07..14a6cc8a7c23 100644
--- a/src/pages/iou/steps/MoneyRequestConfirmPage.js
+++ b/src/pages/iou/steps/MoneyRequestConfirmPage.js
@@ -23,6 +23,7 @@ import reportPropTypes from '../../reportPropTypes';
import personalDetailsPropType from '../../personalDetailsPropType';
import * as FileUtils from '../../../libs/fileDownload/FileUtils';
import * as Policy from '../../../libs/actions/Policy';
+import useNetwork from '../../../hooks/useNetwork';
import useWindowDimensions from '../../../hooks/useWindowDimensions';
import * as StyleUtils from '../../../styles/StyleUtils';
import {iouPropTypes, iouDefaultProps} from '../propTypes';
@@ -59,6 +60,7 @@ const defaultProps = {
};
function MoneyRequestConfirmPage(props) {
+ const {isOffline} = useNetwork();
const {windowHeight} = useWindowDimensions();
const prevMoneyRequestId = useRef(props.iou.id);
const iouType = useRef(lodashGet(props.route, 'params.iouType', ''));
@@ -66,9 +68,10 @@ function MoneyRequestConfirmPage(props) {
const reportID = useRef(lodashGet(props.route, 'params.reportID', ''));
const participants = useMemo(
() =>
- lodashGet(props.iou.participants, [0, 'isPolicyExpenseChat'], false)
- ? OptionsListUtils.getPolicyExpenseReportOptions(props.iou.participants[0])
- : OptionsListUtils.getParticipantsOptions(props.iou.participants, props.personalDetails),
+ _.map(props.iou.participants, (participant) => {
+ const isPolicyExpenseChat = lodashGet(participant, 'isPolicyExpenseChat', false);
+ return isPolicyExpenseChat ? OptionsListUtils.getPolicyExpenseReportOption(participant) : OptionsListUtils.getParticipantsOption(participant, props.personalDetails);
+ }),
[props.iou.participants, props.personalDetails],
);
@@ -77,8 +80,11 @@ function MoneyRequestConfirmPage(props) {
if (policyExpenseChat) {
Policy.openDraftWorkspaceRequest(policyExpenseChat.policyID);
}
- // eslint-disable-next-line react-hooks/exhaustive-deps
- }, []);
+ // Verification to reset billable with a default value, when value in IOU was changed
+ if (typeof props.iou.billable !== 'boolean') {
+ IOU.setMoneyRequestBillable(lodashGet(props.policy, 'defaultBillable', false));
+ }
+ }, [isOffline, participants, props.iou.billable, props.policy]);
useEffect(() => {
// ID in Onyx could change by initiating a new request in a separate browser tab or completing a request
@@ -135,6 +141,8 @@ function MoneyRequestConfirmPage(props) {
trimmedComment,
receipt,
props.iou.category,
+ props.iou.tag,
+ props.iou.billable,
);
},
[
@@ -146,6 +154,8 @@ function MoneyRequestConfirmPage(props) {
props.currentUserPersonalDetails.login,
props.currentUserPersonalDetails.accountID,
props.iou.category,
+ props.iou.tag,
+ props.iou.billable,
],
);
@@ -162,12 +172,13 @@ function MoneyRequestConfirmPage(props) {
props.iou.created,
props.iou.transactionID,
props.iou.category,
+ props.iou.tag,
props.iou.amount,
props.iou.currency,
props.iou.merchant,
);
},
- [props.report, props.iou.created, props.iou.transactionID, props.iou.category, props.iou.amount, props.iou.currency, props.iou.merchant],
+ [props.report, props.iou.created, props.iou.transactionID, props.iou.category, props.iou.tag, props.iou.amount, props.iou.currency, props.iou.merchant],
);
const createTransaction = useCallback(
@@ -246,11 +257,6 @@ function MoneyRequestConfirmPage(props) {
return;
}
- if (paymentMethodType === CONST.IOU.PAYMENT_TYPE.PAYPAL_ME) {
- IOU.sendMoneyViaPaypal(props.report, props.iou.amount, currency, trimmedComment, props.currentUserPersonalDetails.accountID, participant);
- return;
- }
-
if (paymentMethodType === CONST.IOU.PAYMENT_TYPE.EXPENSIFY) {
IOU.sendMoneyWithWallet(props.report, props.iou.amount, currency, trimmedComment, props.currentUserPersonalDetails.accountID, participant);
}
@@ -295,6 +301,8 @@ function MoneyRequestConfirmPage(props) {
iouAmount={props.iou.amount}
iouComment={props.iou.comment}
iouCurrencyCode={props.iou.currency}
+ iouIsBillable={props.iou.billable}
+ onToggleBillable={IOU.setMoneyRequestBillable}
iouCategory={props.iou.category}
iouTag={props.iou.tag}
onConfirm={createTransaction}
@@ -345,6 +353,7 @@ export default compose(
key: ONYXKEYS.IOU,
},
}),
+ // eslint-disable-next-line rulesdir/no-multiple-onyx-in-file
withOnyx({
report: {
key: ({route, iou}) => {
@@ -364,4 +373,10 @@ export default compose(
key: `${ONYXKEYS.COLLECTION.SELECTED_TAB}${CONST.TAB.RECEIPT_TAB_ID}`,
},
}),
+ // eslint-disable-next-line rulesdir/no-multiple-onyx-in-file
+ withOnyx({
+ policy: {
+ key: ({report}) => `${ONYXKEYS.COLLECTION.POLICY}${report.policyID}`,
+ },
+ }),
)(MoneyRequestConfirmPage);
diff --git a/src/pages/iou/steps/MoneyRequstParticipantsPage/MoneyRequestParticipantsPage.js b/src/pages/iou/steps/MoneyRequstParticipantsPage/MoneyRequestParticipantsPage.js
index 1d9f12a9cdbb..b9ee016d4099 100644
--- a/src/pages/iou/steps/MoneyRequstParticipantsPage/MoneyRequestParticipantsPage.js
+++ b/src/pages/iou/steps/MoneyRequstParticipantsPage/MoneyRequestParticipantsPage.js
@@ -50,6 +50,7 @@ function MoneyRequestParticipantsPage({iou, selectedTab, route}) {
const iouType = useRef(lodashGet(route, 'params.iouType', ''));
const reportID = useRef(lodashGet(route, 'params.reportID', ''));
const isDistanceRequest = MoneyRequestUtils.isDistanceRequest(iouType.current, selectedTab);
+ const isScanRequest = MoneyRequestUtils.isScanRequest(selectedTab);
const isSplitRequest = iou.id === CONST.IOU.MONEY_REQUEST_TYPE.SPLIT;
const [headerTitle, setHeaderTitle] = useState();
@@ -62,7 +63,19 @@ function MoneyRequestParticipantsPage({iou, selectedTab, route}) {
setHeaderTitle(_.isEmpty(iou.participants) ? translate('tabSelector.manual') : translate('iou.split'));
}, [iou.participants, isDistanceRequest, translate]);
- const navigateToNextStep = (moneyRequestType) => {
+ const navigateToRequestStep = (moneyRequestType, option) => {
+ if (option.reportID) {
+ isNewReportIDSelectedLocally.current = true;
+ IOU.setMoneyRequestId(`${moneyRequestType}${option.reportID}`);
+ Navigation.navigate(ROUTES.getMoneyRequestConfirmationRoute(moneyRequestType, option.reportID));
+ return;
+ }
+
+ IOU.setMoneyRequestId(moneyRequestType);
+ Navigation.navigate(ROUTES.getMoneyRequestConfirmationRoute(moneyRequestType, reportID.current));
+ };
+
+ const navigateToSplitStep = (moneyRequestType) => {
IOU.setMoneyRequestId(moneyRequestType);
Navigation.navigate(ROUTES.getMoneyRequestConfirmationRoute(moneyRequestType, reportID.current));
};
@@ -112,11 +125,12 @@ function MoneyRequestParticipantsPage({iou, selectedTab, route}) {
ref={(el) => (optionsSelectorRef.current = el)}
participants={iou.participants}
onAddParticipants={IOU.setMoneyRequestParticipants}
- navigateToRequest={() => navigateToNextStep(CONST.IOU.MONEY_REQUEST_TYPE.REQUEST)}
- navigateToSplit={() => navigateToNextStep(CONST.IOU.MONEY_REQUEST_TYPE.SPLIT)}
+ navigateToRequest={(option) => navigateToRequestStep(CONST.IOU.MONEY_REQUEST_TYPE.REQUEST, option)}
+ navigateToSplit={() => navigateToSplitStep(CONST.IOU.MONEY_REQUEST_TYPE.SPLIT)}
safeAreaPaddingBottomStyle={safeAreaPaddingBottomStyle}
iouType={iouType.current}
isDistanceRequest={isDistanceRequest}
+ isScanRequest={isScanRequest}
/>
)}
diff --git a/src/pages/iou/steps/MoneyRequstParticipantsPage/MoneyRequestParticipantsSelector.js b/src/pages/iou/steps/MoneyRequstParticipantsPage/MoneyRequestParticipantsSelector.js
index 9ff787ebe21b..170ee042bffa 100755
--- a/src/pages/iou/steps/MoneyRequstParticipantsPage/MoneyRequestParticipantsSelector.js
+++ b/src/pages/iou/steps/MoneyRequstParticipantsPage/MoneyRequestParticipantsSelector.js
@@ -3,6 +3,7 @@ import {View} from 'react-native';
import PropTypes from 'prop-types';
import {withOnyx} from 'react-native-onyx';
import _ from 'underscore';
+import lodashGet from 'lodash/get';
import ONYXKEYS from '../../../../ONYXKEYS';
import styles from '../../../../styles/styles';
import OptionsSelector from '../../../../components/OptionsSelector';
@@ -58,6 +59,9 @@ const propTypes = {
/** Whether the money request is a distance request or not */
isDistanceRequest: PropTypes.bool,
+ /** Whether the money request is a scan request or not */
+ isScanRequest: PropTypes.bool,
+
...withLocalizePropTypes,
};
@@ -69,6 +73,7 @@ const defaultProps = {
reports: {},
betas: [],
isDistanceRequest: false,
+ isScanRequest: false,
};
function MoneyRequestParticipantsSelector({
@@ -84,6 +89,7 @@ function MoneyRequestParticipantsSelector({
safeAreaPaddingBottomStyle,
iouType,
isDistanceRequest,
+ isScanRequest,
}) {
const [searchTerm, setSearchTerm] = useState('');
const [newChatOptions, setNewChatOptions] = useState({
@@ -105,7 +111,10 @@ function MoneyRequestParticipantsSelector({
newSections.push({
title: undefined,
- data: OptionsListUtils.getParticipantsOptions(participants, personalDetails),
+ data: _.map(participants, (participant) => {
+ const isPolicyExpenseChat = lodashGet(participant, 'isPolicyExpenseChat', false);
+ return isPolicyExpenseChat ? OptionsListUtils.getPolicyExpenseReportOption(participant) : OptionsListUtils.getParticipantsOption(participant, personalDetails);
+ }),
shouldShow: true,
indexOffset,
});
@@ -149,8 +158,10 @@ function MoneyRequestParticipantsSelector({
* @param {Object} option
*/
const addSingleParticipant = (option) => {
- onAddParticipants([{accountID: option.accountID, login: option.login, isPolicyExpenseChat: option.isPolicyExpenseChat, reportID: option.reportID, selected: true}]);
- navigateToRequest();
+ onAddParticipants([
+ {accountID: option.accountID, login: option.login, isPolicyExpenseChat: option.isPolicyExpenseChat, reportID: option.reportID, selected: true, searchText: option.searchText},
+ ]);
+ navigateToRequest(option);
};
/**
@@ -159,19 +170,39 @@ function MoneyRequestParticipantsSelector({
*/
const addParticipantToSelection = useCallback(
(option) => {
- const isOptionInList = _.some(participants, (selectedOption) => selectedOption.accountID === option.accountID);
-
+ const isOptionSelected = (selectedOption) => {
+ if (selectedOption.accountID && selectedOption.accountID === option.accountID) {
+ return true;
+ }
+
+ if (selectedOption.reportID && selectedOption.reportID === option.reportID) {
+ return true;
+ }
+
+ return false;
+ };
+ const isOptionInList = _.some(participants, isOptionSelected);
let newSelectedOptions;
if (isOptionInList) {
- newSelectedOptions = _.reject(participants, (selectedOption) => selectedOption.accountID === option.accountID);
+ newSelectedOptions = _.reject(participants, isOptionSelected);
} else {
- newSelectedOptions = [...participants, {accountID: option.accountID, login: option.login, selected: true}];
+ newSelectedOptions = [
+ ...participants,
+ {
+ accountID: option.accountID,
+ login: option.login,
+ isPolicyExpenseChat: option.isPolicyExpenseChat,
+ reportID: option.reportID,
+ selected: true,
+ searchText: option.searchText,
+ },
+ ];
}
onAddParticipants(newSelectedOptions);
- const chatOptions = OptionsListUtils.getNewChatOptions(
+ const chatOptions = OptionsListUtils.getFilteredOptions(
reports,
personalDetails,
betas,
@@ -199,13 +230,14 @@ function MoneyRequestParticipantsSelector({
const headerMessage = OptionsListUtils.getHeaderMessage(
newChatOptions.personalDetails.length + newChatOptions.recentReports.length !== 0,
Boolean(newChatOptions.userToInvite),
- searchTerm,
+ searchTerm.trim(),
maxParticipantsReached,
+ _.some(participants, (participant) => participant.searchText.toLowerCase().includes(searchTerm.trim().toLowerCase())),
);
const isOptionsDataReady = ReportUtils.isReportDataReady() && OptionsListUtils.isPersonalDetailsReady(personalDetails);
useEffect(() => {
- const chatOptions = OptionsListUtils.getNewChatOptions(
+ const chatOptions = OptionsListUtils.getFilteredOptions(
reports,
personalDetails,
betas,
@@ -227,10 +259,17 @@ function MoneyRequestParticipantsSelector({
});
}, [betas, reports, participants, personalDetails, translate, searchTerm, setNewChatOptions, iouType, isDistanceRequest]);
+ // Right now you can't split a request 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);
+ const shouldShowConfirmButton = !(participants.length > 1 && hasPolicyExpenseChatParticipant);
+ const isAllowedToSplit = !isDistanceRequest && !isScanRequest;
+
return (
0 ? safeAreaPaddingBottomStyle : {}]}>
{
// Component may not be initialized due to navigation transitions
@@ -126,7 +126,7 @@ function NewRequestAmountPage({route, iou, report, selectedTab}) {
}, [iou.participantAccountIDs, iou.amount, iou.id, isEditing, iouType, reportID, isDistanceRequestTab]);
const navigateBack = () => {
- Navigation.goBack(isEditing ? ROUTES.getMoneyRequestConfirmationRoute(iouType, reportID) : null);
+ Navigation.goBack(isEditing ? ROUTES.getMoneyRequestConfirmationRoute(iouType, reportID) : ROUTES.HOME);
};
const navigateToCurrencySelectionPage = () => {
diff --git a/src/pages/settings/InitialSettingsPage.js b/src/pages/settings/InitialSettingsPage.js
index a67e7cbc122e..d10779210b09 100755
--- a/src/pages/settings/InitialSettingsPage.js
+++ b/src/pages/settings/InitialSettingsPage.js
@@ -1,6 +1,6 @@
import lodashGet from 'lodash/get';
import React, {useState, useEffect, useRef, useMemo, useCallback} from 'react';
-import {View, ScrollView} from 'react-native';
+import {View} from 'react-native';
import PropTypes from 'prop-types';
import _ from 'underscore';
import {withOnyx} from 'react-native-onyx';
@@ -12,11 +12,11 @@ import * as Session from '../../libs/actions/Session';
import ONYXKEYS from '../../ONYXKEYS';
import Tooltip from '../../components/Tooltip';
import Avatar from '../../components/Avatar';
-import HeaderWithBackButton from '../../components/HeaderWithBackButton';
import Navigation from '../../libs/Navigation/Navigation';
import * as Expensicons from '../../components/Icon/Expensicons';
-import ScreenWrapper from '../../components/ScreenWrapper';
import MenuItem from '../../components/MenuItem';
+import themeColors from '../../styles/themes/default';
+import SCREENS from '../../SCREENS';
import ROUTES from '../../ROUTES';
import withLocalize, {withLocalizePropTypes} from '../../components/withLocalize';
import compose from '../../libs/compose';
@@ -43,6 +43,7 @@ import PressableWithoutFeedback from '../../components/Pressable/PressableWithou
import useLocalize from '../../hooks/useLocalize';
import useSingleExecution from '../../hooks/useSingleExecution';
import useWaitForNavigation from '../../hooks/useWaitForNavigation';
+import HeaderPageLayout from '../../components/HeaderPageLayout';
const propTypes = {
/* Onyx Props */
@@ -326,79 +327,80 @@ function InitialSettingsPage(props) {
if (_.isEmpty(props.currentUserPersonalDetails)) {
return null;
}
-
- return (
-
- {({safeAreaPaddingBottomStyle}) => (
+ const headerContent = (
+
+ {_.isEmpty(props.currentUserPersonalDetails) || _.isUndefined(props.currentUserPersonalDetails.displayName) ? (
+
+ ) : (
<>
-
-
+
+
+
+
+
+
+
-
- {_.isEmpty(props.currentUserPersonalDetails) || _.isUndefined(props.currentUserPersonalDetails.displayName) ? (
-
- ) : (
-
-
-
-
-
-
-
-
-
-
-
- {props.currentUserPersonalDetails.displayName ? props.currentUserPersonalDetails.displayName : props.formatPhoneNumber(props.session.email)}
-
-
-
- {Boolean(props.currentUserPersonalDetails.displayName) && (
-
- {props.formatPhoneNumber(props.session.email)}
-
- )}
-
- )}
- {getMenuItems}
-
- signOut(true)}
- onCancel={() => toggleSignoutConfirmModal(false)}
- />
-
-
+
+
+ {props.currentUserPersonalDetails.displayName ? props.currentUserPersonalDetails.displayName : props.formatPhoneNumber(props.session.email)}
+
+
+
+ {Boolean(props.currentUserPersonalDetails.displayName) && (
+
+ {props.formatPhoneNumber(props.session.email)}
+
+ )}
>
)}
-
+
+ );
+
+ return (
+
+
+ {getMenuItems}
+ signOut(true)}
+ onCancel={() => toggleSignoutConfirmModal(false)}
+ />
+
+
);
}
diff --git a/src/pages/settings/Profile/CustomStatus/StatusPage.js b/src/pages/settings/Profile/CustomStatus/StatusPage.js
index f178b25fd0fb..807bd73cecc1 100644
--- a/src/pages/settings/Profile/CustomStatus/StatusPage.js
+++ b/src/pages/settings/Profile/CustomStatus/StatusPage.js
@@ -2,10 +2,9 @@ import React, {useMemo, useCallback, useEffect} from 'react';
import {View} from 'react-native';
import {withOnyx} from 'react-native-onyx';
import lodashGet from 'lodash/get';
-import moment from 'moment';
import withCurrentUserPersonalDetails, {withCurrentUserPersonalDetailsPropTypes} from '../../../../components/withCurrentUserPersonalDetails';
import MenuItemWithTopDescription from '../../../../components/MenuItemWithTopDescription';
-import StaticHeaderPageLayout from '../../../../components/StaticHeaderPageLayout';
+import HeaderPageLayout from '../../../../components/HeaderPageLayout';
import * as Expensicons from '../../../../components/Icon/Expensicons';
import withLocalize from '../../../../components/withLocalize';
import MenuItem from '../../../../components/MenuItem';
@@ -20,6 +19,7 @@ import styles from '../../../../styles/styles';
import compose from '../../../../libs/compose';
import ONYXKEYS from '../../../../ONYXKEYS';
import ROUTES from '../../../../ROUTES';
+import SCREENS from '../../../../SCREENS';
const propTypes = {
...withCurrentUserPersonalDetailsPropTypes,
@@ -44,8 +44,7 @@ function StatusPage({draftStatus, currentUserPersonalDetails}) {
const navigateBackToSettingsPage = useCallback(() => Navigation.goBack(ROUTES.SETTINGS_PROFILE, false, true), []);
const updateStatus = useCallback(() => {
- const endOfDay = moment().endOf('day').format('YYYY-MM-DD HH:mm:ss');
- User.updateCustomStatus({text: defaultText, emojiCode: defaultEmoji, clearAfter: endOfDay});
+ User.updateCustomStatus({text: defaultText, emojiCode: defaultEmoji});
User.clearDraftCustomStatus();
Navigation.goBack(ROUTES.SETTINGS_PROFILE);
@@ -65,11 +64,17 @@ function StatusPage({draftStatus, currentUserPersonalDetails}) {
useEffect(() => () => User.clearDraftCustomStatus(), []);
return (
-
+ }
+ headerContainerStyles={[styles.staticHeaderImage]}
+ backgroundColor={themeColors.PAGE_BACKGROUND_COLORS[SCREENS.SETTINGS.STATUS]}
footer={footerComponent}
>
@@ -93,7 +98,7 @@ function StatusPage({draftStatus, currentUserPersonalDetails}) {
wrapperStyle={[styles.cardMenuItem]}
/>
)}
-
+
);
}
diff --git a/src/pages/settings/Profile/LoungeAccessPage.js b/src/pages/settings/Profile/LoungeAccessPage.js
index ce047bd9ccae..c322c8e426f3 100644
--- a/src/pages/settings/Profile/LoungeAccessPage.js
+++ b/src/pages/settings/Profile/LoungeAccessPage.js
@@ -66,7 +66,7 @@ function LoungeAccessPage(props) {
const overlayContent = () => (
diff --git a/src/pages/settings/Profile/ProfilePage.js b/src/pages/settings/Profile/ProfilePage.js
index b0cbe7c9cc32..1864ba60524c 100755
--- a/src/pages/settings/Profile/ProfilePage.js
+++ b/src/pages/settings/Profile/ProfilePage.js
@@ -65,6 +65,8 @@ function ProfilePage(props) {
};
const currentUserDetails = props.currentUserPersonalDetails || {};
const contactMethodBrickRoadIndicator = UserUtils.getLoginListBrickRoadIndicator(props.loginList);
+ const avatarURL = lodashGet(currentUserDetails, 'avatar', '');
+ const accountID = lodashGet(currentUserDetails, 'accountID', '');
const emojiCode = lodashGet(props, 'currentUserPersonalDetails.status.emojiCode', '');
const profileSettingsOptions = [
@@ -113,7 +115,7 @@ function ProfilePage(props) {
diff --git a/src/pages/settings/Security/SecuritySettingsPage.js b/src/pages/settings/Security/SecuritySettingsPage.js
index 7f08247557f4..293e488ede7a 100644
--- a/src/pages/settings/Security/SecuritySettingsPage.js
+++ b/src/pages/settings/Security/SecuritySettingsPage.js
@@ -5,6 +5,7 @@ import {withOnyx} from 'react-native-onyx';
import PropTypes from 'prop-types';
import Navigation from '../../../libs/Navigation/Navigation';
import ROUTES from '../../../ROUTES';
+import SCREENS from '../../../SCREENS';
import styles from '../../../styles/styles';
import * as Expensicons from '../../../components/Icon/Expensicons';
import themeColors from '../../../styles/themes/default';
@@ -54,7 +55,7 @@ function SecuritySettingsPage(props) {
shouldShowBackButton
shouldShowCloseButton
illustration={LottieAnimations.Safe}
- backgroundColor={themeColors.PAGE_BACKGROUND_COLORS[ROUTES.SETTINGS_SECURITY]}
+ backgroundColor={themeColors.PAGE_BACKGROUND_COLORS[SCREENS.SETTINGS.SECURITY]}
>
diff --git a/src/pages/settings/Security/TwoFactorAuth/Steps/CodesStep.js b/src/pages/settings/Security/TwoFactorAuth/Steps/CodesStep.js
index 578f97c6042f..86a478a5571d 100644
--- a/src/pages/settings/Security/TwoFactorAuth/Steps/CodesStep.js
+++ b/src/pages/settings/Security/TwoFactorAuth/Steps/CodesStep.js
@@ -1,4 +1,4 @@
-import React, {useEffect} from 'react';
+import React, {useEffect, useState} from 'react';
import {withOnyx} from 'react-native-onyx';
import {ActivityIndicator, View} from 'react-native';
import {ScrollView} from 'react-native-gesture-handler';
@@ -23,10 +23,12 @@ import useWindowDimensions from '../../../../../hooks/useWindowDimensions';
import StepWrapper from '../StepWrapper/StepWrapper';
import {defaultAccount, TwoFactorAuthPropTypes} from '../TwoFactorAuthPropTypes';
import * as TwoFactorAuthActions from '../../../../../libs/actions/TwoFactorAuthActions';
+import FormHelpMessage from '../../../../../components/FormHelpMessage';
function CodesStep({account = defaultAccount}) {
const {translate} = useLocalize();
const {isExtraSmallScreenWidth, isSmallScreenWidth} = useWindowDimensions();
+ const [error, setError] = useState('');
const {setStep} = useTwoFactorAuthContext();
@@ -83,6 +85,7 @@ function CodesStep({account = defaultAccount}) {
inline={false}
onPress={() => {
Clipboard.setString(account.recoveryCodes);
+ setError('');
TwoFactorAuthActions.setCodesAreCopied();
}}
styles={[styles.button, styles.buttonMedium, styles.twoFactorAuthCodesButton]}
@@ -93,6 +96,7 @@ function CodesStep({account = defaultAccount}) {
icon={Expensicons.Download}
onPress={() => {
localFileDownload('two-factor-auth-codes', account.recoveryCodes);
+ setError('');
TwoFactorAuthActions.setCodesAreCopied();
}}
inline={false}
@@ -105,11 +109,23 @@ function CodesStep({account = defaultAccount}) {
+ {!_.isEmpty(error) && (
+
+ )}
diff --git a/src/pages/settings/Wallet/AddPayPalMePage.js b/src/pages/settings/Wallet/AddPayPalMePage.js
deleted file mode 100644
index 38eae4d46bbc..000000000000
--- a/src/pages/settings/Wallet/AddPayPalMePage.js
+++ /dev/null
@@ -1,142 +0,0 @@
-import React, {useRef, useCallback} from 'react';
-import {View} from 'react-native';
-import _ from 'underscore';
-import {withOnyx} from 'react-native-onyx';
-import lodashGet from 'lodash/get';
-import CONST from '../../../CONST';
-import ROUTES from '../../../ROUTES';
-import compose from '../../../libs/compose';
-import ONYXKEYS from '../../../ONYXKEYS';
-import HeaderWithBackButton from '../../../components/HeaderWithBackButton';
-import TextLink from '../../../components/TextLink';
-import Text from '../../../components/Text';
-import ScreenWrapper from '../../../components/ScreenWrapper';
-import Navigation from '../../../libs/Navigation/Navigation';
-import styles from '../../../styles/styles';
-import withLocalize, {withLocalizePropTypes} from '../../../components/withLocalize';
-import Form from '../../../components/Form';
-import Growl from '../../../libs/Growl';
-import TextInput from '../../../components/TextInput';
-import * as ValidationUtils from '../../../libs/ValidationUtils';
-import * as User from '../../../libs/actions/User';
-import Icon from '../../../components/Icon';
-import * as Expensicons from '../../../components/Icon/Expensicons';
-import variables from '../../../styles/variables';
-import PressableWithoutFeedback from '../../../components/Pressable/PressableWithoutFeedback';
-import paypalMeDataPropTypes from '../../../components/paypalMeDataPropTypes';
-import * as Link from '../../../libs/actions/Link';
-
-const propTypes = {
- /** Account details for PayPal.Me */
- payPalMeData: paypalMeDataPropTypes,
-
- ...withLocalizePropTypes,
-};
-
-const defaultProps = {
- payPalMeData: {},
-};
-
-const validate = (values) => {
- const requiredFields = ['payPalMeUsername'];
- const errors = ValidationUtils.getFieldRequiredErrors(values, requiredFields);
-
- if (values.payPalMeUsername && !ValidationUtils.isValidPaypalUsername(values.payPalMeUsername)) {
- errors.payPalMeUsername = 'addPayPalMePage.formatError';
- }
-
- return errors;
-};
-
-function AddPayPalMePage(props) {
- const payPalMeInput = useRef(null);
-
- const hasPaypalAccount = !_.isEmpty(props.payPalMeData);
-
- const growlMessageOnSave = props.translate(hasPaypalAccount ? 'addPayPalMePage.growlMessageOnUpdate' : 'addPayPalMePage.growlMessageOnSave');
-
- /**
- * Sets the payPalMe username and error data for the current user
- */
- const setPayPalMeData = useCallback(
- (values) => {
- User.addPaypalMeAddress(values.payPalMeUsername);
- Growl.show(growlMessageOnSave, CONST.GROWL.SUCCESS, 3000);
- Navigation.goBack(ROUTES.SETTINGS_WALLET);
- },
- [growlMessageOnSave],
- );
-
- return (
- payPalMeInput.current && payPalMeInput.current.focus()}
- >
- Navigation.goBack(ROUTES.SETTINGS_WALLET)}
- />
-