diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml
index e61307beef50..6b1b72f1f901 100644
--- a/.github/workflows/deploy.yml
+++ b/.github/workflows/deploy.yml
@@ -71,7 +71,10 @@ jobs:
run: echo "PRODUCTION_VERSION=$(npm run print-version --silent)" >> "$GITHUB_ENV"
- name: 🚀 Edit the release to be no longer a prerelease to deploy production 🚀
- run: gh release edit ${{ env.PRODUCTION_VERSION }} --prerelease=false --latest
+ run: |
+ LATEST_RELEASE="$(gh release list --exclude-pre-releases --json tagName,isLatest --jq '.[] | select(.isLatest) | .tagName')"
+ gh api --method POST /repos/Expensify/App/releases/generate-notes -f "tag_name=${{ env.PRODUCTION_VERSION }}" -f "previous_tag_name=$LATEST_RELEASE" >> releaseNotes.md
+ gh release edit ${{ env.PRODUCTION_VERSION }} --prerelease=false --latest --notes-file releaseNotes.md
env:
GITHUB_TOKEN: ${{ steps.setupGitForOSBotify.outputs.OS_BOTIFY_API_TOKEN }}
diff --git a/.github/workflows/platformDeploy.yml b/.github/workflows/platformDeploy.yml
index 83fa80a2cb73..90d81ada23c6 100644
--- a/.github/workflows/platformDeploy.yml
+++ b/.github/workflows/platformDeploy.yml
@@ -172,7 +172,7 @@ jobs:
needs: validateActor
if: ${{ fromJSON(needs.validateActor.outputs.IS_DEPLOYER) }}
env:
- DEVELOPER_DIR: /Applications/Xcode_15.0.1.app/Contents/Developer
+ DEVELOPER_DIR: /Applications/Xcode_15.2.0.app/Contents/Developer
runs-on: macos-13-xlarge
steps:
- name: Checkout
diff --git a/.github/workflows/testBuild.yml b/.github/workflows/testBuild.yml
index a9d8fa4ee66d..95177e0bcefd 100644
--- a/.github/workflows/testBuild.yml
+++ b/.github/workflows/testBuild.yml
@@ -136,7 +136,7 @@ jobs:
if: ${{ fromJSON(needs.validateActor.outputs.READY_TO_BUILD) }}
env:
PULL_REQUEST_NUMBER: ${{ github.event.number || github.event.inputs.PULL_REQUEST_NUMBER }}
- DEVELOPER_DIR: /Applications/Xcode_15.0.1.app/Contents/Developer
+ DEVELOPER_DIR: /Applications/Xcode_15.2.0.app/Contents/Developer
runs-on: macos-13-xlarge
steps:
- name: Checkout
@@ -158,7 +158,7 @@ jobs:
uses: ./.github/actions/composite/setupNode
- name: Setup XCode
- run: sudo xcode-select -switch /Applications/Xcode_15.0.1.app
+ run: sudo xcode-select -switch /Applications/Xcode_15.2.0.app
- name: Setup Ruby
uses: ruby/setup-ruby@v1.187.0
diff --git a/.gitignore b/.gitignore
index f9d74fe950bd..3e899e3175ba 100644
--- a/.gitignore
+++ b/.gitignore
@@ -140,3 +140,6 @@ web-build/
# Jeykll
docs/.bundle
+
+# Output of react compiler healthcheck dev script
+react-compiler-output.txt
diff --git a/Gemfile.lock b/Gemfile.lock
index b386f59b5c11..10acc25586ad 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -15,7 +15,7 @@ GEM
algoliasearch (1.27.5)
httpclient (~> 2.8, >= 2.8.3)
json (>= 1.5.1)
- apktools (0.7.4)
+ apktools (0.7.5)
rubyzip (~> 2.0)
artifactory (3.0.17)
atomos (0.1.3)
diff --git a/README.md b/README.md
index c10c954a1864..529d6cb0ccfd 100644
--- a/README.md
+++ b/README.md
@@ -26,6 +26,7 @@
* [Contributing to Expensify](contributingGuides/CONTRIBUTING.md)
* [Expensify Code of Conduct](CODE_OF_CONDUCT.md)
* [Contributor License Agreement](contributingGuides/CLA.md)
+* [React StrictMode](contributingGuides/STRICT_MODE.md)
----
diff --git a/__mocks__/react-native-document-picker.ts b/__mocks__/react-native-document-picker.ts
index 6d26a0227fc3..524e701f88fc 100644
--- a/__mocks__/react-native-document-picker.ts
+++ b/__mocks__/react-native-document-picker.ts
@@ -18,6 +18,7 @@ const reactNativeDocumentPickerMock: ReactNativeDocumentPickerMock = {
doc: 'com.microsoft.word.doc',
docx: 'org.openxmlformats.wordprocessingml.document',
images: 'public.image',
+ json: 'public.json',
pdf: 'com.adobe.pdf',
plainText: 'public.plain-text',
ppt: 'com.microsoft.powerpoint.ppt',
diff --git a/android/app/build.gradle b/android/app/build.gradle
index 1d09f0d26832..6a1c329980af 100644
--- a/android/app/build.gradle
+++ b/android/app/build.gradle
@@ -110,8 +110,8 @@ android {
minSdkVersion rootProject.ext.minSdkVersion
targetSdkVersion rootProject.ext.targetSdkVersion
multiDexEnabled rootProject.ext.multiDexEnabled
- versionCode 1009002501
- versionName "9.0.25-1"
+ versionCode 1009002800
+ versionName "9.0.28-0"
// Supported language variants must be declared here to avoid from being removed during the compilation.
// This also helps us to not include unnecessary language variants in the APK.
resConfigs "en", "es"
diff --git a/assets/images/caret-up-down.svg b/assets/images/caret-up-down.svg
new file mode 100644
index 000000000000..d08aa2a1ebbd
--- /dev/null
+++ b/assets/images/caret-up-down.svg
@@ -0,0 +1,17 @@
+
+
\ No newline at end of file
diff --git a/assets/images/companyCards/amex.svg b/assets/images/companyCards/amex.svg
new file mode 100644
index 000000000000..73e8164cdc63
--- /dev/null
+++ b/assets/images/companyCards/amex.svg
@@ -0,0 +1,40 @@
+
+
+
diff --git a/assets/images/companyCards/emptystate__card-pos.svg b/assets/images/companyCards/emptystate__card-pos.svg
new file mode 100644
index 000000000000..6a6fbae74a04
--- /dev/null
+++ b/assets/images/companyCards/emptystate__card-pos.svg
@@ -0,0 +1,643 @@
+
+
+
diff --git a/assets/images/companyCards/mastercard.svg b/assets/images/companyCards/mastercard.svg
new file mode 100644
index 000000000000..dcfac5eb33dd
--- /dev/null
+++ b/assets/images/companyCards/mastercard.svg
@@ -0,0 +1,40 @@
+
+
+
diff --git a/assets/images/companyCards/visa.svg b/assets/images/companyCards/visa.svg
new file mode 100644
index 000000000000..4a7a73b66639
--- /dev/null
+++ b/assets/images/companyCards/visa.svg
@@ -0,0 +1,74 @@
+
+
+
diff --git a/assets/images/simple-illustrations/simple-illustration__magnifyingglass-money.svg b/assets/images/simple-illustrations/simple-illustration__magnifyingglass-money.svg
new file mode 100644
index 000000000000..e7f64f69305a
--- /dev/null
+++ b/assets/images/simple-illustrations/simple-illustration__magnifyingglass-money.svg
@@ -0,0 +1,49 @@
+
+
+
diff --git a/assets/images/turtle-in-shell.svg b/assets/images/turtle-in-shell.svg
new file mode 100644
index 000000000000..6c5a8e74bb31
--- /dev/null
+++ b/assets/images/turtle-in-shell.svg
@@ -0,0 +1,87 @@
+
+
\ No newline at end of file
diff --git a/contributingGuides/REACT_COMPILER.md b/contributingGuides/REACT_COMPILER.md
index 144dd56100b7..520cbd7b164a 100644
--- a/contributingGuides/REACT_COMPILER.md
+++ b/contributingGuides/REACT_COMPILER.md
@@ -16,10 +16,10 @@ If the CI check fails for your PR, you need to fix the problem. If you're unsure
## How can I check what exactly prevents file from successful optimization or whether my fix for passing `react-compiler` actually works?
-You can run `npm run react-compiler-healthcheck` and examine the output. This command will list the files that failed to compile and provide details on what caused the failures. The output can be extensive, so you may want to write it to a file for easier review:
+You can run a dedicated script: `react-compiler-healthcheck-test` and examine the output. This command will list the files that failed to compile with details on what caused the failures. It will then save this output to `./react-compiler-output.txt` file. Read and examine the output to find what specific error the react-compiler throws.
```bash
-npm run react-compiler-healthcheck &> output.txt
+npm run react-compiler-healthcheck-test
```
## How to fix a particular problem?
@@ -39,9 +39,14 @@ If you encounter this error, you need to add the `Ref` postfix to the variable n
If you added a modification to `SharedValue`, you'll likely encounter this error. You can ignore this error for now because the current `react-native-reanimated` API is not compatible with `react-compiler` rules. Once [this PR](https://github.com/software-mansion/react-native-reanimated/pull/6312) is merged, we'll rewrite the code to be compatible with `react-compiler`. Until then, you can ignore this error.
-### `manual memoization could not be preserved`
+### Existing manual memoization could not be preserved. [...]
+These types of errors usually occur when the calls to `useMemo` that were made manually are too complex for react-compiler to understand. React compiler is still experimental so unfortunately this can happen.
-This error usually occurs when a dependency used inside a hook is omitted. This omission creates a memoization that is too complex to optimize automatically. Try including the missing dependencies.
+Some specific cases of this error are described below.
+
+#### The inferred dependencies did not match the manually specified dependencies
+
+This usually happens when a dependency used inside a hook is omitted. Try including the missing dependencies.
Please be aware that `react-compiler` struggles with memoization of nested fields, i. e.:
@@ -56,6 +61,27 @@ const selectedQboAccountName = useMemo(() => qboAccountOptions?.find(({id}) => i
// which is great because it reduces the amount of the duplicated code
```
+#### This value may be mutated later, which could cause the value to change unexpectedly
+
+This usually happens when the value returned from `useMemo` is later passed to some other function, and `react-compiler` doesn't know if the value will stay stable or be mutated.
+
+```ts
+// ❌ such code triggers the error
+const myResult = useMemo(() => SearchUtils.buildSearchQueryJSON(foobar), [foobar]);
+// [...] some other code
+const betterQuery = Utils.improveQuery(myResult);
+
+// ✅ this code can be compiled successfully
+const {myResult, betterQuery} = useMemo(() => {
+ const result = SearchUtils.buildSearchQueryJSON(foobar);
+
+ return {
+ myResult: result,
+ betterQuery: Utils.improveQuery(result)
+ }
+},[foobar]);
+```
+
### `Invalid nesting in program blocks or scopes`
Such error may happen if we have a nested memoization, i. e.:
diff --git a/contributingGuides/STRICT_MODE.md b/contributingGuides/STRICT_MODE.md
new file mode 100644
index 000000000000..dfe94ea2c3ba
--- /dev/null
+++ b/contributingGuides/STRICT_MODE.md
@@ -0,0 +1,44 @@
+# Usage of react concurrent mode and StrictMode
+## Concurrent react
+This App is rendered using react concurrent mode, which is the direction that React seems to be moving.
+
+Concurrent mode enables a lot of new behaviours in react, most importantly renders can be interrupted by React, re-run or run more than once. This is supposed to make react more performant and webapps more responsive to user actions.
+
+Further reading:
+ - [What is Concurrent React](https://react.dev/blog/2022/03/29/react-v18#what-is-concurrent-react)
+
+## StrictMode
+Because the previously described concurrent mode could potentially introduce new bugs in the code (related to parallel rendering) we are using ``.
+This is a recommendation from React team as per react official docs.
+
+`` is a component that wraps the whole App in (or parts of App) and it runs extra checks and extra behaviors only in dev. So in essence this is a developer tool.
+
+### Temporarily disabling StrictMode for dev
+Strict mode *by default always* wraps entire Expensify App component tree. This happens in `src/App.tsx`.
+
+However, it might happen you want to temporarily disable `StrictMode` when developing, to verify that your code behaves properly.
+
+To do that:
+ - go to `src/CONFIG.ts`
+ - set `USE_REACT_STRICT_MODE_IN_DEV` flag to `false`
+
+_Important note_: this ☝️flag is strictly for developers. It does not affect production builds of React.
+StrictMode is supposed to always wrap your App regardless of environment, and it will simply do nothing when run on production react build.
+Only use this flag for local development and testing, but do not make it depending on `NODE_ENV` or any other env vars.
+
+### Common StrictMode pitfalls
+ - every component will go through: `mount -> unmount -> mount` on first app render
+ - any code running inside `useEffect(() => {...}, [])` that would be expected to run once on initial render, will run twice, this might include initial api calls
+
+#### Example: How StrictMode Affects AuthScreen
+In AuthScreen, we have a typical pattern where certain logic is executed during mounting and unmounting, this is what happen after a refresh:
+- Mounting: it runs `ReconnectApp`.
+- Unmounting: AuthScreen cleans up data.
+- Re-mounting Due to StrictMode: This behavior will cause `OpenApp` to be executed on the new mount.
+
+Impact: This double execution could lead to unnecessary API calls or unexpected states.
+
+Sources:
+ - [StrictMode docs](https://react.dev/reference/react/StrictMode)
+ - [StrictMode recommended usage](https://react.dev/reference/react/StrictMode)
+ - [Original PR introducing this feature](https://github.com/Expensify/App/pull/42592)
\ No newline at end of file
diff --git a/docs/articles/expensify-classic/connections/quickbooks-desktop/Connect-To-QuickBooks-Desktop.md b/docs/articles/expensify-classic/connections/quickbooks-desktop/Connect-To-QuickBooks-Desktop.md
index 50e3e0971869..1fb1b09328b9 100644
--- a/docs/articles/expensify-classic/connections/quickbooks-desktop/Connect-To-QuickBooks-Desktop.md
+++ b/docs/articles/expensify-classic/connections/quickbooks-desktop/Connect-To-QuickBooks-Desktop.md
@@ -18,17 +18,17 @@ To connect QuickBooks Desktop to Expensify, you must log into QuickBooks Desktop
4. Click **Connect to QuickBooks Desktop**.
5. Click Copy to copy the link, then paste the link into the computer where QuickBooks Desktop is running.
-![QuickBooks Desktop Setup pop-up link, containing the URL to paste](https://help.expensify.com/assets/images/QBO_desktop_01.png){:width="100%"}
+ ![QuickBooks Desktop Setup pop-up link, containing the URL to paste](https://help.expensify.com/assets/images/QBO_desktop_01.png){:width="100%"}
6. Select the version of QuickBooks Desktop that you currently have.
-![The Web Connnector Pop-up to allow you to select the type of QuickBooks Desktop you have](https://help.expensify.com/assets/images/QBO_desktop_02.png){:width="100%"}
+ ![The Web Connnector Pop-up to allow you to select the type of QuickBooks Desktop you have](https://help.expensify.com/assets/images/QBO_desktop_02.png){:width="100%"}
7. Download the Web Connector and go through the guided installation process.
8. Open the Web Connector.
9. Click on **Add an Application**.
-![The Web Connnector Pop-up where you will need to click on Add an Application](https://help.expensify.com/assets/images/QBO_desktop_03.png){:width="100%"}
+ ![The Web Connnector Pop-up where you will need to click on Add an Application](https://help.expensify.com/assets/images/QBO_desktop_03.png){:width="100%"}
{% include info.html %}
For this step, it is key to ensure that the correct company file is open in QuickBooks Desktop and that it is the only one open.
@@ -36,23 +36,23 @@ For this step, it is key to ensure that the correct company file is open in Quic
10. In QuickBooks Desktop, select **"Yes, always allow access, even when QuickBooks is not running"** and click **Continue**.
-![The QuickBooks Desktop pop-up, where you will need to select "Yes, always allow access, even when QuickBooks is not running"](https://help.expensify.com/assets/images/QBO_desktop_04.png){:width="100%"}
+ ![The QuickBooks Desktop pop-up, where you will need to select "Yes, always allow access, even when QuickBooks is not running"](https://help.expensify.com/assets/images/QBO_desktop_04.png){:width="100%"}
11. Click **OK**, then click **Yes**.
-![The QuickBooks Desktop pop-up, where you will need to click "Ok" then select "Yes"](https://help.expensify.com/assets/images/QBO_desktop_05.png){:width="100%"}
+ ![The QuickBooks Desktop pop-up, where you will need to click "Ok" then select "Yes"](https://help.expensify.com/assets/images/QBO_desktop_05.png){:width="100%"}
12. Click **Copy** to copy the password.
-![The Web Connector pop-up, where you will need to click "Copy"](https://help.expensify.com/assets/images/QBO_desktop_06.png){:width="100%"}
+ ![The Web Connector pop-up, where you will need to click "Copy"](https://help.expensify.com/assets/images/QBO_desktop_06.png){:width="100%"}
13. Paste the password into the Password field of the Web Connector and press **Enter**.
-![The Web Connector pop-up, where you will need to paste the password into the password field](https://help.expensify.com/assets/images/QBO_desktop_08.png){:width="100%"}
+ ![The Web Connector pop-up, where you will need to paste the password into the password field](https://help.expensify.com/assets/images/QBO_desktop_08.png){:width="100%"}
14. Click **Yes** to save the password. The new connection now appears in the Web Connector.
-![The Web Connector pop-up, where you will need to click "Yes"](https://help.expensify.com/assets/images/QBO_desktop_07.png){:width="100%"}
+ ![The Web Connector pop-up, where you will need to click "Yes"](https://help.expensify.com/assets/images/QBO_desktop_07.png){:width="100%"}
# FAQ
diff --git a/docs/articles/expensify-classic/reports/Set-default-report-title.md b/docs/articles/expensify-classic/reports/Set-default-report-title.md
index a103ad8d5e5a..666f2caf8493 100644
--- a/docs/articles/expensify-classic/reports/Set-default-report-title.md
+++ b/docs/articles/expensify-classic/reports/Set-default-report-title.md
@@ -11,7 +11,7 @@ Workspace Admins can set a default report title for all reports created under a
3. Click the **Reports** tab on the left.
4. Scroll down to the Default Report Title section.
5. Configure the formula. You can use the example provided on the page as a guide or choose from more [report formula options](https://help.expensify.com/articles/expensify-classic/spending-insights/Custom-Templates).
- - Some formulas will automatically update the report title as changes are made to the report. For example, any formula related to dates, total amounts, workspace name, would adjust the title before the report is submitted for approval. Changes will not retroactively update report titles for reports which have been Approved or Reimbursed.
+ - Some formulas will automatically update the report title as changes are made to the report. For example, any formula related to dates, total amounts, and workspace name would adjust the title before the report is submitted for approval. Note that changes to Report Field values reflected in the Report Title (i.e., `{field:Customer}`) will not be reflected in the title of Open reports until submission. Between submission and approval, changes will update the title immediately. Changes will **not** retroactively update report titles for reports that have been Approved or Reimbursed.
6. If desired, enable the Enforce Default Report Title toggle. This will prevent employees from editing the default title.
diff --git a/docs/articles/expensify-classic/travel/Configure-travel-policy-and-preferences.md b/docs/articles/expensify-classic/travel/Configure-travel-policy-and-preferences.md
index 7e9a217b34ec..f34ed373c1bb 100644
--- a/docs/articles/expensify-classic/travel/Configure-travel-policy-and-preferences.md
+++ b/docs/articles/expensify-classic/travel/Configure-travel-policy-and-preferences.md
@@ -21,6 +21,7 @@ To create or update a travel policy,
2. **To add a new policy:** Click **Add new** under Employee or Non-employee in the left menu. Then under the Edit members section, select the group of employees that belong to this policy.
{% include info.html %}
+The Company name in Expensify Travel is the domain of the Expensify workspace billing owner
A Legal Entity in Expensify Travel is the equivalent of an Expensify Workspace.
{% include end-info.html %}
diff --git a/docs/articles/new-expensify/expenses-&-payments/Approve-and-pay-expenses.md b/docs/articles/new-expensify/expenses-&-payments/Approve-and-pay-expenses.md
index 5754d5f2ff90..397200ce83ef 100644
--- a/docs/articles/new-expensify/expenses-&-payments/Approve-and-pay-expenses.md
+++ b/docs/articles/new-expensify/expenses-&-payments/Approve-and-pay-expenses.md
@@ -7,6 +7,7 @@ description: Approve, hold, or pay expenses submitted to you
As a workspace admin, you can set an approval workflow for the expenses submitted to you. Expenses can be,
- Instantly submitted without needing approval.
+
- Submitted at a desired frequency (daily, weekly, monthly) and follow an approval workflow.
**Setting approval workflow and submission frequencies**
diff --git a/docs/articles/new-expensify/expenses-&-payments/Send-an-invoice.md b/docs/articles/new-expensify/expenses-&-payments/Send-an-invoice.md
index 1154a6a3163e..17da76b7126c 100644
--- a/docs/articles/new-expensify/expenses-&-payments/Send-an-invoice.md
+++ b/docs/articles/new-expensify/expenses-&-payments/Send-an-invoice.md
@@ -28,6 +28,8 @@ Workspace admins can enable invoicing on a workspace to send invoices and receiv
{% include end-option.html %}
+{% include end-selector.html %}
+
# Send an invoice
{% include info.html %}
@@ -44,6 +46,7 @@ Only workspace admins can send invoices. Invoices can be sent directly from Expe
5. Click **Send**.
{% include end-option.html %}
+
{% include option.html value="mobile" %}
1. Tap the + icon in the bottom left menu and select **Send Invoice**.
2. Enter the amount due and tap **Next**.
diff --git a/docs/articles/new-expensify/expenses-&-payments/pay-an-invoice.md b/docs/articles/new-expensify/expenses-&-payments/pay-an-invoice.md
index 6221117ed962..45b62c5d7892 100644
--- a/docs/articles/new-expensify/expenses-&-payments/pay-an-invoice.md
+++ b/docs/articles/new-expensify/expenses-&-payments/pay-an-invoice.md
@@ -30,6 +30,8 @@ You can also view all unpaid invoices by searching for the sender’s email or p
4. Tap **Add Bank Account** or **Add debit or credit card** to issue payment.
{% include end-option.html %}
+{% include end-selector.html %}
+
# FAQ
**Can someone else pay an invoice besides the person who received it?**
diff --git a/docs/articles/new-expensify/travel/Configure-travel-policy-and-preferences.md b/docs/articles/new-expensify/travel/Configure-travel-policy-and-preferences.md
index 7e9a217b34ec..16067624d720 100644
--- a/docs/articles/new-expensify/travel/Configure-travel-policy-and-preferences.md
+++ b/docs/articles/new-expensify/travel/Configure-travel-policy-and-preferences.md
@@ -98,6 +98,7 @@ To create or update a travel policy,
2. **To add a new policy:** Click **Add new** under Employee or Non-employee in the left menu. Then under the Edit members section, select the group of employees that belong to this policy.
{% include info.html %}
+The Company name in Expensify Travel is the domain of the Expensify workspace billing owner
A Legal Entity in Expensify Travel is the equivalent of an Expensify Workspace.
{% include end-info.html %}
diff --git a/docs/assets/js/main.js b/docs/assets/js/main.js
index 9e4880780e91..63ab6f79c737 100644
--- a/docs/assets/js/main.js
+++ b/docs/assets/js/main.js
@@ -165,6 +165,8 @@ window.addEventListener('load', () => {
insertElementAfter(searchInput, searchLabel);
});
+const FIXED_HEADER_HEIGHT = 80;
+
const tocbotOptions = {
// Where to render the table of contents.
tocSelector: '.article-toc',
@@ -188,14 +190,51 @@ const tocbotOptions = {
activeLinkClass: 'selected-article',
// Headings offset between the headings and the top of the document (requires scrollSmooth enabled)
- headingsOffset: 80,
- scrollSmoothOffset: -80,
+ headingsOffset: FIXED_HEADER_HEIGHT,
+ scrollSmoothOffset: -FIXED_HEADER_HEIGHT,
scrollSmooth: true,
// If there is a fixed article scroll container, set to calculate titles' offset
scrollContainer: 'content-area',
+
+ onClick: (e) => {
+ e.preventDefault();
+ const hashText = e.target.href.split('#').pop();
+ // Append hashText to the current URL without saving to history
+ const newUrl = `${window.location.pathname}#${hashText}`;
+ history.replaceState(null, '', newUrl);
+ },
};
+// Define the media query string for the mobile breakpoint
+const mobileBreakpoint = window.matchMedia('(max-width: 799px)');
+
+// Function to update tocbot options and refresh
+function updateTocbotOptions(headingsOffset, scrollSmoothOffset) {
+ tocbotOptions.headingsOffset = headingsOffset;
+ tocbotOptions.scrollSmoothOffset = scrollSmoothOffset;
+ window.tocbot.refresh({
+ ...tocbotOptions,
+ });
+}
+
+function handleBreakpointChange() {
+ const isMobile = mobileBreakpoint.matches;
+ const headingsOffset = isMobile ? FIXED_HEADER_HEIGHT : 0;
+ const scrollSmoothOffset = isMobile ? -FIXED_HEADER_HEIGHT : 0;
+
+ // Update tocbot options only if there is a change in offsets
+ if (tocbotOptions.headingsOffset !== headingsOffset || tocbotOptions.scrollSmoothOffset !== scrollSmoothOffset) {
+ updateTocbotOptions(headingsOffset, scrollSmoothOffset);
+ }
+}
+
+// Add listener for changes to the media query status using addEventListener
+mobileBreakpoint.addEventListener('change', handleBreakpointChange);
+
+// Initial check
+handleBreakpointChange();
+
function selectNewExpensify(newExpensifyTab, newExpensifyContent, expensifyClassicTab, expensifyClassicContent) {
newExpensifyTab.classList.add('active');
newExpensifyContent.classList.remove('hidden');
diff --git a/docs/redirects.csv b/docs/redirects.csv
index a1a8346b4789..480fd4220bd4 100644
--- a/docs/redirects.csv
+++ b/docs/redirects.csv
@@ -53,7 +53,7 @@ https://community.expensify.com/discussion/5934/day-1-with-expensify-admins-and-
https://community.expensify.com/discussion/5694/deep-dive-admin-training-and-setup-resources,https://help.expensify.com/expensify-classic/hubs/getting-started
https://help.expensify.com/articles/expensify-classic/expensify-card/Expensify-Card-Perks.html,https://use.expensify.com/company-credit-card
https://help.expensify.com/articles/expensify-classic/expensify-partner-program/How-to-Join-the-ExpensifyApproved!-Partner-Program.html,https://use.expensify.com/accountants-program
-https://help.expensify.com/articles/expensify-classic/getting-started/approved-accountants/Card-Revenue-Share-For-Expensify-Approved-Partners, https://use.expensify.com/blog/maximizing-rewards-expensifyapproved-accounting-partners-now-earn-0-5-revenue-share
+https://help.expensify.com/articles/expensify-classic/getting-started/approved-accountants/Card-Revenue-Share-For-Expensify-Approved-Partners,https://use.expensify.com/blog/maximizing-rewards-expensifyapproved-accounting-partners-now-earn-0-5-revenue-share
https://help.expensify.com/articles/expensify-classic/bank-accounts-and-credit-cards/International-Reimbursements,https://help.expensify.com/articles/expensify-classic/bank-accounts-and-credit-cards/Global-Reimbursements
https://community.expensify.com/discussion/4452/how-to-merge-accounts,https://help.expensify.com/articles/expensify-classic/settings/account-settings/Merge-accounts
https://community.expensify.com/discussion/4783/how-to-add-or-remove-a-copilot,https://help.expensify.com/articles/expensify-classic/account-settings/Copilot
@@ -526,3 +526,46 @@ https://community.expensify.com/discussion/4905/how-to-setup-the-parking-spot-to
https://community.expensify.com/discussion/5324/how-to-connect-parkwhiz-to-your-expensify-account-using-a-business-profile,https://help.expensify.com/articles/expensify-classic/connections/Additional-Travel-Integrations
https://community.expensify.com/discussion/6983/faq-why-do-i-need-to-provide-personal-documentation-when-setting-up-updating-my-bank-account,https://help.expensify.com/articles/new-expensify/expenses-&-payments/Connect-a-Business-Bank-Account#faq
https://community.expensify.com/discussion/6191/list-of-restricted-businesses,https://help.expensify.com/articles/expensify-classic/bank-accounts-and-payments/bank-accounts/Connect-US-Business-Bank-Account#are-there-certain-industries-or-businesses-for-which-expensify-cannot-process-payments
+https://community.expensify.com/discussion/5344/how-to-redeem-5-000-in-aws-credits-with-the-expensify-card,https://help.expensify.com/articles/expensify-classic/expensify-card/Expensify-Card-Perks#amazon-aws
+https://community.expensify.com/discussion/5403/how-do-i-receive-50-off-quickbooks-online-using-my-expensify-card,https://help.expensify.com/articles/expensify-classic/expensify-card/Expensify-Card-Perks#quickbooks-online
+https://community.expensify.com/discussion/5404/how-do-i-receive-20-off-carta-with-my-expensify-card,https://help.expensify.com/articles/expensify-classic/expensify-card/Expensify-Card-Perks#carta
+https://community.expensify.com/discussion/5406/how-do-i-receive-10-off-lamar-advertising-with-my-expensify-card,https://help.expensify.com/articles/expensify-classic/expensify-card/Expensify-Card-Perks#lamar-advertising
+https://community.expensify.com/discussion/5409/how-do-i-receive-20-off-spotlight-reporting-with-my-expensify-card,https://help.expensify.com/articles/expensify-classic/expensify-card/Expensify-Card-Perks#spotlight-reporting
+https://community.expensify.com/discussion/5410/how-do-i-receive-25-off-pagerduty-with-my-expensify-card,https://help.expensify.com/articles/expensify-classic/expensify-card/Expensify-Card-Perks#pagerduty
+https://community.expensify.com/discussion/5411/how-do-i-receive-3-months-free-intercom-with-my-expensify-card,https://help.expensify.com/articles/expensify-classic/expensify-card/Expensify-Card-Perks#intercom
+https://community.expensify.com/discussion/5412/how-do-i-receive-3-months-free-gusto-with-my-expensify-card,https://help.expensify.com/articles/expensify-classic/expensify-card/Expensify-Card-Perks#gusto
+https://community.expensify.com/discussion/5413/how-do-i-receive-20-off-six-months-of-pilot-with-my-expensify-card,https://help.expensify.com/articles/expensify-classic/expensify-card/Expensify-Card-Perks#pilot
+https://community.expensify.com/discussion/5829/how-do-i-receive-i-receive-30-off-typeform-with-my-expensify-card,https://help.expensify.com/articles/expensify-classic/expensify-card/Expensify-Card-Perks#typeform
+https://community.expensify.com/discussion/6377/how-to-redeem-the-aircall-expensify-card-perk,https://help.expensify.com/articles/expensify-classic/expensify-card/Expensify-Card-Perks#aircall
+https://community.expensify.com/discussion/7474/how-to-redeem-talkspace-offer-with-the-expensify-card,https://help.expensify.com/articles/expensify-classic/expensify-card/Expensify-Card-Perks#talkspace
+https://community.expensify.com/discussion/8118/how-to-redeem-deel-com-perk,https://help.expensify.com/articles/expensify-classic/expensify-card/Expensify-Card-Perks#deelcom
+https://community.expensify.com/discussion/8256/how-to-redeem-25-off-slack-with-the-expensify-card,https://help.expensify.com/articles/expensify-classic/expensify-card/Expensify-Card-Perks#slack
+https://community.expensify.com/discussion/8737/exclusive-perks-for-expensify-card-members,https://help.expensify.com/articles/expensify-classic/expensify-card/Expensify-Card-Perks
+https://community.expensify.com/discussion/9040/how-to-redeem-10-off-netsuite-with-the-expensify-card,https://help.expensify.com/articles/expensify-classic/expensify-card/Expensify-Card-Perks#netsuite
+https://community.expensify.com/discussion/4828/how-to-match-your-company-cards-statement-to-expensify/p1?new=1,https://help.expensify.com/articles/expensify-classic/connect-credit-cards/company-cards/Reconciliation
+https://community.expensify.com/discussion/5580/deep-dive-configure-advanced-settings-for-netsuite/,https://help.expensify.com/articles/expensify-classic/connections/netsuite/Configure-Netsuite#step-3-configure-advanced-settings
+https://community.expensify.com/discussion/7231/how-to-export-invoices-to-netsuite/p1?new=1,https://help.expensify.com/articles/expensify-classic/connections/netsuite/Configure-Netsuite#export-invoices
+https://community.expensify.com/discussion/9168/how-to-troubleshoot-general-errors-when-uploading-your-id-via-onfido,https://help.expensify.com/articles/new-expensify/expenses-&-payments/Resolve-Errors-Adding-a-Bank-Account
+https://community.expensify.com/discussion/4707/how-to-set-up-your-mobile-app,https://use.expensify.com/expensify-mobile-app
+https://community.expensify.com/discussion/7066/introducing-concierge-travel,https://help.expensify.com/expensify-classic/hubs/travel/
+https://help.expensify.com/expensify-classic/hubs/integrations/,https://help.expensify.com/expensify-classic/hubs/connections/
+https://help.expensify.com/articles/expensify-classic/policy-and-domain-settings/reports/Scheduled-Submit,https://help.expensify.com/articles/expensify-classic/reports/Automatically-submit-employee-reports
+https://help.expensify.com/articles/expensify-classic/expensify-card/Set-Up-the-Card-for-Your-Company,https://help.expensify.com/articles/expensify-classic/expensify-card/Set-Up-the-Expensify-Visa%C2%AE-Commercial-Card-for-your-Company
+https://community.expensify.com/discussion/5542/deep-dive-what-are-ereceipts,https://help.expensify.com/articles/expensify-classic/workspaces/Expense-Settings#ereceipts
+https://community.expensify.com/discussion/5738/deep-dive-how-does-concierge-receipt-audit-work,https://help.expensify.com/articles/expensify-classic/workspaces/Expense-Settings#concierge-receipt-audit
+https://community.expensify.com/discussion/4643/how-to-invite-people-to-your-policy-using-a-join-link,https://help.expensify.com/articles/expensify-classic/workspaces/Invite-members-and-assign-roles#invite-with-a-link
+https://community.expensify.com/discussion/4975/how-to-invite-users-to-your-policy-manually-or-in-bulk/p1?new=1,https://help.expensify.com/articles/expensify-classic/workspaces/Invite-members-and-assign-roles
+https://help.expensify.com/articles/expensify-classic/workspaces/Invoicing,https://help.expensify.com/articles/expensify-classic/workspaces/Set-Up-Invoicing
+https://help.expensify.com/articles/expensify-classic/bank-accounts-and-payments/bank-accounts/Enable-Global-Reimbursements.md,https://help.expensify.com/articles/expensify-classic/bank-accounts-and-payments/bank-accounts/Enable-Global-Reimbursements
+https://help.expensify.com/articles/expensify-classic/integrations/travel-integrations/Trip-Actions,https://help.expensify.com/expensify-classic/hubs/connections/
+https://help.expensify.com/articles/expensify-classic/bank-accounts-and-payments/bank-accounts/Add-Personal-Australian-Bank-Account.md,https://help.expensify.com/articles/expensify-classic/bank-accounts-and-payments/bank-accounts/Add-Personal-Australian-Bank-Account
+https://help.expensify.com/articles/expensify-classic/bank-accounts-and-payments/bank-accounts/Connect-Personal-US-Bank-Account.md,https://help.expensify.com/articles/expensify-classic/bank-accounts-and-payments/bank-accounts/Connect-Personal-US-Bank-Account
+https://help.expensify.com/articles/expensify-classic/bank-accounts-and-payments/Pay-Bills,https://help.expensify.com/articles/expensify-classic/bank-accounts-and-payments/payments/Reimburse-Reports-Invoices-and-Bills
+https://help.expensify.com/articles/expensify-classic/bank-accounts-and-payments/Reimbursing-Reports,https://help.expensify.com/articles/expensify-classic/bank-accounts-and-payments/payments/Reimburse-Reports-Invoices-and-Bills
+https://help.expensify.com/articles/expensify-classic/connect-credit-cards/Global-Reimbursements,https://help.expensify.com/articles/expensify-classic/bank-accounts-and-payments/bank-accounts/Enable-Global-Reimbursements
+https://community.expensify.com/discussion/4641/how-to-add-a-deposit-only-bank-account-both-personal-and-business,https://help.expensify.com/articles/expensify-classic/bank-accounts-and-payments/bank-accounts/Connect-US-Business-Bank-Account
+https://community.expensify.com/discussion/5940/how-to-get-reimbursed-outside-the-us-with-wise-for-non-us-employees,https://help.expensify.com/articles/expensify-classic/bank-accounts-and-payments/Third-Party-Payments
+https://community.expensify.com/home/leaving?allowTrusted=1&target=https%3A%2F%2Fqbo.intuit.com%2Fapp%2Fvendors,https://help.expensify.com/articles/expensify-classic/connections/quickbooks-online/Quickbooks-Online-Troubleshooting
+https://community.expensify.com/discussion/5654/deep-dive-using-expense-rules-to-vendor-match-when-exporting-to-an-accounting-package/p1?new=1,https://help.expensify.com/articles/expensify-classic/connections/xero/Xero-Troubleshooting
+https://help.expensify.com/articles/expensify-classic/spending-insights/(https://help.expensify.com/articles/expensify-classic/spending-insights/Custom-Templates),https://help.expensify.com/articles/expensify-classic/spending-insights/Custom-Templates
+https://help.expensify.com/articles/expensify-classic/settings/account-settings/Set-notifications,https://help.expensify.com/articles/expensify-classic/settings/account-settings/Set-Notifications
diff --git a/ios/Certificates.p12.gpg b/ios/Certificates.p12.gpg
index f63d6861f888..91f827416367 100644
Binary files a/ios/Certificates.p12.gpg and b/ios/Certificates.p12.gpg differ
diff --git a/ios/NewApp_AdHoc.mobileprovision.gpg b/ios/NewApp_AdHoc.mobileprovision.gpg
index d5f3c582327d..78fb5d53d9e9 100644
Binary files a/ios/NewApp_AdHoc.mobileprovision.gpg and b/ios/NewApp_AdHoc.mobileprovision.gpg differ
diff --git a/ios/NewApp_AdHoc_Notification_Service.mobileprovision.gpg b/ios/NewApp_AdHoc_Notification_Service.mobileprovision.gpg
index 8300bd34ef76..ac2c76a118d5 100644
Binary files a/ios/NewApp_AdHoc_Notification_Service.mobileprovision.gpg and b/ios/NewApp_AdHoc_Notification_Service.mobileprovision.gpg differ
diff --git a/ios/NewApp_AppStore.mobileprovision.gpg b/ios/NewApp_AppStore.mobileprovision.gpg
index 2e73cc7661e3..22624c6f41d6 100644
Binary files a/ios/NewApp_AppStore.mobileprovision.gpg and b/ios/NewApp_AppStore.mobileprovision.gpg differ
diff --git a/ios/NewApp_AppStore_Notification_Service.mobileprovision.gpg b/ios/NewApp_AppStore_Notification_Service.mobileprovision.gpg
index 4af7b16c5041..503a096f1726 100644
Binary files a/ios/NewApp_AppStore_Notification_Service.mobileprovision.gpg and b/ios/NewApp_AppStore_Notification_Service.mobileprovision.gpg differ
diff --git a/ios/NewExpensify/Info.plist b/ios/NewExpensify/Info.plist
index 9dfb45d14648..71f6fd1ab0f3 100644
--- a/ios/NewExpensify/Info.plist
+++ b/ios/NewExpensify/Info.plist
@@ -19,7 +19,7 @@
CFBundlePackageTypeAPPLCFBundleShortVersionString
- 9.0.25
+ 9.0.28CFBundleSignature????CFBundleURLTypes
@@ -40,7 +40,7 @@
CFBundleVersion
- 9.0.25.1
+ 9.0.28.0FullStoryOrgId
diff --git a/ios/NewExpensifyTests/Info.plist b/ios/NewExpensifyTests/Info.plist
index 96ad662b9c7d..854bccb1b90d 100644
--- a/ios/NewExpensifyTests/Info.plist
+++ b/ios/NewExpensifyTests/Info.plist
@@ -15,10 +15,10 @@
CFBundlePackageTypeBNDLCFBundleShortVersionString
- 9.0.25
+ 9.0.28CFBundleSignature????CFBundleVersion
- 9.0.25.1
+ 9.0.28.0
diff --git a/ios/NotificationServiceExtension/Info.plist b/ios/NotificationServiceExtension/Info.plist
index 077c0051e256..00065808afb9 100644
--- a/ios/NotificationServiceExtension/Info.plist
+++ b/ios/NotificationServiceExtension/Info.plist
@@ -11,9 +11,9 @@
CFBundleName$(PRODUCT_NAME)CFBundleShortVersionString
- 9.0.25
+ 9.0.28CFBundleVersion
- 9.0.25.1
+ 9.0.28.0NSExtensionNSExtensionPointIdentifier
diff --git a/ios/Podfile.lock b/ios/Podfile.lock
index d86ce17cc08d..d688213fafb4 100644
--- a/ios/Podfile.lock
+++ b/ios/Podfile.lock
@@ -19,7 +19,7 @@ PODS:
- Airship/Core
- AirshipFrameworkProxy (7.1.2):
- Airship (= 18.7.2)
- - AirshipServiceExtension (17.8.0)
+ - AirshipServiceExtension (18.7.2)
- AppAuth (1.7.5):
- AppAuth/Core (= 1.7.5)
- AppAuth/ExternalUserAgent (= 1.7.5)
@@ -1618,7 +1618,7 @@ PODS:
- React-Codegen
- React-RCTFabric
- ReactCommon/turbomodule/core
- - react-native-document-picker (9.1.1):
+ - react-native-document-picker (9.3.1):
- DoubleConversion
- glog
- hermes-engine
@@ -1840,7 +1840,7 @@ PODS:
- ReactCommon/turbomodule/bridging
- ReactCommon/turbomodule/core
- Yoga
- - react-native-quick-sqlite (8.0.6):
+ - react-native-quick-sqlite (8.1.0):
- DoubleConversion
- glog
- hermes-engine
@@ -2315,7 +2315,7 @@ PODS:
- Firebase/Performance (= 8.8.0)
- React-Core
- RNFBApp
- - RNFlashList (1.6.3):
+ - RNFlashList (1.7.1):
- DoubleConversion
- glog
- hermes-engine
@@ -2474,7 +2474,51 @@ PODS:
- ReactCommon/turbomodule/bridging
- ReactCommon/turbomodule/core
- Yoga
- - RNReanimated (3.13.0):
+ - RNReanimated (3.15.1):
+ - DoubleConversion
+ - glog
+ - hermes-engine
+ - RCT-Folly (= 2024.01.01.00)
+ - RCTRequired
+ - RCTTypeSafety
+ - React-Core
+ - React-debug
+ - React-Fabric
+ - React-featureflags
+ - React-graphics
+ - React-ImageManager
+ - React-NativeModulesApple
+ - React-RCTFabric
+ - React-rendererdebug
+ - React-utils
+ - ReactCodegen
+ - ReactCommon/turbomodule/bridging
+ - ReactCommon/turbomodule/core
+ - RNReanimated/reanimated (= 3.15.1)
+ - RNReanimated/worklets (= 3.15.1)
+ - Yoga
+ - RNReanimated/reanimated (3.15.1):
+ - DoubleConversion
+ - glog
+ - hermes-engine
+ - RCT-Folly (= 2024.01.01.00)
+ - RCTRequired
+ - RCTTypeSafety
+ - React-Core
+ - React-debug
+ - React-Fabric
+ - React-featureflags
+ - React-graphics
+ - React-ImageManager
+ - React-NativeModulesApple
+ - React-RCTFabric
+ - React-rendererdebug
+ - React-utils
+ - ReactCodegen
+ - ReactCommon/turbomodule/bridging
+ - ReactCommon/turbomodule/core
+ - Yoga
+ - RNReanimated/worklets (3.15.1):
- DoubleConversion
- glog
- hermes-engine
@@ -3055,7 +3099,7 @@ CHECKOUT OPTIONS:
SPEC CHECKSUMS:
Airship: bb32ff2c5a811352da074480357d9f02dbb8f327
AirshipFrameworkProxy: dbd862dc6fb21b13e8b196458d626123e2a43a50
- AirshipServiceExtension: 0a5fb14c3fd1879355ab05a81d10f64512a4f79c
+ AirshipServiceExtension: 9c73369f426396d9fb9ff222d86d842fac76ba46
AppAuth: 501c04eda8a8d11f179dbe8637b7a91bb7e5d2fa
boost: 26992d1adf73c1c7676360643e687aee6dda994b
BVLinearGradient: 421743791a59d259aec53f4c58793aad031da2ca
@@ -3136,7 +3180,7 @@ SPEC CHECKSUMS:
react-native-blob-util: 221c61c98ae507b758472ac4d2d489119d1a6c44
react-native-cameraroll: 478a0c1fcdd39f08f6ac272b7ed06e92b2c7c129
react-native-config: 5ce986133b07fc258828b20b9506de0e683efc1c
- react-native-document-picker: 2789e41dc92aa3256455aa54639a42f4dc4ee824
+ react-native-document-picker: e9d83c149bdd72dc01cf8dcb8df0389c6bd5fddb
react-native-geolocation: b9bd12beaf0ebca61a01514517ca8455bd26fa06
react-native-image-picker: f8a13ff106bcc7eb00c71ce11fdc36aac2a44440
react-native-key-command: aae312752fcdfaa2240be9a015fc41ce54087546
@@ -3147,7 +3191,7 @@ SPEC CHECKSUMS:
react-native-pdf: dd6ae39a93607a80919bef9f3499e840c693989d
react-native-performance: 3c608307be10964f8a97d3af462f37125b6d8fa5
react-native-plaid-link-sdk: f91a22b45b7c3d4cd6c47273200dc57df35068b0
- react-native-quick-sqlite: cc2939134fbd404ac7d51d3dc8d69219eff242a8
+ react-native-quick-sqlite: 7c793c9f5834e756b336257a8d8b8239b7ceb451
react-native-release-profiler: 131ec5e4145d900b2be2a8d6641e2ce0dd784259
react-native-safe-area-context: 38fdd9b3c5561de7cabae64bd0cd2ce05d2768a1
react-native-view-shot: 6b7ed61d77d88580fed10954d45fad0eb2d47688
@@ -3187,7 +3231,7 @@ SPEC CHECKSUMS:
RNFBApp: 729c0666395b1953198dc4a1ec6deb8fbe1c302e
RNFBCrashlytics: 2061ca863e8e2fa1aae9b12477d7dfa8e88ca0f9
RNFBPerf: 389914cda4000fe0d996a752532a591132cbf3f9
- RNFlashList: 65349fc4f2c270ae3feca9769dfb5065dffd1210
+ RNFlashList: 6f169ad83e52579b7754cbbcec1b004c27d82c93
RNFS: 4ac0f0ea233904cb798630b3c077808c06931688
RNGestureHandler: 8781e2529230a1bc3ea8d75e5c3cd071b6c6aed7
RNGoogleSignin: ccaa4a81582cf713eea562c5dd9dc1961a715fd0
@@ -3196,7 +3240,7 @@ SPEC CHECKSUMS:
rnmapbox-maps: 5ab6bfd249cd67262615153c648f8d809aab781c
RNPermissions: 0b1429b55af59d1d08b75a8be2459f65a8ac3f28
RNReactNativeHapticFeedback: a15b431d2903bc2eb3474ff8d9a05d3e67a70199
- RNReanimated: 601912257776588e7c0543f8dea4ba6dd393e9d0
+ RNReanimated: 76901886830e1032f16bbf820153f7dc3f02d51d
RNScreens: de6e57426ba0e6cbc3fb5b4f496e7f08cb2773c2
RNShare: a3c2fbbca5682530b65ff405b34c91dad1e22442
RNSound: 6c156f925295bdc83e8e422e7d8b38d33bc71852
@@ -3208,7 +3252,7 @@ SPEC CHECKSUMS:
SocketRocket: abac6f5de4d4d62d24e11868d7a2f427e0ef940d
Turf: aa2ede4298009639d10db36aba1a7ebaad072a5e
VisionCamera: c6c8aa4b028501fc87644550fbc35a537d4da3fb
- Yoga: 2a45d7e59592db061217551fd3bbe2dd993817ae
+ Yoga: a1d7895431387402a674fd0d1c04ec85e87909b8
PODFILE CHECKSUM: e479ec84cb53e5fd463486d71dfee91708d3fd9a
diff --git a/jest/setup.ts b/jest/setup.ts
index 19e20a6d0395..51385ad19e45 100644
--- a/jest/setup.ts
+++ b/jest/setup.ts
@@ -1,3 +1,4 @@
+/* eslint-disable max-classes-per-file */
import '@shopify/flash-list/jestSetup';
import 'react-native-gesture-handler/jestSetup';
import type * as RNKeyboardController from 'react-native-keyboard-controller';
@@ -78,3 +79,22 @@ jest.mock('@src/libs/actions/Timing', () => ({
start: jest.fn(),
end: jest.fn(),
}));
+
+// This makes FlatList render synchronously for easier testing.
+jest.mock(
+ '@react-native/virtualized-lists/Interaction/Batchinator',
+ () =>
+ class SyncBachinator {
+ #callback: () => void;
+
+ constructor(callback: () => void) {
+ this.#callback = callback;
+ }
+
+ schedule() {
+ this.#callback();
+ }
+
+ dispose() {}
+ },
+);
diff --git a/package-lock.json b/package-lock.json
index 50f6fbe357cc..3504515bd4ed 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,12 +1,12 @@
{
"name": "new.expensify",
- "version": "9.0.25-1",
+ "version": "9.0.28-0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "new.expensify",
- "version": "9.0.25-1",
+ "version": "9.0.28-0",
"hasInstallScript": true,
"license": "MIT",
"dependencies": {
@@ -15,11 +15,11 @@
"@dotlottie/react-player": "^1.6.3",
"@expensify/react-native-live-markdown": "0.1.117",
"@expo/metro-runtime": "~3.1.1",
- "@formatjs/intl-datetimeformat": "^6.10.0",
- "@formatjs/intl-listformat": "^7.2.2",
- "@formatjs/intl-locale": "^3.3.0",
- "@formatjs/intl-numberformat": "^8.5.0",
- "@formatjs/intl-pluralrules": "^5.2.2",
+ "@formatjs/intl-datetimeformat": "^6.12.5",
+ "@formatjs/intl-listformat": "^7.5.7",
+ "@formatjs/intl-locale": "^4.0.0",
+ "@formatjs/intl-numberformat": "^8.10.3",
+ "@formatjs/intl-pluralrules": "^5.2.14",
"@fullstory/babel-plugin-annotate-react": "github:fullstorydev/fullstory-babel-plugin-annotate-react#ryanwang/react-native-web-demo",
"@fullstory/babel-plugin-react-native": "^1.2.1",
"@fullstory/browser": "^2.0.3",
@@ -44,7 +44,7 @@
"@react-navigation/stack": "6.3.29",
"@react-ng/bounds-observer": "^0.2.1",
"@rnmapbox/maps": "10.1.26",
- "@shopify/flash-list": "1.6.3",
+ "@shopify/flash-list": "1.7.1",
"@types/mime-db": "^1.43.5",
"@ua/react-native-airship": "19.2.1",
"@vue/preload-webpack-plugin": "^2.0.0",
@@ -56,7 +56,7 @@
"date-fns-tz": "^2.0.0",
"dom-serializer": "^0.2.2",
"domhandler": "^4.3.0",
- "expensify-common": "2.0.76",
+ "expensify-common": "2.0.83",
"expo": "51.0.17",
"expo-av": "14.0.6",
"expo-image": "1.12.12",
@@ -85,11 +85,11 @@
"react-native": "0.75.2",
"react-native-android-location-enabler": "^2.0.1",
"react-native-blob-util": "0.19.4",
- "react-native-collapsible": "^1.6.1",
+ "react-native-collapsible": "^1.6.2",
"react-native-config": "1.5.0",
"react-native-dev-menu": "^4.1.1",
"react-native-device-info": "10.3.1",
- "react-native-document-picker": "^9.1.1",
+ "react-native-document-picker": "^9.3.1",
"react-native-draggable-flatlist": "^4.0.1",
"react-native-fs": "^2.20.0",
"react-native-gesture-handler": "2.18.0",
@@ -103,7 +103,7 @@
"react-native-linear-gradient": "^2.8.1",
"react-native-localize": "^2.2.6",
"react-native-modal": "^13.0.0",
- "react-native-onyx": "2.0.65",
+ "react-native-onyx": "2.0.66",
"react-native-pager-view": "6.3.4",
"react-native-pdf": "6.7.3",
"react-native-performance": "^5.1.0",
@@ -111,8 +111,8 @@
"react-native-picker-select": "git+https://github.com/Expensify/react-native-picker-select.git#da50d2c5c54e268499047f9cc98b8df4196c1ddf",
"react-native-plaid-link-sdk": "11.11.0",
"react-native-qrcode-svg": "git+https://github.com/Expensify/react-native-qrcode-svg-old",
- "react-native-quick-sqlite": "git+https://github.com/margelo/react-native-quick-sqlite#abc91857d4b3efb2020ec43abd2a508563b64316",
- "react-native-reanimated": "3.13.0",
+ "react-native-quick-sqlite": "git+https://github.com/margelo/react-native-quick-sqlite#99f34ebefa91698945f3ed26622e002bd79489e0",
+ "react-native-reanimated": "3.15.1",
"react-native-release-profiler": "^0.2.1",
"react-native-render-html": "6.3.1",
"react-native-safe-area-context": "4.10.9",
@@ -418,11 +418,6 @@
"node": ">=18.0.0"
}
},
- "node_modules/@azure/abort-controller/node_modules/tslib": {
- "version": "2.6.3",
- "license": "0BSD",
- "peer": true
- },
"node_modules/@azure/core-auth": {
"version": "1.7.2",
"license": "MIT",
@@ -436,11 +431,6 @@
"node": ">=18.0.0"
}
},
- "node_modules/@azure/core-auth/node_modules/tslib": {
- "version": "2.6.3",
- "license": "0BSD",
- "peer": true
- },
"node_modules/@azure/core-rest-pipeline": {
"version": "1.10.1",
"license": "MIT",
@@ -496,11 +486,6 @@
"node": ">=18.0.0"
}
},
- "node_modules/@azure/core-tracing/node_modules/tslib": {
- "version": "2.6.3",
- "license": "0BSD",
- "peer": true
- },
"node_modules/@azure/core-util": {
"version": "1.2.0",
"license": "MIT",
@@ -535,11 +520,6 @@
"node": ">=18.0.0"
}
},
- "node_modules/@azure/logger/node_modules/tslib": {
- "version": "2.6.3",
- "license": "0BSD",
- "peer": true
- },
"node_modules/@azure/opentelemetry-instrumentation-azure-sdk": {
"version": "1.0.0-beta.5",
"license": "MIT",
@@ -5324,92 +5304,86 @@
"license": "MIT"
},
"node_modules/@formatjs/ecma402-abstract": {
- "version": "1.15.0",
- "license": "MIT",
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/@formatjs/ecma402-abstract/-/ecma402-abstract-2.0.0.tgz",
+ "integrity": "sha512-rRqXOqdFmk7RYvj4khklyqzcfQl9vEL/usogncBHRZfZBDOwMGuSRNFl02fu5KGHXdbinju+YXyuR+Nk8xlr/g==",
"dependencies": {
- "@formatjs/intl-localematcher": "0.2.32",
+ "@formatjs/intl-localematcher": "0.5.4",
"tslib": "^2.4.0"
}
},
"node_modules/@formatjs/intl-datetimeformat": {
- "version": "6.10.3",
- "license": "MIT",
- "dependencies": {
- "@formatjs/ecma402-abstract": "1.17.2",
- "@formatjs/intl-localematcher": "0.4.2",
- "tslib": "^2.4.0"
- }
- },
- "node_modules/@formatjs/intl-datetimeformat/node_modules/@formatjs/ecma402-abstract": {
- "version": "1.17.2",
- "license": "MIT",
- "dependencies": {
- "@formatjs/intl-localematcher": "0.4.2",
- "tslib": "^2.4.0"
- }
- },
- "node_modules/@formatjs/intl-datetimeformat/node_modules/@formatjs/intl-localematcher": {
- "version": "0.4.2",
- "license": "MIT",
+ "version": "6.12.5",
+ "resolved": "https://registry.npmjs.org/@formatjs/intl-datetimeformat/-/intl-datetimeformat-6.12.5.tgz",
+ "integrity": "sha512-RYVlgQjUWUKZWMPl7Id8iSETXxHYLbHbii1hrDA/LpWtmFqWKFnF8esMXaHxpysDHviEpJbyGIqrYsABWjrFTw==",
"dependencies": {
+ "@formatjs/ecma402-abstract": "2.0.0",
+ "@formatjs/intl-localematcher": "0.5.4",
"tslib": "^2.4.0"
}
},
"node_modules/@formatjs/intl-enumerator": {
- "version": "1.3.0",
- "license": "MIT",
+ "version": "1.4.7",
+ "resolved": "https://registry.npmjs.org/@formatjs/intl-enumerator/-/intl-enumerator-1.4.7.tgz",
+ "integrity": "sha512-03RHnFqfpB4H/jwCwlzC+wkTDk2Fi24JmVIY2PVGvTUpikN2bSr9+8oTXfOC+y7B7VxjCArUnqWXVoctkmy85w==",
"dependencies": {
"tslib": "^2.4.0"
}
},
"node_modules/@formatjs/intl-getcanonicallocales": {
- "version": "2.2.0",
- "license": "MIT",
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/@formatjs/intl-getcanonicallocales/-/intl-getcanonicallocales-2.3.0.tgz",
+ "integrity": "sha512-BOXbLwqQ7nKua/l7tKqDLRN84WupDXFDhGJQMFvsMVA2dKuOdRaWTxWpL3cJ7qPkoNw11Jf+Xpj4OSPBBvW0eQ==",
"dependencies": {
"tslib": "^2.4.0"
}
},
"node_modules/@formatjs/intl-listformat": {
- "version": "7.2.2",
- "license": "MIT",
+ "version": "7.5.7",
+ "resolved": "https://registry.npmjs.org/@formatjs/intl-listformat/-/intl-listformat-7.5.7.tgz",
+ "integrity": "sha512-MG2TSChQJQT9f7Rlv+eXwUFiG24mKSzmF144PLb8m8OixyXqn4+YWU+5wZracZGCgVTVmx8viCf7IH3QXoiB2g==",
"dependencies": {
- "@formatjs/ecma402-abstract": "1.15.0",
- "@formatjs/intl-localematcher": "0.2.32",
+ "@formatjs/ecma402-abstract": "2.0.0",
+ "@formatjs/intl-localematcher": "0.5.4",
"tslib": "^2.4.0"
}
},
"node_modules/@formatjs/intl-locale": {
- "version": "3.3.0",
- "license": "MIT",
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/@formatjs/intl-locale/-/intl-locale-4.0.0.tgz",
+ "integrity": "sha512-+4dbMEGsp1bvB3JB3UHH6YTjMnFTifnfdaHp4ROrCCu50NedA69RBsDCG3eivcZkbj57X9ehGhMWjLxlP+gyVw==",
"dependencies": {
- "@formatjs/ecma402-abstract": "1.15.0",
- "@formatjs/intl-enumerator": "1.3.0",
- "@formatjs/intl-getcanonicallocales": "2.2.0",
+ "@formatjs/ecma402-abstract": "2.0.0",
+ "@formatjs/intl-enumerator": "1.4.7",
+ "@formatjs/intl-getcanonicallocales": "2.3.0",
"tslib": "^2.4.0"
}
},
"node_modules/@formatjs/intl-localematcher": {
- "version": "0.2.32",
- "license": "MIT",
+ "version": "0.5.4",
+ "resolved": "https://registry.npmjs.org/@formatjs/intl-localematcher/-/intl-localematcher-0.5.4.tgz",
+ "integrity": "sha512-zTwEpWOzZ2CiKcB93BLngUX59hQkuZjT2+SAQEscSm52peDW/getsawMcWF1rGRpMCX6D7nSJA3CzJ8gn13N/g==",
"dependencies": {
"tslib": "^2.4.0"
}
},
"node_modules/@formatjs/intl-numberformat": {
- "version": "8.5.0",
- "license": "MIT",
+ "version": "8.10.3",
+ "resolved": "https://registry.npmjs.org/@formatjs/intl-numberformat/-/intl-numberformat-8.10.3.tgz",
+ "integrity": "sha512-lH3liLMeIjZ19Zxt8RRPnBcpPweS1YNSXRURDiFfvFmRlDZUOd8+GlcVyECcPZPkIoSH/p4lfGrnaUzepxJ92g==",
"dependencies": {
- "@formatjs/ecma402-abstract": "1.15.0",
- "@formatjs/intl-localematcher": "0.2.32",
+ "@formatjs/ecma402-abstract": "2.0.0",
+ "@formatjs/intl-localematcher": "0.5.4",
"tslib": "^2.4.0"
}
},
"node_modules/@formatjs/intl-pluralrules": {
- "version": "5.2.2",
- "license": "MIT",
+ "version": "5.2.14",
+ "resolved": "https://registry.npmjs.org/@formatjs/intl-pluralrules/-/intl-pluralrules-5.2.14.tgz",
+ "integrity": "sha512-l6Ev7aOGXJSh5EPDEqzsbyufdCCKXZk993QXRQebLsB0TXRhIyF4alqjdMEatLwIigK/Mka8kiVIOLeFP5Cj9Q==",
"dependencies": {
- "@formatjs/ecma402-abstract": "1.15.0",
- "@formatjs/intl-localematcher": "0.2.32",
+ "@formatjs/ecma402-abstract": "2.0.0",
+ "@formatjs/intl-localematcher": "0.5.4",
"tslib": "^2.4.0"
}
},
@@ -10287,11 +10261,12 @@
}
},
"node_modules/@shopify/flash-list": {
- "version": "1.6.3",
- "license": "MIT",
+ "version": "1.7.1",
+ "resolved": "https://registry.npmjs.org/@shopify/flash-list/-/flash-list-1.7.1.tgz",
+ "integrity": "sha512-sUYl7h8ydJutufA26E42Hj7cLvaBTpkMIyNJiFrxUspkcANb6jnFiLt9rEwAuDjvGk/C0lHau+WyT6ZOxqVPwg==",
"dependencies": {
- "recyclerlistview": "4.2.0",
- "tslib": "2.4.0"
+ "recyclerlistview": "4.2.1",
+ "tslib": "2.6.3"
},
"peerDependencies": {
"@babel/runtime": "*",
@@ -10299,19 +10274,6 @@
"react-native": "*"
}
},
- "node_modules/@shopify/flash-list/node_modules/recyclerlistview": {
- "version": "4.2.0",
- "license": "Apache-2.0",
- "dependencies": {
- "lodash.debounce": "4.0.8",
- "prop-types": "15.8.1",
- "ts-object-utils": "0.0.5"
- },
- "peerDependencies": {
- "react": ">= 15.2.1",
- "react-native": ">= 0.30.0"
- }
- },
"node_modules/@sideway/address": {
"version": "4.1.5",
"license": "BSD-3-Clause",
@@ -26323,9 +26285,9 @@
}
},
"node_modules/expensify-common": {
- "version": "2.0.76",
- "resolved": "https://registry.npmjs.org/expensify-common/-/expensify-common-2.0.76.tgz",
- "integrity": "sha512-nCM6laaj25kurdiD9rhAXmQKhi8eciIFlshN8P4A7gsjFIScXnRG8fwXM5tX+8ET0vqAOA2SACeNVTT3vGUUsQ==",
+ "version": "2.0.83",
+ "resolved": "https://registry.npmjs.org/expensify-common/-/expensify-common-2.0.83.tgz",
+ "integrity": "sha512-7CHVxV5yEJ43GGKF0UXiLKaSdfKaSHE4YC2+30gKxuWbs5XrOLOK3TcCzk54uBfbmPjmx6VrADbR9uzS4H0A0g==",
"dependencies": {
"awesome-phonenumber": "^5.4.0",
"classnames": "2.5.0",
@@ -37240,8 +37202,9 @@
}
},
"node_modules/react-native-collapsible": {
- "version": "1.6.1",
- "license": "MIT",
+ "version": "1.6.2",
+ "resolved": "https://registry.npmjs.org/react-native-collapsible/-/react-native-collapsible-1.6.2.tgz",
+ "integrity": "sha512-MCOBVJWqHNjnDaGkvxX997VONmJeebh6wyJxnHEgg0L1PrlcXU1e/bo6eK+CDVFuMrCafw8Qh4DOv/C4V/+Iew==",
"peerDependencies": {
"react": "*",
"react-native": "*"
@@ -37276,8 +37239,9 @@
}
},
"node_modules/react-native-document-picker": {
- "version": "9.1.1",
- "license": "MIT",
+ "version": "9.3.1",
+ "resolved": "https://registry.npmjs.org/react-native-document-picker/-/react-native-document-picker-9.3.1.tgz",
+ "integrity": "sha512-Vcofv9wfB0j67zawFjfq9WQPMMzXxOZL9kBmvWDpjVuEcVK73ndRmlXHlkeFl5ZHVsv4Zb6oZYhqm9u5omJOPA==",
"dependencies": {
"invariant": "^2.2.4"
},
@@ -38200,9 +38164,9 @@
}
},
"node_modules/react-native-onyx": {
- "version": "2.0.65",
- "resolved": "https://registry.npmjs.org/react-native-onyx/-/react-native-onyx-2.0.65.tgz",
- "integrity": "sha512-1ilE2uzROrgh05cj6I6X2Nrr8T7QM4lCcr5JrfMTJpiLvV9UZMBvZk9xNJrr7KexUhpzcqhrsCbQeTT14ylT1w==",
+ "version": "2.0.66",
+ "resolved": "https://registry.npmjs.org/react-native-onyx/-/react-native-onyx-2.0.66.tgz",
+ "integrity": "sha512-Ns9WzcAjarAUl9g/bftf2EUJYdgcb6BAraxwqBWVeGWk3dGBR1hVEvZ7p/3rpKjidJQqiM3LWBaM6DkNHoYd1g==",
"dependencies": {
"ascii-table": "0.0.9",
"fast-equals": "^4.0.3",
@@ -38311,23 +38275,27 @@
}
},
"node_modules/react-native-quick-sqlite": {
- "version": "8.0.6",
- "resolved": "git+ssh://git@github.com/margelo/react-native-quick-sqlite.git#abc91857d4b3efb2020ec43abd2a508563b64316",
- "integrity": "sha512-/tBM6Oh8ye3d+hIhURRA9hlBausKqQmscgyt4ZcKluPjBti0bgLb0cyL8Gyd0cbCakaVgym25VyGjaeicV/01A==",
+ "version": "8.1.0",
+ "resolved": "git+ssh://git@github.com/margelo/react-native-quick-sqlite.git#99f34ebefa91698945f3ed26622e002bd79489e0",
+ "integrity": "sha512-7uuHmOEnc6SOAVoAdvkQhvaYhUZMORM75qo+v6PZoH6Qk21j5CmrcxJE3gNh0FhMfxK73hQ3ZtugC/NI2jVhrw==",
"peerDependencies": {
"react": "*",
"react-native": "*"
}
},
"node_modules/react-native-reanimated": {
- "version": "3.13.0",
- "license": "MIT",
+ "version": "3.15.1",
+ "resolved": "https://registry.npmjs.org/react-native-reanimated/-/react-native-reanimated-3.15.1.tgz",
+ "integrity": "sha512-DbBeUUExtJ1x1nfE94I8qgDgWjq5ztM3IO/+XFO+agOkPeVpBs5cRnxHfJKrjqJ2MgwhJOUDmtHxo+tDsoeitg==",
"dependencies": {
"@babel/plugin-transform-arrow-functions": "^7.0.0-0",
+ "@babel/plugin-transform-class-properties": "^7.0.0-0",
+ "@babel/plugin-transform-classes": "^7.0.0-0",
"@babel/plugin-transform-nullish-coalescing-operator": "^7.0.0-0",
"@babel/plugin-transform-optional-chaining": "^7.0.0-0",
"@babel/plugin-transform-shorthand-properties": "^7.0.0-0",
"@babel/plugin-transform-template-literals": "^7.0.0-0",
+ "@babel/plugin-transform-unicode-regex": "^7.0.0-0",
"@babel/preset-typescript": "^7.16.7",
"convert-source-map": "^2.0.0",
"invariant": "^2.2.4"
@@ -40363,6 +40331,20 @@
"node": ">= 10.13.0"
}
},
+ "node_modules/recyclerlistview": {
+ "version": "4.2.1",
+ "resolved": "https://registry.npmjs.org/recyclerlistview/-/recyclerlistview-4.2.1.tgz",
+ "integrity": "sha512-NtVYjofwgUCt1rEsTp6jHQg/47TWjnO92TU2kTVgJ9wsc/ely4HnizHHa+f/dI7qaw4+zcSogElrLjhMltN2/g==",
+ "dependencies": {
+ "lodash.debounce": "4.0.8",
+ "prop-types": "15.8.1",
+ "ts-object-utils": "0.0.5"
+ },
+ "peerDependencies": {
+ "react": ">= 15.2.1",
+ "react-native": ">= 0.30.0"
+ }
+ },
"node_modules/redent": {
"version": "3.0.0",
"dev": true,
@@ -43477,7 +43459,8 @@
},
"node_modules/ts-object-utils": {
"version": "0.0.5",
- "license": "ISC"
+ "resolved": "https://registry.npmjs.org/ts-object-utils/-/ts-object-utils-0.0.5.tgz",
+ "integrity": "sha512-iV0GvHqOmilbIKJsfyfJY9/dNHCs969z3so90dQWsO1eMMozvTpnB1MEaUbb3FYtZTGjv5sIy/xmslEz0Rg2TA=="
},
"node_modules/ts-regex-builder": {
"version": "1.7.1",
@@ -43513,8 +43496,9 @@
}
},
"node_modules/tslib": {
- "version": "2.4.0",
- "license": "0BSD"
+ "version": "2.6.3",
+ "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.3.tgz",
+ "integrity": "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ=="
},
"node_modules/tsutils": {
"version": "3.21.0",
diff --git a/package.json b/package.json
index 2c4dd2bbd458..3f86d843ae52 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "new.expensify",
- "version": "9.0.25-1",
+ "version": "9.0.28-0",
"author": "Expensify, Inc.",
"homepage": "https://new.expensify.com",
"description": "New Expensify is the next generation of Expensify: a reimagination of payments based atop a foundation of chat.",
@@ -62,6 +62,7 @@
"setup-https": "mkcert -install && mkcert -cert-file config/webpack/certificate.pem -key-file config/webpack/key.pem dev.new.expensify.com localhost 127.0.0.1",
"e2e-test-runner-build": "node --max-old-space-size=8192 node_modules/.bin/ncc build tests/e2e/testRunner.ts -o tests/e2e/dist/",
"react-compiler-healthcheck": "react-compiler-healthcheck --verbose",
+ "react-compiler-healthcheck-test": "react-compiler-healthcheck --verbose &> react-compiler-output.txt",
"generate-search-parser": "peggy --format es -o src/libs/SearchParser/searchParser.js src/libs/SearchParser/searchParser.peggy ",
"web:prod": "http-server ./dist --cors"
},
@@ -71,11 +72,11 @@
"@dotlottie/react-player": "^1.6.3",
"@expensify/react-native-live-markdown": "0.1.117",
"@expo/metro-runtime": "~3.1.1",
- "@formatjs/intl-datetimeformat": "^6.10.0",
- "@formatjs/intl-listformat": "^7.2.2",
- "@formatjs/intl-locale": "^3.3.0",
- "@formatjs/intl-numberformat": "^8.5.0",
- "@formatjs/intl-pluralrules": "^5.2.2",
+ "@formatjs/intl-datetimeformat": "^6.12.5",
+ "@formatjs/intl-listformat": "^7.5.7",
+ "@formatjs/intl-locale": "^4.0.0",
+ "@formatjs/intl-numberformat": "^8.10.3",
+ "@formatjs/intl-pluralrules": "^5.2.14",
"@fullstory/babel-plugin-annotate-react": "github:fullstorydev/fullstory-babel-plugin-annotate-react#ryanwang/react-native-web-demo",
"@fullstory/babel-plugin-react-native": "^1.2.1",
"@fullstory/browser": "^2.0.3",
@@ -100,7 +101,7 @@
"@react-navigation/stack": "6.3.29",
"@react-ng/bounds-observer": "^0.2.1",
"@rnmapbox/maps": "10.1.26",
- "@shopify/flash-list": "1.6.3",
+ "@shopify/flash-list": "1.7.1",
"@types/mime-db": "^1.43.5",
"@ua/react-native-airship": "19.2.1",
"@vue/preload-webpack-plugin": "^2.0.0",
@@ -112,7 +113,7 @@
"date-fns-tz": "^2.0.0",
"dom-serializer": "^0.2.2",
"domhandler": "^4.3.0",
- "expensify-common": "2.0.76",
+ "expensify-common": "2.0.83",
"expo": "51.0.17",
"expo-av": "14.0.6",
"expo-image": "1.12.12",
@@ -141,11 +142,11 @@
"react-native": "0.75.2",
"react-native-android-location-enabler": "^2.0.1",
"react-native-blob-util": "0.19.4",
- "react-native-collapsible": "^1.6.1",
+ "react-native-collapsible": "^1.6.2",
"react-native-config": "1.5.0",
"react-native-dev-menu": "^4.1.1",
"react-native-device-info": "10.3.1",
- "react-native-document-picker": "^9.1.1",
+ "react-native-document-picker": "^9.3.1",
"react-native-draggable-flatlist": "^4.0.1",
"react-native-fs": "^2.20.0",
"react-native-gesture-handler": "2.18.0",
@@ -159,7 +160,7 @@
"react-native-linear-gradient": "^2.8.1",
"react-native-localize": "^2.2.6",
"react-native-modal": "^13.0.0",
- "react-native-onyx": "2.0.65",
+ "react-native-onyx": "2.0.66",
"react-native-pager-view": "6.3.4",
"react-native-pdf": "6.7.3",
"react-native-performance": "^5.1.0",
@@ -167,8 +168,8 @@
"react-native-picker-select": "git+https://github.com/Expensify/react-native-picker-select.git#da50d2c5c54e268499047f9cc98b8df4196c1ddf",
"react-native-plaid-link-sdk": "11.11.0",
"react-native-qrcode-svg": "git+https://github.com/Expensify/react-native-qrcode-svg-old",
- "react-native-quick-sqlite": "git+https://github.com/margelo/react-native-quick-sqlite#abc91857d4b3efb2020ec43abd2a508563b64316",
- "react-native-reanimated": "3.13.0",
+ "react-native-quick-sqlite": "git+https://github.com/margelo/react-native-quick-sqlite#99f34ebefa91698945f3ed26622e002bd79489e0",
+ "react-native-reanimated": "3.15.1",
"react-native-release-profiler": "^0.2.1",
"react-native-render-html": "6.3.1",
"react-native-safe-area-context": "4.10.9",
diff --git a/patches/@shopify+flash-list++recyclerlistview+4.2.0.patch b/patches/@shopify+flash-list++recyclerlistview+4.2.0.patch
deleted file mode 100644
index 3bfe2597c816..000000000000
--- a/patches/@shopify+flash-list++recyclerlistview+4.2.0.patch
+++ /dev/null
@@ -1,92 +0,0 @@
-diff --git a/node_modules/@shopify/flash-list/node_modules/recyclerlistview/dist/reactnative/core/RecyclerListView.js b/node_modules/@shopify/flash-list/node_modules/recyclerlistview/dist/reactnative/core/RecyclerListView.js
-index a98072e..67b8f30 100644
---- a/node_modules/@shopify/flash-list/node_modules/recyclerlistview/dist/reactnative/core/RecyclerListView.js
-+++ b/node_modules/@shopify/flash-list/node_modules/recyclerlistview/dist/reactnative/core/RecyclerListView.js
-@@ -375,6 +375,12 @@ var RecyclerListView = /** @class */ (function (_super) {
- }
- return null;
- };
-+ RecyclerListView.prototype.getNativeScrollRef = function () {
-+ if (this._scrollComponent && this._scrollComponent.getNativeScrollRef) {
-+ return this._scrollComponent.getNativeScrollRef();
-+ }
-+ return null;
-+ };
- RecyclerListView.prototype.renderCompat = function () {
- //TODO:Talha
- // const {
-diff --git a/node_modules/@shopify/flash-list/node_modules/recyclerlistview/dist/reactnative/core/scrollcomponent/BaseScrollComponent.js b/node_modules/@shopify/flash-list/node_modules/recyclerlistview/dist/reactnative/core/scrollcomponent/BaseScrollComponent.js
-index 96355ba..4d32c92 100644
---- a/node_modules/@shopify/flash-list/node_modules/recyclerlistview/dist/reactnative/core/scrollcomponent/BaseScrollComponent.js
-+++ b/node_modules/@shopify/flash-list/node_modules/recyclerlistview/dist/reactnative/core/scrollcomponent/BaseScrollComponent.js
-@@ -23,6 +23,9 @@ var BaseScrollComponent = /** @class */ (function (_super) {
- BaseScrollComponent.prototype.getScrollableNode = function () {
- return null;
- };
-+ BaseScrollComponent.prototype.getNativeScrollRef = function () {
-+ return null;
-+ };
- return BaseScrollComponent;
- }(React.Component));
- exports.default = BaseScrollComponent;
-diff --git a/node_modules/@shopify/flash-list/node_modules/recyclerlistview/dist/reactnative/platform/reactnative/scrollcomponent/ScrollComponent.js b/node_modules/@shopify/flash-list/node_modules/recyclerlistview/dist/reactnative/platform/reactnative/scrollcomponent/ScrollComponent.js
-index 2f7033c..a559e18 100644
---- a/node_modules/@shopify/flash-list/node_modules/recyclerlistview/dist/reactnative/platform/reactnative/scrollcomponent/ScrollComponent.js
-+++ b/node_modules/@shopify/flash-list/node_modules/recyclerlistview/dist/reactnative/platform/reactnative/scrollcomponent/ScrollComponent.js
-@@ -76,6 +76,9 @@ var ScrollComponent = /** @class */ (function (_super) {
- }
- return null;
- };
-+ ScrollComponent.prototype.getNativeScrollRef = function () {
-+ return this._scrollViewRef;
-+ };
- ScrollComponent.prototype.render = function () {
- var Scroller = TSCast_1.default.cast(this.props.externalScrollView); //TSI
- var renderContentContainer = this.props.renderContentContainer ? this.props.renderContentContainer : this._defaultContainer;
-diff --git a/node_modules/@shopify/flash-list/node_modules/recyclerlistview/src/core/RecyclerListView.tsx b/node_modules/@shopify/flash-list/node_modules/recyclerlistview/src/core/RecyclerListView.tsx
-index 95dd87b..03de7ed 100644
---- a/node_modules/@shopify/flash-list/node_modules/recyclerlistview/src/core/RecyclerListView.tsx
-+++ b/node_modules/@shopify/flash-list/node_modules/recyclerlistview/src/core/RecyclerListView.tsx
-@@ -394,6 +394,13 @@ export default class RecyclerListView
(this.props.externalScrollView); //TSI
- const renderContentContainer = this.props.renderContentContainer ? this.props.renderContentContainer : this._defaultContainer;
diff --git a/patches/@shopify+flash-list+1.6.3.patch b/patches/@shopify+flash-list+1.6.3.patch
deleted file mode 100644
index e3d690055ff8..000000000000
--- a/patches/@shopify+flash-list+1.6.3.patch
+++ /dev/null
@@ -1,1395 +0,0 @@
-diff --git a/node_modules/@shopify/flash-list/CHANGELOG.md b/node_modules/@shopify/flash-list/CHANGELOG.md
-deleted file mode 100644
-index 9c6bc58..0000000
---- a/node_modules/@shopify/flash-list/CHANGELOG.md
-+++ /dev/null
-@@ -1,279 +0,0 @@
--# Changelog
--
--All notable changes to this project will be documented in this file.
--
--The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
--and adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
--
--## [Unreleased]
--
--## [1.6.3] - 2023-11-09
--
--- Changes for RN 0.73 support
-- - https://github.com/Shopify/flash-list/pull/930
--
--## [1.6.2] - 2023-10-19
--
--- Move shouldRefreshWithAnchoring configuration so it is possible to disable it from outside FlashList by invalidating layout
-- - https://github.com/Shopify/flash-list/pull/935
--
--## [1.6.1] - 2023-09-14
--
--- Prevent an expired layout provider from being used again
-- - https://github.com/Shopify/flash-list/pull/915
--
--## [1.6.0] - 2023-09-13
--
--- Update types to match `react-native@0.72` view types.
-- - https://github.com/Shopify/flash-list/pull/890
--- Add option to clear cached layouts on update
-- - https://github.com/Shopify/flash-list/pull/910
--
--## [1.5.0] - 2023-07-12
--
--- Update kotlin version to 1.8.10 for RN 0.72 compatibility
-- - https://github.com/Shopify/flash-list/pull/865
--
--## [1.4.3] - 2023-04-24
--
--- Fix definition conflicts with previous value
-- - https://github.com/Shopify/flash-list/pull/795
--- Fix Android unit test
-- - https://github.com/Shopify/flash-list/pull/815
--- Fix performance issues with inverted lists on Android
-- - https://github.com/Shopify/flash-list/pull/819
--
--## [1.4.2] - 2023-03-20
--
--- Apply layout correction only to consecutive cells
-- - https://github.com/Shopify/flash-list/pull/788
--
--## [1.4.1] - 2023-01-24
--
--- Prevent overflow of sticky headers
-- - https://github.com/Shopify/flash-list/pull/714
--- Skip footer correction when layout correction is skipped
-- - https://github.com/Shopify/flash-list/pull/743
--
--## [1.4.0] - 2022-11-07
--
--- Add content padding support to FlashList
-- - https://github.com/Shopify/flash-list/pull/626
--- Upgrade recyclerlistview to v4.2.0
-- - https://github.com/Shopify/flash-list/pull/660
--
--## [1.3.1] - 2022-10-11
--
--- Expose `columnIndex` and `columnSpan` to `MasonryFlashList.renderItem`
-- - https://github.com/Shopify/flash-list/pull/625
--
--## [1.3.0] - 2022-09-26
--
--- Added `MasonryFlashList` which adds support for rendering masonry layouts
-- - https://github.com/Shopify/flash-list/pull/587
--
--## [1.2.2] - 2022-09-06
--
--- Fixes type checking error in `AutoLayoutView` due to `children` not being an explicit type
-- - https://github.com/Shopify/flash-list/pull/567
--
--## [1.2.1] - 2022-08-03
--
--- Fixed crash when `estimatedListSize` is used in an empty list
-- - https://github.com/Shopify/flash-list/pull/546
--
--## [1.2.0] - 2022-07-18
--
--- Fixed out of bound read from data
-- - https://github.com/Shopify/flash-list/pull/523
--- Added JS only fallbacks for unsupported platforms
-- - https://github.com/Shopify/flash-list/pull/518
--- Added footer correction in AutoLayoutView
-- - https://github.com/Shopify/flash-list/pull/519
--- Added `viewPosition` and `viewOffset` support scrollTo methods
-- - https://github.com/Shopify/flash-list/pull/521
--- Fix inverted mode while being horizontal
-- - https://github.com/Shopify/flash-list/pull/520
--- Upgrade recyclerlistview to v4.1.1
-- - https://github.com/Shopify/flash-list/pull/526
--
--## [1.1.0] - 2022-07-06
--
--- Added render target info to `renderItem` callback
-- - https://github.com/Shopify/flash-list/pull/454
--- Add Apple TV support
-- - https://github.com/Shopify/flash-list/pull/511
--- Clarify installation instructions in Expo projects
-- - https://github.com/Shopify/flash-list/pull/497
--- Upgrade recyclerlistview to v4.0.1
-- - https://github.com/Shopify/flash-list/pull/507
--- Add tslib as a dependency
-- - https://github.com/Shopify/flash-list/pull/514
--
--## [1.0.4] - 2022-07-02
--
--- Build fix for Android projects having `kotlinVersion` defined in `build.gradle`.
--- Allow providing an external scrollview.
-- - https://github.com/Shopify/flash-list/pull/502
--
--## [1.0.3] - 2022-07-01
--
--- Add kotlin-gradle-plugin to buildscript in project build.gradle
-- - https://github.com/Shopify/flash-list/pull/481
--
--## [1.0.2] - 2022-06-30
--
--- Minor changes
--
--## [1.0.1] - 2022-06-30
--
--- `data` prop change will force update items only if `renderItem` is also updated
-- - https://github.com/Shopify/flash-list/pull/453
--
--## [1.0.0] - 2022-06-17
--
--- Upgrade recyclerlistview to v3.3.0-beta.2
-- - https://github.com/Shopify/flash-list/pull/445
--- Added web support
-- - https://github.com/Shopify/flash-list/pull/444
--- Added `disableAutoLayout` prop to prevent conflicts with custom `CellRendererComponent`
-- - https://github.com/Shopify/flash-list/pull/452
--
--## [0.6.1] - 2022-05-26
--
--- Fix amending layout on iOS
-- - https://github.com/Shopify/flash-list/pull/412
--- Define `FlashList` props previously inherited from `VirtualizedList` and `FlatList` explicitly
-- - https://github.com/Shopify/flash-list/pull/386
--- Make `estimatedItemSize` optional
-- - https://github.com/Shopify/flash-list/pull/378
--- Change `overrideItemType` prop name to `getItemType`
-- - https://github.com/Shopify/flash-list/pull/369
--- Added `useBlankAreaTracker` hook for tracking blank area in production
-- - https://github.com/Shopify/flash-list/pull/411
--- Added `CellRendererComponent` prop
-- - https://github.com/Shopify/flash-list/pull/362
--- Added automatic height measurement for horizontal lists even when parent isn't deterministic
-- - https://github.com/Shopify/flash-list/pull/409
--
--## [0.5.0] - 2022-04-29
--
--- Fix finding props with testId
-- - https://github.com/Shopify/flash-list/pull/357
--- Reuse cached layouts on orientation change
-- - https://github.com/Shopify/flash-list/pull/319
--
--## [0.4.6] - 2022-04-13
--
--- Match FlashList's empty list behavior with FlatList
-- - https://github.com/Shopify/flash-list/pull/312
--
--## [0.4.5] - 2022-04-13
--
--- Upgrade recyclerlistview to v3.2.0-beta.4
--
-- - https://github.com/Shopify/flash-list/pull/315
--
--- Add viewability callbacks
--
-- - https://github.com/Shopify/flash-list/pull/301
--
--- Calculate average item sizes automatically
-- - https://github.com/Shopify/flash-list/pull/296
--
--## [0.4.4] - 2022-04-06
--
--- Fix `FlashList` mock when no data is provided
-- - https://github.com/Shopify/flash-list/pull/295
--
--## [0.4.3] - 2022-04-04
--
--- Reduce number of render item calls
--
-- - https://github.com/Shopify/flash-list/pull/253
--
--- Upgrade recyclerlistview to v3.2.0-beta.2
-- - https://github.com/Shopify/flash-list/pull/284
--
--## [0.4.2] - 2022-04-04
--
--- Minor changes
--
--## [0.4.1] - 2022-03-29
--
--- Crash fix for android activity switching (#256)
--
-- - https://github.com/Shopify/flash-list/pull/257
--
--- initialScrollIndex, scrollTo methods will now account for size of header
--
-- - https://github.com/Shopify/flash-list/pull/194
--
--- Added a new mock for easier testing of components with `FlashList`
-- - https://github.com/Shopify/flash-list/pull/236
--
--## [0.4.0] - 2022-03-23
--
--- Add support for layout animations
--
-- - https://github.com/Shopify/flash-list/pull/183
--
--- Suppress recyclerlistview's bounded size exception for some missing cases.
--
-- - https://github.com/Shopify/flash-list/pull/192
--
--- Expose reference to recyclerlistview and firstItemOffset
--
-- - https://github.com/Shopify/flash-list/pull/217
--
--- recyclerlistview upgraded to v3.1.0-alpha.9
-- - https://github.com/Shopify/flash-list/pull/227
--
--## [0.3.3] - 2022-03-16
--
--- Prevent implicit scroll to top on device orientation change
--- Change recyclerlistview's bounded size exception to a warning
-- - https://github.com/Shopify/flash-list/pull/187
--
--## [0.3.2] - 2022-03-15
--
--- Minor changes
--
--## [0.3.1] - 2022-03-15
--
--- Revert react-native-safe-area upgrade and minSdkVersion bump
-- - https://github.com/Shopify/flash-list/pull/184
--
--## [0.3.0] - 2022-03-15
--
--- Fixed untranspiled library code by enforcing stricter TS rules.
-- - https://github.com/Shopify/flash-list/pull/181
--
--## [0.2.4] - 2022-03-14
--
--- Added `onLoad` event that is called once the list has rendered items. This is required because FlashList doesn't render items in the first cycle.
-- - https://github.com/Shopify/flash-list/pull/180
--
--## [0.2.3] - 2022-03-10
--
--- Fixing publish steps for transpiled code
-- - https://github.com/Shopify/flash-list/pull/150
--
--## [0.2.2] - 2022-03-10
--
--- Fixing publish steps for transpiled code
-- - https://github.com/Shopify/flash-list/pull/149
--
--## [0.2.1] - 2022-03-09
--
--- Bug fix for style and last separator
-- - https://github.com/Shopify/flash-list/pull/141
--
--## [0.2.0] - 2022-03-08
--
--- Rename the component from `RecyclerFlatList` to `FlashList`
-- - https://github.com/Shopify/flash-list/pull/140
--
--## [0.1.0] - 2022-03-02
--
--- Initial release
-diff --git a/node_modules/@shopify/flash-list/RNFlashList.podspec b/node_modules/@shopify/flash-list/RNFlashList.podspec
-index 38ff029..f5f6c80 100644
---- a/node_modules/@shopify/flash-list/RNFlashList.podspec
-+++ b/node_modules/@shopify/flash-list/RNFlashList.podspec
-@@ -2,6 +2,13 @@ require 'json'
-
- package = JSON.parse(File.read(File.join(__dir__, 'package.json')))
-
-+default_config = { 'OTHER_SWIFT_FLAGS' => '-D RCT_NEW_ARCH_ENABLED', }
-+
-+frameworks_flags = {
-+ "OTHER_CFLAGS" => "$(inherited) -DUSE_FRAMEWORKS",
-+ "OTHER_CPLUSPLUSFLAGS" => "$(inherited) -DUSE_FRAMEWORKS",
-+}
-+
- Pod::Spec.new do |s|
- s.name = 'RNFlashList'
- s.version = package['version']
-@@ -9,14 +16,24 @@ Pod::Spec.new do |s|
- s.homepage = package['homepage']
- s.license = package['license']
- s.author = package['author']
-- s.platforms = { :ios => '11.0', :tvos => '12.0' }
- s.source = { git: 'https://github.com/shopify/flash-list.git', tag: "v#{s.version}" }
- s.source_files = 'ios/Sources/**/*'
- s.requires_arc = true
- s.swift_version = '5.0'
-+ s.pod_target_xcconfig = default_config
-
-- # Dependencies
-- s.dependency 'React-Core'
-+ if ENV['USE_FRAMEWORKS'] == '1'
-+ s.pod_target_xcconfig = default_config.merge(frameworks_flags)
-+ end
-+
-+ if defined?(install_modules_dependencies()) != nil
-+ install_modules_dependencies(s)
-+ s.ios.deployment_target = "12.4"
-+ s.platforms = { :ios => '12.4', :tvos => '12.0' }
-+ else
-+ s.dependency "React-Core"
-+ s.platforms = { :ios => '11.0', :tvos => '12.0' }
-+ end
-
- # Tests spec
- s.test_spec 'Tests' do |test_spec|
-diff --git a/node_modules/@shopify/flash-list/android/build.gradle b/node_modules/@shopify/flash-list/android/build.gradle
-index bed08cd..bad4465 100644
---- a/node_modules/@shopify/flash-list/android/build.gradle
-+++ b/node_modules/@shopify/flash-list/android/build.gradle
-@@ -1,7 +1,14 @@
--apply plugin: 'com.android.library'
-+def isNewArchitectureEnabled() {
-+ return project.hasProperty("newArchEnabled") && project.newArchEnabled == "true"
-+}
-
-+apply plugin: 'com.android.library'
- apply plugin: 'kotlin-android'
-
-+if (isNewArchitectureEnabled()) {
-+ apply plugin: 'com.facebook.react'
-+}
-+
- def _ext = rootProject.ext
-
- def _reactNativeVersion = _ext.has('reactNative') ? _ext.reactNative : '+'
-@@ -40,11 +47,19 @@ android {
- debug.java.srcDirs += 'src/debug/kotlin'
- test.java.srcDirs += 'src/test/kotlin'
- androidTest.java.srcDirs += 'src/androidTest/kotlin'
-+
-+ if (isNewArchitectureEnabled()) {
-+ main.java.srcDirs += ["${project.buildDir}/generated/source/codegen/java"]
-+ main.java.srcDirs += ["src/fabric/java"]
-+ } else {
-+ main.java.srcDirs += ['src/paper/java']
-+ }
- }
-
- defaultConfig {
- minSdkVersion _minSdkVersion
- targetSdkVersion _targetSdkVersion
-+ buildConfigField "boolean", "IS_NEW_ARCHITECTURE_ENABLED", isNewArchitectureEnabled().toString()
- versionCode 1
- versionName "1.0"
- testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
-@@ -61,7 +76,7 @@ android {
-
- dependencies {
- compileOnly "com.facebook.react:react-native:${_reactNativeVersion}"
-- implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:${_kotlinVersion}"
-+ implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:${_kotlinVersion}"
- testImplementation "junit:junit:${_junitVersion}"
- testImplementation "org.mockito.kotlin:mockito-kotlin:${_mockitoVersion}"
- testImplementation "org.mockito:mockito-inline:${_mockitoVersion}"
-diff --git a/node_modules/@shopify/flash-list/android/src/fabric/java/com/shopify/reactnative/flash_list/ReactContextExtensions.kt b/node_modules/@shopify/flash-list/android/src/fabric/java/com/shopify/reactnative/flash_list/ReactContextExtensions.kt
-new file mode 100644
-index 0000000..6e850fe
---- /dev/null
-+++ b/node_modules/@shopify/flash-list/android/src/fabric/java/com/shopify/reactnative/flash_list/ReactContextExtensions.kt
-@@ -0,0 +1,12 @@
-+package com.shopify.reactnative.flash_list
-+
-+import com.facebook.react.bridge.ReactContext
-+import com.facebook.react.fabric.FabricUIManager
-+import com.facebook.react.uimanager.UIManagerHelper
-+import com.facebook.react.uimanager.common.UIManagerType
-+import com.facebook.react.bridge.WritableMap
-+
-+fun ReactContext.dispatchEvent(nativeTag: Int, eventName: String, event: WritableMap) {
-+ val fabricUIManager = UIManagerHelper.getUIManager(this, UIManagerType.FABRIC) as FabricUIManager
-+ fabricUIManager.receiveEvent(nativeTag, eventName, event)
-+}
-diff --git a/node_modules/@shopify/flash-list/android/src/main/kotlin/com/shopify/reactnative/flash_list/AutoLayoutView.kt b/node_modules/@shopify/flash-list/android/src/main/kotlin/com/shopify/reactnative/flash_list/AutoLayoutView.kt
-index 4571798..c6c3cce 100644
---- a/node_modules/@shopify/flash-list/android/src/main/kotlin/com/shopify/reactnative/flash_list/AutoLayoutView.kt
-+++ b/node_modules/@shopify/flash-list/android/src/main/kotlin/com/shopify/reactnative/flash_list/AutoLayoutView.kt
-@@ -2,8 +2,6 @@ package com.shopify.reactnative.flash_list
-
- import android.content.Context
- import android.graphics.Canvas
--import android.util.DisplayMetrics
--import android.util.Log
- import android.view.View
- import android.view.ViewGroup
- import android.widget.HorizontalScrollView
-@@ -11,7 +9,6 @@ import android.widget.ScrollView
- import com.facebook.react.bridge.Arguments
- import com.facebook.react.bridge.ReactContext
- import com.facebook.react.bridge.WritableMap
--import com.facebook.react.uimanager.events.RCTEventEmitter
- import com.facebook.react.views.view.ReactViewGroup
-
-
-@@ -142,9 +139,8 @@ class AutoLayoutView(context: Context) : ReactViewGroup(context) {
- val event: WritableMap = Arguments.createMap()
- event.putDouble("offsetStart", alShadow.blankOffsetAtStart / pixelDensity)
- event.putDouble("offsetEnd", alShadow.blankOffsetAtEnd / pixelDensity)
-+
- val reactContext = context as ReactContext
-- reactContext
-- .getJSModule(RCTEventEmitter::class.java)
-- .receiveEvent(id, "onBlankAreaEvent", event)
-+ reactContext.dispatchEvent(id, "onBlankAreaEvent", event)
- }
- }
-diff --git a/node_modules/@shopify/flash-list/android/src/main/kotlin/com/shopify/reactnative/flash_list/AutoLayoutViewManager.kt b/node_modules/@shopify/flash-list/android/src/main/kotlin/com/shopify/reactnative/flash_list/AutoLayoutViewManager.kt
-index b646f09..7bda0ea 100644
---- a/node_modules/@shopify/flash-list/android/src/main/kotlin/com/shopify/reactnative/flash_list/AutoLayoutViewManager.kt
-+++ b/node_modules/@shopify/flash-list/android/src/main/kotlin/com/shopify/reactnative/flash_list/AutoLayoutViewManager.kt
-@@ -3,15 +3,22 @@ package com.shopify.reactnative.flash_list
- import com.facebook.react.module.annotations.ReactModule
- import com.facebook.react.uimanager.ThemedReactContext
- import com.facebook.react.uimanager.annotations.ReactProp
--import com.facebook.react.views.view.ReactViewGroup
--import com.facebook.react.views.view.ReactViewManager
-+import com.facebook.react.uimanager.ViewGroupManager
-+import com.facebook.react.uimanager.ViewManagerDelegate
-+import com.facebook.react.viewmanagers.AutoLayoutViewManagerDelegate
-+import com.facebook.react.viewmanagers.AutoLayoutViewManagerInterface
- import com.facebook.react.common.MapBuilder
- import kotlin.math.roundToInt
-
- /** ViewManager for AutoLayoutView - Container for all RecyclerListView children. Automatically removes all gaps and overlaps for GridLayouts with flexible spans.
- * Note: This cannot work for masonry layouts i.e, pinterest like layout */
- @ReactModule(name = AutoLayoutViewManager.REACT_CLASS)
--class AutoLayoutViewManager: ReactViewManager() {
-+class AutoLayoutViewManager: ViewGroupManager(), AutoLayoutViewManagerInterface {
-+ private val mDelegate: ViewManagerDelegate
-+
-+ init {
-+ mDelegate = AutoLayoutViewManagerDelegate(this)
-+ }
-
- companion object {
- const val REACT_CLASS = "AutoLayoutView"
-@@ -21,45 +28,42 @@ class AutoLayoutViewManager: ReactViewManager() {
- return REACT_CLASS
- }
-
-- override fun createViewInstance(context: ThemedReactContext): ReactViewGroup {
-+ override fun createViewInstance(context: ThemedReactContext): AutoLayoutView {
- return AutoLayoutView(context).also { it.pixelDensity = context.resources.displayMetrics.density.toDouble() }
- }
-
-- override fun getExportedCustomDirectEventTypeConstants(): MutableMap {
-- return MapBuilder.builder().put(
-- "onBlankAreaEvent",
-- MapBuilder.of(
-- "registrationName", "onBlankAreaEvent")
-- ).build();
-- }
-+ override fun getExportedCustomDirectEventTypeConstants() = mutableMapOf(
-+ "onBlankAreaEvent" to mutableMapOf("registrationName" to "onBlankAreaEvent"),
-+ "topOnBlankAreaEvent" to mutableMapOf("registrationName" to "onBlankAreaEvent"),
-+ )
-
- @ReactProp(name = "horizontal")
-- fun setHorizontal(view: AutoLayoutView, isHorizontal: Boolean) {
-+ override fun setHorizontal(view: AutoLayoutView, isHorizontal: Boolean) {
- view.alShadow.horizontal = isHorizontal
- }
-
- @ReactProp(name = "disableAutoLayout")
-- fun setDisableAutoLayout(view: AutoLayoutView, disableAutoLayout: Boolean) {
-+ override fun setDisableAutoLayout(view: AutoLayoutView, disableAutoLayout: Boolean) {
- view.disableAutoLayout = disableAutoLayout
- }
-
- @ReactProp(name = "scrollOffset")
-- fun setScrollOffset(view: AutoLayoutView, scrollOffset: Double) {
-+ override fun setScrollOffset(view: AutoLayoutView, scrollOffset: Double) {
- view.alShadow.scrollOffset = convertToPixelLayout(scrollOffset, view.pixelDensity)
- }
-
- @ReactProp(name = "windowSize")
-- fun setWindowSize(view: AutoLayoutView, windowSize: Double) {
-+ override fun setWindowSize(view: AutoLayoutView, windowSize: Double) {
- view.alShadow.windowSize = convertToPixelLayout(windowSize, view.pixelDensity)
- }
-
- @ReactProp(name = "renderAheadOffset")
-- fun setRenderAheadOffset(view: AutoLayoutView, renderOffset: Double) {
-+ override fun setRenderAheadOffset(view: AutoLayoutView, renderOffset: Double) {
- view.alShadow.renderOffset = convertToPixelLayout(renderOffset, view.pixelDensity)
- }
-
- @ReactProp(name = "enableInstrumentation")
-- fun setEnableInstrumentation(view: AutoLayoutView, enableInstrumentation: Boolean) {
-+ override fun setEnableInstrumentation(view: AutoLayoutView, enableInstrumentation: Boolean) {
- view.enableInstrumentation = enableInstrumentation
- }
-
-diff --git a/node_modules/@shopify/flash-list/android/src/main/kotlin/com/shopify/reactnative/flash_list/CellContainerManager.kt b/node_modules/@shopify/flash-list/android/src/main/kotlin/com/shopify/reactnative/flash_list/CellContainerManager.kt
-index 1434caa..590ba1d 100644
---- a/node_modules/@shopify/flash-list/android/src/main/kotlin/com/shopify/reactnative/flash_list/CellContainerManager.kt
-+++ b/node_modules/@shopify/flash-list/android/src/main/kotlin/com/shopify/reactnative/flash_list/CellContainerManager.kt
-@@ -2,12 +2,20 @@ package com.shopify.reactnative.flash_list
-
- import com.facebook.react.module.annotations.ReactModule
- import com.facebook.react.uimanager.ThemedReactContext
-+import com.facebook.react.uimanager.ViewGroupManager
-+import com.facebook.react.uimanager.ViewManagerDelegate
- import com.facebook.react.uimanager.annotations.ReactProp
--import com.facebook.react.views.view.ReactViewGroup
--import com.facebook.react.views.view.ReactViewManager
-+import com.facebook.react.viewmanagers.CellContainerManagerDelegate
-+import com.facebook.react.viewmanagers.CellContainerManagerInterface
-
- @ReactModule(name = AutoLayoutViewManager.REACT_CLASS)
--class CellContainerManager: ReactViewManager() {
-+class CellContainerManager: ViewGroupManager(), CellContainerManagerInterface {
-+ private val mDelegate: ViewManagerDelegate
-+
-+ init {
-+ mDelegate = CellContainerManagerDelegate(this);
-+ }
-+
- companion object {
- const val REACT_CLASS = "CellContainer"
- }
-@@ -16,12 +24,12 @@ class CellContainerManager: ReactViewManager() {
- return REACT_CLASS
- }
-
-- override fun createViewInstance(context: ThemedReactContext): ReactViewGroup {
-+ override fun createViewInstance(context: ThemedReactContext): CellContainerImpl {
- return CellContainerImpl(context)
- }
-
- @ReactProp(name = "index")
-- fun setIndex(view: CellContainerImpl, index: Int) {
-+ override fun setIndex(view: CellContainerImpl, index: Int) {
- view.index = index
- }
- }
-diff --git a/node_modules/@shopify/flash-list/android/src/paper/java/com/facebook/react/viewmanagers/AutoLayoutViewManagerDelegate.java b/node_modules/@shopify/flash-list/android/src/paper/java/com/facebook/react/viewmanagers/AutoLayoutViewManagerDelegate.java
-new file mode 100644
-index 0000000..4c90807
---- /dev/null
-+++ b/node_modules/@shopify/flash-list/android/src/paper/java/com/facebook/react/viewmanagers/AutoLayoutViewManagerDelegate.java
-@@ -0,0 +1,46 @@
-+/**
-+* This code was generated by [react-native-codegen](https://www.npmjs.com/package/react-native-codegen).
-+*
-+* Do not edit this file as changes may cause incorrect behavior and will be lost
-+* once the code is regenerated.
-+*
-+* @generated by codegen project: GeneratePropsJavaDelegate.js
-+*/
-+
-+package com.facebook.react.viewmanagers;
-+
-+import android.view.View;
-+import androidx.annotation.Nullable;
-+import com.facebook.react.uimanager.BaseViewManagerDelegate;
-+import com.facebook.react.uimanager.BaseViewManagerInterface;
-+
-+public class AutoLayoutViewManagerDelegate & AutoLayoutViewManagerInterface> extends BaseViewManagerDelegate {
-+ public AutoLayoutViewManagerDelegate(U viewManager) {
-+ super(viewManager);
-+ }
-+ @Override
-+ public void setProperty(T view, String propName, @Nullable Object value) {
-+ switch (propName) {
-+ case "horizontal":
-+ mViewManager.setHorizontal(view, value == null ? false : (boolean) value);
-+ break;
-+ case "scrollOffset":
-+ mViewManager.setScrollOffset(view, value == null ? 0f : ((Double) value).doubleValue());
-+ break;
-+ case "windowSize":
-+ mViewManager.setWindowSize(view, value == null ? 0f : ((Double) value).doubleValue());
-+ break;
-+ case "renderAheadOffset":
-+ mViewManager.setRenderAheadOffset(view, value == null ? 0f : ((Double) value).doubleValue());
-+ break;
-+ case "enableInstrumentation":
-+ mViewManager.setEnableInstrumentation(view, value == null ? false : (boolean) value);
-+ break;
-+ case "disableAutoLayout":
-+ mViewManager.setDisableAutoLayout(view, value == null ? false : (boolean) value);
-+ break;
-+ default:
-+ super.setProperty(view, propName, value);
-+ }
-+ }
-+}
-diff --git a/node_modules/@shopify/flash-list/android/src/paper/java/com/facebook/react/viewmanagers/AutoLayoutViewManagerInterface.java b/node_modules/@shopify/flash-list/android/src/paper/java/com/facebook/react/viewmanagers/AutoLayoutViewManagerInterface.java
-new file mode 100644
-index 0000000..8ad2622
---- /dev/null
-+++ b/node_modules/@shopify/flash-list/android/src/paper/java/com/facebook/react/viewmanagers/AutoLayoutViewManagerInterface.java
-@@ -0,0 +1,21 @@
-+/**
-+* This code was generated by [react-native-codegen](https://www.npmjs.com/package/react-native-codegen).
-+*
-+* Do not edit this file as changes may cause incorrect behavior and will be lost
-+* once the code is regenerated.
-+*
-+* @generated by codegen project: GeneratePropsJavaInterface.js
-+*/
-+
-+package com.facebook.react.viewmanagers;
-+
-+import android.view.View;
-+
-+public interface AutoLayoutViewManagerInterface {
-+ void setHorizontal(T view, boolean value);
-+ void setScrollOffset(T view, double value);
-+ void setWindowSize(T view, double value);
-+ void setRenderAheadOffset(T view, double value);
-+ void setEnableInstrumentation(T view, boolean value);
-+ void setDisableAutoLayout(T view, boolean value);
-+}
-diff --git a/node_modules/@shopify/flash-list/android/src/paper/java/com/facebook/react/viewmanagers/CellContainerManagerDelegate.java b/node_modules/@shopify/flash-list/android/src/paper/java/com/facebook/react/viewmanagers/CellContainerManagerDelegate.java
-new file mode 100644
-index 0000000..2b64af7
---- /dev/null
-+++ b/node_modules/@shopify/flash-list/android/src/paper/java/com/facebook/react/viewmanagers/CellContainerManagerDelegate.java
-@@ -0,0 +1,31 @@
-+/**
-+* This code was generated by [react-native-codegen](https://www.npmjs.com/package/react-native-codegen).
-+*
-+* Do not edit this file as changes may cause incorrect behavior and will be lost
-+* once the code is regenerated.
-+*
-+* @generated by codegen project: GeneratePropsJavaDelegate.js
-+*/
-+
-+package com.facebook.react.viewmanagers;
-+
-+import android.view.View;
-+import androidx.annotation.Nullable;
-+import com.facebook.react.uimanager.BaseViewManagerDelegate;
-+import com.facebook.react.uimanager.BaseViewManagerInterface;
-+
-+public class CellContainerManagerDelegate & CellContainerManagerInterface> extends BaseViewManagerDelegate {
-+ public CellContainerManagerDelegate(U viewManager) {
-+ super(viewManager);
-+ }
-+ @Override
-+ public void setProperty(T view, String propName, @Nullable Object value) {
-+ switch (propName) {
-+ case "index":
-+ mViewManager.setIndex(view, value == null ? 0 : ((Double) value).intValue());
-+ break;
-+ default:
-+ super.setProperty(view, propName, value);
-+ }
-+ }
-+}
-diff --git a/node_modules/@shopify/flash-list/android/src/paper/java/com/facebook/react/viewmanagers/CellContainerManagerInterface.java b/node_modules/@shopify/flash-list/android/src/paper/java/com/facebook/react/viewmanagers/CellContainerManagerInterface.java
-new file mode 100644
-index 0000000..b37ddbd
---- /dev/null
-+++ b/node_modules/@shopify/flash-list/android/src/paper/java/com/facebook/react/viewmanagers/CellContainerManagerInterface.java
-@@ -0,0 +1,16 @@
-+/**
-+* This code was generated by [react-native-codegen](https://www.npmjs.com/package/react-native-codegen).
-+*
-+* Do not edit this file as changes may cause incorrect behavior and will be lost
-+* once the code is regenerated.
-+*
-+* @generated by codegen project: GeneratePropsJavaInterface.js
-+*/
-+
-+package com.facebook.react.viewmanagers;
-+
-+import android.view.View;
-+
-+public interface CellContainerManagerInterface {
-+ void setIndex(T view, int value);
-+}
-diff --git a/node_modules/@shopify/flash-list/android/src/paper/java/com/shopify/reactnative/flash_list/ReactContextExtensions.kt b/node_modules/@shopify/flash-list/android/src/paper/java/com/shopify/reactnative/flash_list/ReactContextExtensions.kt
-new file mode 100644
-index 0000000..b867c18
---- /dev/null
-+++ b/node_modules/@shopify/flash-list/android/src/paper/java/com/shopify/reactnative/flash_list/ReactContextExtensions.kt
-@@ -0,0 +1,10 @@
-+package com.shopify.reactnative.flash_list
-+
-+import com.facebook.react.bridge.ReactContext
-+import com.facebook.react.uimanager.events.RCTEventEmitter
-+import com.facebook.react.bridge.WritableMap
-+
-+fun ReactContext.dispatchEvent(nativeTag: Int, eventName: String, event: WritableMap) {
-+ this.getJSModule(RCTEventEmitter::class.java)
-+ .receiveEvent(nativeTag, eventName, event)
-+}
-diff --git a/node_modules/@shopify/flash-list/dist/FlashList.d.ts b/node_modules/@shopify/flash-list/dist/FlashList.d.ts
-index ed81d7d..1e24103 100644
---- a/node_modules/@shopify/flash-list/dist/FlashList.d.ts
-+++ b/node_modules/@shopify/flash-list/dist/FlashList.d.ts
-@@ -103,6 +103,7 @@ declare class FlashList extends React.PureComponent, FlashL
- offset: number;
- }): void;
- getScrollableNode(): number | null;
-+ getNativeScrollRef(): number | null;
- /**
- * Allows access to internal recyclerlistview. This is useful for enabling access to its public APIs.
- * Warning: We may swap recyclerlistview for something else in the future. Use with caution.
-diff --git a/node_modules/@shopify/flash-list/dist/FlashList.d.ts.map b/node_modules/@shopify/flash-list/dist/FlashList.d.ts.map
-index 97c8cc6..a915a05 100644
---- a/node_modules/@shopify/flash-list/dist/FlashList.d.ts.map
-+++ b/node_modules/@shopify/flash-list/dist/FlashList.d.ts.map
-@@ -1 +1 @@
--{"version":3,"file":"FlashList.d.ts","sourceRoot":"","sources":["../src/FlashList.tsx"],"names":[],"mappings":"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"}
-\ No newline at end of file
-+{"version":3,"file":"FlashList.d.ts","sourceRoot":"","sources":["../src/FlashList.tsx"],"names":[],"mappings":"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"}
-\ No newline at end of file
-diff --git a/node_modules/@shopify/flash-list/dist/FlashList.js b/node_modules/@shopify/flash-list/dist/FlashList.js
-index 1a5d026..37852b8 100644
---- a/node_modules/@shopify/flash-list/dist/FlashList.js
-+++ b/node_modules/@shopify/flash-list/dist/FlashList.js
-@@ -478,6 +478,12 @@ var FlashList = /** @class */ (function (_super) {
- var _a, _b;
- return ((_b = (_a = this.rlvRef) === null || _a === void 0 ? void 0 : _a.getScrollableNode) === null || _b === void 0 ? void 0 : _b.call(_a)) || null;
- };
-+ FlashList.prototype.getNativeScrollRef = function () {
-+ var _a, _b;
-+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
-+ // @ts-ignore
-+ return ((_b = (_a = this.rlvRef) === null || _a === void 0 ? void 0 : _a.getNativeScrollRef) === null || _b === void 0 ? void 0 : _b.call(_a)) || null;
-+ };
- Object.defineProperty(FlashList.prototype, "recyclerlistview_unsafe", {
- /**
- * Allows access to internal recyclerlistview. This is useful for enabling access to its public APIs.
-diff --git a/node_modules/@shopify/flash-list/dist/FlashList.js.map b/node_modules/@shopify/flash-list/dist/FlashList.js.map
-index 375f7fc..8fc9c3f 100644
---- a/node_modules/@shopify/flash-list/dist/FlashList.js.map
-+++ b/node_modules/@shopify/flash-list/dist/FlashList.js.map
-@@ -1 +1 @@
--{"version":3,"file":"FlashList.js","sourceRoot":"","sources":["../src/FlashList.tsx"],"names":[],"mappings":"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"}
-\ No newline at end of file
-+{"version":3,"file":"FlashList.js","sourceRoot":"","sources":["../src/FlashList.tsx"],"names":[],"mappings":"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"}
-\ No newline at end of file
-diff --git a/node_modules/@shopify/flash-list/dist/fabric/AutoLayoutNativeComponent.d.ts b/node_modules/@shopify/flash-list/dist/fabric/AutoLayoutNativeComponent.d.ts
-new file mode 100644
-index 0000000..661dc81
---- /dev/null
-+++ b/node_modules/@shopify/flash-list/dist/fabric/AutoLayoutNativeComponent.d.ts
-@@ -0,0 +1,18 @@
-+import type { ViewProps } from "react-native";
-+import type { Int32, Double, DirectEventHandler } from "react-native/Libraries/Types/CodegenTypes";
-+declare type BlankAreaEvent = Readonly<{
-+ offsetStart: Int32;
-+ offsetEnd: Int32;
-+}>;
-+interface NativeProps extends ViewProps {
-+ horizontal?: boolean;
-+ scrollOffset?: Double;
-+ windowSize?: Double;
-+ renderAheadOffset?: Double;
-+ enableInstrumentation?: boolean;
-+ disableAutoLayout?: boolean;
-+ onBlankAreaEvent?: DirectEventHandler;
-+}
-+declare const _default: import("react-native/Libraries/Utilities/codegenNativeComponent").NativeComponentType;
-+export default _default;
-+//# sourceMappingURL=AutoLayoutNativeComponent.d.ts.map
-\ No newline at end of file
-diff --git a/node_modules/@shopify/flash-list/dist/fabric/AutoLayoutNativeComponent.d.ts.map b/node_modules/@shopify/flash-list/dist/fabric/AutoLayoutNativeComponent.d.ts.map
-new file mode 100644
-index 0000000..25297b9
---- /dev/null
-+++ b/node_modules/@shopify/flash-list/dist/fabric/AutoLayoutNativeComponent.d.ts.map
-@@ -0,0 +1 @@
-+{"version":3,"file":"AutoLayoutNativeComponent.d.ts","sourceRoot":"","sources":["../../src/fabric/AutoLayoutNativeComponent.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAC9C,OAAO,KAAK,EACV,KAAK,EACL,MAAM,EACN,kBAAkB,EACnB,MAAM,2CAA2C,CAAC;AAEnD,aAAK,cAAc,GAAG,QAAQ,CAAC;IAC7B,WAAW,EAAE,KAAK,CAAC;IACnB,SAAS,EAAE,KAAK,CAAC;CAClB,CAAC,CAAC;AAEH,UAAU,WAAY,SAAQ,SAAS;IACrC,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,qBAAqB,CAAC,EAAE,OAAO,CAAC;IAChC,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B,gBAAgB,CAAC,EAAE,kBAAkB,CAAC,cAAc,CAAC,CAAC;CACvD;;AAED,wBAAqE"}
-\ No newline at end of file
-diff --git a/node_modules/@shopify/flash-list/dist/fabric/AutoLayoutNativeComponent.js b/node_modules/@shopify/flash-list/dist/fabric/AutoLayoutNativeComponent.js
-new file mode 100644
-index 0000000..c69ba24
---- /dev/null
-+++ b/node_modules/@shopify/flash-list/dist/fabric/AutoLayoutNativeComponent.js
-@@ -0,0 +1,6 @@
-+"use strict";
-+Object.defineProperty(exports, "__esModule", { value: true });
-+var tslib_1 = require("tslib");
-+var codegenNativeComponent_1 = tslib_1.__importDefault(require("react-native/Libraries/Utilities/codegenNativeComponent"));
-+exports.default = (0, codegenNativeComponent_1.default)("AutoLayoutView");
-+//# sourceMappingURL=AutoLayoutNativeComponent.js.map
-\ No newline at end of file
-diff --git a/node_modules/@shopify/flash-list/dist/fabric/AutoLayoutNativeComponent.js.map b/node_modules/@shopify/flash-list/dist/fabric/AutoLayoutNativeComponent.js.map
-new file mode 100644
-index 0000000..890db63
---- /dev/null
-+++ b/node_modules/@shopify/flash-list/dist/fabric/AutoLayoutNativeComponent.js.map
-@@ -0,0 +1 @@
-+{"version":3,"file":"AutoLayoutNativeComponent.js","sourceRoot":"","sources":["../../src/fabric/AutoLayoutNativeComponent.ts"],"names":[],"mappings":";;;AAAA,2HAA6F;AAuB7F,kBAAe,IAAA,gCAAsB,EAAc,gBAAgB,CAAC,CAAC"}
-\ No newline at end of file
-diff --git a/node_modules/@shopify/flash-list/dist/fabric/CellContainerNativeComponent.d.ts b/node_modules/@shopify/flash-list/dist/fabric/CellContainerNativeComponent.d.ts
-new file mode 100644
-index 0000000..bb8f63b
---- /dev/null
-+++ b/node_modules/@shopify/flash-list/dist/fabric/CellContainerNativeComponent.d.ts
-@@ -0,0 +1,8 @@
-+import type { Int32 } from "react-native/Libraries/Types/CodegenTypes";
-+import type { ViewProps } from "react-native";
-+interface NativeProps extends ViewProps {
-+ index?: Int32;
-+}
-+declare const _default: import("react-native/Libraries/Utilities/codegenNativeComponent").NativeComponentType;
-+export default _default;
-+//# sourceMappingURL=CellContainerNativeComponent.d.ts.map
-\ No newline at end of file
-diff --git a/node_modules/@shopify/flash-list/dist/fabric/CellContainerNativeComponent.d.ts.map b/node_modules/@shopify/flash-list/dist/fabric/CellContainerNativeComponent.d.ts.map
-new file mode 100644
-index 0000000..993a2c3
---- /dev/null
-+++ b/node_modules/@shopify/flash-list/dist/fabric/CellContainerNativeComponent.d.ts.map
-@@ -0,0 +1 @@
-+{"version":3,"file":"CellContainerNativeComponent.d.ts","sourceRoot":"","sources":["../../src/fabric/CellContainerNativeComponent.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,2CAA2C,CAAC;AACvE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAE9C,UAAU,WAAY,SAAQ,SAAS;IACrC,KAAK,CAAC,EAAE,KAAK,CAAC;CACf;;AAED,wBAAoE"}
-\ No newline at end of file
-diff --git a/node_modules/@shopify/flash-list/dist/fabric/CellContainerNativeComponent.js b/node_modules/@shopify/flash-list/dist/fabric/CellContainerNativeComponent.js
-new file mode 100644
-index 0000000..b21acb7
---- /dev/null
-+++ b/node_modules/@shopify/flash-list/dist/fabric/CellContainerNativeComponent.js
-@@ -0,0 +1,6 @@
-+"use strict";
-+Object.defineProperty(exports, "__esModule", { value: true });
-+var tslib_1 = require("tslib");
-+var codegenNativeComponent_1 = tslib_1.__importDefault(require("react-native/Libraries/Utilities/codegenNativeComponent"));
-+exports.default = (0, codegenNativeComponent_1.default)("CellContainer");
-+//# sourceMappingURL=CellContainerNativeComponent.js.map
-\ No newline at end of file
-diff --git a/node_modules/@shopify/flash-list/dist/fabric/CellContainerNativeComponent.js.map b/node_modules/@shopify/flash-list/dist/fabric/CellContainerNativeComponent.js.map
-new file mode 100644
-index 0000000..d7c2469
---- /dev/null
-+++ b/node_modules/@shopify/flash-list/dist/fabric/CellContainerNativeComponent.js.map
-@@ -0,0 +1 @@
-+{"version":3,"file":"CellContainerNativeComponent.js","sourceRoot":"","sources":["../../src/fabric/CellContainerNativeComponent.ts"],"names":[],"mappings":";;;AAAA,2HAA6F;AAQ7F,kBAAe,IAAA,gCAAsB,EAAc,eAAe,CAAC,CAAC"}
-\ No newline at end of file
-diff --git a/node_modules/@shopify/flash-list/dist/tsconfig.tsbuildinfo b/node_modules/@shopify/flash-list/dist/tsconfig.tsbuildinfo
-deleted file mode 100644
-index 023b94a..0000000
---- a/node_modules/@shopify/flash-list/dist/tsconfig.tsbuildinfo
-+++ /dev/null
-@@ -1 +0,0 @@
--{"program":{"fileNames":["../node_modules/typescript/lib/lib.es5.d.ts","../node_modules/typescript/lib/lib.es2015.d.ts","../node_modules/typescript/lib/lib.es2016.d.ts","../node_modules/typescript/lib/lib.es2017.d.ts","../node_modules/typescript/lib/lib.es2018.d.ts","../node_modules/typescript/lib/lib.es2019.d.ts","../node_modules/typescript/lib/lib.es2020.d.ts","../node_modules/typescript/lib/lib.dom.d.ts","../node_modules/typescript/lib/lib.dom.iterable.d.ts","../node_modules/typescript/lib/lib.es2015.core.d.ts","../node_modules/typescript/lib/lib.es2015.collection.d.ts","../node_modules/typescript/lib/lib.es2015.generator.d.ts","../node_modules/typescript/lib/lib.es2015.iterable.d.ts","../node_modules/typescript/lib/lib.es2015.promise.d.ts","../node_modules/typescript/lib/lib.es2015.proxy.d.ts","../node_modules/typescript/lib/lib.es2015.reflect.d.ts","../node_modules/typescript/lib/lib.es2015.symbol.d.ts","../node_modules/typescript/lib/lib.es2015.symbol.wellknown.d.ts","../node_modules/typescript/lib/lib.es2016.array.include.d.ts","../node_modules/typescript/lib/lib.es2017.object.d.ts","../node_modules/typescript/lib/lib.es2017.sharedmemory.d.ts","../node_modules/typescript/lib/lib.es2017.string.d.ts","../node_modules/typescript/lib/lib.es2017.intl.d.ts","../node_modules/typescript/lib/lib.es2017.typedarrays.d.ts","../node_modules/typescript/lib/lib.es2018.asyncgenerator.d.ts","../node_modules/typescript/lib/lib.es2018.asynciterable.d.ts","../node_modules/typescript/lib/lib.es2018.intl.d.ts","../node_modules/typescript/lib/lib.es2018.promise.d.ts","../node_modules/typescript/lib/lib.es2018.regexp.d.ts","../node_modules/typescript/lib/lib.es2019.array.d.ts","../node_modules/typescript/lib/lib.es2019.object.d.ts","../node_modules/typescript/lib/lib.es2019.string.d.ts","../node_modules/typescript/lib/lib.es2019.symbol.d.ts","../node_modules/typescript/lib/lib.es2020.bigint.d.ts","../node_modules/typescript/lib/lib.es2020.date.d.ts","../node_modules/typescript/lib/lib.es2020.promise.d.ts","../node_modules/typescript/lib/lib.es2020.sharedmemory.d.ts","../node_modules/typescript/lib/lib.es2020.string.d.ts","../node_modules/typescript/lib/lib.es2020.symbol.wellknown.d.ts","../node_modules/typescript/lib/lib.es2020.intl.d.ts","../node_modules/typescript/lib/lib.es2020.number.d.ts","../node_modules/typescript/lib/lib.esnext.intl.d.ts","../node_modules/tslib/tslib.d.ts","../node_modules/@types/react-native/modules/BatchedBridge.d.ts","../node_modules/@types/react-native/modules/Codegen.d.ts","../node_modules/@types/react-native/modules/Devtools.d.ts","../node_modules/@types/react-native/modules/globals.d.ts","../node_modules/@types/react-native/modules/LaunchScreen.d.ts","../node_modules/@types/react/global.d.ts","../node_modules/csstype/index.d.ts","../node_modules/@types/prop-types/index.d.ts","../node_modules/@types/scheduler/tracing.d.ts","../node_modules/@types/react/index.d.ts","../node_modules/@types/react-native/private/Utilities.d.ts","../node_modules/@types/react-native/public/Insets.d.ts","../node_modules/@types/react-native/Libraries/ReactNative/RendererProxy.d.ts","../node_modules/@types/react-native/public/ReactNativeTypes.d.ts","../node_modules/@types/react-native/Libraries/Types/CoreEventTypes.d.ts","../node_modules/@types/react-native/public/ReactNativeRenderer.d.ts","../node_modules/@types/react-native/Libraries/Components/Touchable/Touchable.d.ts","../node_modules/@types/react-native/Libraries/Components/View/ViewAccessibility.d.ts","../node_modules/@types/react-native/Libraries/Components/View/ViewPropTypes.d.ts","../node_modules/@types/react-native/Libraries/Components/RefreshControl/RefreshControl.d.ts","../node_modules/@types/react-native/Libraries/Components/ScrollView/ScrollView.d.ts","../node_modules/@types/react-native/Libraries/Components/View/View.d.ts","../node_modules/@types/react-native/Libraries/Image/ImageResizeMode.d.ts","../node_modules/@types/react-native/Libraries/Image/ImageSource.d.ts","../node_modules/@types/react-native/Libraries/Image/Image.d.ts","../node_modules/@react-native/virtualized-lists/Lists/VirtualizedList.d.ts","../node_modules/@react-native/virtualized-lists/index.d.ts","../node_modules/@types/react-native/Libraries/Lists/FlatList.d.ts","../node_modules/@types/react-native/Libraries/Lists/SectionList.d.ts","../node_modules/@types/react-native/Libraries/Text/Text.d.ts","../node_modules/@types/react-native/Libraries/Animated/Animated.d.ts","../node_modules/@types/react-native/Libraries/StyleSheet/StyleSheetTypes.d.ts","../node_modules/@types/react-native/Libraries/StyleSheet/StyleSheet.d.ts","../node_modules/@types/react-native/Libraries/StyleSheet/processColor.d.ts","../node_modules/@types/react-native/Libraries/ActionSheetIOS/ActionSheetIOS.d.ts","../node_modules/@types/react-native/Libraries/Alert/Alert.d.ts","../node_modules/@types/react-native/Libraries/Animated/Easing.d.ts","../node_modules/@types/react-native/Libraries/Animated/useAnimatedValue.d.ts","../node_modules/@types/react-native/Libraries/vendor/emitter/EventEmitter.d.ts","../node_modules/@types/react-native/Libraries/EventEmitter/RCTDeviceEventEmitter.d.ts","../node_modules/@types/react-native/Libraries/EventEmitter/RCTNativeAppEventEmitter.d.ts","../node_modules/@types/react-native/Libraries/AppState/AppState.d.ts","../node_modules/@types/react-native/Libraries/BatchedBridge/NativeModules.d.ts","../node_modules/@types/react-native/Libraries/Components/AccessibilityInfo/AccessibilityInfo.d.ts","../node_modules/@types/react-native/Libraries/Components/ActivityIndicator/ActivityIndicator.d.ts","../node_modules/@types/react-native/Libraries/Components/Clipboard/Clipboard.d.ts","../node_modules/@types/react-native/Libraries/Components/DrawerAndroid/DrawerLayoutAndroid.d.ts","../node_modules/@types/react-native/Libraries/EventEmitter/NativeEventEmitter.d.ts","../node_modules/@types/react-native/Libraries/Components/Keyboard/Keyboard.d.ts","../node_modules/@types/react-native/private/TimerMixin.d.ts","../node_modules/@types/react-native/Libraries/Components/Keyboard/KeyboardAvoidingView.d.ts","../node_modules/@types/react-native/Libraries/Components/Pressable/Pressable.d.ts","../node_modules/@types/react-native/Libraries/Components/ProgressBarAndroid/ProgressBarAndroid.d.ts","../node_modules/@types/react-native/Libraries/Components/SafeAreaView/SafeAreaView.d.ts","../node_modules/@types/react-native/Libraries/Components/StatusBar/StatusBar.d.ts","../node_modules/@types/react-native/Libraries/Components/Switch/Switch.d.ts","../node_modules/@types/react-native/Libraries/Components/TextInput/InputAccessoryView.d.ts","../node_modules/@types/react-native/Libraries/Components/TextInput/TextInput.d.ts","../node_modules/@types/react-native/Libraries/Components/ToastAndroid/ToastAndroid.d.ts","../node_modules/@types/react-native/Libraries/Components/Touchable/TouchableWithoutFeedback.d.ts","../node_modules/@types/react-native/Libraries/Components/Touchable/TouchableHighlight.d.ts","../node_modules/@types/react-native/Libraries/Components/Touchable/TouchableOpacity.d.ts","../node_modules/@types/react-native/Libraries/Components/Touchable/TouchableNativeFeedback.d.ts","../node_modules/@types/react-native/Libraries/Components/Button.d.ts","../node_modules/@types/react-native/Libraries/DevToolsSettings/DevToolsSettingsManager.d.ts","../node_modules/@types/react-native/Libraries/Interaction/InteractionManager.d.ts","../node_modules/@types/react-native/Libraries/Interaction/PanResponder.d.ts","../node_modules/@types/react-native/Libraries/LayoutAnimation/LayoutAnimation.d.ts","../node_modules/@types/react-native/Libraries/Linking/Linking.d.ts","../node_modules/@types/react-native/Libraries/LogBox/LogBox.d.ts","../node_modules/@types/react-native/Libraries/Modal/Modal.d.ts","../node_modules/@types/react-native/Libraries/Performance/Systrace.d.ts","../node_modules/@types/react-native/Libraries/PermissionsAndroid/PermissionsAndroid.d.ts","../node_modules/@types/react-native/Libraries/PushNotificationIOS/PushNotificationIOS.d.ts","../node_modules/@types/react-native/Libraries/Utilities/IPerformanceLogger.d.ts","../node_modules/@types/react-native/Libraries/ReactNative/AppRegistry.d.ts","../node_modules/@types/react-native/Libraries/ReactNative/I18nManager.d.ts","../node_modules/@types/react-native/Libraries/ReactNative/RootTag.d.ts","../node_modules/@types/react-native/Libraries/ReactNative/UIManager.d.ts","../node_modules/@types/react-native/Libraries/ReactNative/requireNativeComponent.d.ts","../node_modules/@types/react-native/Libraries/Settings/Settings.d.ts","../node_modules/@types/react-native/Libraries/Share/Share.d.ts","../node_modules/@types/react-native/Libraries/StyleSheet/PlatformColorValueTypesIOS.d.ts","../node_modules/@types/react-native/Libraries/StyleSheet/PlatformColorValueTypes.d.ts","../node_modules/@types/react-native/Libraries/TurboModule/RCTExport.d.ts","../node_modules/@types/react-native/Libraries/TurboModule/TurboModuleRegistry.d.ts","../node_modules/@types/react-native/Libraries/Utilities/Appearance.d.ts","../node_modules/@types/react-native/Libraries/Utilities/BackHandler.d.ts","../node_modules/@types/react-native/Libraries/Utilities/DevSettings.d.ts","../node_modules/@types/react-native/Libraries/Utilities/Dimensions.d.ts","../node_modules/@types/react-native/Libraries/Utilities/PixelRatio.d.ts","../node_modules/@types/react-native/Libraries/Utilities/Platform.d.ts","../node_modules/@types/react-native/Libraries/Vibration/Vibration.d.ts","../node_modules/@types/react-native/Libraries/YellowBox/YellowBoxDeprecated.d.ts","../node_modules/@types/react-native/Libraries/vendor/core/ErrorUtils.d.ts","../node_modules/@types/react-native/public/DeprecatedPropertiesAlias.d.ts","../node_modules/@types/react-native/index.d.ts","../node_modules/recyclerlistview/dist/reactnative/core/dependencies/ContextProvider.d.ts","../node_modules/recyclerlistview/dist/reactnative/core/dependencies/DataProvider.d.ts","../node_modules/recyclerlistview/dist/reactnative/core/layoutmanager/LayoutManager.d.ts","../node_modules/recyclerlistview/dist/reactnative/core/dependencies/LayoutProvider.d.ts","../node_modules/recyclerlistview/dist/reactnative/core/dependencies/GridLayoutProvider.d.ts","../node_modules/recyclerlistview/dist/reactnative/core/scrollcomponent/BaseScrollView.d.ts","../node_modules/recyclerlistview/dist/reactnative/core/ViewabilityTracker.d.ts","../node_modules/recyclerlistview/dist/reactnative/core/VirtualRenderer.d.ts","../node_modules/recyclerlistview/dist/reactnative/core/ItemAnimator.d.ts","../node_modules/recyclerlistview/dist/reactnative/utils/ComponentCompat.d.ts","../node_modules/recyclerlistview/dist/reactnative/core/RecyclerListView.d.ts","../node_modules/recyclerlistview/dist/reactnative/utils/AutoScroll.d.ts","../node_modules/recyclerlistview/dist/reactnative/core/layoutmanager/GridLayoutManager.d.ts","../node_modules/recyclerlistview/dist/reactnative/core/ProgressiveListView.d.ts","../node_modules/recyclerlistview/dist/reactnative/core/devutils/debughandlers/resize/ResizeDebugHandler.d.ts","../node_modules/recyclerlistview/dist/reactnative/core/devutils/debughandlers/DebugHandlers.d.ts","../node_modules/recyclerlistview/dist/reactnative/index.d.ts","../node_modules/recyclerlistview/dist/reactnative/core/StickyContainer.d.ts","../node_modules/recyclerlistview/sticky/index.d.ts","../src/native/auto-layout/AutoLayoutViewNativeComponentProps.ts","../src/native/auto-layout/AutoLayoutViewNativeComponent.ts","../src/native/auto-layout/AutoLayoutView.tsx","../src/native/cell-container/CellContainer.tsx","../src/PureComponentWrapper.tsx","../src/viewability/ViewToken.ts","../src/FlashListProps.ts","../src/utils/AverageWindow.ts","../src/utils/ContentContainerUtils.ts","../src/GridLayoutProviderWithProps.ts","../src/errors/CustomError.ts","../src/errors/ExceptionList.ts","../src/errors/Warnings.ts","../src/viewability/ViewabilityHelper.ts","../src/viewability/ViewabilityManager.ts","../node_modules/recyclerlistview/dist/reactnative/platform/reactnative/itemanimators/defaultjsanimator/DefaultJSItemAnimator.d.ts","../src/native/config/PlatformHelper.ts","../src/FlashList.tsx","../src/AnimatedFlashList.ts","../src/MasonryFlashList.tsx","../src/benchmark/AutoScrollHelper.ts","../src/benchmark/roundToDecimalPlaces.ts","../src/benchmark/JSFPSMonitor.ts","../src/benchmark/useBlankAreaTracker.ts","../src/benchmark/useBenchmark.ts","../src/benchmark/useDataMultiplier.ts","../src/benchmark/useFlatListBenchmark.ts","../src/index.ts","../src/__tests__/AverageWindow.test.ts","../src/__tests__/ContentContainerUtils.test.ts","../node_modules/@quilted/react-testing/build/typescript/types.d.ts","../node_modules/@quilted/react-testing/build/typescript/matchers/index.d.ts","../node_modules/@quilted/react-testing/build/typescript/environment.d.ts","../node_modules/@quilted/react-testing/build/typescript/implementations/test-renderer.d.ts","../node_modules/@quilted/react-testing/build/typescript/index.d.ts","../src/__tests__/helpers/mountFlashList.tsx","../src/__tests__/FlashList.test.tsx","../src/__tests__/GridLayoutProviderWithProps.test.ts","../src/__tests__/helpers/mountMasonryFlashList.tsx","../src/__tests__/MasonryFlashList.test.ts","../src/native/config/PlatformHelper.web.ts","../src/__tests__/PlatformHelper.web.test.ts","../src/__tests__/ViewabilityHelper.test.ts","../src/__tests__/useBlankAreaTracker.test.tsx","../src/native/auto-layout/AutoLayoutViewNativeComponent.android.ts","../src/native/auto-layout/AutoLayoutViewNativeComponent.ios.ts","../src/native/cell-container/CellContainer.android.ts","../src/native/cell-container/CellContainer.ios.ts","../src/native/cell-container/CellContainer.web.tsx","../src/native/config/PlatformHelper.android.ts","../src/native/config/PlatformHelper.ios.ts","../node_modules/@babel/types/lib/index.d.ts","../node_modules/@types/babel__generator/index.d.ts","../node_modules/@babel/parser/typings/babel-parser.d.ts","../node_modules/@types/babel__template/index.d.ts","../node_modules/@types/babel__traverse/index.d.ts","../node_modules/@types/babel__core/index.d.ts","../node_modules/@types/node/assert.d.ts","../node_modules/@types/node/assert/strict.d.ts","../node_modules/@types/node/globals.d.ts","../node_modules/@types/node/async_hooks.d.ts","../node_modules/@types/node/buffer.d.ts","../node_modules/@types/node/child_process.d.ts","../node_modules/@types/node/cluster.d.ts","../node_modules/@types/node/console.d.ts","../node_modules/@types/node/constants.d.ts","../node_modules/@types/node/crypto.d.ts","../node_modules/@types/node/dgram.d.ts","../node_modules/@types/node/diagnostics_channel.d.ts","../node_modules/@types/node/dns.d.ts","../node_modules/@types/node/dns/promises.d.ts","../node_modules/@types/node/domain.d.ts","../node_modules/@types/node/events.d.ts","../node_modules/@types/node/fs.d.ts","../node_modules/@types/node/fs/promises.d.ts","../node_modules/@types/node/http.d.ts","../node_modules/@types/node/http2.d.ts","../node_modules/@types/node/https.d.ts","../node_modules/@types/node/inspector.d.ts","../node_modules/@types/node/module.d.ts","../node_modules/@types/node/net.d.ts","../node_modules/@types/node/os.d.ts","../node_modules/@types/node/path.d.ts","../node_modules/@types/node/perf_hooks.d.ts","../node_modules/@types/node/process.d.ts","../node_modules/@types/node/punycode.d.ts","../node_modules/@types/node/querystring.d.ts","../node_modules/@types/node/readline.d.ts","../node_modules/@types/node/repl.d.ts","../node_modules/@types/node/stream.d.ts","../node_modules/@types/node/stream/promises.d.ts","../node_modules/@types/node/stream/consumers.d.ts","../node_modules/@types/node/stream/web.d.ts","../node_modules/@types/node/string_decoder.d.ts","../node_modules/@types/node/timers.d.ts","../node_modules/@types/node/timers/promises.d.ts","../node_modules/@types/node/tls.d.ts","../node_modules/@types/node/trace_events.d.ts","../node_modules/@types/node/tty.d.ts","../node_modules/@types/node/url.d.ts","../node_modules/@types/node/util.d.ts","../node_modules/@types/node/v8.d.ts","../node_modules/@types/node/vm.d.ts","../node_modules/@types/node/wasi.d.ts","../node_modules/@types/node/worker_threads.d.ts","../node_modules/@types/node/zlib.d.ts","../node_modules/@types/node/globals.global.d.ts","../node_modules/@types/node/index.d.ts","../node_modules/@types/graceful-fs/index.d.ts","../node_modules/@types/istanbul-lib-coverage/index.d.ts","../node_modules/@types/istanbul-lib-report/index.d.ts","../node_modules/@types/istanbul-reports/index.d.ts","../node_modules/chalk/index.d.ts","../node_modules/@sinclair/typebox/typebox.d.ts","../node_modules/@jest/schemas/build/index.d.ts","../node_modules/pretty-format/build/index.d.ts","../node_modules/jest-diff/build/index.d.ts","../node_modules/jest-matcher-utils/build/index.d.ts","../node_modules/@types/jest/index.d.ts","../node_modules/@types/json-schema/index.d.ts","../node_modules/@types/json5/index.d.ts","../node_modules/@types/parse-json/index.d.ts","../node_modules/@types/prettier/index.d.ts","../node_modules/@types/react-test-renderer/index.d.ts","../node_modules/@types/scheduler/index.d.ts","../node_modules/@types/stack-utils/index.d.ts","../node_modules/@types/websocket/index.d.ts","../node_modules/@types/yargs-parser/index.d.ts","../node_modules/@types/yargs/index.d.ts"],"fileInfos":[{"version":"f5c28122bee592cfaf5c72ed7bcc47f453b79778ffa6e301f45d21a0970719d4","affectsGlobalScope":true},"dc47c4fa66b9b9890cf076304de2a9c5201e94b740cffdf09f87296d877d71f6","7a387c58583dfca701b6c85e0adaf43fb17d590fb16d5b2dc0a2fbd89f35c467","8a12173c586e95f4433e0c6dc446bc88346be73ffe9ca6eec7aa63c8f3dca7f9","5f4e733ced4e129482ae2186aae29fde948ab7182844c3a5a51dd346182c7b06","e6b724280c694a9f588847f754198fb96c43d805f065c3a5b28bbc9594541c84","1fc5ab7a764205c68fa10d381b08417795fc73111d6dd16b5b1ed36badb743d9",{"version":"3f149f903dd20dfeb7c80e228b659f0e436532de772469980dbd00702cc05cc1","affectsGlobalScope":true},{"version":"1272277fe7daa738e555eb6cc45ded42cc2d0f76c07294142283145d49e96186","affectsGlobalScope":true},{"version":"adb996790133eb33b33aadb9c09f15c2c575e71fb57a62de8bf74dbf59ec7dfb","affectsGlobalScope":true},{"version":"43fb1d932e4966a39a41b464a12a81899d9ae5f2c829063f5571b6b87e6d2f9c","affectsGlobalScope":true},{"version":"cdccba9a388c2ee3fd6ad4018c640a471a6c060e96f1232062223063b0a5ac6a","affectsGlobalScope":true},{"version":"c5c05907c02476e4bde6b7e76a79ffcd948aedd14b6a8f56e4674221b0417398","affectsGlobalScope":true},{"version":"0d5f52b3174bee6edb81260ebcd792692c32c81fd55499d69531496f3f2b25e7","affectsGlobalScope":true},{"version":"810627a82ac06fb5166da5ada4159c4ec11978dfbb0805fe804c86406dab8357","affectsGlobalScope":true},{"version":"181f1784c6c10b751631b24ce60c7f78b20665db4550b335be179217bacc0d5f","affectsGlobalScope":true},{"version":"3013574108c36fd3aaca79764002b3717da09725a36a6fc02eac386593110f93","affectsGlobalScope":true},{"version":"75ec0bdd727d887f1b79ed6619412ea72ba3c81d92d0787ccb64bab18d261f14","affectsGlobalScope":true},{"version":"3be5a1453daa63e031d266bf342f3943603873d890ab8b9ada95e22389389006","affectsGlobalScope":true},{"version":"17bb1fc99591b00515502d264fa55dc8370c45c5298f4a5c2083557dccba5a2a","affectsGlobalScope":true},{"version":"7ce9f0bde3307ca1f944119f6365f2d776d281a393b576a18a2f2893a2d75c98","affectsGlobalScope":true},{"version":"6a6b173e739a6a99629a8594bfb294cc7329bfb7b227f12e1f7c11bc163b8577","affectsGlobalScope":true},{"version":"12a310447c5d23c7d0d5ca2af606e3bd08afda69100166730ab92c62999ebb9d","affectsGlobalScope":true},{"version":"b0124885ef82641903d232172577f2ceb5d3e60aed4da1153bab4221e1f6dd4e","affectsGlobalScope":true},{"version":"0eb85d6c590b0d577919a79e0084fa1744c1beba6fd0d4e951432fa1ede5510a","affectsGlobalScope":true},{"version":"da233fc1c8a377ba9e0bed690a73c290d843c2c3d23a7bd7ec5cd3d7d73ba1e0","affectsGlobalScope":true},{"version":"d154ea5bb7f7f9001ed9153e876b2d5b8f5c2bb9ec02b3ae0d239ec769f1f2ae","affectsGlobalScope":true},{"version":"bb2d3fb05a1d2ffbca947cc7cbc95d23e1d053d6595391bd325deb265a18d36c","affectsGlobalScope":true},{"version":"c80df75850fea5caa2afe43b9949338ce4e2de086f91713e9af1a06f973872b8","affectsGlobalScope":true},{"version":"9d57b2b5d15838ed094aa9ff1299eecef40b190722eb619bac4616657a05f951","affectsGlobalScope":true},{"version":"6c51b5dd26a2c31dbf37f00cfc32b2aa6a92e19c995aefb5b97a3a64f1ac99de","affectsGlobalScope":true},{"version":"6e7997ef61de3132e4d4b2250e75343f487903ddf5370e7ce33cf1b9db9a63ed","affectsGlobalScope":true},{"version":"2ad234885a4240522efccd77de6c7d99eecf9b4de0914adb9a35c0c22433f993","affectsGlobalScope":true},{"version":"09aa50414b80c023553090e2f53827f007a301bc34b0495bfb2c3c08ab9ad1eb","affectsGlobalScope":true},{"version":"d7f680a43f8cd12a6b6122c07c54ba40952b0c8aa140dcfcf32eb9e6cb028596","affectsGlobalScope":true},{"version":"3787b83e297de7c315d55d4a7c546ae28e5f6c0a361b7a1dcec1f1f50a54ef11","affectsGlobalScope":true},{"version":"e7e8e1d368290e9295ef18ca23f405cf40d5456fa9f20db6373a61ca45f75f40","affectsGlobalScope":true},{"version":"faf0221ae0465363c842ce6aa8a0cbda5d9296940a8e26c86e04cc4081eea21e","affectsGlobalScope":true},{"version":"06393d13ea207a1bfe08ec8d7be562549c5e2da8983f2ee074e00002629d1871","affectsGlobalScope":true},{"version":"cd483c056da900716879771893a3c9772b66c3c88f8943b4205aec738a94b1d0","affectsGlobalScope":true},{"version":"b248e32ca52e8f5571390a4142558ae4f203ae2f94d5bac38a3084d529ef4e58","affectsGlobalScope":true},{"version":"c37f8a49593a0030eecb51bbfa270e709bec9d79a6cc3bb851ef348d4e6b26f8","affectsGlobalScope":true},"14a84fbe4ec531dcbaf5d2594fd95df107258e60ae6c6a076404f13c3f66f28e",{"version":"1c0e04c54479b57b49fec4e93556974b3d071b65d0b750897e07b3b7d2145fc5","affectsGlobalScope":true},"bc1852215dc1488e6747ca43ae0605041de22ab9a6eeef39542d29837919c414","ae6da60c852e7bacc4a49ff14a42dc1a3fdbb44e11bd9b4acb1bf3d58866ee71",{"version":"0dab023e564abb43c817779fff766e125017e606db344f9633fdba330c970532","affectsGlobalScope":true},"4cbd76eafece5844dc0a32807e68047aecbdd8d863edba651f34c050624f18df",{"version":"ecf78e637f710f340ec08d5d92b3f31b134a46a4fcf2e758690d8c46ce62cba6","affectsGlobalScope":true},"ea0aa24a32c073b8639aa1f3130ba0add0f0f2f76b314d9ba988a5cb91d7e3c4","f7b46d22a307739c145e5fddf537818038fdfffd580d79ed717f4d4d37249380","f5a8b384f182b3851cec3596ccc96cb7464f8d3469f48c74bf2befb782a19de5",{"version":"29b8a3a533884705024eab54e56465614ad167f5dd87fdc2567d8e451f747224","affectsGlobalScope":true},"4f2490e3f420ea6345cade9aee5eada76888848e053726956aaf2af8705477ea","b3ac03d0c853c0ac076a10cfef4dc21d810f54dac5899ade2b1c628c35263533","d17a689ac1bd689f37d6f0d3d9a21afac349e60633844044f7a7b7b9d6f7fd83","019650941b03d4978f62d21ae874788a665c02b54e3268ef2029b02d3b4f7561","ae591c8a4d5c7f7fa44b6965016391457d9c1fd763475f68340599a2a2987a24","fbdef0c642b82cc1713b965f07b4da8005bbbb2c026039bfdc15ca2d20769e38","c2c004e7f1a150541d06bc4a408b96e45ac1f08e0b1b35dfd07fc0f678205f95","1f2081eb2cbeb0828f9baa1dd12cf6d207f8104ae0b085ab9975d11adc7f7e6f","cda9069fc4c312ff484c1373455e4297a02d38ae3bd7d0959aad772a2809623c","c028d20108bcaa3b1fdf3514956a8a90ccf680f18672fa3c92ce5acf81d7ab23","1054f6e8774a75aaf17e7cfea4899344f69590b2db1e06da21048ed1e063c693","9533301b8f75664e1b40a8484a4fd9c77efc04aef526409c2447aab7d12ddc63","b78b5b3fdb4e30976c4263c66c0ad38fb81edcc8075a4160a39d99c6dedd35be","032b51d656feaece529823992f5a39fe9e24d44dfa21b3a149982f7787fc7bdf","5bbfdfb694b019cb2a2022fba361a7a857efc1fc2b77a892c92ebc1349b7e984","46bc25e3501d321a70d0878e82a1d47b16ab77bdf017c8fecc76343f50806a0d","42bacb33cddecbcfe3e043ee1117ba848801749e44f947626765b3e0aec74b1c","49dba0d7a37268e6ae2026e84ad4362eac7e776d816756abf649be7fa177dcd5","5f2b5ab209daae571eb9acc1fd2067ccc94e2a13644579a245875bc4f02b562f","f072acf9547f89b814b9fdb3e72f4ebb1649191591cec99db43d35383906f87f","42450dba65ba1307f27c914a8e45e0b602c6f8f78773c052e42b0b87562f081e","f5870d0ca7b0dfb7e2b9ba9abad3a2e2bffe5c711b53dab2e6e76ca2df58302b","aeb20169389e9f508b1a4eb2a30371b64d64bb7c8543120bc39a3c6b78adfcc9","2a3d3acbab8567057a943f9f56113c0144f5fc561623749fbd6bb5c2b33bf738","9cf21fdcd1beb5142a514887133fa59057e06275bb3070713f3b6d51e830ffa0","0ad4f0b67db47064b404df89c50f99552ce12d6c4bb6154255be61eb6beed094","f8a464b9999126fe1095968c266c0d9c6174612cf256379a1ed1993a87bccdc6","49f981ca657ac160b5de5919ee5602d48bc8f8aac0805107c2ce4fd41dc9a2a1","56e4e08d95a3a7886266a2b4f66b67065c340480d9f1beb73ed7578aa83c639a","eb4360d3818dcd879ee965ae2f4b3fdfdc4149db921b6be338cb7dc7c2bd6710","1c1275f325f13af001aa5873418cb497a26b4b8271f9ad20a45e33f61ea3f9d9","b33e8426136c4f9b349b02c940d23310d350179f790899733aa097ed76457061","05aab001669a230a88820be09a54031c45d9af2488b27d53d4a9c8880ce73e8f","d93a066d4b8b33335dfff910fb25abb8979f8814f8ba45ea902a1360907da1f6","41e97e42d182b4d5f0733ebaad69294faaa507d95e595f317168b8f2325da9ca","debc734fc99b6e1684ed565946bad008913c769d4d2e400d8722c0c23d079c06","5a9f7e087aacb01fa0cdbc36b703a60367239f62beed2507a507199e4c417549","c7c23798fbf564983ed69c1ced3371970d986aaed4801a6e0fb41862550dc034","921f5bce372610ae8948ade7d82decbd2cf56d263de578976189585edd0abac0","ac11f8b13beef593e2f097450a7e214b23dca0d428babd570a2f39582f10e9ab","2499beb5d3e2b4c606977bcc2e08b6ef77b2ecda70e78e0622f5af3bed95c9ba","a11057410396907b84051cbdb8b0cd7f7049d72b58d2b6ac1c14ac2608191a52","bb630c26d487cc45ed107f4f2d3c2a95434716f6367f059de734c40d288c31eb","67cbce0ccdfa96b25de478a93cc493266c152e256c3c96b3d16d1f811e3d881f","19905c928bc4c016d05d915625bb08568447266c4661232faf89f7ddc4417ccc","26204eb4c326e8c975f1b789cbf345c6820205bded6d72e57246a83918d3bc84","618f25b2d41a99216e71817a3bc578991eee86c858c3f0f62a9e70707f4d279d","4cd2947878536ec078e4115b7d53cdcd4dcecd3a8288760caa79098db4f8f61f","2129e984399e94c82b77a32b975f3371ca5ee96341ab9f123474f1a5a1a9921f","798120aaa4952d68cd4b43d6625524c62a135c2f5a3eb705caee98de2355230d","6047365397173788c34bd71fea2bf07a9036d981212efd059b33e52d2c405e97","d7e25d7c03ccf8b10972c2a3a57e29a8d9024e6dbc4ac223baf633a6e8c7145c","6c2e2dead2d80007ee44c429b925d0a7b86f8f3d4c237b2197f7db9f39545dc6","38fbc8f9610fbf4bf619854b26e28c4fbbab16dc1944c4317a4af9bf1ac08d8e","1bd0470a72e6869c330e6e978f15ef32ba2c245249aca097b410448152e8a06b","dd05d7970a92b789f7df3b2252574b2e60f1b9a3758e2839e167b498b8f77159","7092be1889127b2f319efd5d9bdcc0b5cf6fe0740e47247ed039446045518898","0a3d5dbf7c2091017e697ebf9af0a727571f5d99cb4c19e6856212a745c6c355","d05f9c767924db6fb89f6075acb64c042cebdb12779bbd1aaca12c850b772d49","d032678e20ff0f4b8ef6f1e4823b6ae37931b776e8381676dc9141999909b3d7","3e4ab0e8e96e968ac84a2484104892c881ded1757acd81b5e969b6229851f54c","d43a36641f5812794a3b4a941e3dfb5fa070f9fff64cfd6daf5291cb962c8b05","32468df81188116040636844517fbe4f67fc37af4fe565c7592353df8e11d2f3","c12b5f9bf412c891cad443ef00a378ad2d3f1301f140943414308665a7d90af8","cf1b65c20036885ed99ce1c18aa0a0ed66f42acd6d415e99b48a8fa4105c23ed","173aec8be1be982c8244df6f94880d77a9b766c8c1ec3eb0af662c8dc6da7f2e","08188020373062e07955835a996fda1aff97a89e57d469edc6b9210bd9c8926f","cad5c2c0085a3e3b74f58aa199944b25ed8d24f93f51c99ebe2463e4f1694785","3e2d93a797c41ab081fbcd80e959b7c30d5d1c358f091c22a6ebe416ef7c5e19","c440df5735a3305e7db118bf821efb597c8318910861f735372846db9f7b506b","d6d8de719a75e5d2ed9dd9d6a99296d1337259e1c96166579db50797edd72ede","32b4c732e183bf5d123f88d526ac21b71a681089c18d2d761be342df31179d94","212d16020e7dce1b5509f3b9813de73612de57c6a3d74536714eb88787b96dc3","1a63d5212341783aa49cf78d667bf2a6cd03208ea09620b2fc3e647ae07f4e0d","84ea58841272970e6e3247cba4dbb326cf22764c2f4bbcb03f1c634315bbbcb5","86f9fbecdd848d02c90f861cc9839d8f3449c518a77e77ea65362f6a4126c63b","ecdaf317a4a1e7e3540e2f1b6aae38acd78dd99d564b52f98eea7358ac74416d","c30430960f1a0552b3cdaf1ef8164fdd4f289c782a8912df5180d57bc9ddfc03","a348081c01502c9f87d39d9e4b5dd58e1111b34c62686d6e569c595a0417bb35","eff69aee13c76502a16b756cde9c451fb4b5c4234052f3b3bee9dbfe92e1b1d5","9943f44400939f4ff008a882ff71162f70ba0c2f735c9743fd4645ef5c925fc4","b7836eba6c5173a1683aee8aa1771ff339e795cb9c21411590edb910274febe4","6fe447aa7e6fabc4f6c536f2997e3b1116b7f73dbe5bf3fc8d958bad434e4a84","15d3908d453d14be4dae760122ed5d74ad789a19f1fec2edd4034e57217436e9","ef00bc701f382da70870ab7721ed8f6552a38e332e60370b93cf340b6470845c","18891a02fa046e57b43a543dddc7212086fcb04ae6c8e8f28f8605dd3ccf57ed",{"version":"5980a888624dce1b0937a0d21c623f97056501bb61a8da29cbe07f1a0be2c9a8","affectsGlobalScope":true},"590a41ccab332c66a6aa62746612b03ceb2e92cc1da58c140e90fb7ff6e8c851","dc1d2996f23fe7f0da0b2c843e05c0ac170a36b51da11e58de089d344de93c3b","78ff01b50e7e9761f239527ec70b96171bccc28a08d909243e193db03b6f6983","ed18472ee2247563a26d754dd4c8bd66383013df13ce7c2927b03cab1a27b7e8","28ac9ac1fa163e5f2321fafa49b9931908c0076216ed3c82646d79abdf79775e","07dd4bed8ddab685f82a2125bf3aa41b42e36f28c16a5aec7357b727649076fb","fc15a2216f29b825747c0c3a54d6989518dd0f4aa0b580520e5526b4a47bec8f","c656d5baf3d4a8f358fc083db04b0fda8cb8503a613a9ba42327ecbd7909773c","397c2c81eaeae1388f7459699d7606feecfc304b212eb9113407c1315746a578","c2d923e9adc26a3efe5186f3a4a72413d24c80f03b306c68c30fa146690fb101","d34782833b7d5f72486a5fb926d3d96198706ed76aeaf1d435c748ebcf9169fc","b093e56054755189dd891ea832dec40d729d110a0a3f432fff5ea5ab1078cdde","98affe620e6230a3888b445c32376e4edbf6b1b376a71f2bf9c07bee11fcdd65","1e05491bef32ff48393d605d557152735899da3d9b111ba3588a1800f2927f4a","1ff7813974b1b9a0524c1e5a99aa52a05e79fc7df7749ada75ded8c53fe1b7e0","cd8c517f54d4ff3475755b290d741c8799df3265ce73d454d8fafe423f8ff749","bf431147b104ae92d61de6b43e9f25d27e8d3eaeaffd612e0c0d3bb8e2423926","f0f21604ae8f880c0ab529f00303806fdeadc943e32a25ca063fc8fea0fa063c","8dc4f45212fba9381e1674e8bd934a588730efbb8a6681b661cad8cd09b081c5",{"version":"52bf774bd30177ebb3e450c808d8d46f67896848a942e6203ae78b65b33d0106","signature":"688c437017a53e69ff66aac2036a0d7f6263082f676a408c9998cbd87ea2ec73"},{"version":"8b6ee36fd764378c62dca37041c5a12fd5a77b9e853c78908b7ed1c90dc149e4","signature":"03846acca031c757d910dbc017d846c87574faf90bde82316fb9b8537896d5ee"},{"version":"0d089d33f31b56697d142aa7395738c0323cf761b4c79fd6bf65a54ab1ddf02f","signature":"027c87e1cb049497d4f185bc9b922ce91cad59832da8faf3411e6b298b9deb78"},{"version":"ec0982b9e7d6c1b6c80e2829c5909eefb9ecee687e60621e0bb937e8ad5d1d43","signature":"8478b617a5be940f1b4b4d19d2fc6149c21ac69c4a7e00c8a7db2c2c21aa2274"},{"version":"84c5fc9d0d22f4566791b88d5fc2c24f56508b50c9ce894ac549ebaa158b1fca","signature":"677ea66c6fa02f1cebf82df19f416a8302c7a7d10e2de265b162760fcd865eef"},{"version":"8455135ea42310a73404fa2513e212d170af1191584061f583ec1e0f6b75dd91","signature":"83e4298f0b6834e955ee6a76569d3e5b3192065d47f1daf4535bb9edb16e88cb"},{"version":"73529962207605bdc5285d5e745919b8d57b776daa0f22a14b75cd8a92d63af9","signature":"422fcd2a7fd87f05efdfaa6eab382ca607d5d54e1f175ba2efccd4aacd5433ef"},{"version":"ebe927d8a9739c9d32ef4df28c1c36cf82daa9abba7cdf3f79e320c5e99e99d8","signature":"2421f9c6b1ecedd50818719090a77e9d2748c2339c33f3d4817beebf7a39d211"},{"version":"165c56632fea46c85e2a62f1b4eae600b846ea0deacd3c137fde9bacb845c30e","signature":"79bf9e3846b43e706d181c00f3c1c50ae8fc60e587c97a16e521adc150317624"},{"version":"866e1d2cf16a41851b056a2cc0cdc5f0f00df0435376cc2c723a8c609f61fbd0","signature":"5f5bbca60f0bfed6ff714163c4e962a5e260e59db754c89ee2063403accd03e3"},{"version":"ecfa1b63e3829b310ac968b2cc1cc7016ba76ffb8532439aebecbcbc57173b99","signature":"2f1dda63ade2bd085704674523b56ede942bc8c2c37fe8ed9b9b0fdfd69b1262"},{"version":"51d2f746d7e599a5549f5a946565934b4556bb9155be1eed2c474e25f1474872","signature":"c15585fe8935ed5cfedec39b7d41ec49990973f40faaba4b3e14278861643d79"},{"version":"b1d1378906c54a2f4d230ad69d212beedd2552afe3f7ad171b7eacb4cecc26d7","signature":"f9e60e8f79a7f606f19e02e2d39a24995719767dbe587f564f970bb24e3ca29d"},{"version":"f5a156e5b3783ea0399ac0326b7ab31a00e8874c5fa9b5e26fac217da8b5adfd","signature":"cfa7179e0306fc04d93f062c96e7ae8bad58d0cc4a7aa0dd4494ff9d262b101c"},{"version":"3c9fefca9303bcfd5712de11a3cbda20b3d6e85f29019bc75cab24690fb0f90d","signature":"306683152ff5a6038cf05b03ddff85a15b1bc8e18ef268aad26b02fd8e0e8b9d"},"a11c3e55d22d6379fe0949793e2638a6b43aa6e9def4f7452c3e352a296ef8da",{"version":"2770956c9437d7d66650084891c559ff6bb94200b7e2820940fd5d5dd0efa489","signature":"2faaf4f254008bf5be0e145be10dba35dccfac7116e9083f9d697a476a8e7076"},{"version":"ceee917fd557b841b93f7e13103dfdad79d38fe9962408f538f27db03dc9368d","signature":"15003ff6ed10d259dca775c7e5f7a64b272a9c370b6085db2d42a2d4a1d81579"},{"version":"a1691ae6d70af82f3e26d9e2e021dc5063021bd9c335bfdb40dc97d3574d1b3f","signature":"cd1c566b611a70ff987a79d0465da67649a8ed7e7668feddfcdf6dceb01c09a8"},{"version":"a105417dd540f1a400f0665c877e5d7e48e2efe08f01c2e5c7272256e644faa5","signature":"b3a6ee392811d6cddb38378ebaa373d4a39aa7dc4ecac73497c6b33627e6430b"},{"version":"581b44cf6122e3ad267d6bda2428c214fef3d38b6d7249df9fa6bc240a880a78","signature":"0ca09d92d6469d906a3d1c7192a6294c7f65b75f4f7eb8072bbd1b68c7f021e1"},{"version":"2e6426c1a1ff8561aa5f01d9398426bf06e55307f688464939de3196f0d4c143","signature":"5357bd09c9816a9765e617f86a9b49f85133d0bc0f9c5e29e834f2f8e6d52acb"},{"version":"508279c48de5627ae6c30a0aee01f4391bf32450335d7f09d5dd82acbc4d13c5","signature":"11d546a505f70f9c5f8092916027d8045c280a817b709fcaf2c4e63fa026c89c"},{"version":"557f2e0a4e5ac8a59b7c3068b2b30162fb963d72d50152482ab8c414e81caf37","signature":"008eaae28119118f1c589a1e29ea7fd17277f2280d2d3bfddeacd71fd1671bb5"},{"version":"f45c172ca54fb28d64b2dd04e495f17038034e20f37bd286a9f3eeb286cf1388","signature":"75a8761564c8fc5581b062dd339ea698921baf60e52eae055c8177dfa89eba90"},{"version":"ea696a0517ad69afea472e47eb1f904aba1667f54d4557eb98b8c766469d56a2","signature":"7e125d9abc19f62d1480f6c04a45d7bb2c89153316245ae8b8e5a0234b078c4e"},{"version":"902937c505f88d8b5b32829b4c14243eb740013fd0e2f58e6485324bbfe197a6","signature":"dc7de7650e5a64fc010387db18e84d48fe8f562dbd9caac01e54f83681ac976b"},{"version":"842accda78bb1b6f494f264aae307b84d933486d607e91f6e1d3a4d2e4851783","signature":"430d9683c8e5aaab71f0e3b271c4240cd5120a91191f953722985499af51d7e6"},{"version":"45b1a895868587c78a2ddff937967669b4e1968ea72c01e1c2b6dd5993f53b36","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"99cab9373415bac71e9d2c84279782c0a361b59551d0ca8dfaee8d4c08ed3247","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},"ba1fed463e8a21ffddb67a53df3f0d818b351991723736e167d065e2de1c7183",{"version":"22e311fec88bcc49b2b1fb3c9a7c082cd84b3388c9bcc7b9ef08253f6fa74e26","affectsGlobalScope":true},"c186097fd9b86681981cdeba08c0b6bbfcd8b562ab490c25656d85fef8f10c79","0b0c483e991e81c3f26e5f2da53ff26a15994c98c8b89cda1e4156dfc2428111","3340eb7b30bdee5f0349107d4068fd6f2f4712e11a2ba68e203b2f2489350317",{"version":"2000d60bd5195730ffff0d4ce9389003917928502c455ed2a9e296d3bf1a4b42","signature":"56335d3c9b867cc8654c05e633c508dd8de0038157f9958eb8794b7c123bb90e"},{"version":"dfceb5b9355a4a9002a7c291b1c3315511977c73cb23d9c123a72567783a18c0","signature":"b1802850887a3ea11a06df1fc1c65c6579332eefba1e63b3967a73dc937a2574"},{"version":"384fc0e3fa5966f524c96f1782b9d7a005346ba1621c43d0d1d819bf39077fbc","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"7fde517b3f03bb21ec3a46ba5f85c6797f8abf27deacb862183126e2f072788e","signature":"8b310edcfec83da25bc4f3adb20a7583bc5dae56d7d06c5b1431b76d390c1b72"},{"version":"894d93831d2afcd26f7362347e4960dd6d53f4153dad08813f3670e1327e387c","signature":"b1802850887a3ea11a06df1fc1c65c6579332eefba1e63b3967a73dc937a2574"},{"version":"8f9eac2c3ae305c25d4ffeff800b9811c8d3ec6a11b142fe96d08a2bc40f6440","signature":"08d6a2d1b004bbcac4249cd5baf6e9c662adc6139939c266b42e0422ef0c68b3"},{"version":"ac8980bdd810c30c444b59cca584c9b61d5ab274fa9474d778970537f3090240","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"1c024431c672cf9c6dcdb4d30c5b625435d81a5423b9d45e8de0082e969af8a8","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"eee1b57475023853cd09dd79b8d0d6639b6b82c3baee5863c2f2022b710f4102","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"377ba49d29102653a4b0c72b3870f9c599575df7db3a3fae7a21be5327ff84e2","signature":"c47f5db4df0a5031ed84bc6ee192c412b9e2d4d5e94681af77ccdcc25c851839"},{"version":"377ba49d29102653a4b0c72b3870f9c599575df7db3a3fae7a21be5327ff84e2","signature":"c47f5db4df0a5031ed84bc6ee192c412b9e2d4d5e94681af77ccdcc25c851839"},{"version":"39833acf7547216b2f31b2279dcfec3ed1359dec8adc9d1cb87c695ebf9bff94","signature":"7292d4dc9dac6d815dc30245a4a4a4959845d3a2b84ba0166857e4b23f2d033f"},{"version":"39833acf7547216b2f31b2279dcfec3ed1359dec8adc9d1cb87c695ebf9bff94","signature":"7292d4dc9dac6d815dc30245a4a4a4959845d3a2b84ba0166857e4b23f2d033f"},{"version":"529dd364d169ab3dbbb177ccdc4987c4a6f69187f553f3d36460ab65879ad998","signature":"3919e9d5911da2254732c31942e2cdc0057056ebfc2a16d34041c76a9b58d447"},{"version":"ebea587ca6477b9db29baf75d359924c55ab490fecdc38d7c0f16e589f0d27f9","signature":"0688c25f38e78e052338305d23046c7841074b3da5709a8f9e598ed705b9932b"},{"version":"de411013305dbe5c7a1ac13d2ea16dc36e52e6efd255b4e912fe53862058c649","signature":"2faaf4f254008bf5be0e145be10dba35dccfac7116e9083f9d697a476a8e7076"},"e432b56911b58550616fc4d54c1606f65fe98c74875b81d74601f5f965767c60","cc957354aa3c94c9961ebf46282cfde1e81d107fc5785a61f62c67f1dd3ac2eb","a46a2e69d12afe63876ec1e58d70e5dbee6d3e74132f4468f570c3d69f809f1c","93de1c6dab503f053efe8d304cb522bb3a89feab8c98f307a674a4fae04773e9","3b043cf9a81854a72963fdb57d1884fc4da1cf5be69b5e0a4c5b751e58cb6d88","dd5647a9ccccb2b074dca8a02b00948ac293091ebe73fdf2e6e98f718819f669","0cba3a5d7b81356222594442753cf90dd2892e5ccfe1d262aaca6896ba6c1380","a69c09dbea52352f479d3e7ac949fde3d17b195abe90b045d619f747b38d6d1a",{"version":"c2ab70bbc7a24c42a790890739dd8a0ba9d2e15038b40dff8163a97a5d148c00","affectsGlobalScope":true},"422dbb183fdced59425ca072c8bd09efaa77ce4e2ab928ec0d8a1ce062d2a45a",{"version":"712ba0d43b44d144dfd01593f61af6e2e21cfae83e834d297643e7973e55ed61","affectsGlobalScope":true},"1dab5ab6bcf11de47ab9db295df8c4f1d92ffa750e8f095e88c71ce4c3299628","f71f46ccd5a90566f0a37b25b23bc4684381ab2180bdf6733f4e6624474e1894",{"version":"54e65985a3ee3cec182e6a555e20974ea936fc8b8d1738c14e8ed8a42bd921d4","affectsGlobalScope":true},"82408ed3e959ddc60d3e9904481b5a8dc16469928257af22a3f7d1a3bc7fd8c4","98a3ebfa494b46265634a73459050befba5da8fdc6ca0ef9b7269421780f4ff3","34e5de87d983bc6aefef8b17658556e3157003e8d9555d3cb098c6bef0b5fbc8","cc0b61316c4f37393f1f9595e93b673f4184e9d07f4c127165a490ec4a928668","f27371653aded82b2b160f7a7033fb4a5b1534b6f6081ef7be1468f0f15327d3","c762cd6754b13a461c54b59d0ae0ab7aeef3c292c6cf889873f786ee4d8e75c9","f4ea7d5df644785bd9fbf419930cbaec118f0d8b4160037d2339b8e23c059e79",{"version":"bfea28e6162ed21a0aeed181b623dcf250aa79abf49e24a6b7e012655af36d81","affectsGlobalScope":true},"7a5459efa09ea82088234e6533a203d528c594b01787fb90fba148885a36e8b6","ae97e20f2e10dbeec193d6a2f9cd9a367a1e293e7d6b33b68bacea166afd7792","10d4796a130577d57003a77b95d8723530bbec84718e364aa2129fa8ffba0378","ad41bb744149e92adb06eb953da195115620a3f2ad48e7d3ae04d10762dae197","bf73c576885408d4a176f44a9035d798827cc5020d58284cb18d7573430d9022","7ae078ca42a670445ae0c6a97c029cb83d143d62abd1730efb33f68f0b2c0e82",{"version":"e8b18c6385ff784228a6f369694fcf1a6b475355ba89090a88de13587a9391d5","affectsGlobalScope":true},"5d0a9ea09d990b5788f867f1c79d4878f86f7384cb7dab38eecbf22f9efd063d","12eea70b5e11e924bb0543aea5eadc16ced318aa26001b453b0d561c2fd0bd1e","08777cd9318d294646b121838574e1dd7acbb22c21a03df84e1f2c87b1ad47f2","08a90bcdc717df3d50a2ce178d966a8c353fd23e5c392fd3594a6e39d9bb6304",{"version":"4cd4cff679c9b3d9239fd7bf70293ca4594583767526916af8e5d5a47d0219c7","affectsGlobalScope":true},"2a12d2da5ac4c4979401a3f6eaafa874747a37c365e4bc18aa2b171ae134d21b","002b837927b53f3714308ecd96f72ee8a053b8aeb28213d8ec6de23ed1608b66","1dc9c847473bb47279e398b22c740c83ea37a5c88bf66629666e3cf4c5b9f99c","a9e4a5a24bf2c44de4c98274975a1a705a0abbaad04df3557c2d3cd8b1727949","00fa7ce8bc8acc560dc341bbfdf37840a8c59e6a67c9bfa3fa5f36254df35db2","1b952304137851e45bc009785de89ada562d9376177c97e37702e39e60c2f1ff",{"version":"806ef4cac3b3d9fa4a48d849c8e084d7c72fcd7b16d76e06049a9ed742ff79c0","affectsGlobalScope":true},"44b8b584a338b190a59f4f6929d072431950c7bd92ec2694821c11bce180c8a5","5f0ed51db151c2cdc4fa3bb0f44ce6066912ad001b607a34e65a96c52eb76248",{"version":"3345c276cab0e76dda86c0fb79104ff915a4580ba0f3e440870e183b1baec476","affectsGlobalScope":true},"664d8f2d59164f2e08c543981453893bc7e003e4dfd29651ce09db13e9457980","e383ff72aabf294913f8c346f5da1445ae6ad525836d28efd52cbadc01a361a6","f52fbf64c7e480271a9096763c4882d356b05cab05bf56a64e68a95313cd2ce2","59bdb65f28d7ce52ccfc906e9aaf422f8b8534b2d21c32a27d7819be5ad81df7",{"version":"3a2da34079a2567161c1359316a32e712404b56566c45332ac9dcee015ecce9f","affectsGlobalScope":true},"28a2e7383fd898c386ffdcacedf0ec0845e5d1a86b5a43f25b86bc315f556b79","3aff9c8c36192e46a84afe7b926136d520487155154ab9ba982a8b544ea8fc95","a880cf8d85af2e4189c709b0fea613741649c0e40fffb4360ec70762563d5de0","85bbf436a15bbeda4db888be3062d47f99c66fd05d7c50f0f6473a9151b6a070","9f9c49c95ecd25e0cb2587751925976cf64fd184714cb11e213749c80cf0f927","f0c75c08a71f9212c93a719a25fb0320d53f2e50ca89a812640e08f8ad8c408c",{"version":"ab9b9a36e5284fd8d3bf2f7d5fcbc60052f25f27e4d20954782099282c60d23e","affectsGlobalScope":true},"9cafe917bf667f1027b2bb62e2de454ecd2119c80873ad76fc41d941089753b8","3ebae8c00411116a66fca65b08228ea0cf0b72724701f9b854442100aab55aba","8b06ac3faeacb8484d84ddb44571d8f410697f98d7bfa86c0fda60373a9f5215","7eb06594824ada538b1d8b48c3925a83e7db792f47a081a62cf3e5c4e23cf0ee","f5638f7c2f12a9a1a57b5c41b3c1ea7db3876c003bab68e6a57afd6bcc169af0","0d14fa22c41fdc7277e6f71473b20ebc07f40f00e38875142335d5b63cdfc9d2","7980bf9d2972585cdf76b5a72105f7817be0723ccb2256090f6335f45b462abe","301d7466eb591139c7d456958f732153b3400f3243f68d3321956b43a64769e9","22f13de9e2fe5f0f4724797abd3d34a1cdd6e47ef81fc4933fea3b8bf4ad524b","e3ba509d3dce019b3190ceb2f3fc88e2610ab717122dabd91a9efaa37804040d","cda0cb09b995489b7f4c57f168cd31b83dcbaa7aad49612734fb3c9c73f6e4f2",{"version":"2abad7477cf6761b55c18bea4c21b5a5dcf319748c13696df3736b35f8ac149e","affectsGlobalScope":true},"d38e588a10943bbab1d4ce03d94759bf065ff802a9a72fc57aa75a72f1725b71","96d14f21b7652903852eef49379d04dbda28c16ed36468f8c9fa08f7c14c9538","2b8264b2fefd7367e0f20e2c04eed5d3038831fe00f5efbc110ff0131aab899b","6209c901f30cc321f4b86800d11fad3d67e73a3308f19946b1bc642af0280298","60aaac5fb1858fbd4c4eb40e01706eb227eed9eca5c665564bd146971280dbd3","74b0245c42990ed8a849df955db3f4362c81b13f799ebc981b7bec2d5b414a57","b0d10e46cfe3f6c476b69af02eaa38e4ccc7430221ce3109ae84bb9fb8282298","4266ccd2cf1d6a281efd9c7ddf9efd7daecf76575364148bd233e18919cac3ed","70e9a18da08294f75bf23e46c7d69e67634c0765d355887b9b41f0d959e1426e","105b9a2234dcb06ae922f2cd8297201136d416503ff7d16c72bfc8791e9895c1"],"options":{"composite":true,"declaration":true,"declarationMap":true,"downlevelIteration":true,"esModuleInterop":true,"experimentalDecorators":true,"importHelpers":true,"jsx":2,"noEmitOnError":false,"noImplicitAny":true,"noUnusedLocals":true,"outDir":"./","rootDir":"../src","skipLibCheck":true,"sourceMap":true,"strictNullChecks":true,"target":1,"tsBuildInfoFile":"./tsconfig.tsbuildinfo"},"fileIdsList":[[211,260],[260],[260,273],[53,190,260],[192,194,260],[190,192,193,260],[53,260],[53,140,260],[69,260],[211,212,213,214,215,260],[211,213,260],[233,260,267],[260,269],[260,270],[260,275,277],[217,260],[220,260],[221,226,260],[222,232,233,240,249,259,260],[222,223,232,240,260],[224,260],[225,226,233,241,260],[226,249,256,260],[227,229,232,240,260],[228,260],[229,230,260],[231,232,260],[232,260],[232,233,234,249,259,260],[232,233,234,249,260],[235,240,249,259,260],[232,233,235,236,240,249,256,259,260],[235,237,249,256,259,260],[217,218,219,220,221,222,223,224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239,240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255,256,257,258,259,260,261,262,263,264,265,266],[232,238,260],[239,259,260],[229,232,240,249,260],[241,260],[242,260],[220,243,260],[244,258,260,264],[245,260],[246,260],[232,247,260],[247,248,260,262],[232,249,250,251,260],[249,251,260],[249,250,260],[252,260],[253,260],[232,254,255,260],[254,255,260],[226,240,249,256,260],[257,260],[240,258,260],[221,235,246,259,260],[226,260],[249,260,261],[260,262],[260,263],[221,226,232,234,243,249,259,260,262,264],[249,260,265],[76,77,260],[53,58,64,65,68,71,72,73,76,260],[74,260],[84,260],[53,57,82,260],[53,54,57,58,62,75,76,260],[53,76,105,106,260],[53,54,57,58,62,76,260],[82,91,260],[53,54,62,75,76,93,260],[53,55,58,61,62,65,75,76,260],[53,54,57,62,76,260],[53,54,57,62,260],[53,54,55,58,60,62,63,75,76,260],[53,76,260],[53,75,76,260],[53,54,57,58,61,62,75,76,82,93,260],[53,55,58,260],[53,54,57,60,75,76,93,103,260],[53,54,60,76,103,105,260],[53,54,57,60,62,93,103,260],[53,54,55,58,60,61,75,76,93,260],[58,260],[53,55,58,59,60,61,75,76,260],[82,260],[83,260],[53,54,55,57,58,61,66,67,75,76,260],[58,59,260],[53,64,65,70,75,76,260],[53,56,64,70,75,76,260],[53,58,62,260],[53,118,260],[53,57,260],[57,260],[76,260],[75,260],[66,74,76,260],[53,54,57,58,61,75,76,260],[128,260],[53,56,57,260],[91,260],[44,45,46,47,48,55,56,57,58,59,60,61,62,63,64,65,66,67,68,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,260],[140,260],[46,260],[49,50,51,52,260],[232,235,237,240,259,260,267],[260,287],[260,275],[260,272,276],[260,274],[151,260],[53,141,142,143,144,146,147,148,149,150,157,260],[53,140,147,150,151,260],[143,144,260],[142,143,144,147,260],[143,260],[155,260],[157,260],[144,260],[53,144,260],[141,142,143,144,145,146,147,149,150,151,152,153,154,156,260],[149,260],[158,260],[43,140,166,177,260],[43,53,140,157,159,162,163,164,166,168,169,170,171,172,174,176,260],[43,53,140,162,165,260],[43,157,166,167,168,260],[43,53,140,165,166,168,170,171,177,260],[43,53,260],[43,167,260],[43,168,260],[43,53,140,157,162,163,166,172,191,195,260],[43,140,157,177,195,260],[43,53,140,157,177,179,191,198,260],[43,200,260],[43,157,170,171,173,260],[43,53,140,166,177,191,194,260],[43,53,140,166,179,191,194,260],[43,53,177,183,194,195,260],[43,260],[43,181,260],[43,53,177,180,181,182,183,260],[43,53,157,162,177,260],[43,53,140,180,182,184,260],[43,162,163,165,166,177,178,179,180,182,183,184,185,186,260],[43,53,140,160,161,260],[43,140,160,260],[43,140,260],[43,53,140,260],[43,157,260],[43,157,175,260],[43,53,140,157,175,260],[43,140,157,166,260],[43,140,157,170,171,260],[43,140,165,173,177,260],[53,140,166],[53,157,166,169],[53,140,162,165],[157,166],[53,140,166,177],[53],[191],[53,166,177,191,194],[53,166,179,191,194],[53,177,182,183,187],[53,162,177],[140,184],[162,163,165,166,177,178,179,180,182,183,184,185,186],[53,140],[140,160],[140],[157],[53,157],[140,157,166],[140,157],[177]],"referencedMap":[[213,1],[211,2],[274,3],[192,4],[193,5],[194,6],[191,4],[190,7],[69,8],[70,9],[273,2],[216,10],[212,1],[214,11],[215,1],[268,12],[269,2],[270,13],[271,14],[278,15],[279,2],[280,2],[217,16],[218,16],[220,17],[221,18],[222,19],[223,20],[224,21],[225,22],[226,23],[227,24],[228,25],[229,26],[230,26],[231,27],[232,28],[233,29],[234,30],[219,2],[266,2],[235,31],[236,32],[237,33],[267,34],[238,35],[239,36],[240,37],[241,38],[242,39],[243,40],[244,41],[245,42],[246,43],[247,44],[248,45],[249,46],[251,47],[250,48],[252,49],[253,50],[254,51],[255,52],[256,53],[257,54],[258,55],[259,56],[260,57],[261,58],[262,59],[263,60],[264,61],[265,62],[281,2],[282,2],[51,2],[78,63],[79,2],[74,64],[80,2],[81,65],[85,66],[86,2],[87,67],[88,68],[107,69],[89,2],[90,70],[92,71],[94,72],[95,73],[96,74],[63,74],[97,75],[64,76],[98,77],[99,68],[100,78],[101,79],[102,2],[60,80],[104,81],[106,82],[105,83],[103,84],[65,75],[61,85],[62,86],[108,2],[91,87],[83,87],[84,88],[68,89],[66,2],[67,2],[109,87],[110,90],[111,2],[112,71],[71,91],[72,92],[113,2],[114,93],[115,2],[116,2],[117,2],[119,94],[120,2],[56,7],[121,7],[122,95],[123,96],[124,2],[125,97],[127,97],[126,97],[76,98],[75,99],[77,97],[73,100],[128,2],[129,101],[58,102],[130,66],[131,66],[132,103],[133,87],[118,2],[134,2],[135,2],[136,2],[137,7],[138,2],[82,2],[140,104],[44,2],[45,105],[46,106],[48,2],[47,2],[93,2],[54,2],[139,105],[55,2],[59,85],[57,7],[283,7],[49,2],[53,107],[284,2],[52,2],[285,2],[286,108],[287,2],[288,109],[272,2],[50,2],[276,110],[277,111],[275,112],[149,2],[154,113],[151,114],[158,115],[147,116],[148,117],[141,2],[142,2],[145,116],[144,118],[156,119],[155,120],[153,116],[143,121],[146,122],[157,123],[175,124],[152,2],[150,7],[159,125],[43,2],[8,2],[9,2],[11,2],[10,2],[2,2],[12,2],[13,2],[14,2],[15,2],[16,2],[17,2],[18,2],[19,2],[3,2],[4,2],[23,2],[20,2],[21,2],[22,2],[24,2],[25,2],[26,2],[5,2],[27,2],[28,2],[29,2],[30,2],[6,2],[31,2],[32,2],[33,2],[34,2],[7,2],[35,2],[40,2],[41,2],[36,2],[37,2],[38,2],[39,2],[1,2],[42,2],[178,126],[177,127],[166,128],[169,129],[179,130],[164,131],[188,132],[189,133],[196,134],[197,135],[199,136],[201,137],[202,138],[195,139],[198,140],[203,141],[180,142],[182,143],[181,142],[184,144],[183,145],[185,142],[186,146],[170,142],[171,142],[172,142],[187,147],[162,148],[204,149],[205,149],[161,149],[160,131],[206,150],[207,150],[163,151],[208,131],[209,152],[210,152],[176,153],[200,154],[167,142],[168,155],[165,142],[173,156],[174,157]],"exportedModulesMap":[[213,1],[211,2],[274,3],[192,4],[193,5],[194,6],[191,4],[190,7],[69,8],[70,9],[273,2],[216,10],[212,1],[214,11],[215,1],[268,12],[269,2],[270,13],[271,14],[278,15],[279,2],[280,2],[217,16],[218,16],[220,17],[221,18],[222,19],[223,20],[224,21],[225,22],[226,23],[227,24],[228,25],[229,26],[230,26],[231,27],[232,28],[233,29],[234,30],[219,2],[266,2],[235,31],[236,32],[237,33],[267,34],[238,35],[239,36],[240,37],[241,38],[242,39],[243,40],[244,41],[245,42],[246,43],[247,44],[248,45],[249,46],[251,47],[250,48],[252,49],[253,50],[254,51],[255,52],[256,53],[257,54],[258,55],[259,56],[260,57],[261,58],[262,59],[263,60],[264,61],[265,62],[281,2],[282,2],[51,2],[78,63],[79,2],[74,64],[80,2],[81,65],[85,66],[86,2],[87,67],[88,68],[107,69],[89,2],[90,70],[92,71],[94,72],[95,73],[96,74],[63,74],[97,75],[64,76],[98,77],[99,68],[100,78],[101,79],[102,2],[60,80],[104,81],[106,82],[105,83],[103,84],[65,75],[61,85],[62,86],[108,2],[91,87],[83,87],[84,88],[68,89],[66,2],[67,2],[109,87],[110,90],[111,2],[112,71],[71,91],[72,92],[113,2],[114,93],[115,2],[116,2],[117,2],[119,94],[120,2],[56,7],[121,7],[122,95],[123,96],[124,2],[125,97],[127,97],[126,97],[76,98],[75,99],[77,97],[73,100],[128,2],[129,101],[58,102],[130,66],[131,66],[132,103],[133,87],[118,2],[134,2],[135,2],[136,2],[137,7],[138,2],[82,2],[140,104],[44,2],[45,105],[46,106],[48,2],[47,2],[93,2],[54,2],[139,105],[55,2],[59,85],[57,7],[283,7],[49,2],[53,107],[284,2],[52,2],[285,2],[286,108],[287,2],[288,109],[272,2],[50,2],[276,110],[277,111],[275,112],[149,2],[154,113],[151,114],[158,115],[147,116],[148,117],[141,2],[142,2],[145,116],[144,118],[156,119],[155,120],[153,116],[143,121],[146,122],[157,123],[175,124],[152,2],[150,7],[159,125],[43,2],[8,2],[9,2],[11,2],[10,2],[2,2],[12,2],[13,2],[14,2],[15,2],[16,2],[17,2],[18,2],[19,2],[3,2],[4,2],[23,2],[20,2],[21,2],[22,2],[24,2],[25,2],[26,2],[5,2],[27,2],[28,2],[29,2],[30,2],[6,2],[31,2],[32,2],[33,2],[34,2],[7,2],[35,2],[40,2],[41,2],[36,2],[37,2],[38,2],[39,2],[1,2],[42,2],[178,158],[177,159],[166,160],[169,161],[179,162],[164,163],[196,164],[199,164],[195,165],[198,166],[184,167],[183,168],[186,169],[187,170],[162,171],[204,172],[205,172],[161,172],[160,163],[206,173],[207,173],[163,171],[208,163],[209,174],[210,174],[176,174],[200,175],[168,176],[173,177],[174,178]],"semanticDiagnosticsPerFile":[213,211,274,192,193,194,191,190,69,70,273,216,212,214,215,268,269,270,271,278,279,280,217,218,220,221,222,223,224,225,226,227,228,229,230,231,232,233,234,219,266,235,236,237,267,238,239,240,241,242,243,244,245,246,247,248,249,251,250,252,253,254,255,256,257,258,259,260,261,262,263,264,265,281,282,51,78,79,74,80,81,85,86,87,88,107,89,90,92,94,95,96,63,97,64,98,99,100,101,102,60,104,106,105,103,65,61,62,108,91,83,84,68,66,67,109,110,111,112,71,72,113,114,115,116,117,119,120,56,121,122,123,124,125,127,126,76,75,77,73,128,129,58,130,131,132,133,118,134,135,136,137,138,82,140,44,45,46,48,47,93,54,139,55,59,57,283,49,53,284,52,285,286,287,288,272,50,276,277,275,149,154,151,158,147,148,141,142,145,144,156,155,153,143,146,157,175,152,150,159,43,8,9,11,10,2,12,13,14,15,16,17,18,19,3,4,23,20,21,22,24,25,26,5,27,28,29,30,6,31,32,33,34,7,35,40,41,36,37,38,39,1,42,178,177,166,169,179,164,188,189,196,197,199,201,202,195,198,203,180,182,181,184,183,185,186,170,171,172,187,162,204,205,161,160,206,207,163,208,209,210,176,200,167,168,165,173,174]},"version":"4.7.4"}
-\ No newline at end of file
-diff --git a/node_modules/@shopify/flash-list/ios/Sources/AutoLayoutView.swift b/node_modules/@shopify/flash-list/ios/Sources/AutoLayoutView.swift
-index f18e92c..f166553 100644
---- a/node_modules/@shopify/flash-list/ios/Sources/AutoLayoutView.swift
-+++ b/node_modules/@shopify/flash-list/ios/Sources/AutoLayoutView.swift
-@@ -4,31 +4,35 @@ import UIKit
-
- /// Container for all RecyclerListView children. This will automatically remove all gaps and overlaps for GridLayouts with flexible spans.
- /// Note: This cannot work for masonry layouts i.e, pinterest like layout
--@objc class AutoLayoutView: UIView {
-+@objc public class AutoLayoutView: UIView {
-+ #if RCT_NEW_ARCH_ENABLED
-+ @objc public var onBlankAreaEventHandler: ((CGFloat, CGFloat) -> Void)?
-+ #endif
-+
- @objc(onBlankAreaEvent)
- var onBlankAreaEvent: RCTDirectEventBlock?
-
-- @objc func setHorizontal(_ horizontal: Bool) {
-+ @objc public func setHorizontal(_ horizontal: Bool) {
- self.horizontal = horizontal
- }
-
-- @objc func setScrollOffset(_ scrollOffset: Int) {
-+ @objc public func setScrollOffset(_ scrollOffset: Int) {
- self.scrollOffset = CGFloat(scrollOffset)
- }
-
-- @objc func setWindowSize(_ windowSize: Int) {
-+ @objc public func setWindowSize(_ windowSize: Int) {
- self.windowSize = CGFloat(windowSize)
- }
-
-- @objc func setRenderAheadOffset(_ renderAheadOffset: Int) {
-+ @objc public func setRenderAheadOffset(_ renderAheadOffset: Int) {
- self.renderAheadOffset = CGFloat(renderAheadOffset)
- }
-
-- @objc func setEnableInstrumentation(_ enableInstrumentation: Bool) {
-+ @objc public func setEnableInstrumentation(_ enableInstrumentation: Bool) {
- self.enableInstrumentation = enableInstrumentation
- }
-
-- @objc func setDisableAutoLayout(_ disableAutoLayout: Bool) {
-+ @objc public func setDisableAutoLayout(_ disableAutoLayout: Bool) {
- self.disableAutoLayout = disableAutoLayout
- }
-
-@@ -46,7 +50,15 @@ import UIKit
- /// Tracks where first pixel is drawn in the visible window
- private var lastMinBound: CGFloat = 0
-
-- override func layoutSubviews() {
-+ private var viewsToLayout: [UIView] {
-+ #if RCT_NEW_ARCH_ENABLED
-+ return superview?.subviews ?? []
-+ #else
-+ return subviews
-+ #endif
-+ }
-+
-+ override public func layoutSubviews() {
- fixLayout()
- super.layoutSubviews()
-
-@@ -69,12 +81,16 @@ import UIKit
- distanceFromWindowEnd: distanceFromWindowEnd
- )
-
-+ #if RCT_NEW_ARCH_ENABLED
-+ onBlankAreaEventHandler?(blankOffsetStart, blankOffsetEnd)
-+ #else
- onBlankAreaEvent?(
- [
- "offsetStart": blankOffsetStart,
- "offsetEnd": blankOffsetEnd,
- ]
- )
-+ #endif
- }
-
- func getScrollView() -> UIScrollView? {
-@@ -85,15 +101,21 @@ import UIKit
- /// Performance: Sort is needed. Given relatively low number of views in RecyclerListView render tree this should be a non issue.
- private func fixLayout() {
- guard
-- subviews.count > 1,
-+ viewsToLayout.count > 1,
- // Fixing layout during animation can interfere with it.
- layer.animationKeys()?.isEmpty ?? true,
- !disableAutoLayout
- else { return }
-- let cellContainers = subviews
-- .compactMap { subview -> CellContainer? in
-- if let cellContainer = subview as? CellContainer {
-+ let cellContainers = viewsToLayout
-+ .compactMap { subview -> CellContainerComponentView? in
-+ if let cellContainer = subview as? CellContainerComponentView {
- return cellContainer
-+ } else if subview is AutoLayoutView {
-+ // On Fabric, due to view flattening children of AutoLayoutView are moved one level up, so they appear
-+ // as children of AutoLayoutViewComponentView in view hierarchy. viewsToLayout property takes it under
-+ // consideration, returning children of AutoLayoutViewComponentView when on Fabric. Because of that
-+ // AutoLayoutView may be on the list, in which case we want to ignore it.
-+ return nil
- } else {
- assertionFailure("CellRendererComponent outer view should always be CellContainer. Learn more here: https://shopify.github.io/flash-list/docs/usage#cellrenderercomponent.")
- return nil
-@@ -106,12 +128,16 @@ import UIKit
-
- /// Checks for overlaps or gaps between adjacent items and then applies a correction.
- /// Performance: RecyclerListView renders very small number of views and this is not going to trigger multiple layouts on the iOS side.
-- private func clearGaps(for cellContainers: [CellContainer]) {
-+ private func clearGaps(for cellContainers: [CellContainerComponentView]) {
- var maxBound: CGFloat = 0
- var minBound: CGFloat = CGFloat(Int.max)
- var maxBoundNextCell: CGFloat = 0
- let correctedScrollOffset = scrollOffset - (horizontal ? frame.minX : frame.minY)
- lastMaxBoundOverall = 0
-+ if cellContainers.count == 1 {
-+ let firstCellContainer = cellContainers[0]
-+ lastMaxBoundOverall = horizontal ? firstCellContainer.frame.maxX : firstCellContainer.frame.maxY
-+ }
- cellContainers.indices.dropLast().forEach { index in
- let cellContainer = cellContainers[index]
- let cellTop = cellContainer.frame.minY
-@@ -192,7 +218,7 @@ import UIKit
- lastMinBound = minBound
- }
-
-- private func updateLastMaxBoundOverall(currentCell: CellContainer, nextCell: CellContainer) {
-+ private func updateLastMaxBoundOverall(currentCell: CellContainerComponentView, nextCell: CellContainerComponentView) {
- lastMaxBoundOverall = max(lastMaxBoundOverall, horizontal ? currentCell.frame.maxX : currentCell.frame.maxY, horizontal ? nextCell.frame.maxX : nextCell.frame.maxY)
- }
-
-@@ -217,7 +243,7 @@ import UIKit
-
- /// It's important to avoid correcting views outside the render window. An item that isn't being recycled might still remain in the view tree. If views outside get considered then gaps between unused items will cause algorithm to fail.
- func isWithinBounds(
-- _ cellContainer: CellContainer,
-+ _ cellContainer: CellContainerComponentView,
- scrollOffset: CGFloat,
- renderAheadOffset: CGFloat,
- windowSize: CGFloat,
-@@ -260,17 +286,18 @@ import UIKit
- }
-
- private func footerDiff() -> CGFloat {
-- if subviews.count == 0 {
-- lastMaxBoundOverall = 0
-- } else if subviews.count == 1 {
-- let firstChild = subviews[0]
-- lastMaxBoundOverall = horizontal ? firstChild.frame.maxX : firstChild.frame.maxY
-- }
- let autoLayoutEnd = horizontal ? frame.width : frame.height
- return lastMaxBoundOverall - autoLayoutEnd
- }
-
- private func footer() -> UIView? {
-- return superview?.subviews.first(where:{($0 as? CellContainer)?.index == -1})
-+ // On Fabric, AutoLayoutView is wrapped with AutoLayoutViewComponentView, so we need to go up one more level
-+ #if RCT_NEW_ARCH_ENABLED
-+ let parentSubviews = superview?.superview?.subviews
-+ #else
-+ let parentSubviews = superview?.subviews
-+ #endif
-+
-+ return parentSubviews?.first(where:{($0 as? CellContainerComponentView)?.index == -1})
- }
- }
-diff --git a/node_modules/@shopify/flash-list/ios/Sources/AutoLayoutViewComponentView.h b/node_modules/@shopify/flash-list/ios/Sources/AutoLayoutViewComponentView.h
-new file mode 100644
-index 0000000..1ae0b66
---- /dev/null
-+++ b/node_modules/@shopify/flash-list/ios/Sources/AutoLayoutViewComponentView.h
-@@ -0,0 +1,16 @@
-+#ifndef AutoLayoutViewComponentView_h
-+#define AutoLayoutViewComponentView_h
-+
-+#ifdef RCT_NEW_ARCH_ENABLED
-+
-+#import
-+#import
-+
-+@interface AutoLayoutViewComponentView : RCTViewComponentView
-+
-+@end
-+
-+
-+#endif /* RCT_NEW_ARCH_ENABLED */
-+
-+#endif /* AutoLayoutViewComponentView_h */
-diff --git a/node_modules/@shopify/flash-list/ios/Sources/AutoLayoutViewComponentView.mm b/node_modules/@shopify/flash-list/ios/Sources/AutoLayoutViewComponentView.mm
-new file mode 100644
-index 0000000..6ef6a41
---- /dev/null
-+++ b/node_modules/@shopify/flash-list/ios/Sources/AutoLayoutViewComponentView.mm
-@@ -0,0 +1,86 @@
-+#ifdef RCT_NEW_ARCH_ENABLED
-+#import "AutoLayoutViewComponentView.h"
-+#import
-+#import
-+
-+#import
-+#import
-+#import
-+#import
-+
-+#import "RCTFabricComponentsPlugins.h"
-+
-+#ifdef USE_FRAMEWORKS
-+#import
-+#else
-+#import
-+#endif
-+
-+
-+using namespace facebook::react;
-+
-+@interface AutoLayoutViewComponentView ()
-+@end
-+
-+@implementation AutoLayoutViewComponentView
-+{
-+ AutoLayoutView *_autoLayoutView;
-+}
-+
-+- (instancetype)initWithFrame:(CGRect)frame
-+{
-+ if (self = [super initWithFrame:frame]) {
-+ static const auto defaultProps = std::make_shared();
-+ _props = defaultProps;
-+ _autoLayoutView = [[AutoLayoutView alloc] initWithFrame:self.bounds];
-+
-+ // Due to view flattening, AutoLayoutView's children get moved to its parent (AutoLayoutViewComponentView) and
-+ // AutoLayoutView is positioned above them consuming all events. Turning off userInteraction prevents that.
-+ _autoLayoutView.userInteractionEnabled = false;
-+
-+ self.contentView = _autoLayoutView;
-+
-+ __weak AutoLayoutViewComponentView* weakSelf = self;
-+ _autoLayoutView.onBlankAreaEventHandler = ^(CGFloat start, CGFloat end) {
-+ AutoLayoutViewComponentView *strongSelf = weakSelf;
-+ if (strongSelf != nullptr && strongSelf->_eventEmitter != nullptr) {
-+ std::dynamic_pointer_cast(strongSelf->_eventEmitter)
-+ ->onBlankAreaEvent(facebook::react::AutoLayoutViewEventEmitter::OnBlankAreaEvent{
-+ .offsetStart = (int) floor(start),
-+ .offsetEnd = (int) floor(end),
-+ });
-+ }
-+ };
-+ }
-+
-+ return self;
-+}
-+
-+#pragma mark - RCTComponentViewProtocol
-+
-++ (ComponentDescriptorProvider)componentDescriptorProvider
-+{
-+ return concreteComponentDescriptorProvider();
-+}
-+
-+- (void)updateProps:(const Props::Shared &)props oldProps:(const Props::Shared &)oldProps
-+{
-+ const auto &newProps = *std::static_pointer_cast(props);
-+
-+ [_autoLayoutView setHorizontal:newProps.horizontal];
-+ [_autoLayoutView setScrollOffset:newProps.scrollOffset];
-+ [_autoLayoutView setWindowSize:newProps.windowSize];
-+ [_autoLayoutView setRenderAheadOffset:newProps.renderAheadOffset];
-+ [_autoLayoutView setEnableInstrumentation:newProps.enableInstrumentation];
-+ [_autoLayoutView setDisableAutoLayout:newProps.disableAutoLayout];
-+
-+ [super updateProps:props oldProps:oldProps];
-+}
-+@end
-+
-+Class AutoLayoutViewCls(void)
-+{
-+ return AutoLayoutViewComponentView.class;
-+}
-+
-+#endif /* RCT_NEW_ARCH_ENABLED */
-diff --git a/node_modules/@shopify/flash-list/ios/Sources/AutoLayoutViewManager.m b/node_modules/@shopify/flash-list/ios/Sources/AutoLayoutViewManager.mm
-similarity index 100%
-rename from node_modules/@shopify/flash-list/ios/Sources/AutoLayoutViewManager.m
-rename to node_modules/@shopify/flash-list/ios/Sources/AutoLayoutViewManager.mm
-diff --git a/node_modules/@shopify/flash-list/ios/Sources/CellContainer.swift b/node_modules/@shopify/flash-list/ios/Sources/CellContainer.swift
-deleted file mode 100644
-index 7f09ce7..0000000
---- a/node_modules/@shopify/flash-list/ios/Sources/CellContainer.swift
-+++ /dev/null
-@@ -1,9 +0,0 @@
--import Foundation
--
--@objc class CellContainer: UIView {
-- var index: Int = -1
--
-- @objc func setIndex(_ index: Int) {
-- self.index = index
-- }
--}
-diff --git a/node_modules/@shopify/flash-list/ios/Sources/CellContainerComponentView.h b/node_modules/@shopify/flash-list/ios/Sources/CellContainerComponentView.h
-new file mode 100644
-index 0000000..ca1cbfe
---- /dev/null
-+++ b/node_modules/@shopify/flash-list/ios/Sources/CellContainerComponentView.h
-@@ -0,0 +1,18 @@
-+#ifndef CellContainer_h
-+#define CellContainer_h
-+
-+#import
-+
-+#ifdef RCT_NEW_ARCH_ENABLED
-+#import
-+
-+@interface CellContainerComponentView : RCTViewComponentView
-+#else
-+@interface CellContainerComponentView : UIView
-+#endif
-+
-+@property int64_t index;
-+
-+@end
-+
-+#endif /* CellContainer_h */
-diff --git a/node_modules/@shopify/flash-list/ios/Sources/CellContainerComponentView.mm b/node_modules/@shopify/flash-list/ios/Sources/CellContainerComponentView.mm
-new file mode 100644
-index 0000000..ae489b8
---- /dev/null
-+++ b/node_modules/@shopify/flash-list/ios/Sources/CellContainerComponentView.mm
-@@ -0,0 +1,62 @@
-+#import "CellContainerComponentView.h"
-+
-+#ifdef RCT_NEW_ARCH_ENABLED
-+#import
-+
-+#import
-+#import
-+#import
-+#import
-+
-+#import "RCTFabricComponentsPlugins.h"
-+
-+#ifdef USE_FRAMEWORKS
-+#import
-+#else
-+#import
-+#endif
-+
-+using namespace facebook::react;
-+
-+@interface CellContainerComponentView ()
-+@end
-+
-+@implementation CellContainerComponentView
-+
-+- (instancetype)initWithFrame:(CGRect)frame
-+{
-+ if (self = [super initWithFrame:frame]) {
-+ static const auto defaultProps = std::make_shared();
-+ _props = defaultProps;
-+
-+ self.userInteractionEnabled = true;
-+ }
-+
-+ return self;
-+}
-+
-+#pragma mark - RCTComponentViewProtocol
-+
-++ (ComponentDescriptorProvider)componentDescriptorProvider
-+{
-+ return concreteComponentDescriptorProvider();
-+}
-+
-+- (void)updateProps:(const Props::Shared &)props oldProps:(const Props::Shared &)oldProps
-+{
-+ const auto &newProps = *std::static_pointer_cast(props);
-+
-+ self.index = newProps.index;
-+
-+ [super updateProps:props oldProps:oldProps];
-+}
-+@end
-+
-+Class CellContainerCls(void)
-+{
-+ return CellContainerComponentView.class;
-+}
-+#else
-+@implementation CellContainerComponentView
-+@end
-+#endif /* RCT_NEW_ARCH_ENABLED */
-diff --git a/node_modules/@shopify/flash-list/ios/Sources/CellContainerManager.m b/node_modules/@shopify/flash-list/ios/Sources/CellContainerManager.mm
-similarity index 100%
-rename from node_modules/@shopify/flash-list/ios/Sources/CellContainerManager.m
-rename to node_modules/@shopify/flash-list/ios/Sources/CellContainerManager.mm
-diff --git a/node_modules/@shopify/flash-list/ios/Sources/CellContainerManager.swift b/node_modules/@shopify/flash-list/ios/Sources/CellContainerManager.swift
-index a36fccd..dbe6c14 100644
---- a/node_modules/@shopify/flash-list/ios/Sources/CellContainerManager.swift
-+++ b/node_modules/@shopify/flash-list/ios/Sources/CellContainerManager.swift
-@@ -3,7 +3,7 @@ import Foundation
- @objc(CellContainerManager)
- class CellContainerManager: RCTViewManager {
- override func view() -> UIView! {
-- return CellContainer()
-+ return CellContainerComponentView()
- }
-
- override static func requiresMainQueueSetup() -> Bool {
-diff --git a/node_modules/@shopify/flash-list/ios/Sources/FlatListPro-Bridging-Header.h b/node_modules/@shopify/flash-list/ios/Sources/FlatListPro-Bridging-Header.h
-index e3e23d5..7ed83ce 100644
---- a/node_modules/@shopify/flash-list/ios/Sources/FlatListPro-Bridging-Header.h
-+++ b/node_modules/@shopify/flash-list/ios/Sources/FlatListPro-Bridging-Header.h
-@@ -4,5 +4,8 @@
- #import
- #import
- #import
-+#import
-+
-+#import "CellContainerComponentView.h"
-
- #endif /* FlatListPro_Bridging_Header_h */
-diff --git a/node_modules/@shopify/flash-list/package.json b/node_modules/@shopify/flash-list/package.json
-index b90c287..ccca16d 100644
---- a/node_modules/@shopify/flash-list/package.json
-+++ b/node_modules/@shopify/flash-list/package.json
-@@ -25,6 +25,7 @@
- "author": "shopify",
- "license": "MIT",
- "homepage": "https://shopify.github.io/flash-list/",
-+ "react-native": "src/index.ts",
- "main": "dist/index.js",
- "types": "dist/index.d.ts",
- "scripts": {
-@@ -64,7 +65,7 @@
- "@react-native-community/eslint-config": "^3.0.3",
- "@shopify/eslint-plugin": "^41.3.1",
- "@types/jest": "^28.1.3",
-- "@types/react-native": "0.72.2",
-+ "@types/react-native": "^0.72.2",
- "babel-jest": "^28.1.1",
- "enhanced-resolve": "^5.9.3",
- "eslint": "8.18.0",
-@@ -74,7 +75,7 @@
- "prettier": "^2.7.1",
- "react": "17.0.2",
- "react-native": "0.68.5",
-- "typescript": "^4.7.4"
-+ "typescript": "4.8.4"
- },
- "files": [
- "android",
-@@ -87,5 +88,10 @@
- "dependencies": {
- "recyclerlistview": "4.2.0",
- "tslib": "2.4.0"
-+ },
-+ "codegenConfig": {
-+ "name": "rnflashlist",
-+ "type": "components",
-+ "jsSrcsDir": "./src/fabric"
- }
- }
-diff --git a/node_modules/@shopify/flash-list/src/FlashList.tsx b/node_modules/@shopify/flash-list/src/FlashList.tsx
-index 64748fe..87dea16 100644
---- a/node_modules/@shopify/flash-list/src/FlashList.tsx
-+++ b/node_modules/@shopify/flash-list/src/FlashList.tsx
-@@ -827,6 +827,12 @@ class FlashList extends React.PureComponent<
- return this.rlvRef?.getScrollableNode?.() || null;
- }
-
-+ public getNativeScrollRef(): number | null {
-+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
-+ // @ts-ignore
-+ return this.rlvRef?.getNativeScrollRef?.() || null;
-+ }
-+
- /**
- * Allows access to internal recyclerlistview. This is useful for enabling access to its public APIs.
- * Warning: We may swap recyclerlistview for something else in the future. Use with caution.
-diff --git a/node_modules/@shopify/flash-list/src/fabric/AutoLayoutNativeComponent.ts b/node_modules/@shopify/flash-list/src/fabric/AutoLayoutNativeComponent.ts
-new file mode 100644
-index 0000000..93750de
---- /dev/null
-+++ b/node_modules/@shopify/flash-list/src/fabric/AutoLayoutNativeComponent.ts
-@@ -0,0 +1,24 @@
-+import codegenNativeComponent from "react-native/Libraries/Utilities/codegenNativeComponent";
-+import type { ViewProps } from "react-native";
-+import type {
-+ Int32,
-+ Double,
-+ DirectEventHandler,
-+} from "react-native/Libraries/Types/CodegenTypes";
-+
-+type BlankAreaEvent = Readonly<{
-+ offsetStart: Int32;
-+ offsetEnd: Int32;
-+}>;
-+
-+interface NativeProps extends ViewProps {
-+ horizontal?: boolean;
-+ scrollOffset?: Double;
-+ windowSize?: Double;
-+ renderAheadOffset?: Double;
-+ enableInstrumentation?: boolean;
-+ disableAutoLayout?: boolean;
-+ onBlankAreaEvent?: DirectEventHandler;
-+}
-+
-+export default codegenNativeComponent("AutoLayoutView");
-diff --git a/node_modules/@shopify/flash-list/src/fabric/CellContainerNativeComponent.ts b/node_modules/@shopify/flash-list/src/fabric/CellContainerNativeComponent.ts
-new file mode 100644
-index 0000000..dd284ac
---- /dev/null
-+++ b/node_modules/@shopify/flash-list/src/fabric/CellContainerNativeComponent.ts
-@@ -0,0 +1,9 @@
-+import codegenNativeComponent from "react-native/Libraries/Utilities/codegenNativeComponent";
-+import type { Int32 } from "react-native/Libraries/Types/CodegenTypes";
-+import type { ViewProps } from "react-native";
-+
-+interface NativeProps extends ViewProps {
-+ index?: Int32;
-+}
-+
-+export default codegenNativeComponent("CellContainer");
diff --git a/patches/react-native+0.75.2+012+Add-onPaste-to-TextInput.patch b/patches/react-native+0.75.2+012+Add-onPaste-to-TextInput.patch
index f406a36239c6..55657e61dc09 100644
--- a/patches/react-native+0.75.2+012+Add-onPaste-to-TextInput.patch
+++ b/patches/react-native+0.75.2+012+Add-onPaste-to-TextInput.patch
@@ -303,7 +303,7 @@ index 9a02b82..06442b4 100644
@property (nonatomic, assign) NSInteger mostRecentEventCount;
@property (nonatomic, assign, readonly) NSInteger nativeEventCount;
diff --git a/node_modules/react-native/Libraries/Text/TextInput/RCTBaseTextInputView.mm b/node_modules/react-native/Libraries/Text/TextInput/RCTBaseTextInputView.mm
-index 826ff3a..04ea28f 100644
+index e3ed34e..88c8d36 100644
--- a/node_modules/react-native/Libraries/Text/TextInput/RCTBaseTextInputView.mm
+++ b/node_modules/react-native/Libraries/Text/TextInput/RCTBaseTextInputView.mm
@@ -551,6 +551,26 @@ - (void)textInputDidChangeSelection
@@ -406,7 +406,7 @@ index 0318671..bb165d7 100644
#pragma mark - Layout
diff --git a/node_modules/react-native/React/Fabric/Mounting/ComponentViews/TextInput/RCTTextInputComponentView.mm b/node_modules/react-native/React/Fabric/Mounting/ComponentViews/TextInput/RCTTextInputComponentView.mm
-index 29f733a..79bd345 100644
+index 8bbe925..db7cba4 100644
--- a/node_modules/react-native/React/Fabric/Mounting/ComponentViews/TextInput/RCTTextInputComponentView.mm
+++ b/node_modules/react-native/React/Fabric/Mounting/ComponentViews/TextInput/RCTTextInputComponentView.mm
@@ -421,6 +421,13 @@ - (void)textInputDidChangeSelection
@@ -528,13 +528,13 @@ index 014e0b4..119219b 100644
* Attempt to set a selection or fail silently. Intentionally meant to handle bad inputs.
* EventCounter is the same one used as with text.
diff --git a/node_modules/react-native/ReactAndroid/src/main/java/com/facebook/react/views/textinput/ReactTextInputManager.java b/node_modules/react-native/ReactAndroid/src/main/java/com/facebook/react/views/textinput/ReactTextInputManager.java
-index a8c5d94..dd7969f 100644
+index a8c5d94..2cceb14 100644
--- a/node_modules/react-native/ReactAndroid/src/main/java/com/facebook/react/views/textinput/ReactTextInputManager.java
+++ b/node_modules/react-native/ReactAndroid/src/main/java/com/facebook/react/views/textinput/ReactTextInputManager.java
-@@ -246,6 +246,9 @@ public class ReactTextInputManager extends BaseViewManager "\"$(PODS_ROOT)/boost\"",
-- "CLANG_CXX_LANGUAGE_STANDARD" => "c++17"
-- }
--
-- s.dependency "React-Codegen"
-- s.dependency "RCT-Folly"
-- s.dependency "RCTRequired"
-- s.dependency "RCTTypeSafety"
-- s.dependency "ReactCommon/turbomodule/core"
-- end
-+ install_modules_dependencies(s)
-
- end
diff --git a/patches/react-native-quick-sqlite+8.0.6.patch b/patches/react-native-quick-sqlite+8.0.6.patch
deleted file mode 100644
index 5d4dcfad8e35..000000000000
--- a/patches/react-native-quick-sqlite+8.0.6.patch
+++ /dev/null
@@ -1,13 +0,0 @@
-diff --git a/node_modules/react-native-quick-sqlite/android/build.gradle b/node_modules/react-native-quick-sqlite/android/build.gradle
-index afcda02..3faaf76 100644
---- a/node_modules/react-native-quick-sqlite/android/build.gradle
-+++ b/node_modules/react-native-quick-sqlite/android/build.gradle
-@@ -63,7 +63,7 @@ android {
- }
-
- defaultConfig {
-- minSdkVersion 21
-+ minSdkVersion 23
- targetSdkVersion safeExtGet('targetSdkVersion', 28)
- versionCode 1
- versionName "1.0"
diff --git a/patches/react-native-reanimated+3.13.0+002+rn-75-fixes.patch b/patches/react-native-reanimated+3.13.0+002+rn-75-fixes.patch
deleted file mode 100644
index 68b45d4658a7..000000000000
--- a/patches/react-native-reanimated+3.13.0+002+rn-75-fixes.patch
+++ /dev/null
@@ -1,142 +0,0 @@
-diff --git a/node_modules/react-native-reanimated/android/src/main/java/com/swmansion/reanimated/ReactNativeUtils.java b/node_modules/react-native-reanimated/android/src/main/java/com/swmansion/reanimated/ReactNativeUtils.java
-index 3667652..2c2d9e1 100644
---- a/node_modules/react-native-reanimated/android/src/main/java/com/swmansion/reanimated/ReactNativeUtils.java
-+++ b/node_modules/react-native-reanimated/android/src/main/java/com/swmansion/reanimated/ReactNativeUtils.java
-@@ -2,6 +2,9 @@ package com.swmansion.reanimated;
-
- import android.graphics.drawable.Drawable;
- import android.view.View;
-+import com.facebook.react.uimanager.drawable.CSSBackgroundDrawable;
-+import com.facebook.react.uimanager.style.BorderRadiusProp;
-+import com.facebook.react.uimanager.style.ComputedBorderRadius;
- import com.facebook.react.views.image.ReactImageView;
- import com.facebook.react.views.view.ReactViewBackgroundDrawable;
- import java.lang.reflect.Field;
-@@ -29,15 +32,15 @@ public class ReactNativeUtils {
- public static BorderRadii getBorderRadii(View view) {
- if (view.getBackground() != null) {
- Drawable background = view.getBackground();
-- if (background instanceof ReactViewBackgroundDrawable) {
-- ReactViewBackgroundDrawable drawable = (ReactViewBackgroundDrawable) background;
-+ if (background instanceof CSSBackgroundDrawable drawable) {
-+ ComputedBorderRadius computedBorderRadius = drawable.getComputedBorderRadius();
- return new BorderRadii(
-- drawable.getFullBorderRadius(),
-- drawable.getBorderRadius(ReactViewBackgroundDrawable.BorderRadiusLocation.TOP_LEFT),
-- drawable.getBorderRadius(ReactViewBackgroundDrawable.BorderRadiusLocation.TOP_RIGHT),
-- drawable.getBorderRadius(ReactViewBackgroundDrawable.BorderRadiusLocation.BOTTOM_LEFT),
-- drawable.getBorderRadius(
-- ReactViewBackgroundDrawable.BorderRadiusLocation.BOTTOM_RIGHT));
-+ // TODO: get full border radius
-+ computedBorderRadius.getTopLeft(),
-+ computedBorderRadius.getTopLeft(),
-+ computedBorderRadius.getTopRight(),
-+ computedBorderRadius.getBottomLeft(),
-+ computedBorderRadius.getBottomRight());
- }
- } else if (view instanceof ReactImageView) {
- try {
-diff --git a/node_modules/react-native-reanimated/android/src/reactNativeVersionPatch/ReactFeatureFlagsWrapper/latest/com/swmansion/reanimated/ReactFeatureFlagsWrapper.java b/node_modules/react-native-reanimated/android/src/reactNativeVersionPatch/ReactFeatureFlagsWrapper/latest/com/swmansion/reanimated/ReactFeatureFlagsWrapper.java
-deleted file mode 100644
-index a6a2c16..0000000
---- a/node_modules/react-native-reanimated/android/src/reactNativeVersionPatch/ReactFeatureFlagsWrapper/latest/com/swmansion/reanimated/ReactFeatureFlagsWrapper.java
-+++ /dev/null
-@@ -1,11 +0,0 @@
--package com.swmansion.reanimated;
--
--import com.facebook.react.config.ReactFeatureFlags;
--
--public class ReactFeatureFlagsWrapper {
--
-- public static void enableMountHooks() {
-- ReactFeatureFlags.enableMountHooks = true;
-- }
--
--}
-diff --git a/node_modules/react-native-reanimated/android/src/reactNativeVersionPatch/ReanimatedUIManager/latest/com/swmansion/reanimated/layoutReanimation/ReanimatedUIManager.java b/node_modules/react-native-reanimated/android/src/reactNativeVersionPatch/ReanimatedUIManager/latest/com/swmansion/reanimated/layoutReanimation/ReanimatedUIManager.java
-index 256906f..9c84f87 100644
---- a/node_modules/react-native-reanimated/android/src/reactNativeVersionPatch/ReanimatedUIManager/latest/com/swmansion/reanimated/layoutReanimation/ReanimatedUIManager.java
-+++ b/node_modules/react-native-reanimated/android/src/reactNativeVersionPatch/ReanimatedUIManager/latest/com/swmansion/reanimated/layoutReanimation/ReanimatedUIManager.java
-@@ -125,22 +125,6 @@ public class ReanimatedUIManager extends UIManagerModule {
- super.measureLayout(tag, ancestorTag, errorCallback, successCallback);
- }
-
-- /**
-- * Like {@link #measure} and {@link #measureLayout} but measures relative to the immediate parent.
-- *
-- *
NB: Unlike {@link #measure}, this will measure relative to the view layout, not the visible
-- * window which can cause unexpected results when measuring relative to things like ScrollViews
-- * that can have offset content on the screen.
-- *
-- * @deprecated this method will not be available in FabricUIManager class.
-- */
-- @ReactMethod
-- @Deprecated
-- public void measureLayoutRelativeToParent(
-- int tag, Callback errorCallback, Callback successCallback) {
-- super.measureLayoutRelativeToParent(tag, errorCallback, successCallback);
-- }
--
- /**
- * Find the touch target child native view in the supplied root view hierarchy, given a react
- * target location.
-@@ -186,26 +170,6 @@ public class ReanimatedUIManager extends UIManagerModule {
- super.dispatchViewManagerCommand(reactTag, commandId, commandArgs);
- }
-
-- /**
-- * Show a PopupMenu.
-- *
-- * @param reactTag the tag of the anchor view (the PopupMenu is displayed next to this view); this
-- * needs to be the tag of a native view (shadow views can not be anchors)
-- * @param items the menu items as an array of strings
-- * @param error will be called if there is an error displaying the menu
-- * @param success will be called with the position of the selected item as the first argument, or
-- * no arguments if the menu is dismissed
-- */
-- @ReactMethod
-- public void showPopupMenu(int reactTag, ReadableArray items, Callback error, Callback success) {
-- super.showPopupMenu(reactTag, items, error, success);
-- }
--
-- @ReactMethod
-- public void dismissPopupMenu() {
-- super.dismissPopupMenu();
-- }
--
- /**
- * LayoutAnimation API on Android is currently experimental. Therefore, it needs to be enabled
- * explicitly in order to avoid regression in existing application written for iOS using this API.
-diff --git a/node_modules/react-native-reanimated/android/src/reactNativeVersionPatch/RuntimeExecutor/latest/com/swmansion/reanimated/NativeProxy.java b/node_modules/react-native-reanimated/android/src/reactNativeVersionPatch/RuntimeExecutor/latest/com/swmansion/reanimated/NativeProxy.java
-index 3902e82..673b2bb 100644
---- a/node_modules/react-native-reanimated/android/src/reactNativeVersionPatch/RuntimeExecutor/latest/com/swmansion/reanimated/NativeProxy.java
-+++ b/node_modules/react-native-reanimated/android/src/reactNativeVersionPatch/RuntimeExecutor/latest/com/swmansion/reanimated/NativeProxy.java
-@@ -26,7 +26,6 @@ public class NativeProxy extends NativeProxyCommon {
-
- public @OptIn(markerClass = FrameworkAPI.class) NativeProxy(ReactApplicationContext context, String valueUnpackerCode) {
- super(context);
-- ReactFeatureFlagsWrapper.enableMountHooks();
-
- FabricUIManager fabricUIManager =
- (FabricUIManager) UIManagerHelper.getUIManager(context, UIManagerType.FABRIC);
-@@ -37,7 +36,7 @@ public class NativeProxy extends NativeProxyCommon {
-
-
- if (context.isBridgeless()) {
-- RuntimeExecutor runtimeExecutor = context.getRuntimeExecutor();
-+ RuntimeExecutor runtimeExecutor = context.getCatalystInstance().getRuntimeExecutor();
- mHybridData = initHybridBridgeless(
- Objects.requireNonNull(context.getJavaScriptContextHolder()).get(),
- runtimeExecutor,
-diff --git a/node_modules/react-native-reanimated/Common/cpp/LayoutAnimations/LayoutAnimationsProxy.cpp b/node_modules/react-native-reanimated/Common/cpp/LayoutAnimations/LayoutAnimationsProxy.cpp
-index f219e5d..dd714dc 100644
---- a/node_modules/react-native-reanimated/Common/cpp/LayoutAnimations/LayoutAnimationsProxy.cpp
-+++ b/node_modules/react-native-reanimated/Common/cpp/LayoutAnimations/LayoutAnimationsProxy.cpp
-@@ -697,6 +697,7 @@ void LayoutAnimationsProxy::transferConfigFromNativeID(
- auto nativeId = stoi(nativeIdString);
- layoutAnimationsManager_->transferConfigFromNativeID(nativeId, tag);
- } catch (std::invalid_argument) {
-+ } catch (std::out_of_range) {
- }
- }
-
diff --git a/patches/react-native-reanimated+3.13.0+005+proper-navigator-types.patch b/patches/react-native-reanimated+3.13.0+005+proper-navigator-types.patch
deleted file mode 100644
index 59e577444d3e..000000000000
--- a/patches/react-native-reanimated+3.13.0+005+proper-navigator-types.patch
+++ /dev/null
@@ -1,29 +0,0 @@
-diff --git a/node_modules/react-native-reanimated/lib/typescript/js-reanimated/JSReanimated.d.ts b/node_modules/react-native-reanimated/lib/typescript/js-reanimated/JSReanimated.d.ts
-index 5c34476..df9a9b7 100644
---- a/node_modules/react-native-reanimated/lib/typescript/js-reanimated/JSReanimated.d.ts
-+++ b/node_modules/react-native-reanimated/lib/typescript/js-reanimated/JSReanimated.d.ts
-@@ -36,8 +36,8 @@ declare enum Platform {
- }
- declare global {
- interface Navigator {
-- userAgent?: string;
-- vendor?: string;
-+ userAgent: string;
-+ vendor: string;
- }
- }
- export {};
-diff --git a/node_modules/react-native-reanimated/src/js-reanimated/JSReanimated.ts b/node_modules/react-native-reanimated/src/js-reanimated/JSReanimated.ts
-index 29cda27..28ec9d2 100644
---- a/node_modules/react-native-reanimated/src/js-reanimated/JSReanimated.ts
-+++ b/node_modules/react-native-reanimated/src/js-reanimated/JSReanimated.ts
-@@ -311,7 +311,7 @@ enum Platform {
-
- declare global {
- interface Navigator {
-- userAgent?: string;
-- vendor?: string;
-+ userAgent: string;
-+ vendor: string;
- }
- }
diff --git a/patches/react-native-reanimated+3.13.0+006+fixNoExportedMember.patch b/patches/react-native-reanimated+3.13.0+006+fixNoExportedMember.patch
deleted file mode 100644
index 22827fa1f96d..000000000000
--- a/patches/react-native-reanimated+3.13.0+006+fixNoExportedMember.patch
+++ /dev/null
@@ -1,24 +0,0 @@
-diff --git a/node_modules/react-native-reanimated/lib/module/platform-specific/RNRenderer.web.js b/node_modules/react-native-reanimated/lib/module/platform-specific/RNRenderer.web.js
-index a9b2299..6a97aea 100644
---- a/node_modules/react-native-reanimated/lib/module/platform-specific/RNRenderer.web.js
-+++ b/node_modules/react-native-reanimated/lib/module/platform-specific/RNRenderer.web.js
-@@ -1,5 +1,6 @@
- 'use strict';
-
- // RNRender is not used for web. An export is still defined to eliminate warnings from bundlers such as esbuild.
--export {};
-+const RNRenderer = {};
-+export { RNRenderer };
- //# sourceMappingURL=RNRenderer.web.js.map
-\ No newline at end of file
-diff --git a/node_modules/react-native-reanimated/src/platform-specific/RNRenderer.web.ts b/node_modules/react-native-reanimated/src/platform-specific/RNRenderer.web.ts
-index f9eb5e3..b51ae1d 100644
---- a/node_modules/react-native-reanimated/src/platform-specific/RNRenderer.web.ts
-+++ b/node_modules/react-native-reanimated/src/platform-specific/RNRenderer.web.ts
-@@ -1,3 +1,4 @@
- 'use strict';
- // RNRender is not used for web. An export is still defined to eliminate warnings from bundlers such as esbuild.
--export {};
-+const RNRenderer = {};
-+export { RNRenderer };
-\ No newline at end of file
diff --git a/patches/react-native-reanimated+3.13.0+001+hybrid-app.patch b/patches/react-native-reanimated+3.15.1+001+hybrid-app.patch
similarity index 100%
rename from patches/react-native-reanimated+3.13.0+001+hybrid-app.patch
rename to patches/react-native-reanimated+3.15.1+001+hybrid-app.patch
diff --git a/patches/react-native-reanimated+3.13.0+003+dontWhitelistTextProp.patch b/patches/react-native-reanimated+3.15.1+002+dontWhitelistTextProp.patch
similarity index 80%
rename from patches/react-native-reanimated+3.13.0+003+dontWhitelistTextProp.patch
rename to patches/react-native-reanimated+3.15.1+002+dontWhitelistTextProp.patch
index 4ba96ac8659f..6084dca4adc8 100644
--- a/patches/react-native-reanimated+3.13.0+003+dontWhitelistTextProp.patch
+++ b/patches/react-native-reanimated+3.15.1+002+dontWhitelistTextProp.patch
@@ -1,11 +1,11 @@
diff --git a/node_modules/react-native-reanimated/src/component/PerformanceMonitor.tsx b/node_modules/react-native-reanimated/src/component/PerformanceMonitor.tsx
-index 9e66803..3620fe9 100644
+index 38e3d39..9936670 100644
--- a/node_modules/react-native-reanimated/src/component/PerformanceMonitor.tsx
+++ b/node_modules/react-native-reanimated/src/component/PerformanceMonitor.tsx
-@@ -47,7 +47,6 @@ function createCircularDoublesBuffer(size: number) {
+@@ -46,7 +46,6 @@ function createCircularDoublesBuffer(size: number) {
}
- const DEFAULT_BUFFER_SIZE = 60;
+ const DEFAULT_BUFFER_SIZE = 20;
-addWhitelistedNativeProps({ text: true });
const AnimatedTextInput = createAnimatedComponent(TextInput);
diff --git a/patches/react-native-reanimated+3.13.0+004+fixNullViewTag.patch b/patches/react-native-reanimated+3.15.1+003+fixNullViewTag.patch
similarity index 100%
rename from patches/react-native-reanimated+3.13.0+004+fixNullViewTag.patch
rename to patches/react-native-reanimated+3.15.1+003+fixNullViewTag.patch
diff --git a/patches/react-pdf+7.7.3.patch b/patches/react-pdf+7.7.3.patch
index 5b1b3ebb6f6e..0f9a2f47d3c7 100644
--- a/patches/react-pdf+7.7.3.patch
+++ b/patches/react-pdf+7.7.3.patch
@@ -1,3 +1,15 @@
+diff --git a/node_modules/react-pdf/dist/cjs/Document.js b/node_modules/react-pdf/dist/cjs/Document.js
+index 9bb0398..032d898 100644
+--- a/node_modules/react-pdf/dist/cjs/Document.js
++++ b/node_modules/react-pdf/dist/cjs/Document.js
+@@ -289,6 +289,7 @@ const Document = (0, react_1.forwardRef)(function Document(_a, ref) {
+ pdfDispatch({ type: 'REJECT', error });
+ });
+ return () => {
++ loadingTask._worker.destroy();
+ loadingTask.destroy();
+ };
+ }
diff --git a/node_modules/react-pdf/dist/esm/Document.js b/node_modules/react-pdf/dist/esm/Document.js
index b1c5a81..569769e 100644
--- a/node_modules/react-pdf/dist/esm/Document.js
diff --git a/src/App.tsx b/src/App.tsx
index 98b5d4afeb1d..2480d169980b 100644
--- a/src/App.tsx
+++ b/src/App.tsx
@@ -20,7 +20,6 @@ import OnyxProvider from './components/OnyxProvider';
import PopoverContextProvider from './components/PopoverProvider';
import SafeArea from './components/SafeArea';
import ScrollOffsetContextProvider from './components/ScrollOffsetContextProvider';
-import {SearchContextProvider} from './components/Search/SearchContext';
import ThemeIllustrationsProvider from './components/ThemeIllustrationsProvider';
import ThemeProvider from './components/ThemeProvider';
import ThemeStylesProvider from './components/ThemeStylesProvider';
@@ -50,11 +49,14 @@ LogBox.ignoreLogs([
// the timer is lost. Currently Expensify is using a 30 minutes interval to refresh personal details.
// More details here: https://git.io/JJYeb
'Setting a timer for a long period of time',
+ // We silence this warning for now and will address all the places where it happens separately.
+ // Then we can remove this line so the problem does not occur in the future.
+ '[Reanimated] Tried to modify key `current`',
]);
const fill = {flex: 1};
-const StrictModeWrapper = CONFIG.USE_REACT_STRICT_MODE ? React.StrictMode : ({children}: {children: React.ReactElement}) => children;
+const StrictModeWrapper = CONFIG.USE_REACT_STRICT_MODE_IN_DEV ? React.StrictMode : ({children}: {children: React.ReactElement}) => children;
function App({url}: AppProps) {
useDefaultDragAndDrop();
@@ -92,7 +94,6 @@ function App({url}: AppProps) {
VolumeContextProvider,
VideoPopoverMenuContextProvider,
KeyboardProvider,
- SearchContextProvider,
]}
>
diff --git a/src/CONFIG.ts b/src/CONFIG.ts
index 8800cc907588..a1a72b86fadd 100644
--- a/src/CONFIG.ts
+++ b/src/CONFIG.ts
@@ -96,5 +96,6 @@ export default {
IOS_CLIENT_ID: '921154746561-s3uqn2oe4m85tufi6mqflbfbuajrm2i3.apps.googleusercontent.com',
},
GCP_GEOLOCATION_API_KEY: googleGeolocationAPIKey,
- USE_REACT_STRICT_MODE: true,
+ // to read more about StrictMode see: contributingGuides/STRICT_MODE.md
+ USE_REACT_STRICT_MODE_IN_DEV: true,
} as const;
diff --git a/src/CONST.ts b/src/CONST.ts
index 175aa8cd3c16..92ab86d93f4d 100755
--- a/src/CONST.ts
+++ b/src/CONST.ts
@@ -387,6 +387,7 @@ const CONST = {
REPORT_FIELDS_FEATURE: 'reportFieldsFeature',
WORKSPACE_FEEDS: 'workspaceFeeds',
NETSUITE_USA_TAX: 'netsuiteUsaTax',
+ NEW_DOT_COPILOT: 'newDotCopilot',
WORKSPACE_RULES: 'workspaceRules',
COMBINED_TRACK_SUBMIT: 'combinedTrackSubmit',
},
@@ -641,6 +642,9 @@ const CONST = {
INBOX: 'inbox',
},
+ EXPENSIFY_POLICY_DOMAIN: 'expensify-policy',
+ EXPENSIFY_POLICY_DOMAIN_EXTENSION: '.exfy',
+
SIGN_IN_FORM_WIDTH: 300,
REQUEST_CODE_DELAY: 30,
@@ -737,6 +741,7 @@ const CONST = {
REIMBURSEMENT_REQUESTED: 'REIMBURSEMENTREQUESTED', // Deprecated OldDot Action
REIMBURSEMENT_SETUP: 'REIMBURSEMENTSETUP', // Deprecated OldDot Action
REIMBURSEMENT_SETUP_REQUESTED: 'REIMBURSEMENTSETUPREQUESTED', // Deprecated OldDot Action
+ REJECTED: 'REJECTED',
RENAMED: 'RENAMED',
REPORT_PREVIEW: 'REPORTPREVIEW',
SELECTED_FOR_RANDOM_AUDIT: 'SELECTEDFORRANDOMAUDIT', // OldDot Action
@@ -2276,6 +2281,13 @@ const CONST = {
MENTION_ICON: 'mention-icon',
SMALL_NORMAL: 'small-normal',
},
+ COMPANY_CARD: {
+ FEED_BANK_NAME: {
+ MASTER_CARD: 'cdf',
+ VISA: 'vcf',
+ AMEX: 'gl1025',
+ },
+ },
EXPENSIFY_CARD: {
BANK: 'Expensify Card',
FRAUD_TYPES: {
@@ -2330,6 +2342,12 @@ const CONST = {
PAYPERUSE: 'monthly2018',
},
},
+ COMPANY_CARDS: {
+ DELETE_TRANSACTIONS: {
+ RESTRICT: 'corporate',
+ ALLOW: 'personal',
+ },
+ },
REGEX: {
SPECIAL_CHARS_WITHOUT_NEWLINE: /((?!\n)[()-\s\t])/g,
DIGITS_AND_PLUS: /^\+?[0-9]*$/,
@@ -2491,6 +2509,10 @@ const CONST = {
get RESTRICTED_ACCOUNT_IDS() {
return [this.ACCOUNT_ID.NOTIFICATIONS];
},
+ // Account IDs that can't be added as a group member
+ get NON_ADDABLE_ACCOUNT_IDS() {
+ return [this.ACCOUNT_ID.NOTIFICATIONS, this.ACCOUNT_ID.CHRONOS];
+ },
// 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: 10000,
@@ -3866,6 +3888,11 @@ const CONST = {
SUCCESS: 'SUCCESS',
ENABLED: 'ENABLED',
DISABLED: 'DISABLED',
+ GETCODE: 'GETCODE',
+ },
+ DELEGATE_ROLE: {
+ SUBMITTER: 'submitter',
+ ALL: 'all',
},
STRIPE_GBP_AUTH_STATUSES: {
SUCCEEDED: 'succeeded',
@@ -5210,6 +5237,7 @@ const CONST = {
EXPENSE: 'expense',
INVOICE: 'invoice',
TRIP: 'trip',
+ CHAT: 'chat',
},
ACTION_TYPES: {
VIEW: 'view',
@@ -5253,6 +5281,10 @@ const CONST = {
PAID: 'paid',
},
},
+ CHAT_TYPES: {
+ LINK: 'link',
+ ATTACHMENT: 'attachment',
+ },
TABLE_COLUMNS: {
RECEIPT: 'receipt',
DATE: 'date',
@@ -5282,6 +5314,7 @@ const CONST = {
STATUS: 'status',
SORT_BY: 'sortBy',
SORT_ORDER: 'sortOrder',
+ POLICY_ID: 'policyID',
},
SYNTAX_FILTER_KEYS: {
DATE: 'date',
@@ -5298,6 +5331,8 @@ const CONST = {
CARD_ID: 'cardID',
REPORT_ID: 'reportID',
KEYWORD: 'keyword',
+ IN: 'in',
+ HAS: 'has',
},
},
diff --git a/src/ONYXKEYS.ts b/src/ONYXKEYS.ts
index 101656ae1ea1..19439ddd1f40 100755
--- a/src/ONYXKEYS.ts
+++ b/src/ONYXKEYS.ts
@@ -102,6 +102,9 @@ const ONYXKEYS = {
/** Contains metadata (partner, login, validation date) for all of the user's logins */
LOGIN_LIST: 'loginList',
+ /** Object containing contact method that's going to be added */
+ PENDING_CONTACT_ACTION: 'pendingContactAction',
+
/** Information about the current session (authToken, accountID, email, loading, error) */
SESSION: 'session',
STASHED_SESSION: 'stashedSession',
@@ -275,9 +278,6 @@ const ONYXKEYS = {
/** Is report data loading? */
IS_LOADING_APP: 'isLoadingApp',
- /** Is the user in the process of switching to OldDot? */
- IS_SWITCHING_TO_OLD_DOT: 'isSwitchingToOldDot',
-
/** Is the test tools modal open? */
IS_TEST_TOOLS_MODAL_OPEN: 'isTestToolsModalOpen',
@@ -461,6 +461,8 @@ const ONYXKEYS = {
// Shared NVPs
/** Collection of objects where each object represents the owner of the workspace that is past due billing AND the user is a member of. */
SHARED_NVP_PRIVATE_USER_BILLING_GRACE_PERIOD_END: 'sharedNVP_private_billingGracePeriodEnd_',
+
+ /** The collection of card feeds */
SHARED_NVP_PRIVATE_DOMAIN_MEMBER: 'sharedNVP_private_domain_member_',
/**
@@ -477,6 +479,9 @@ const ONYXKEYS = {
/** The value that indicates whether Continuous Reconciliation should be used on the domain */
EXPENSIFY_CARD_USE_CONTINUOUS_RECONCILIATION: 'expensifyCard_useContinuousReconciliation_',
+
+ /** Currently displaying feed */
+ LAST_SELECTED_FEED: 'lastSelectedFeed_',
},
/** List of Form ids */
@@ -495,6 +500,8 @@ const ONYXKEYS = {
WORKSPACE_RATE_AND_UNIT_FORM_DRAFT: 'workspaceRateAndUnitFormDraft',
WORKSPACE_TAX_CUSTOM_NAME: 'workspaceTaxCustomName',
WORKSPACE_TAX_CUSTOM_NAME_DRAFT: 'workspaceTaxCustomNameDraft',
+ WORKSPACE_COMPANY_CARD_FEED_NAME: 'workspaceCompanyCardFeedName',
+ WORKSPACE_COMPANY_CARD_FEED_NAME_DRAFT: 'workspaceCompanyCardFeedNameDraft',
WORKSPACE_REPORT_FIELDS_FORM: 'workspaceReportFieldForm',
WORKSPACE_REPORT_FIELDS_FORM_DRAFT: 'workspaceReportFieldFormDraft',
POLICY_CREATE_DISTANCE_RATE_FORM: 'policyCreateDistanceRateForm',
@@ -645,6 +652,7 @@ type OnyxFormValuesMapping = {
[ONYXKEYS.FORMS.WORKSPACE_TAG_FORM]: FormTypes.WorkspaceTagForm;
[ONYXKEYS.FORMS.WORKSPACE_RATE_AND_UNIT_FORM]: FormTypes.WorkspaceRateAndUnitForm;
[ONYXKEYS.FORMS.WORKSPACE_TAX_CUSTOM_NAME]: FormTypes.WorkspaceTaxCustomName;
+ [ONYXKEYS.FORMS.WORKSPACE_COMPANY_CARD_FEED_NAME]: FormTypes.WorkspaceCompanyCardFeedName;
[ONYXKEYS.FORMS.WORKSPACE_REPORT_FIELDS_FORM]: FormTypes.WorkspaceReportFieldForm;
[ONYXKEYS.FORMS.CLOSE_ACCOUNT_FORM]: FormTypes.CloseAccountForm;
[ONYXKEYS.FORMS.PROFILE_SETTINGS_FORM]: FormTypes.ProfileSettingsForm;
@@ -762,11 +770,12 @@ type OnyxCollectionValuesMapping = {
[ONYXKEYS.COLLECTION.POLICY_CONNECTION_SYNC_PROGRESS]: OnyxTypes.PolicyConnectionSyncProgress;
[ONYXKEYS.COLLECTION.SNAPSHOT]: OnyxTypes.SearchResults;
[ONYXKEYS.COLLECTION.SHARED_NVP_PRIVATE_USER_BILLING_GRACE_PERIOD_END]: OnyxTypes.BillingGraceEndPeriod;
- [ONYXKEYS.COLLECTION.SHARED_NVP_PRIVATE_DOMAIN_MEMBER]: OnyxTypes.CompanyCards;
+ [ONYXKEYS.COLLECTION.SHARED_NVP_PRIVATE_DOMAIN_MEMBER]: OnyxTypes.CardFeeds;
[ONYXKEYS.COLLECTION.PRIVATE_EXPENSIFY_CARD_SETTINGS]: OnyxTypes.ExpensifyCardSettings;
[ONYXKEYS.COLLECTION.WORKSPACE_CARDS_LIST]: OnyxTypes.WorkspaceCardsList;
[ONYXKEYS.COLLECTION.EXPENSIFY_CARD_CONTINUOUS_RECONCILIATION_CONNECTION]: OnyxTypes.PolicyConnectionName;
[ONYXKEYS.COLLECTION.EXPENSIFY_CARD_USE_CONTINUOUS_RECONCILIATION]: boolean;
+ [ONYXKEYS.COLLECTION.LAST_SELECTED_FEED]: string;
};
type OnyxValuesMapping = {
@@ -805,6 +814,7 @@ type OnyxValuesMapping = {
[ONYXKEYS.USER]: OnyxTypes.User;
[ONYXKEYS.USER_LOCATION]: OnyxTypes.UserLocation;
[ONYXKEYS.LOGIN_LIST]: OnyxTypes.LoginList;
+ [ONYXKEYS.PENDING_CONTACT_ACTION]: OnyxTypes.PendingContactAction;
[ONYXKEYS.SESSION]: OnyxTypes.Session;
[ONYXKEYS.USER_METADATA]: OnyxTypes.UserMetadata;
[ONYXKEYS.STASHED_SESSION]: OnyxTypes.Session;
@@ -856,7 +866,6 @@ type OnyxValuesMapping = {
[ONYXKEYS.IS_TEST_TOOLS_MODAL_OPEN]: boolean;
[ONYXKEYS.APP_PROFILING_IN_PROGRESS]: boolean;
[ONYXKEYS.IS_LOADING_APP]: boolean;
- [ONYXKEYS.IS_SWITCHING_TO_OLD_DOT]: boolean;
[ONYXKEYS.WALLET_TRANSFER]: OnyxTypes.WalletTransfer;
[ONYXKEYS.LAST_ACCESSED_WORKSPACE_POLICY_ID]: string;
[ONYXKEYS.SHOULD_SHOW_COMPOSE_INPUT]: boolean;
diff --git a/src/ROUTES.ts b/src/ROUTES.ts
index 2db861121375..48076178bcb9 100644
--- a/src/ROUTES.ts
+++ b/src/ROUTES.ts
@@ -35,8 +35,7 @@ const ROUTES = {
SEARCH_CENTRAL_PANE: {
route: 'search',
- getRoute: ({query, isCustomQuery = false, policyIDs}: {query: SearchQueryString; isCustomQuery?: boolean; policyIDs?: string}) =>
- `search?q=${query}&isCustomQuery=${isCustomQuery}${policyIDs ? `&policyIDs=${policyIDs}` : ''}` as const,
+ getRoute: ({query, isCustomQuery = false}: {query: SearchQueryString; isCustomQuery?: boolean}) => `search?q=${query}&isCustomQuery=${isCustomQuery}` as const,
},
SEARCH_ADVANCED_FILTERS: 'search/filters',
SEARCH_ADVANCED_FILTERS_DATE: 'search/filters/date',
@@ -53,6 +52,8 @@ const ROUTES = {
SEARCH_ADVANCED_FILTERS_TAG: 'search/filters/tag',
SEARCH_ADVANCED_FILTERS_FROM: 'search/filters/from',
SEARCH_ADVANCED_FILTERS_TO: 'search/filters/to',
+ SEARCH_ADVANCED_FILTERS_IN: 'search/filters/in',
+ SEARCH_ADVANCED_FILTERS_HAS: 'search/filters/has',
SEARCH_REPORT: {
route: 'search/view/:reportID',
@@ -196,6 +197,7 @@ const ROUTES = {
route: 'settings/profile/contact-methods/:contactMethod/details',
getRoute: (contactMethod: string, backTo?: string) => getUrlWithBackToParam(`settings/profile/contact-methods/${encodeURIComponent(contactMethod)}/details`, backTo),
},
+ SETINGS_CONTACT_METHOD_VALIDATE_ACTION: 'settings/profile/contact-methods/validate-action',
SETTINGS_NEW_CONTACT_METHOD: {
route: 'settings/profile/contact-methods/new',
getRoute: (backTo?: string) => getUrlWithBackToParam('settings/profile/contact-methods/new', backTo),
@@ -888,10 +890,18 @@ const ROUTES = {
route: 'settings/workspaces/:policyID/reportFields/:reportFieldID/edit/initialValue',
getRoute: (policyID: string, reportFieldID: string) => `settings/workspaces/${policyID}/reportFields/${encodeURIComponent(reportFieldID)}/edit/initialValue` as const,
},
+ WORKSPACE_COMPANY_CARDS_SELECT_FEED: {
+ route: 'settings/workspaces/:policyID/company-cards/select-feed',
+ getRoute: (policyID: string) => `settings/workspaces/${policyID}/company-cards/select-feed` as const,
+ },
WORKSPACE_EXPENSIFY_CARD: {
route: 'settings/workspaces/:policyID/expensify-card',
getRoute: (policyID: string) => `settings/workspaces/${policyID}/expensify-card` as const,
},
+ WORKSPACE_COMPANY_CARDS: {
+ route: 'settings/workspaces/:policyID/company-cards',
+ getRoute: (policyID: string) => `settings/workspaces/${policyID}/company-cards` as const,
+ },
WORKSPACE_EXPENSIFY_CARD_DETAILS: {
route: 'settings/workspaces/:policyID/expensify-card/:cardID',
getRoute: (policyID: string, cardID: string, backTo?: string) => getUrlWithBackToParam(`settings/workspaces/${policyID}/expensify-card/${cardID}`, backTo),
@@ -910,7 +920,7 @@ const ROUTES = {
},
WORKSPACE_EXPENSIFY_CARD_ISSUE_NEW: {
route: 'settings/workspaces/:policyID/expensify-card/issue-new',
- getRoute: (policyID: string) => `settings/workspaces/${policyID}/expensify-card/issue-new` as const,
+ getRoute: (policyID: string, backTo?: string) => getUrlWithBackToParam(`settings/workspaces/${policyID}/expensify-card/issue-new`, backTo),
},
WORKSPACE_EXPENSIFY_CARD_BANK_ACCOUNT: {
route: 'settings/workspaces/:policyID/expensify-card/choose-bank-account',
@@ -928,9 +938,13 @@ const ROUTES = {
route: 'settings/workspaces/:policyID/expensify-card/settings/frequency',
getRoute: (policyID: string) => `settings/workspaces/${policyID}/expensify-card/settings/frequency` as const,
},
- WORKSPACE_COMPANY_CARDS: {
- route: 'settings/workspaces/:policyID/company-cards',
- getRoute: (policyID: string) => `settings/workspaces/${policyID}/company-cards` as const,
+ WORKSPACE_COMPANY_CARDS_SETTINGS: {
+ route: 'settings/workspaces/:policyID/company-cards/settings',
+ getRoute: (policyID: string) => `settings/workspaces/${policyID}/company-cards/settings` as const,
+ },
+ WORKSPACE_COMPANY_CARDS_SETTINGS_FEED_NAME: {
+ route: 'settings/workspaces/:policyID/company-cards/settings/feed-name',
+ getRoute: (policyID: string) => `settings/workspaces/${policyID}/company-cards/settings/feed-name` as const,
},
WORKSPACE_RULES: {
route: 'settings/workspaces/:policyID/rules',
diff --git a/src/SCREENS.ts b/src/SCREENS.ts
index 7ffca6889688..a072307c133b 100644
--- a/src/SCREENS.ts
+++ b/src/SCREENS.ts
@@ -46,6 +46,8 @@ const SCREENS = {
ADVANCED_FILTERS_TAG_RHP: 'Search_Advanced_Filters_Tag_RHP',
ADVANCED_FILTERS_FROM_RHP: 'Search_Advanced_Filters_From_RHP',
ADVANCED_FILTERS_TO_RHP: 'Search_Advanced_Filters_To_RHP',
+ ADVANCED_FILTERS_IN_RHP: 'Search_Advanced_Filters_In_RHP',
+ ADVANCED_FILTERS_HAS_RHP: 'Search_Advanced_Filters_Has_RHP',
TRANSACTION_HOLD_REASON_RHP: 'Search_Transaction_Hold_Reason_RHP',
BOTTOM_TAB: 'Search_Bottom_Tab',
},
@@ -72,6 +74,7 @@ const SCREENS = {
DISPLAY_NAME: 'Settings_Display_Name',
CONTACT_METHODS: 'Settings_ContactMethods',
CONTACT_METHOD_DETAILS: 'Settings_ContactMethodDetails',
+ CONTACT_METHOD_VALIDATE_ACTION: 'Settings_ValidateContactMethodAction',
NEW_CONTACT_METHOD: 'Settings_NewContactMethod',
STATUS_CLEAR_AFTER: 'Settings_Status_Clear_After',
STATUS_CLEAR_AFTER_DATE: 'Settings_Status_Clear_After_Date',
@@ -365,6 +368,9 @@ const SCREENS = {
RATE_AND_UNIT_RATE: 'Workspace_RateAndUnit_Rate',
RATE_AND_UNIT_UNIT: 'Workspace_RateAndUnit_Unit',
COMPANY_CARDS: 'Workspace_CompanyCards',
+ COMPANY_CARDS_SELECT_FEED: 'Workspace_CompanyCards_Select_Feed',
+ COMPANY_CARDS_SETTINGS: 'Workspace_CompanyCards_Settings',
+ COMPANY_CARDS_SETTINGS_FEED_NAME: 'Workspace_CompanyCards_Settings_Feed_Name',
EXPENSIFY_CARD: 'Workspace_ExpensifyCard',
EXPENSIFY_CARD_DETAILS: 'Workspace_ExpensifyCard_Details',
EXPENSIFY_CARD_LIMIT: 'Workspace_ExpensifyCard_Limit',
diff --git a/src/components/AccountSwitcher.tsx b/src/components/AccountSwitcher.tsx
new file mode 100644
index 000000000000..ba30ea0062b9
--- /dev/null
+++ b/src/components/AccountSwitcher.tsx
@@ -0,0 +1,202 @@
+import React, {useRef, useState} from 'react';
+import {View} from 'react-native';
+import {useOnyx} from 'react-native-onyx';
+import useCurrentUserPersonalDetails from '@hooks/useCurrentUserPersonalDetails';
+import useLocalize from '@hooks/useLocalize';
+import useNetwork from '@hooks/useNetwork';
+import usePermissions from '@hooks/usePermissions';
+import useResponsiveLayout from '@hooks/useResponsiveLayout';
+import useTheme from '@hooks/useTheme';
+import useThemeStyles from '@hooks/useThemeStyles';
+import {clearDelegatorErrors, connect, disconnect} from '@libs/actions/Delegate';
+import * as PersonalDetailsUtils from '@libs/PersonalDetailsUtils';
+import variables from '@styles/variables';
+import * as Modal from '@userActions/Modal';
+import CONST from '@src/CONST';
+import type {TranslationPaths} from '@src/languages/types';
+import ONYXKEYS from '@src/ONYXKEYS';
+import type {PersonalDetails} from '@src/types/onyx';
+import Avatar from './Avatar';
+import ConfirmModal from './ConfirmModal';
+import Icon from './Icon';
+import * as Expensicons from './Icon/Expensicons';
+import type {MenuItemProps} from './MenuItem';
+import MenuItemList from './MenuItemList';
+import type {MenuItemWithLink} from './MenuItemList';
+import Popover from './Popover';
+import {PressableWithFeedback} from './Pressable';
+import Text from './Text';
+
+function AccountSwitcher() {
+ const currentUserPersonalDetails = useCurrentUserPersonalDetails();
+ const styles = useThemeStyles();
+ const theme = useTheme();
+ const {translate} = useLocalize();
+ const {isOffline} = useNetwork();
+ const {canUseNewDotCopilot} = usePermissions();
+ const {shouldUseNarrowLayout} = useResponsiveLayout();
+ const [account] = useOnyx(ONYXKEYS.ACCOUNT);
+ const buttonRef = useRef(null);
+
+ const [shouldShowDelegatorMenu, setShouldShowDelegatorMenu] = useState(false);
+ const [shouldShowOfflineModal, setShouldShowOfflineModal] = useState(false);
+ const delegators = account?.delegatedAccess?.delegators ?? [];
+
+ const isActingAsDelegate = !!account?.delegatedAccess?.delegate ?? false;
+ const canSwitchAccounts = canUseNewDotCopilot && (delegators.length > 0 || isActingAsDelegate);
+
+ const createBaseMenuItem = (personalDetails: PersonalDetails | undefined, error?: TranslationPaths, additionalProps: MenuItemWithLink = {}): MenuItemWithLink => {
+ return {
+ title: personalDetails?.displayName ?? personalDetails?.login,
+ description: personalDetails?.login,
+ avatarID: personalDetails?.accountID ?? -1,
+ icon: personalDetails?.avatar ?? '',
+ iconType: CONST.ICON_TYPE_AVATAR,
+ outerWrapperStyle: shouldUseNarrowLayout ? {} : styles.accountSwitcherPopover,
+ numberOfLinesDescription: 1,
+ errorText: error ? translate(error) : '',
+ shouldShowRedDotIndicator: !!error,
+ errorTextStyle: styles.mt2,
+ ...additionalProps,
+ };
+ };
+
+ const menuItems = (): MenuItemProps[] => {
+ const currentUserMenuItem = createBaseMenuItem(currentUserPersonalDetails, undefined, {
+ wrapperStyle: [styles.buttonDefaultBG],
+ focused: true,
+ shouldShowRightIcon: true,
+ iconRight: Expensicons.Checkmark,
+ success: true,
+ key: `${currentUserPersonalDetails?.login}-current`,
+ });
+
+ if (isActingAsDelegate) {
+ const delegateEmail = account?.delegatedAccess?.delegate ?? '';
+
+ // Avoid duplicating the current user in the list when switching accounts
+ if (delegateEmail === currentUserPersonalDetails.login) {
+ return [currentUserMenuItem];
+ }
+
+ const delegatePersonalDetails = PersonalDetailsUtils.getPersonalDetailByEmail(delegateEmail);
+ const error = account?.delegatedAccess?.error;
+
+ return [
+ createBaseMenuItem(delegatePersonalDetails, error, {
+ onPress: () => {
+ if (isOffline) {
+ Modal.close(() => setShouldShowOfflineModal(true));
+ return;
+ }
+ disconnect();
+ },
+ key: `${delegateEmail}-delegate`,
+ }),
+ currentUserMenuItem,
+ ];
+ }
+
+ const delegatorMenuItems: MenuItemProps[] = delegators
+ .filter(({email}) => email !== currentUserPersonalDetails.login)
+ .map(({email, role, error}, index) => {
+ const personalDetails = PersonalDetailsUtils.getPersonalDetailByEmail(email);
+ return createBaseMenuItem(personalDetails, error, {
+ badgeText: translate('delegate.role', role),
+ onPress: () => {
+ if (isOffline) {
+ Modal.close(() => setShouldShowOfflineModal(true));
+ return;
+ }
+ connect(email);
+ },
+ key: `${email}-${index}`,
+ });
+ });
+
+ return [currentUserMenuItem, ...delegatorMenuItems];
+ };
+
+ return (
+ <>
+ {
+ setShouldShowDelegatorMenu(!shouldShowDelegatorMenu);
+ }}
+ ref={buttonRef}
+ interactive={canSwitchAccounts}
+ wrapperStyle={[styles.flexGrow1, styles.flex1, styles.mnw0, styles.justifyContentCenter]}
+ >
+
+
+
+
+
+ {currentUserPersonalDetails?.displayName}
+
+ {canSwitchAccounts && (
+
+
+
+ )}
+
+
+ {currentUserPersonalDetails?.login}
+
+
+
+
+ {canSwitchAccounts && (
+ {
+ setShouldShowDelegatorMenu(false);
+ clearDelegatorErrors();
+ }}
+ anchorRef={buttonRef}
+ anchorPosition={styles.accountSwitcherAnchorPosition}
+ >
+
+ {translate('delegate.switchAccount')}
+
+
+
+ )}
+ setShouldShowOfflineModal(false)}
+ onCancel={() => setShouldShowOfflineModal(false)}
+ confirmText={translate('common.buttonConfirm')}
+ prompt={translate('common.offlinePrompt')}
+ shouldShowCancelButton={false}
+ />
+ >
+ );
+}
+
+AccountSwitcher.displayName = 'AccountSwitcher';
+
+export default AccountSwitcher;
diff --git a/src/components/CurrentUserPersonalDetailsSkeletonView/index.tsx b/src/components/AccountSwitcherSkeletonView/index.tsx
similarity index 50%
rename from src/components/CurrentUserPersonalDetailsSkeletonView/index.tsx
rename to src/components/AccountSwitcherSkeletonView/index.tsx
index 21e82c26f769..eb01a23e9ade 100644
--- a/src/components/CurrentUserPersonalDetailsSkeletonView/index.tsx
+++ b/src/components/AccountSwitcherSkeletonView/index.tsx
@@ -6,10 +6,9 @@ import SkeletonViewContentLoader from '@components/SkeletonViewContentLoader';
import useStyleUtils from '@hooks/useStyleUtils';
import useTheme from '@hooks/useTheme';
import useThemeStyles from '@hooks/useThemeStyles';
-import variables from '@styles/variables';
import CONST from '@src/CONST';
-type CurrentUserPersonalDetailsSkeletonViewProps = {
+type AccountSwitcherSkeletonViewProps = {
/** Whether to animate the skeleton view */
shouldAnimate?: boolean;
@@ -17,45 +16,43 @@ type CurrentUserPersonalDetailsSkeletonViewProps = {
avatarSize?: ValueOf;
};
-function CurrentUserPersonalDetailsSkeletonView({shouldAnimate = true, avatarSize = CONST.AVATAR_SIZE.LARGE}: CurrentUserPersonalDetailsSkeletonViewProps) {
+function AccountSwitcherSkeletonView({shouldAnimate = true, avatarSize = CONST.AVATAR_SIZE.LARGE}: AccountSwitcherSkeletonViewProps) {
const theme = useTheme();
const styles = useThemeStyles();
const StyleUtils = useStyleUtils();
const avatarPlaceholderSize = StyleUtils.getAvatarSize(avatarSize);
const avatarPlaceholderRadius = avatarPlaceholderSize / 2;
- const spaceBetweenAvatarAndHeadline = styles.mb3.marginBottom + styles.mt1.marginTop + (variables.lineHeightXXLarge - variables.fontSizeXLarge) / 2;
- const headlineSize = variables.fontSizeXLarge;
- const spaceBetweenHeadlineAndLabel = styles.mt1.marginTop + (variables.lineHeightXXLarge - variables.fontSizeXLarge) / 2;
- const labelSize = variables.fontSizeLabel;
+ const startPositionX = 30;
+
return (
);
}
-CurrentUserPersonalDetailsSkeletonView.displayName = 'CurrentUserPersonalDetailsSkeletonView';
-export default CurrentUserPersonalDetailsSkeletonView;
+AccountSwitcherSkeletonView.displayName = 'AccountSwitcherSkeletonView';
+export default AccountSwitcherSkeletonView;
diff --git a/src/components/AddPaymentCard/PaymentCardCurrencyModal.tsx b/src/components/AddPaymentCard/PaymentCardCurrencyModal.tsx
index c3c38c4aec72..dc27f115dc05 100644
--- a/src/components/AddPaymentCard/PaymentCardCurrencyModal.tsx
+++ b/src/components/AddPaymentCard/PaymentCardCurrencyModal.tsx
@@ -8,6 +8,7 @@ import RadioListItem from '@components/SelectionList/RadioListItem';
import useLocalize from '@hooks/useLocalize';
import useThemeStyles from '@hooks/useThemeStyles';
import useWindowDimensions from '@hooks/useWindowDimensions';
+import Navigation from '@libs/Navigation/Navigation';
import CONST from '@src/CONST';
type PaymentCardCurrencyModalProps = {
@@ -55,6 +56,7 @@ function PaymentCardCurrencyModal({isVisible, currencies, currentCurrency = CONS
onModalHide={onClose}
hideModalContentWhileAnimating
innerContainerStyle={styles.RHPNavigatorContainer(isSmallScreenWidth)}
+ onBackdropPress={Navigation.dismissModal}
useNativeDriver
>
{
setIsDeleteReceiptConfirmModalVisible(true);
},
+ shouldCallAfterModalHide: true,
});
}
return menuItems;
diff --git a/src/components/AttachmentPicker/index.native.tsx b/src/components/AttachmentPicker/index.native.tsx
index edcdabed9101..366366423324 100644
--- a/src/components/AttachmentPicker/index.native.tsx
+++ b/src/components/AttachmentPicker/index.native.tsx
@@ -68,7 +68,7 @@ const getImagePickerOptions = (type: string): CameraOptions => {
* @returns {Object}
*/
-const getDocumentPickerOptions = (type: string): DocumentPickerOptions<'ios' | 'android'> => {
+const getDocumentPickerOptions = (type: string): DocumentPickerOptions => {
if (type === CONST.ATTACHMENT_PICKER_TYPE.IMAGE) {
return {
type: [RNDocumentPicker.types.images],
diff --git a/src/components/Attachments/AttachmentCarousel/CarouselItem.tsx b/src/components/Attachments/AttachmentCarousel/CarouselItem.tsx
index 453b7b4fd106..103abb2df1bb 100644
--- a/src/components/Attachments/AttachmentCarousel/CarouselItem.tsx
+++ b/src/components/Attachments/AttachmentCarousel/CarouselItem.tsx
@@ -83,7 +83,6 @@ function CarouselItem({item, onPress, isFocused, isModalHovered}: CarouselItemPr
isHovered={isModalHovered}
isFocused={isFocused}
duration={item.duration}
- isUsedInCarousel
/>
diff --git a/src/components/Attachments/AttachmentCarousel/Pager/index.tsx b/src/components/Attachments/AttachmentCarousel/Pager/index.tsx
index a3a0f1d6a893..6fe32050b6d5 100644
--- a/src/components/Attachments/AttachmentCarousel/Pager/index.tsx
+++ b/src/components/Attachments/AttachmentCarousel/Pager/index.tsx
@@ -58,12 +58,11 @@ function AttachmentCarouselPager(
{items, activeSource, initialPage, setShouldShowArrows, onPageSelected, onClose}: AttachmentCarouselPagerProps,
ref: ForwardedRef,
) {
- const {handleTap, handleScaleChange} = useCarouselContextEvents(setShouldShowArrows);
+ const {handleTap, handleScaleChange, isScrollEnabled} = useCarouselContextEvents(setShouldShowArrows);
const styles = useThemeStyles();
const pagerRef = useRef(null);
const isPagerScrolling = useSharedValue(false);
- const isScrollEnabled = useSharedValue(true);
const activePage = useSharedValue(initialPage);
const [activePageIndex, setActivePageIndex] = useState(initialPage);
diff --git a/src/components/Attachments/AttachmentCarousel/useCarouselContextEvents.ts b/src/components/Attachments/AttachmentCarousel/useCarouselContextEvents.ts
index cc2c3c5c8229..1c54d7841347 100644
--- a/src/components/Attachments/AttachmentCarousel/useCarouselContextEvents.ts
+++ b/src/components/Attachments/AttachmentCarousel/useCarouselContextEvents.ts
@@ -58,7 +58,7 @@ function useCarouselContextEvents(setShouldShowArrows: (show?: SetStateAction & {
+type AttachmentViewPdfProps = Pick & {
encryptedSourceUrl: string;
onLoadComplete: (path: string) => void;
diff --git a/src/components/Attachments/AttachmentView/index.tsx b/src/components/Attachments/AttachmentView/index.tsx
index b5eb3891d27f..e9406db118e7 100644
--- a/src/components/Attachments/AttachmentView/index.tsx
+++ b/src/components/Attachments/AttachmentView/index.tsx
@@ -1,9 +1,10 @@
import {Str} from 'expensify-common';
-import React, {memo, useEffect, useState} from 'react';
+import React, {memo, useContext, useEffect, useState} from 'react';
import type {GestureResponderEvent, StyleProp, ViewStyle} from 'react-native';
import {View} from 'react-native';
import type {OnyxEntry} from 'react-native-onyx';
import {withOnyx} from 'react-native-onyx';
+import AttachmentCarouselPagerContext from '@components/Attachments/AttachmentCarousel/Pager/AttachmentCarouselPagerContext';
import type {Attachment, AttachmentSource} from '@components/Attachments/types';
import DistanceEReceipt from '@components/DistanceEReceipt';
import EReceipt from '@components/EReceipt';
@@ -42,9 +43,6 @@ type AttachmentViewProps = AttachmentViewOnyxProps &
/** Function for handle on press */
onPress?: (e?: GestureResponderEvent | KeyboardEvent) => void;
- /** Whether this AttachmentView is shown as part of a AttachmentCarousel */
- isUsedInCarousel?: boolean;
-
isUsedInAttachmentModal?: boolean;
/** Flag to show/hide download icon */
@@ -93,7 +91,6 @@ function AttachmentView({
onToggleKeyboard,
onPDFLoadError: onPDFLoadErrorProp = () => {},
isFocused,
- isUsedInCarousel,
isUsedInAttachmentModal,
isWorkspaceAvatar,
maybeIcon,
@@ -107,6 +104,7 @@ function AttachmentView({
}: AttachmentViewProps) {
const {translate} = useLocalize();
const {updateCurrentlyPlayingURL} = usePlaybackContext();
+ const attachmentCarouselPagerContext = useContext(AttachmentCarouselPagerContext);
const theme = useTheme();
const styles = useThemeStyles();
const StyleUtils = useStyleUtils();
@@ -114,6 +112,7 @@ function AttachmentView({
const [isHighResolution, setIsHighResolution] = useState(false);
const [hasPDFFailedToLoad, setHasPDFFailedToLoad] = useState(false);
const isVideo = (typeof source === 'string' && Str.isVideo(source)) || (file?.name && Str.isVideo(file.name));
+ const isUsedInCarousel = !!attachmentCarouselPagerContext?.pagerRef;
useEffect(() => {
if (!isFocused && !(file && isUsedInAttachmentModal)) {
diff --git a/src/components/AutoCompleteSuggestions/AutoCompleteSuggestionsPortal/TransparentOverlay/TransparentOverlay.tsx b/src/components/AutoCompleteSuggestions/AutoCompleteSuggestionsPortal/TransparentOverlay/TransparentOverlay.tsx
index c2081fa33bd1..86126d1827fa 100644
--- a/src/components/AutoCompleteSuggestions/AutoCompleteSuggestionsPortal/TransparentOverlay/TransparentOverlay.tsx
+++ b/src/components/AutoCompleteSuggestions/AutoCompleteSuggestionsPortal/TransparentOverlay/TransparentOverlay.tsx
@@ -1,4 +1,4 @@
-import React, {useCallback} from 'react';
+import React, {useCallback, useMemo} from 'react';
import {View} from 'react-native';
import type {PointerEvent} from 'react-native';
import type PressableProps from '@components/Pressable/GenericPressable/types';
@@ -29,6 +29,15 @@ function TransparentOverlay({onPress: onPressProp}: TransparentOverlayProps) {
e?.preventDefault();
}, []);
+ // The overlay is a semi-transparent layer that covers the entire screen and is used to close a modal when clicked.
+ // The touch event passes through the transparent overlay to the elements underneath, so we need to prevent that by adding a nearly invisible background color to the overlay.
+ const overlay = useMemo(
+ () => ({
+ backgroundColor: 'rgba(0, 0, 0, 0.005)',
+ }),
+ [],
+ );
+
return (
diff --git a/src/components/AvatarWithImagePicker.tsx b/src/components/AvatarWithImagePicker.tsx
index 337bbaf98ca1..6b9995e77814 100644
--- a/src/components/AvatarWithImagePicker.tsx
+++ b/src/components/AvatarWithImagePicker.tsx
@@ -11,7 +11,6 @@ import * as FileUtils from '@libs/fileDownload/FileUtils';
import getImageResolution from '@libs/fileDownload/getImageResolution';
import type {AvatarSource} from '@libs/UserUtils';
import variables from '@styles/variables';
-import * as Modal from '@userActions/Modal';
import CONST from '@src/CONST';
import type {TranslationPaths} from '@src/languages/types';
import type * as OnyxCommon from '@src/types/onyx/OnyxCommon';
@@ -44,6 +43,7 @@ type MenuItem = {
icon: IconAsset;
text: string;
onSelected: () => void;
+ shouldCallAfterModalHide?: boolean;
};
type AvatarWithImagePickerProps = {
@@ -260,19 +260,19 @@ function AvatarWithImagePicker({
* Create menu items list for avatar menu
*/
const createMenuItems = (openPicker: OpenPicker): MenuItem[] => {
- const menuItems = [
+ const menuItems: MenuItem[] = [
{
icon: Expensicons.Upload,
text: translate('avatarWithImagePicker.uploadPhoto'),
- onSelected: () =>
- Modal.close(() => {
- if (Browser.isSafari()) {
- return;
- }
- openPicker({
- onPicked: showAvatarCropModal,
- });
- }),
+ onSelected: () => {
+ if (Browser.isSafari()) {
+ return;
+ }
+ openPicker({
+ onPicked: showAvatarCropModal,
+ });
+ },
+ shouldCallAfterModalHide: true,
},
];
@@ -344,14 +344,14 @@ function AvatarWithImagePicker({
menuItems.push({
icon: Expensicons.Eye,
text: translate('avatarWithImagePicker.viewPhoto'),
- onSelected: () =>
- Modal.close(() => {
- if (typeof onViewPhotoPress !== 'function') {
- show();
- return;
- }
- onViewPhotoPress();
- }),
+ onSelected: () => {
+ if (typeof onViewPhotoPress !== 'function') {
+ show();
+ return;
+ }
+ onViewPhotoPress();
+ },
+ shouldCallAfterModalHide: true,
});
}
diff --git a/src/components/ButtonWithDropdownMenu/index.tsx b/src/components/ButtonWithDropdownMenu/index.tsx
index 2d748cead627..74b38f515a06 100644
--- a/src/components/ButtonWithDropdownMenu/index.tsx
+++ b/src/components/ButtonWithDropdownMenu/index.tsx
@@ -11,7 +11,6 @@ import useTheme from '@hooks/useTheme';
import useThemeStyles from '@hooks/useThemeStyles';
import useWindowDimensions from '@hooks/useWindowDimensions';
import mergeRefs from '@libs/mergeRefs';
-import * as Modal from '@userActions/Modal';
import CONST from '@src/CONST';
import type {AnchorPosition} from '@src/styles';
import type {ButtonWithDropdownMenuProps} from './types';
@@ -65,7 +64,7 @@ function ButtonWithDropdownMenu({
if ('measureInWindow' in dropdownAnchor.current) {
dropdownAnchor.current.measureInWindow((x, y, w, h) => {
setPopoverAnchorPosition({
- horizontal: x + w + h,
+ horizontal: x + w,
vertical:
anchorAlignment.vertical === CONST.MODAL.ANCHOR_ORIGIN_VERTICAL.TOP
? y + h + CONST.MODAL.POPOVER_MENU_PADDING // if vertical anchorAlignment is TOP, menu will open below the button and we need to add the height of button and padding
@@ -178,11 +177,12 @@ function ButtonWithDropdownMenu({
menuItems={options.map((item, index) => ({
...item,
onSelected: item.onSelected
- ? () => Modal.close(() => item.onSelected?.())
+ ? () => item.onSelected?.()
: () => {
onOptionSelected?.(item);
setSelectedItemIndex(index);
},
+ shouldCallAfterModalHide: true,
}))}
/>
)}
diff --git a/src/components/ButtonWithDropdownMenu/types.ts b/src/components/ButtonWithDropdownMenu/types.ts
index 9be13696721c..e4b81da94942 100644
--- a/src/components/ButtonWithDropdownMenu/types.ts
+++ b/src/components/ButtonWithDropdownMenu/types.ts
@@ -1,13 +1,12 @@
import type {RefObject} from 'react';
import type {GestureResponderEvent, StyleProp, View, ViewStyle} from 'react-native';
import type {ValueOf} from 'type-fest';
-import type {PaymentMethodType} from '@components/KYCWall/types';
import type CONST from '@src/CONST';
import type AnchorAlignment from '@src/types/utils/AnchorAlignment';
import type DeepValueOf from '@src/types/utils/DeepValueOf';
import type IconAsset from '@src/types/utils/IconAsset';
-type PaymentType = DeepValueOf;
+type PaymentType = DeepValueOf;
type WorkspaceMemberBulkActionType = DeepValueOf;
diff --git a/src/components/EmojiPicker/EmojiPickerMenu/index.tsx b/src/components/EmojiPicker/EmojiPickerMenu/index.tsx
index 5a59585ad72e..92c35d7c7685 100755
--- a/src/components/EmojiPicker/EmojiPickerMenu/index.tsx
+++ b/src/components/EmojiPicker/EmojiPickerMenu/index.tsx
@@ -10,6 +10,7 @@ import TextInput from '@components/TextInput';
import isTextInputFocused from '@components/TextInput/BaseTextInput/isTextInputFocused';
import type {BaseTextInputRef} from '@components/TextInput/BaseTextInput/types';
import useArrowKeyFocusManager from '@hooks/useArrowKeyFocusManager';
+import useKeyboardShortcut from '@hooks/useKeyboardShortcut';
import useLocalize from '@hooks/useLocalize';
import useResponsiveLayout from '@hooks/useResponsiveLayout';
import useSingleExecution from '@hooks/useSingleExecution';
@@ -141,21 +142,7 @@ function EmojiPickerMenu({onEmojiSelected, activeEmoji}: EmojiPickerMenuProps, r
return;
}
- // Select the currently highlighted emoji if enter is pressed
if (!isEnterWhileComposition(keyBoardEvent) && keyBoardEvent.key === CONST.KEYBOARD_SHORTCUTS.ENTER.shortcutKey) {
- let indexToSelect = focusedIndex;
- if (highlightFirstEmoji) {
- indexToSelect = 0;
- }
-
- const item = filteredEmojis[indexToSelect];
- if (!item) {
- return;
- }
- if ('types' in item || 'name' in item) {
- const emoji = typeof preferredSkinTone === 'number' && item?.types?.[preferredSkinTone] ? item?.types?.[preferredSkinTone] : item.code;
- onEmojiSelected(emoji, item);
- }
// On web, avoid this Enter default input action; otherwise, it will add a new line in the subsequently focused composer.
keyBoardEvent.preventDefault();
// On mWeb, avoid propagating this Enter keystroke to Pressable child component; otherwise, it will trigger the onEmojiSelected callback again.
@@ -175,7 +162,32 @@ function EmojiPickerMenu({onEmojiSelected, activeEmoji}: EmojiPickerMenuProps, r
searchInputRef.current.focus();
}
},
- [filteredEmojis, focusedIndex, highlightFirstEmoji, isFocused, onEmojiSelected, preferredSkinTone],
+ [isFocused],
+ );
+
+ useKeyboardShortcut(
+ CONST.KEYBOARD_SHORTCUTS.ENTER,
+ (keyBoardEvent) => {
+ if (!(keyBoardEvent instanceof KeyboardEvent) || isEnterWhileComposition(keyBoardEvent) || keyBoardEvent.key !== CONST.KEYBOARD_SHORTCUTS.ENTER.shortcutKey) {
+ return;
+ }
+
+ // Select the currently highlighted emoji if enter is pressed
+ let indexToSelect = focusedIndex;
+ if (highlightFirstEmoji) {
+ indexToSelect = 0;
+ }
+
+ const item = filteredEmojis[indexToSelect];
+ if (!item) {
+ return;
+ }
+ if ('types' in item || 'name' in item) {
+ const emoji = typeof preferredSkinTone === 'number' && item?.types?.[preferredSkinTone] ? item?.types?.[preferredSkinTone] : item.code;
+ onEmojiSelected(emoji, item);
+ }
+ },
+ {shouldPreventDefault: true, shouldStopPropagation: true},
);
/**
diff --git a/src/components/FeatureList.tsx b/src/components/FeatureList.tsx
index 88dfed67d0c6..30c72d467073 100644
--- a/src/components/FeatureList.tsx
+++ b/src/components/FeatureList.tsx
@@ -54,6 +54,9 @@ type FeatureListProps = {
/** The background color to apply in the upper half of the screen. */
illustrationBackgroundColor?: string;
+ /** Customize the Illustration container */
+ illustrationContainerStyle?: StyleProp;
+
/** The style used for the title */
titleStyles?: StyleProp;
@@ -78,6 +81,7 @@ function FeatureList({
illustration,
illustrationStyle,
illustrationBackgroundColor,
+ illustrationContainerStyle,
titleStyles,
contentPaddingOnLargeScreens,
}: FeatureListProps) {
@@ -94,6 +98,7 @@ function FeatureList({
illustrationBackgroundColor={illustrationBackgroundColor}
illustrationStyle={illustrationStyle}
titleStyles={titleStyles}
+ illustrationContainerStyle={illustrationContainerStyle}
contentPaddingOnLargeScreens={contentPaddingOnLargeScreens}
>
diff --git a/src/components/FeatureTrainingModal.tsx b/src/components/FeatureTrainingModal.tsx
index 257627361a0e..a2ff1c2824f3 100644
--- a/src/components/FeatureTrainingModal.tsx
+++ b/src/components/FeatureTrainingModal.tsx
@@ -229,12 +229,6 @@ function FeatureTrainingModal({
text={helpText}
/>
)}
-
)}
- {allHavePendingRTERViolation && !shouldUseNarrowLayout && (
+ {hasAllPendingRTERViolations && !shouldUseNarrowLayout && (
)}
- {allHavePendingRTERViolation && shouldUseNarrowLayout && (
+ {hasAllPendingRTERViolations && shouldUseNarrowLayout && (
!PolicyUtils.isExpensifyTeam(participantOption.login))
- .map((participantOption: Participant) => ({
- ...participantOption,
- tabIndex: -1,
- isSelected: false,
- isInteractive: !shouldDisableParticipant(participantOption),
- rightElement: (
- onSplitShareChange(participantOption.accountID ?? -1, Number(value))}
- maxLength={formattedTotalAmount.length}
- contentWidth={formattedTotalAmount.length * 8}
- />
- ),
- }));
+ return [payeeOption, ...selectedParticipants].map((participantOption: Participant) => ({
+ ...participantOption,
+ tabIndex: -1,
+ isSelected: false,
+ isInteractive: !shouldDisableParticipant(participantOption),
+ rightElement: (
+ onSplitShareChange(participantOption.accountID ?? -1, Number(value))}
+ maxLength={formattedTotalAmount.length}
+ contentWidth={formattedTotalAmount.length * 8}
+ />
+ ),
+ }));
}, [
isTypeSplit,
payeePersonalDetails,
@@ -839,6 +837,7 @@ function MoneyRequestConfirmationList({
onPress={confirm}
enablePaymentsRoute={ROUTES.IOU_SEND_ENABLE_PAYMENTS}
addBankAccountRoute={bankAccountRoute}
+ shouldShowPersonalBankAccountOption
currency={iouCurrencyCode}
policyID={policyID}
buttonSize={CONST.DROPDOWN_BUTTON_SIZE.LARGE}
diff --git a/src/components/MoneyRequestConfirmationListFooter.tsx b/src/components/MoneyRequestConfirmationListFooter.tsx
index ff18b48d5901..4c3641bc7c59 100644
--- a/src/components/MoneyRequestConfirmationListFooter.tsx
+++ b/src/components/MoneyRequestConfirmationListFooter.tsx
@@ -28,7 +28,6 @@ import type * as OnyxTypes from '@src/types/onyx';
import type {Participant} from '@src/types/onyx/IOU';
import type {Unit} from '@src/types/onyx/Policy';
import ConfirmedRoute from './ConfirmedRoute';
-import MentionReportContext from './HTMLEngineProvider/HTMLRenderers/MentionReportRenderer/MentionReportContext';
import MenuItem from './MenuItem';
import MenuItemWithTopDescription from './MenuItemWithTopDescription';
import PDFThumbnail from './PDFThumbnail';
@@ -218,6 +217,7 @@ function MoneyRequestConfirmationListFooter({
const {translate, toLocaleDigit} = useLocalize();
const {isOffline} = useNetwork();
const [allPolicies] = useOnyx(ONYXKEYS.COLLECTION.POLICY);
+ const [currentUserLogin] = useOnyx(ONYXKEYS.SESSION, {selector: (session) => session?.email});
// A flag and a toggler for showing the rest of the form fields
const [shouldExpandFields, toggleShouldExpandFields] = useReducer((state) => !state, false);
@@ -233,8 +233,8 @@ function MoneyRequestConfirmationListFooter({
const canUpdateSenderWorkspace = useMemo(() => {
const isInvoiceRoomParticipant = selectedParticipants.some((participant) => participant.isInvoiceRoom);
- return PolicyUtils.canSendInvoice(allPolicies) && !!transaction?.isFromGlobalCreate && !isInvoiceRoomParticipant;
- }, [allPolicies, selectedParticipants, transaction?.isFromGlobalCreate]);
+ return PolicyUtils.canSendInvoice(allPolicies, currentUserLogin) && !!transaction?.isFromGlobalCreate && !isInvoiceRoomParticipant;
+ }, [allPolicies, currentUserLogin, selectedParticipants, transaction?.isFromGlobalCreate]);
const isTypeSend = iouType === CONST.IOU.TYPE.PAY;
const taxRates = policy?.taxRates ?? null;
@@ -267,8 +267,6 @@ function MoneyRequestConfirmationListFooter({
const resolvedThumbnail = isLocalFile ? receiptThumbnail : tryResolveUrlFromApiRoot(receiptThumbnail ?? '');
const resolvedReceiptImage = isLocalFile ? receiptImage : tryResolveUrlFromApiRoot(receiptImage ?? '');
- const mentionReportContextValue = useMemo(() => ({currentReportID: reportID}), [reportID]);
-
// An intermediate structure that helps us classify the fields as "primary" and "supplementary".
// The primary fields are always shown to the user, while an extra action is needed to reveal the supplementary ones.
const classifiedFields = [
@@ -299,26 +297,23 @@ function MoneyRequestConfirmationListFooter({
},
{
item: (
-
- {
- Navigation.navigate(
- ROUTES.MONEY_REQUEST_STEP_DESCRIPTION.getRoute(action, iouType, transactionID, reportID, Navigation.getActiveRouteWithoutParams(), reportActionID),
- );
- }}
- style={[styles.moneyRequestMenuItem]}
- titleStyle={styles.flex1}
- disabled={didConfirm}
- interactive={!isReadOnly}
- numberOfLinesTitle={2}
- />
-
+ {
+ Navigation.navigate(
+ ROUTES.MONEY_REQUEST_STEP_DESCRIPTION.getRoute(action, iouType, transactionID, reportID, Navigation.getActiveRouteWithoutParams(), reportActionID),
+ );
+ }}
+ style={[styles.moneyRequestMenuItem]}
+ titleStyle={styles.flex1}
+ disabled={didConfirm}
+ interactive={!isReadOnly}
+ numberOfLinesTitle={2}
+ />
),
shouldShow: true,
isSupplementary: false,
diff --git a/src/components/PopoverMenu.tsx b/src/components/PopoverMenu.tsx
index f7dd40177275..ce8417efabed 100644
--- a/src/components/PopoverMenu.tsx
+++ b/src/components/PopoverMenu.tsx
@@ -8,6 +8,8 @@ import useKeyboardShortcut from '@hooks/useKeyboardShortcut';
import useResponsiveLayout from '@hooks/useResponsiveLayout';
import useTheme from '@hooks/useTheme';
import useThemeStyles from '@hooks/useThemeStyles';
+import * as Browser from '@libs/Browser';
+import * as Modal from '@userActions/Modal';
import CONST from '@src/CONST';
import type {AnchorPosition} from '@src/styles';
import type AnchorAlignment from '@src/types/utils/AnchorAlignment';
@@ -35,6 +37,11 @@ type PopoverMenuItem = MenuItemProps & {
/** Determines whether the menu item is disabled or not */
disabled?: boolean;
+
+ /** Determines whether the menu item's onSelected() function is called after the modal is hidden
+ * It is meant to be used in situations where, after clicking on the modal, another one is opened.
+ */
+ shouldCallAfterModalHide?: boolean;
};
type PopoverModalProps = Pick;
@@ -128,6 +135,11 @@ function PopoverMenu({
setEnteredSubMenuIndexes([...enteredSubMenuIndexes, index]);
const selectedSubMenuItemIndex = selectedItem?.subMenuItems.findIndex((option) => option.isSelected);
setFocusedIndex(selectedSubMenuItemIndex);
+ } else if (selectedItem.shouldCallAfterModalHide && !Browser.isSafari()) {
+ Modal.close(() => {
+ onItemSelected(selectedItem, index);
+ selectedItem.onSelected?.();
+ });
} else {
onItemSelected(selectedItem, index);
selectedItem.onSelected?.();
@@ -270,6 +282,7 @@ function PopoverMenu({
renderTooltipContent={item.renderTooltipContent}
numberOfLinesTitle={item.numberOfLinesTitle}
interactive={item.interactive}
+ badgeText={item.badgeText}
/>
))}
diff --git a/src/components/Reactions/ReportActionItemEmojiReactions.tsx b/src/components/Reactions/ReportActionItemEmojiReactions.tsx
index c6bf4f9e4016..943158607db4 100644
--- a/src/components/Reactions/ReportActionItemEmojiReactions.tsx
+++ b/src/components/Reactions/ReportActionItemEmojiReactions.tsx
@@ -33,7 +33,7 @@ type ReportActionItemEmojiReactionsProps = WithCurrentUserPersonalDetailsProps &
* This can also be an emoji the user already reacted with,
* hence this function asks to toggle the reaction by emoji.
*/
- toggleReaction: (emoji: Emoji) => void;
+ toggleReaction: (emoji: Emoji, ignoreSkinToneOnCompare?: boolean) => void;
/** We disable reacting with emojis on report actions that have errors */
shouldBlockReactions?: boolean;
@@ -107,7 +107,7 @@ function ReportActionItemEmojiReactions({
totalReactionCount += reactionCount;
const onPress = () => {
- toggleReaction(emoji);
+ toggleReaction(emoji, true);
};
const onReactionListOpen = (event: ReactionListEvent) => {
diff --git a/src/components/ReportActionItem/IssueCardMessage.tsx b/src/components/ReportActionItem/IssueCardMessage.tsx
index 292b010cd851..3bb8842195e3 100644
--- a/src/components/ReportActionItem/IssueCardMessage.tsx
+++ b/src/components/ReportActionItem/IssueCardMessage.tsx
@@ -3,7 +3,6 @@ import type {OnyxEntry} from 'react-native-onyx';
import {useOnyx} from 'react-native-onyx';
import Button from '@components/Button';
import RenderHTML from '@components/RenderHTML';
-import useCurrentUserPersonalDetails from '@hooks/useCurrentUserPersonalDetails';
import useEnvironment from '@hooks/useEnvironment';
import useLocalize from '@hooks/useLocalize';
import useThemeStyles from '@hooks/useThemeStyles';
@@ -12,22 +11,24 @@ import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
import ROUTES from '@src/ROUTES';
import type {ReportAction} from '@src/types/onyx';
+import type OriginalMessage from '@src/types/onyx/OriginalMessage';
import {isEmptyObject} from '@src/types/utils/EmptyObject';
type IssueCardMessageProps = {
action: OnyxEntry;
};
+type IssueNewCardOriginalMessage = OriginalMessage<
+ typeof CONST.REPORT.ACTIONS.TYPE.CARD_MISSING_ADDRESS | typeof CONST.REPORT.ACTIONS.TYPE.CARD_ISSUED | typeof CONST.REPORT.ACTIONS.TYPE.CARD_ISSUED_VIRTUAL
+>;
+
function IssueCardMessage({action}: IssueCardMessageProps) {
const {translate} = useLocalize();
const styles = useThemeStyles();
const {environmentURL} = useEnvironment();
- // TODO: now mocking accountID with current user accountID instead of action.message.assigneeAccountID
- const personalData = useCurrentUserPersonalDetails();
const [privatePersonalDetails] = useOnyx(ONYXKEYS.PRIVATE_PERSONAL_DETAILS);
- // TODO: now mocking accountID with current user accountID instead of action.message.assigneeAccountID
- const assignee = ``;
+ const assignee = ``;
const link = `${translate('cardPage.expensifyCard')}`;
const noMailingAddress = action?.actionName === CONST.REPORT.ACTIONS.TYPE.CARD_MISSING_ADDRESS && isEmptyObject(privatePersonalDetails?.address);
diff --git a/src/components/ReportActionItem/MoneyReportView.tsx b/src/components/ReportActionItem/MoneyReportView.tsx
index fbf03821fa16..9b3b02a1abac 100644
--- a/src/components/ReportActionItem/MoneyReportView.tsx
+++ b/src/components/ReportActionItem/MoneyReportView.tsx
@@ -10,6 +10,7 @@ import MenuItemWithTopDescription from '@components/MenuItemWithTopDescription';
import OfflineWithFeedback from '@components/OfflineWithFeedback';
import SpacerView from '@components/SpacerView';
import Text from '@components/Text';
+import UnreadActionIndicator from '@components/UnreadActionIndicator';
import useLocalize from '@hooks/useLocalize';
import useStyleUtils from '@hooks/useStyleUtils';
import useTheme from '@hooks/useTheme';
@@ -76,6 +77,22 @@ function MoneyReportView({report, policy, isCombinedReport = false, shouldShowTo
const shouldShowReportField =
!ReportUtils.isClosedExpenseReportWithNoExpenses(report) && ReportUtils.isPaidGroupPolicyExpenseReport(report) && (!isCombinedReport || !isOnlyTitleFieldEnabled);
+ const renderThreadDivider = useMemo(
+ () =>
+ shouldHideThreadDividerLine && !isCombinedReport ? (
+
+ ) : (
+
+ ),
+ [shouldHideThreadDividerLine, report.reportID, styles.reportHorizontalRule, isCombinedReport],
+ );
+
return (
<>
@@ -197,12 +214,7 @@ function MoneyReportView({report, policy, isCombinedReport = false, shouldShowTo
>
)}
- {(shouldShowReportField || shouldShowBreakdown || shouldShowTotal) && (
-
- )}
+ {(shouldShowReportField || shouldShowBreakdown || shouldShowTotal) && renderThreadDivider}
>
);
}
diff --git a/src/components/ReportActionItem/MoneyRequestPreview/MoneyRequestPreviewContent.tsx b/src/components/ReportActionItem/MoneyRequestPreview/MoneyRequestPreviewContent.tsx
index ac61cb74d229..6ab1c0937278 100644
--- a/src/components/ReportActionItem/MoneyRequestPreview/MoneyRequestPreviewContent.tsx
+++ b/src/components/ReportActionItem/MoneyRequestPreview/MoneyRequestPreviewContent.tsx
@@ -116,14 +116,15 @@ function MoneyRequestPreviewContent({
const isFetchingWaypointsFromServer = TransactionUtils.isFetchingWaypointsFromServer(transaction);
const isCardTransaction = TransactionUtils.isCardTransaction(transaction);
const isSettled = ReportUtils.isSettled(iouReport?.reportID);
+ const isApproved = ReportUtils.isReportApproved(iouReport);
const isDeleted = action?.pendingAction === CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE;
const isReviewDuplicateTransactionPage = route.name === SCREENS.TRANSACTION_DUPLICATE.REVIEW;
const isFullySettled = isSettled && !isSettlementOrApprovalPartial;
- const isFullyApproved = ReportUtils.isReportApproved(iouReport) && !isSettlementOrApprovalPartial;
+ const isFullyApproved = isApproved && !isSettlementOrApprovalPartial;
// Get transaction violations for given transaction id from onyx, find duplicated transactions violations and get duplicates
- const duplicates = useMemo(
+ const allDuplicates = useMemo(
() =>
transactionViolations?.[`${ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS}${transaction?.transactionID}`]?.find(
(violation) => violation.name === CONST.VIOLATIONS.DUPLICATED_TRANSACTION,
@@ -131,6 +132,12 @@ function MoneyRequestPreviewContent({
[transaction?.transactionID, transactionViolations],
);
+ // Remove settled transactions from duplicates
+ const duplicates = useMemo(() => TransactionUtils.removeSettledAndApprovedTransactions(allDuplicates), [allDuplicates]);
+
+ // When there are no settled transactions in duplicates, show the "Keep this one" button
+ const shouldShowKeepButton = allDuplicates.length === duplicates.length;
+
const hasDuplicates = duplicates.length > 0;
const shouldShowRBR = hasNoticeTypeViolations || hasViolations || hasFieldErrors || (!isFullySettled && !isFullyApproved && isOnHold) || hasDuplicates;
@@ -437,7 +444,7 @@ function MoneyRequestPreviewContent({
]}
>
{childContainer}
- {isReviewDuplicateTransactionPage && (
+ {isReviewDuplicateTransactionPage && !isSettled && !isApproved && shouldShowKeepButton && (
IOU.canIOUBePaid(iouReport, chatReport, policy, allTransactions), [iouReport, chatReport, policy, allTransactions]);
- const shouldShowApproveButton = useMemo(() => IOU.canApproveIOU(iouReport, chatReport, policy), [iouReport, chatReport, policy]);
+ const shouldShowApproveButton = useMemo(() => IOU.canApproveIOU(iouReport, policy), [iouReport, policy]);
const shouldDisableApproveButton = shouldShowApproveButton && !ReportUtils.isAllowedToApproveExpenseReport(iouReport);
diff --git a/src/components/Search/SearchFiltersChatsSelector.tsx b/src/components/Search/SearchFiltersChatsSelector.tsx
new file mode 100644
index 000000000000..689f917fdccf
--- /dev/null
+++ b/src/components/Search/SearchFiltersChatsSelector.tsx
@@ -0,0 +1,188 @@
+import React, {useCallback, useEffect, useMemo, useState} from 'react';
+import {useOnyx} from 'react-native-onyx';
+import Button from '@components/Button';
+import {usePersonalDetails} from '@components/OnyxProvider';
+import {useOptionsList} from '@components/OptionListContextProvider';
+import SelectionList from '@components/SelectionList';
+import InviteMemberListItem from '@components/SelectionList/InviteMemberListItem';
+import useDebouncedState from '@hooks/useDebouncedState';
+import useLocalize from '@hooks/useLocalize';
+import useScreenWrapperTransitionStatus from '@hooks/useScreenWrapperTransitionStatus';
+import * as DeviceCapabilities from '@libs/DeviceCapabilities';
+import * as OptionsListUtils from '@libs/OptionsListUtils';
+import type {Option} from '@libs/OptionsListUtils';
+import type {OptionData} from '@libs/ReportUtils';
+import Navigation from '@navigation/Navigation';
+import * as Report from '@userActions/Report';
+import CONST from '@src/CONST';
+import ONYXKEYS from '@src/ONYXKEYS';
+import ROUTES from '@src/ROUTES';
+
+const defaultListOptions = {
+ recentReports: [],
+ personalDetails: [],
+ userToInvite: null,
+ currentUserOption: null,
+ categoryOptions: [],
+ tagOptions: [],
+ taxRatesOptions: [],
+ headerMessage: '',
+};
+
+function getSelectedOptionData(option: Option): OptionData {
+ return {...option, isSelected: true, reportID: option.reportID ?? '-1'};
+}
+
+type SearchFiltersParticipantsSelectorProps = {
+ initialReportIDs: string[];
+ onFiltersUpdate: (initialReportIDs: string[]) => void;
+ isScreenTransitionEnd: boolean;
+};
+
+function SearchFiltersChatsSelector({initialReportIDs, onFiltersUpdate, isScreenTransitionEnd}: SearchFiltersParticipantsSelectorProps) {
+ const {translate} = useLocalize();
+ const personalDetails = usePersonalDetails();
+ const {didScreenTransitionEnd} = useScreenWrapperTransitionStatus();
+ const {options, areOptionsInitialized} = useOptionsList({
+ shouldInitialize: didScreenTransitionEnd,
+ });
+
+ const [reports] = useOnyx(ONYXKEYS.COLLECTION.REPORT);
+ const [isSearchingForReports] = useOnyx(ONYXKEYS.IS_SEARCHING_FOR_REPORTS, {initWithStoredValues: false});
+ const [selectedReportIDs, setSelectedReportIDs] = useState(initialReportIDs);
+ const [searchTerm, debouncedSearchTerm, setSearchTerm] = useDebouncedState('');
+ const cleanSearchTerm = useMemo(() => searchTerm.trim().toLowerCase(), [searchTerm]);
+
+ const selectedOptions = useMemo(() => {
+ return selectedReportIDs.map((id) => {
+ const report = getSelectedOptionData(OptionsListUtils.createOptionFromReport({...reports?.[`${ONYXKEYS.COLLECTION.REPORT}${id}`], reportID: id}, personalDetails));
+ const alternateText = OptionsListUtils.getAlternateText(report, {showChatPreviewLine: true});
+ return {...report, alternateText};
+ });
+ }, [personalDetails, reports, selectedReportIDs]);
+
+ const defaultOptions = useMemo(() => {
+ if (!areOptionsInitialized || !isScreenTransitionEnd) {
+ return defaultListOptions;
+ }
+ return OptionsListUtils.getSearchOptions(options);
+ }, [areOptionsInitialized, isScreenTransitionEnd, options]);
+
+ const chatOptions = useMemo(() => {
+ return OptionsListUtils.filterOptions(defaultOptions, cleanSearchTerm, {
+ selectedOptions,
+ excludeLogins: CONST.EXPENSIFY_EMAILS,
+ maxRecentReportsToShow: 0,
+ });
+ }, [defaultOptions, cleanSearchTerm, selectedOptions]);
+
+ const {sections, headerMessage} = useMemo(() => {
+ const newSections: OptionsListUtils.CategorySection[] = [];
+ if (!areOptionsInitialized) {
+ return {sections: [], headerMessage: undefined};
+ }
+
+ const formattedResults = OptionsListUtils.formatSectionsFromSearchTerm(
+ cleanSearchTerm,
+ selectedOptions,
+ chatOptions.recentReports,
+ chatOptions.personalDetails,
+ personalDetails,
+ false,
+ );
+
+ newSections.push(formattedResults.section);
+
+ const visibleReportsWhenSearchTermNonEmpty = chatOptions.recentReports.map((report) => (selectedReportIDs.includes(report.reportID) ? getSelectedOptionData(report) : report));
+ const visibleReportsWhenSearchTermEmpty = chatOptions.recentReports.filter((report) => !selectedReportIDs.includes(report.reportID));
+ const reportsFiltered = cleanSearchTerm === '' ? visibleReportsWhenSearchTermEmpty : visibleReportsWhenSearchTermNonEmpty;
+
+ newSections.push({
+ title: undefined,
+ data: reportsFiltered,
+ shouldShow: chatOptions.recentReports.length > 0,
+ });
+
+ const areResultsFound = didScreenTransitionEnd && formattedResults.section.data.length === 0 && reportsFiltered.length === 0;
+ const message = areResultsFound ? translate('common.noResultsFound') : undefined;
+
+ return {
+ sections: newSections,
+ headerMessage: message,
+ };
+ }, [
+ areOptionsInitialized,
+ chatOptions.personalDetails,
+ chatOptions.recentReports,
+ cleanSearchTerm,
+ didScreenTransitionEnd,
+ personalDetails,
+ selectedOptions,
+ selectedReportIDs,
+ translate,
+ ]);
+
+ useEffect(() => {
+ Report.searchInServer(debouncedSearchTerm.trim());
+ }, [debouncedSearchTerm]);
+
+ const handleParticipantSelection = useCallback(
+ (selectedOption: Option) => {
+ const optionReportID = selectedOption.reportID;
+ if (!optionReportID) {
+ return;
+ }
+ const foundOptionIndex = selectedReportIDs.findIndex((reportID: string) => {
+ return reportID && reportID !== '' && selectedOption.reportID === reportID;
+ });
+
+ if (foundOptionIndex < 0) {
+ setSelectedReportIDs([...selectedReportIDs, optionReportID]);
+ } else {
+ const newSelectedReports = [...selectedReportIDs.slice(0, foundOptionIndex), ...selectedReportIDs.slice(foundOptionIndex + 1)];
+ setSelectedReportIDs(newSelectedReports);
+ }
+ },
+ [selectedReportIDs],
+ );
+
+ const footerContent = (
+ {
+ onFiltersUpdate(selectedReportIDs);
+ Navigation.goBack(ROUTES.SEARCH_ADVANCED_FILTERS);
+ }}
+ large
+ />
+ );
+
+ const isLoadingNewOptions = !!isSearchingForReports;
+ const showLoadingPlaceholder = !didScreenTransitionEnd || !areOptionsInitialized || !initialReportIDs || !personalDetails;
+
+ return (
+ {
+ setSearchTerm(value);
+ }}
+ onSelectRow={handleParticipantSelection}
+ isLoadingNewOptions={isLoadingNewOptions}
+ showLoadingPlaceholder={showLoadingPlaceholder}
+ />
+ );
+}
+
+SearchFiltersChatsSelector.displayName = 'SearchFiltersChatsSelector';
+
+export default SearchFiltersChatsSelector;
diff --git a/src/components/Search/SearchMultipleSelectionPicker.tsx b/src/components/Search/SearchMultipleSelectionPicker.tsx
index 57003110fd1a..558b89715b61 100644
--- a/src/components/Search/SearchMultipleSelectionPicker.tsx
+++ b/src/components/Search/SearchMultipleSelectionPicker.tsx
@@ -1,4 +1,4 @@
-import React, {useCallback, useMemo, useState} from 'react';
+import React, {useCallback, useEffect, useMemo, useState} from 'react';
import Button from '@components/Button';
import SelectionList from '@components/SelectionList';
import SelectableListItem from '@components/SelectionList/SelectableListItem';
@@ -28,6 +28,10 @@ function SearchMultipleSelectionPicker({items, initiallySelectedItems, pickerTit
const [searchTerm, debouncedSearchTerm, setSearchTerm] = useDebouncedState('');
const [selectedItems, setSelectedItems] = useState(initiallySelectedItems ?? []);
+ useEffect(() => {
+ setSelectedItems(initiallySelectedItems ?? []);
+ }, [initiallySelectedItems]);
+
const {sections, noResultsFound} = useMemo(() => {
const selectedItemsSection = selectedItems
.filter((item) => item?.name.toLowerCase().includes(debouncedSearchTerm?.toLowerCase()))
diff --git a/src/components/Search/SearchPageHeader.tsx b/src/components/Search/SearchPageHeader.tsx
index fe2815e07d5d..c28eed688aaa 100644
--- a/src/components/Search/SearchPageHeader.tsx
+++ b/src/components/Search/SearchPageHeader.tsx
@@ -83,7 +83,7 @@ function HeaderWrapper({icon, title, subtitle, children, subtitleStyles = {}}: H
)}
{middleContent}
- {children}
+ {children}
);
@@ -309,13 +309,13 @@ function SearchPageHeader({queryJSON, hash, onSelectDeleteOption, setOfflineModa
customText={translate('workspace.common.selected', {selectedNumber: selectedTransactionsKeys.length})}
options={headerButtonsOptions}
isSplitButton={false}
- style={styles.ml2}
/>
)}
Navigation.navigate(ROUTES.SEARCH_ADVANCED_FILTERS)}
+ medium
/>
);
diff --git a/src/components/Search/index.tsx b/src/components/Search/index.tsx
index db729a9aa77d..958e238d4598 100644
--- a/src/components/Search/index.tsx
+++ b/src/components/Search/index.tsx
@@ -9,7 +9,9 @@ import SearchTableHeader from '@components/SelectionList/SearchTableHeader';
import type {ReportListItemType, TransactionListItemType} from '@components/SelectionList/types';
import SelectionListWithModal from '@components/SelectionListWithModal';
import SearchRowSkeleton from '@components/Skeletons/SearchRowSkeleton';
+import SearchStatusSkeleton from '@components/Skeletons/SearchStatusSkeleton';
import useLocalize from '@hooks/useLocalize';
+import useMobileSelectionMode from '@hooks/useMobileSelectionMode';
import useNetwork from '@hooks/useNetwork';
import usePrevious from '@hooks/usePrevious';
import useThemeStyles from '@hooks/useThemeStyles';
@@ -37,7 +39,6 @@ import type {SearchColumnType, SearchQueryJSON, SearchStatus, SelectedTransactio
type SearchProps = {
queryJSON: SearchQueryJSON;
isCustomQuery: boolean;
- policyIDs?: string;
};
const transactionItemMobileHeight = 100;
@@ -74,7 +75,7 @@ function prepareTransactionsList(item: TransactionListItemType, selectedTransact
return {...selectedTransactions, [item.keyForList]: {isSelected: true, canDelete: item.canDelete, canHold: item.canHold, canUnhold: item.canUnhold, action: item.action}};
}
-function Search({queryJSON, policyIDs, isCustomQuery}: SearchProps) {
+function Search({queryJSON, isCustomQuery}: SearchProps) {
const {isOffline} = useNetwork();
const {translate} = useLocalize();
const styles = useThemeStyles();
@@ -82,7 +83,7 @@ function Search({queryJSON, policyIDs, isCustomQuery}: SearchProps) {
const navigation = useNavigation>();
const lastSearchResultsRef = useRef>();
const {setCurrentSearchHash, setSelectedTransactions, selectedTransactions, clearSelectedTransactions} = useSearchContext();
- const [selectionMode] = useOnyx(ONYXKEYS.MOBILE_SELECTION_MODE);
+ const {selectionMode} = useMobileSelectionMode();
const [offset, setOffset] = useState(0);
const [offlineModalVisible, setOfflineModalVisible] = useState(false);
@@ -118,7 +119,7 @@ function Search({queryJSON, policyIDs, isCustomQuery}: SearchProps) {
return;
}
- SearchActions.search({queryJSON, offset, policyIDs});
+ SearchActions.search({queryJSON, offset});
// eslint-disable-next-line react-compiler/react-compiler, react-hooks/exhaustive-deps
}, [isOffline, offset, queryJSON]);
@@ -161,7 +162,7 @@ function Search({queryJSON, policyIDs, isCustomQuery}: SearchProps) {
[isLargeScreenWidth],
);
- const getItemHeightMemoized = memoize((item: TransactionListItemType | ReportListItemType) => getItemHeight(item), {
+ const getItemHeightMemoized = memoize(getItemHeight, {
transformKey: ([item]) => {
// List items are displayed differently on "L"arge and "N"arrow screens so the height will differ
// in addition the same items might be displayed as part of different Search screens ("Expenses", "All", "Finished")
@@ -200,6 +201,16 @@ function Search({queryJSON, policyIDs, isCustomQuery}: SearchProps) {
queryJSON={queryJSON}
hash={hash}
/>
+
+ {/* We only want to display the skeleton for the status filters the first time we load them for a specific data type */}
+ {searchResults?.search?.type === type ? (
+
+ ) : (
+
+ )}
>
);
@@ -390,7 +401,7 @@ function Search({queryJSON, policyIDs, isCustomQuery}: SearchProps) {
/>
setOfflineModalVisible(false)}
secondOptionText={translate('common.buttonConfirm')}
diff --git a/src/components/Search/types.ts b/src/components/Search/types.ts
index c933deb8ee03..9f2aca1ff957 100644
--- a/src/components/Search/types.ts
+++ b/src/components/Search/types.ts
@@ -63,6 +63,7 @@ type SearchQueryAST = {
sortBy: SearchColumnType;
sortOrder: SortOrder;
filters: ASTNode;
+ policyID?: string;
};
type SearchQueryJSON = {
diff --git a/src/components/SelectionList/Search/TransactionListItemRow.tsx b/src/components/SelectionList/Search/TransactionListItemRow.tsx
index be42642316b0..64c5f6a768f4 100644
--- a/src/components/SelectionList/Search/TransactionListItemRow.tsx
+++ b/src/components/SelectionList/Search/TransactionListItemRow.tsx
@@ -15,6 +15,7 @@ import useThemeStyles from '@hooks/useThemeStyles';
import useWindowDimensions from '@hooks/useWindowDimensions';
import * as CurrencyUtils from '@libs/CurrencyUtils';
import DateUtils from '@libs/DateUtils';
+import Parser from '@libs/Parser';
import StringUtils from '@libs/StringUtils';
import * as TransactionUtils from '@libs/TransactionUtils';
import tryResolveUrlFromApiRoot from '@libs/tryResolveUrlFromApiRoot';
@@ -126,9 +127,9 @@ function MerchantCell({transactionItem, showTooltip, isLargeScreenWidth}: Transa
const description = TransactionUtils.getDescription(transactionItem);
let merchantOrDescriptionToDisplay = transactionItem.formattedMerchant;
if (!merchantOrDescriptionToDisplay && !isLargeScreenWidth) {
- merchantOrDescriptionToDisplay = description;
+ merchantOrDescriptionToDisplay = Parser.htmlToText(Parser.replace(description));
}
- let merchant = transactionItem.shouldShowMerchant ? merchantOrDescriptionToDisplay : description;
+ let merchant = transactionItem.shouldShowMerchant ? merchantOrDescriptionToDisplay : Parser.htmlToText(Parser.replace(description));
if (TransactionUtils.hasReceipt(transactionItem) && TransactionUtils.isReceiptBeingScanned(transactionItem) && transactionItem.shouldShowMerchant) {
merchant = translate('iou.receiptStatusTitle');
diff --git a/src/components/SelectionListWithModal/index.tsx b/src/components/SelectionListWithModal/index.tsx
index 907fd4f740a1..1f1a4522540d 100644
--- a/src/components/SelectionListWithModal/index.tsx
+++ b/src/components/SelectionListWithModal/index.tsx
@@ -1,16 +1,15 @@
-import React, {forwardRef, useEffect, useState} from 'react';
import type {ForwardedRef} from 'react';
-import {useOnyx} from 'react-native-onyx';
+import React, {forwardRef, useEffect, useState} from 'react';
import * as Expensicons from '@components/Icon/Expensicons';
import MenuItem from '@components/MenuItem';
import Modal from '@components/Modal';
import SelectionList from '@components/SelectionList';
import type {BaseSelectionListProps, ListItem, SelectionListHandle} from '@components/SelectionList/types';
import useLocalize from '@hooks/useLocalize';
+import useMobileSelectionMode from '@hooks/useMobileSelectionMode';
import useResponsiveLayout from '@hooks/useResponsiveLayout';
import {turnOffMobileSelectionMode, turnOnMobileSelectionMode} from '@libs/actions/MobileSelectionMode';
import CONST from '@src/CONST';
-import ONYXKEYS from '@src/ONYXKEYS';
type SelectionListWithModalProps = BaseSelectionListProps & {
turnOnSelectionModeOnLongPress?: boolean;
@@ -25,7 +24,7 @@ function SelectionListWithModal(
const [longPressedItem, setLongPressedItem] = useState(null);
const {translate} = useLocalize();
const {isSmallScreenWidth} = useResponsiveLayout();
- const [selectionMode] = useOnyx(ONYXKEYS.MOBILE_SELECTION_MODE);
+ const {selectionMode} = useMobileSelectionMode(true);
useEffect(() => {
// We can access 0 index safely as we are not displaying multiple sections in table view
@@ -63,8 +62,6 @@ function SelectionListWithModal(
}
};
- useEffect(() => turnOffMobileSelectionMode(), []);
-
return (
<>
PolicyActions.getPrimaryPolicy(activePolicyID), [activePolicyID]);
-
const session = useSession();
+ const primaryPolicy = useMemo(() => PolicyActions.getPrimaryPolicy(activePolicyID, session?.email), [activePolicyID, session?.email]);
+
// The app would crash due to subscribing to the entire report collection if chatReportID is an empty string. So we should have a fallback ID here.
const [chatReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${chatReportID || -1}`);
const isInvoiceReport = (!isEmptyObject(iouReport) && ReportUtils.isInvoiceReport(iouReport)) || false;
const isPaidGroupPolicy = ReportUtils.isPaidGroupPolicyExpenseChat(chatReport);
- const shouldShowPaywithExpensifyOption = !isPaidGroupPolicy || (!shouldHidePaymentOptions && ReportUtils.isPayer(session, iouReport));
+ const shouldShowPaywithExpensifyOption =
+ !isPaidGroupPolicy ||
+ (!shouldHidePaymentOptions && ReportUtils.isPayer(session, iouReport) && policy?.reimbursementChoice !== CONST.POLICY.REIMBURSEMENT_CHOICES.REIMBURSEMENT_MANUAL);
const shouldShowPayElsewhereOption = (!isPaidGroupPolicy || policy?.reimbursementChoice === CONST.POLICY.REIMBURSEMENT_CHOICES.REIMBURSEMENT_MANUAL) && !isInvoiceReport;
const paymentButtonOptions = useMemo(() => {
+ const buttonOptions = [];
const isExpenseReport = ReportUtils.isExpenseReport(iouReport);
const paymentMethods = {
+ [CONST.IOU.PAYMENT_TYPE.EXPENSIFY]: {
+ text: translate('iou.settleExpensify', {formattedAmount}),
+ icon: Expensicons.Wallet,
+ value: CONST.IOU.PAYMENT_TYPE.EXPENSIFY,
+ },
[CONST.IOU.PAYMENT_TYPE.VBBA]: {
text: translate('iou.settleExpensify', {formattedAmount}),
icon: Expensicons.Wallet,
value: CONST.IOU.PAYMENT_TYPE.VBBA,
},
- [CONST.PAYMENT_METHODS.PERSONAL_BANK_ACCOUNT]: {
- text: translate('iou.settlePersonalBank', {formattedAmount}),
- icon: Expensicons.Bank,
- value: CONST.PAYMENT_METHODS.PERSONAL_BANK_ACCOUNT,
- },
- [CONST.PAYMENT_METHODS.BUSINESS_BANK_ACCOUNT]: {
- text: translate('iou.settleBusinessBank', {formattedAmount}),
- icon: Expensicons.Bank,
- value: CONST.PAYMENT_METHODS.BUSINESS_BANK_ACCOUNT,
- },
- [CONST.PAYMENT_METHODS.DEBIT_CARD]: {
- text: translate('iou.settleDebitCard', {formattedAmount}),
- icon: Expensicons.CreditCard,
- value: CONST.PAYMENT_METHODS.DEBIT_CARD,
- },
[CONST.IOU.PAYMENT_TYPE.ELSEWHERE]: {
text: translate('iou.payElsewhere', {formattedAmount}),
icon: Expensicons.Cash,
value: CONST.IOU.PAYMENT_TYPE.ELSEWHERE,
},
};
- const buttonOptions = [];
const approveButtonOption = {
text: translate('iou.approve'),
icon: Expensicons.ThumbsUp,
@@ -211,10 +206,12 @@ function SettlementButton({
// If the user has previously chosen a specific payment option or paid for some expense,
// let's use the last payment method or use default.
const paymentMethod = nvpLastPaymentMethod?.[policyID] ?? '-1';
- if (canUseWallet || (isExpenseReport && shouldShowPaywithExpensifyOption)) {
- buttonOptions.push(paymentMethods[CONST.PAYMENT_METHODS.BUSINESS_BANK_ACCOUNT]);
+ if (canUseWallet) {
+ buttonOptions.push(paymentMethods[CONST.IOU.PAYMENT_TYPE.EXPENSIFY]);
+ }
+ if (isExpenseReport && shouldShowPaywithExpensifyOption) {
+ buttonOptions.push(paymentMethods[CONST.IOU.PAYMENT_TYPE.VBBA]);
}
-
if (shouldShowPayElsewhereOption) {
buttonOptions.push(paymentMethods[CONST.IOU.PAYMENT_TYPE.ELSEWHERE]);
}
@@ -274,12 +271,7 @@ function SettlementButton({
return;
}
- if (
- iouPaymentType === CONST.IOU.PAYMENT_TYPE.VBBA ||
- iouPaymentType === CONST.PAYMENT_METHODS.BUSINESS_BANK_ACCOUNT ||
- iouPaymentType === CONST.PAYMENT_METHODS.PERSONAL_BANK_ACCOUNT ||
- iouPaymentType === CONST.PAYMENT_METHODS.DEBIT_CARD
- ) {
+ if (iouPaymentType === CONST.IOU.PAYMENT_TYPE.EXPENSIFY || iouPaymentType === CONST.IOU.PAYMENT_TYPE.VBBA) {
triggerKYCFlow(event, iouPaymentType);
BankAccounts.setPersonalBankAccountContinueKYCOnSuccess(ROUTES.ENABLE_PAYMENTS);
return;
@@ -313,6 +305,7 @@ function SettlementButton({
chatReportID={chatReportID}
iouReport={iouReport}
anchorAlignment={kycWallAnchorAlignment}
+ shouldShowPersonalBankAccountOption={shouldShowPersonalBankAccountOption}
>
{(triggerKYCFlow, buttonRef) => (
@@ -320,7 +313,10 @@ function SettlementButton({
onOptionsMenuShow={onPaymentOptionsShow}
onOptionsMenuHide={onPaymentOptionsHide}
buttonRef={buttonRef}
+ shouldAlwaysShowDropdownMenu={isInvoiceReport}
+ customText={isInvoiceReport ? translate('iou.settlePayment', {formattedAmount}) : undefined}
menuHeaderText={isInvoiceReport ? translate('workspace.invoices.paymentMethods.chooseInvoiceMethod') : undefined}
+ isSplitButton={!isInvoiceReport}
isDisabled={isDisabled}
isLoading={isLoading}
onPress={(event, iouPaymentType) => selectPaymentType(event, iouPaymentType, triggerKYCFlow)}
diff --git a/src/components/Skeletons/SearchStatusSkeleton.tsx b/src/components/Skeletons/SearchStatusSkeleton.tsx
new file mode 100644
index 000000000000..bf897233c584
--- /dev/null
+++ b/src/components/Skeletons/SearchStatusSkeleton.tsx
@@ -0,0 +1,96 @@
+import React from 'react';
+import {View} from 'react-native';
+import {Rect} from 'react-native-svg';
+import SkeletonViewContentLoader from '@components/SkeletonViewContentLoader';
+import useTheme from '@hooks/useTheme';
+import useThemeStyles from '@hooks/useThemeStyles';
+
+type SearchStatusSkeletonProps = {
+ shouldAnimate?: boolean;
+};
+
+function SearchStatusSkeleton({shouldAnimate = true}: SearchStatusSkeletonProps) {
+ const theme = useTheme();
+ const styles = useThemeStyles();
+
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+}
+
+SearchStatusSkeleton.displayName = 'SearchStatusSkeleton';
+
+export default SearchStatusSkeleton;
diff --git a/src/components/TaxPicker.tsx b/src/components/TaxPicker.tsx
index c62b84911835..fb7ab9ce53c1 100644
--- a/src/components/TaxPicker.tsx
+++ b/src/components/TaxPicker.tsx
@@ -131,7 +131,7 @@ export default withOnyx({
},
transaction: {
key: ({transactionID, action}) => {
- if (action === CONST.IOU.ACTION.CREATE || IOUUtils.isMovingTransactionFromTrackExpense(action)) {
+ if (IOUUtils.shouldUseTransactionDraft(action)) {
return `${ONYXKEYS.COLLECTION.TRANSACTION_DRAFT}${transactionID}` as `${typeof ONYXKEYS.COLLECTION.TRANSACTION}${string}`;
}
return `${ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`;
diff --git a/src/components/TextInput/BaseTextInput/index.native.tsx b/src/components/TextInput/BaseTextInput/index.native.tsx
index a03e9dbb9aa2..59f205da023f 100644
--- a/src/components/TextInput/BaseTextInput/index.native.tsx
+++ b/src/components/TextInput/BaseTextInput/index.native.tsx
@@ -61,7 +61,6 @@ function BaseTextInput(
prefixCharacter = '',
inputID,
isMarkdownEnabled = false,
- excludedMarkdownStyles = [],
shouldShowClearButton = false,
prefixContainerStyle = [],
prefixStyle = [],
@@ -75,7 +74,7 @@ function BaseTextInput(
const inputProps = {shouldSaveDraft: false, shouldUseDefaultValue: false, ...props};
const theme = useTheme();
const styles = useThemeStyles();
- const markdownStyle = useMarkdownStyle(undefined, excludedMarkdownStyles);
+ const markdownStyle = useMarkdownStyle();
const StyleUtils = useStyleUtils();
const {translate} = useLocalize();
diff --git a/src/components/TextInput/BaseTextInput/index.tsx b/src/components/TextInput/BaseTextInput/index.tsx
index c5471fa11bce..3f55ff97ff02 100644
--- a/src/components/TextInput/BaseTextInput/index.tsx
+++ b/src/components/TextInput/BaseTextInput/index.tsx
@@ -64,7 +64,6 @@ function BaseTextInput(
suffixCharacter = '',
inputID,
isMarkdownEnabled = false,
- excludedMarkdownStyles = [],
shouldShowClearButton = false,
prefixContainerStyle = [],
prefixStyle = [],
@@ -79,7 +78,7 @@ function BaseTextInput(
const theme = useTheme();
const styles = useThemeStyles();
- const markdownStyle = useMarkdownStyle(undefined, excludedMarkdownStyles);
+ const markdownStyle = useMarkdownStyle();
const {hasError = false} = inputProps;
const StyleUtils = useStyleUtils();
const {translate} = useLocalize();
diff --git a/src/components/TextInput/BaseTextInput/types.ts b/src/components/TextInput/BaseTextInput/types.ts
index 32a408e4d348..5b05d9d8613b 100644
--- a/src/components/TextInput/BaseTextInput/types.ts
+++ b/src/components/TextInput/BaseTextInput/types.ts
@@ -1,4 +1,3 @@
-import type {MarkdownStyle} from '@expensify/react-native-live-markdown';
import type {GestureResponderEvent, StyleProp, TextInputProps, TextStyle, ViewStyle} from 'react-native';
import type {AnimatedTextInputRef} from '@components/RNTextInput';
import type IconAsset from '@src/types/utils/IconAsset';
@@ -116,9 +115,6 @@ type CustomBaseTextInputProps = {
/** Should live markdown be enabled. Changes RNTextInput component to RNMarkdownTextInput */
isMarkdownEnabled?: boolean;
- /** List of markdowns that won't be styled as a markdown */
- excludedMarkdownStyles?: Array;
-
/** Whether the clear button should be displayed */
shouldShowClearButton?: boolean;
diff --git a/src/hooks/useActiveWorkspaceFromNavigationState.ts b/src/hooks/useActiveWorkspaceFromNavigationState.ts
index db7d13a00aaa..d2851e83ab6c 100644
--- a/src/hooks/useActiveWorkspaceFromNavigationState.ts
+++ b/src/hooks/useActiveWorkspaceFromNavigationState.ts
@@ -12,22 +12,20 @@ import SCREENS from '@src/SCREENS';
*/
function useActiveWorkspaceFromNavigationState() {
// The last policyID value is always stored in the last route in BottomTabNavigator.
- const activeWorkpsaceID = useNavigationState((state) => {
+ const activeWorkspaceID = useNavigationState((state) => {
// SCREENS.HOME is a screen located in the BottomTabNavigator, if it's not in state.routeNames it means that this hook was called from a screen in another navigator.
if (!state.routeNames.includes(SCREENS.HOME)) {
Log.warn('useActiveWorkspaceFromNavigationState should be called only from BottomTab screens');
}
- const policyID = state.routes.at(-1)?.params?.policyID;
+ const params = state.routes.at(-1)?.params ?? {};
- if (!policyID) {
- return undefined;
+ if ('policyID' in params) {
+ return params?.policyID;
}
-
- return policyID;
});
- return activeWorkpsaceID;
+ return activeWorkspaceID;
}
export default useActiveWorkspaceFromNavigationState;
diff --git a/src/hooks/useFetchRoute.ts b/src/hooks/useFetchRoute.ts
new file mode 100644
index 000000000000..736e99a31d66
--- /dev/null
+++ b/src/hooks/useFetchRoute.ts
@@ -0,0 +1,34 @@
+import {isEqual} from 'lodash';
+import {useEffect} from 'react';
+import type {OnyxEntry} from 'react-native-onyx';
+import * as IOUUtils from '@libs/IOUUtils';
+import * as TransactionUtils from '@libs/TransactionUtils';
+import * as TransactionAction from '@userActions/Transaction';
+import type {IOUAction} from '@src/CONST';
+import type {Transaction} from '@src/types/onyx';
+import type {WaypointCollection} from '@src/types/onyx/Transaction';
+import useNetwork from './useNetwork';
+import usePrevious from './usePrevious';
+
+export default function useFetchRoute(transaction: OnyxEntry, waypoints: WaypointCollection | undefined, action: IOUAction) {
+ const {isOffline} = useNetwork();
+ const hasRouteError = !!transaction?.errorFields?.route;
+ const hasRoute = TransactionUtils.hasRoute(transaction);
+ const isRouteAbsentWithoutErrors = !hasRoute && !hasRouteError;
+ const isLoadingRoute = transaction?.comment?.isLoading ?? false;
+ const validatedWaypoints = TransactionUtils.getValidWaypoints(waypoints);
+ const previousValidatedWaypoints = usePrevious(validatedWaypoints);
+ const haveValidatedWaypointsChanged = !isEqual(previousValidatedWaypoints, validatedWaypoints);
+ const isDistanceRequest = TransactionUtils.isDistanceRequest(transaction);
+ const shouldFetchRoute = isDistanceRequest && (isRouteAbsentWithoutErrors || haveValidatedWaypointsChanged) && !isLoadingRoute && Object.keys(validatedWaypoints).length > 1;
+
+ useEffect(() => {
+ if (isOffline || !shouldFetchRoute || !transaction?.transactionID) {
+ return;
+ }
+
+ TransactionAction.getRoute(transaction.transactionID, validatedWaypoints, IOUUtils.shouldUseTransactionDraft(action));
+ }, [shouldFetchRoute, transaction?.transactionID, validatedWaypoints, isOffline, action]);
+
+ return {shouldFetchRoute, validatedWaypoints};
+}
diff --git a/src/hooks/useMobileSelectionMode.ts b/src/hooks/useMobileSelectionMode.ts
new file mode 100644
index 000000000000..0f2fcb9558f7
--- /dev/null
+++ b/src/hooks/useMobileSelectionMode.ts
@@ -0,0 +1,17 @@
+import {useEffect} from 'react';
+import {useOnyx} from 'react-native-onyx';
+import {turnOffMobileSelectionMode} from '@libs/actions/MobileSelectionMode';
+import ONYXKEYS from '@src/ONYXKEYS';
+
+export default function useMobileSelectionMode(shouldAutoTurnOff = true) {
+ const [selectionMode] = useOnyx(ONYXKEYS.MOBILE_SELECTION_MODE);
+
+ useEffect(() => {
+ if (!shouldAutoTurnOff) {
+ return;
+ }
+ turnOffMobileSelectionMode();
+ }, [shouldAutoTurnOff]);
+
+ return {selectionMode};
+}
diff --git a/src/languages/en.ts b/src/languages/en.ts
index 5fce9017084b..686f5dcf1cc8 100755
--- a/src/languages/en.ts
+++ b/src/languages/en.ts
@@ -2,6 +2,7 @@ import {CONST as COMMON_CONST, Str} from 'expensify-common';
import {startCase} from 'lodash';
import CONST from '@src/CONST';
import type {Country} from '@src/CONST';
+import type {DelegateRole} from '@src/types/onyx/Account';
import type {ConnectionName, PolicyConnectionSyncStage, SageIntacctMappingName} from '@src/types/onyx/Policy';
import type {
AddressLineParams,
@@ -18,6 +19,7 @@ import type {
ChangePolicyParams,
ChangeTypeParams,
CharacterLimitParams,
+ CompanyCardFeedNameParams,
ConfirmHoldExpenseParams,
ConfirmThatParams,
DateShouldBeAfterParams,
@@ -147,6 +149,7 @@ export default {
center: 'Center',
from: 'From',
to: 'To',
+ in: 'In',
optional: 'Optional',
new: 'New',
search: 'Search',
@@ -375,6 +378,7 @@ export default {
filterLogs: 'Filter Logs',
network: 'Network',
reportID: 'Report ID',
+ offlinePrompt: "You can't take this action right now.",
outstanding: 'Outstanding',
days: 'days',
},
@@ -774,9 +778,6 @@ export default {
settlePayment: ({formattedAmount}: SettleExpensifyCardParams) => `Pay ${formattedAmount}`,
settleBusiness: ({formattedAmount}: SettleExpensifyCardParams) => (formattedAmount ? `Pay ${formattedAmount} as a business` : `Pay as a business`),
payElsewhere: ({formattedAmount}: SettleExpensifyCardParams) => (formattedAmount ? `Pay ${formattedAmount} elsewhere` : `Pay elsewhere`),
- settlePersonalBank: ({formattedAmount}: SettleExpensifyCardParams) => (formattedAmount ? `Pay ${formattedAmount} with personal bank account` : `Pay with personal bank account`),
- settleBusinessBank: ({formattedAmount}: SettleExpensifyCardParams) => (formattedAmount ? `Pay ${formattedAmount} with business bank account` : `Pay with business bank account`),
- settleDebitCard: ({formattedAmount}: SettleExpensifyCardParams) => (formattedAmount ? `Pay ${formattedAmount} with debit card` : `Pay with debit card`),
nextStep: 'Next steps',
finished: 'Finished',
sendInvoice: ({amount}: RequestAmountParams) => `Send ${amount} invoice`,
@@ -798,6 +799,7 @@ export default {
payerSettledWithMissingBankAccount: ({amount}: PayerSettledParams) => `paid ${amount}. Add a bank account to receive your payment.`,
approvedAmount: ({amount}: ApprovedAmountParams) => `approved ${amount}`,
forwardedAmount: ({amount}: ForwardedAmountParams) => `approved ${amount}`,
+ rejectedThisReport: 'rejected this report',
waitingOnBankAccount: ({submitterDisplayName}: WaitingOnBankAccountParams) => `started settling up. Payment is on hold until ${submitterDisplayName} adds a bank account.`,
adminCanceledRequest: ({manager, amount}: AdminCanceledRequestParams) => `${manager ? `${manager}: ` : ''}cancelled the ${amount} payment.`,
canceledRequest: ({amount, submitterDisplayName}: CanceledRequestParams) =>
@@ -859,6 +861,7 @@ export default {
expenseOnHold: 'This expense was put on hold. Please review the comments for next steps.',
expensesOnHold: 'All expenses were put on hold. Please review the comments for next steps.',
expenseDuplicate: 'This expense has the same details as another one. Please review the duplicates to remove the hold.',
+ someDuplicatesArePaid: 'Some of these duplicates have been approved or paid already.',
reviewDuplicates: 'Review duplicates',
keepAll: 'Keep all',
confirmApprove: 'Confirm approval amount',
@@ -1116,7 +1119,7 @@ export default {
twoFactorAuthEnabled: 'Two-factor authentication enabled',
whatIsTwoFactorAuth: 'Two-factor authentication (2FA) helps keep your account safe. When logging in, you’ll need to enter a code generated by your preferred authenticator app.',
disableTwoFactorAuth: 'Disable two-factor authentication',
- disableTwoFactorAuthConfirmation: 'Two-factor authentication keeps your account more secure. Are you sure you want to disable it?',
+ explainProcessToRemove: 'In order to disable two-factor authentication (2FA), please enter a valid code from your authentication app.',
disabled: 'Two-factor authentication is now disabled',
noAuthenticatorApp: 'You’ll no longer require an authenticator app to log into Expensify.',
stepCodes: 'Recovery codes',
@@ -1348,6 +1351,11 @@ export default {
approverInMultipleWorkflows: 'This member already belongs to another approval workflow. Any updates here will reflect there too.',
approverCircularReference: ({name1, name2}: ApprovalWorkflowErrorParams) =>
`${name1} already approves reports to ${name2}. Please choose a different approver to avoid a circular workflow.`,
+ emptyContent: {
+ title: 'No members to display',
+ expensesFromSubtitle: 'All workspace members already belong to an existing approval workflow.',
+ approverSubtitle: 'All approvers belong to an existing workflow.',
+ },
},
workflowsDelayedSubmissionPage: {
autoReportingErrorMessage: "Delayed submission couldn't be changed. Please try again or contact support.",
@@ -2116,9 +2124,9 @@ export default {
common: {
card: 'Cards',
expensifyCard: 'Expensify Card',
+ companyCards: 'Company Cards',
workflows: 'Workflows',
workspace: 'Workspace',
- companyCards: 'Company cards',
edit: 'Edit workspace',
enabled: 'Enabled',
disabled: 'Disabled',
@@ -2751,6 +2759,10 @@ export default {
control: 'Control',
collect: 'Collect',
},
+ companyCards: {
+ addCompanyCards: 'Add company cards',
+ selectCardFeed: 'Select card feed',
+ },
expensifyCard: {
issueAndManageCards: 'Issue and manage your Expensify Cards',
getStartedIssuing: 'Get started by issuing your first virtual or physical card.',
@@ -2885,10 +2897,38 @@ export default {
},
companyCards: {
title: 'Company Cards',
- subtitle: 'Import spend from existing company cards',
+ subtitle: 'Import spend from existing company cards.',
+ feed: {
+ title: 'Import company cards',
+ features: {
+ support: 'Support for all major card providers',
+ assignCards: 'Assign cards to the entire team',
+ automaticImport: 'Automatic transaction import',
+ },
+ ctaTitle: 'Add company cards',
+ },
disableCardTitle: 'Disable Company Cards',
disableCardPrompt: 'You can’t disable company cards because this feature is in use. Reach out to the Concierge for next steps.',
disableCardButton: 'Chat with Concierge',
+ assignCard: 'Assign card',
+ cardFeedName: 'Card feed name',
+ cardFeedNameDescription: 'Give the card feed a unique name so you can tell it apart from the others.',
+ cardFeedTransaction: 'Delete transactions',
+ cardFeedTransactionDescription: 'Choose whether cardholders can delete card transactions. New transactions will follow these rules.',
+ cardFeedRestrictDeletingTransaction: 'Restrict deleting transactions',
+ cardFeedAllowDeletingTransaction: 'Allow deleting transactions',
+ removeCardFeed: 'Remove card feed',
+ removeCardFeedTitle: ({feedName}: CompanyCardFeedNameParams) => `Remove ${feedName} feed`,
+ removeCardFeedDescription: 'Are you sure you want to remove this card feed? This will unassign all cards.',
+ error: {
+ feedNameRequired: 'Card feed name is required.',
+ },
+ corporate: 'Restrict deleting transactions',
+ personal: 'Allow deleting transactions',
+ setFeedNameDescription: 'Give the card feed a unique name so you can tell it apart from the others.',
+ setTransactionLiabilityDescription: 'When enabled, cardholders can delete card transactions. New transactions will follow this rule.',
+ emptyAddedFeedTitle: 'Assign company cards',
+ emptyAddedFeedDescription: 'Get started by assigning your first card to a member.',
},
workflows: {
title: 'Workflows',
@@ -3758,6 +3798,7 @@ export default {
search: {
resultsAreLimited: 'Search results are limited.',
viewResults: 'View results',
+ resetFilters: 'Reset filters',
searchResults: {
emptyResults: {
title: 'Nothing to show',
@@ -3776,7 +3817,6 @@ export default {
unhold: 'Unhold',
noOptionsAvailable: 'No options available for the selected group of expenses.',
},
- offlinePrompt: "You can't take this action right now.",
filtersHeader: 'Filters',
filters: {
date: {
@@ -3787,6 +3827,8 @@ export default {
keyword: 'Keyword',
hasKeywords: 'Has keywords',
currency: 'Currency',
+ has: 'Has',
+ link: 'Link',
amount: {
lessThan: (amount?: string) => `Less than ${amount ?? ''}`,
greaterThan: (amount?: string) => `Greater than ${amount ?? ''}`,
@@ -4488,4 +4530,18 @@ export default {
updateRoomDescription: 'set the room description to:',
clearRoomDescription: 'cleared the room description',
},
+ delegate: {
+ switchAccount: 'Switch accounts:',
+ role: (role: DelegateRole): string => {
+ switch (role) {
+ case CONST.DELEGATE_ROLE.ALL:
+ return 'Full';
+ case CONST.DELEGATE_ROLE.SUBMITTER:
+ return 'Limited';
+ default:
+ return '';
+ }
+ },
+ genericError: 'Oops, something went wrong. Please try again.',
+ },
} satisfies TranslationBase;
diff --git a/src/languages/es.ts b/src/languages/es.ts
index 0966c6524b00..85a705489141 100644
--- a/src/languages/es.ts
+++ b/src/languages/es.ts
@@ -1,5 +1,6 @@
import {Str} from 'expensify-common';
import CONST from '@src/CONST';
+import type {DelegateRole} from '@src/types/onyx/Account';
import type {ConnectionName, PolicyConnectionSyncStage, SageIntacctMappingName} from '@src/types/onyx/Policy';
import type {
AddressLineParams,
@@ -16,6 +17,7 @@ import type {
ChangePolicyParams,
ChangeTypeParams,
CharacterLimitParams,
+ CompanyCardFeedNameParams,
ConfirmHoldExpenseParams,
ConfirmThatParams,
DateShouldBeAfterParams,
@@ -136,6 +138,7 @@ export default {
attachment: 'Archivo adjunto',
from: 'De',
to: 'A',
+ in: 'En',
optional: 'Opcional',
new: 'Nuevo',
center: 'Centrar',
@@ -365,6 +368,7 @@ export default {
filterLogs: 'Registros de filtrado',
network: 'La red',
reportID: 'ID del informe',
+ offlinePrompt: 'No puedes realizar esta acción ahora mismo.',
outstanding: 'Pendiente',
days: 'días',
},
@@ -767,11 +771,6 @@ export default {
settlePayment: ({formattedAmount}: SettleExpensifyCardParams) => `Pagar ${formattedAmount}`,
settleBusiness: ({formattedAmount}: SettleExpensifyCardParams) => (formattedAmount ? `Pagar ${formattedAmount} como negocio` : `Pagar como empresa`),
payElsewhere: ({formattedAmount}: SettleExpensifyCardParams) => (formattedAmount ? `Pagar ${formattedAmount} de otra forma` : `Pagar de otra forma`),
- settlePersonalBank: ({formattedAmount}: SettleExpensifyCardParams) =>
- formattedAmount ? `Pagar ${formattedAmount} con cuenta bancaria personal` : `Pagar con cuenta bancaria personal`,
- settleBusinessBank: ({formattedAmount}: SettleExpensifyCardParams) =>
- formattedAmount ? `Pagar ${formattedAmount} con cuenta bancaria comercial` : `Pagar con cuenta bancaria comercial`,
- settleDebitCard: ({formattedAmount}: SettleExpensifyCardParams) => (formattedAmount ? `Pagar ${formattedAmount} con tarjeta de débito` : `Pagar con tarjeta de débito`),
nextStep: 'Pasos siguientes',
finished: 'Finalizado',
sendInvoice: ({amount}: RequestAmountParams) => `Enviar factura de ${amount}`,
@@ -793,6 +792,7 @@ export default {
payerSettledWithMissingBankAccount: ({amount}: PayerSettledParams) => `pagó ${amount}. Agrega una cuenta bancaria para recibir tu pago.`,
approvedAmount: ({amount}: ApprovedAmountParams) => `aprobó ${amount}`,
forwardedAmount: ({amount}: ForwardedAmountParams) => `aprobó ${amount}`,
+ rejectedThisReport: 'rechazó este informe',
waitingOnBankAccount: ({submitterDisplayName}: WaitingOnBankAccountParams) => `inició el pago, pero no se procesará hasta que ${submitterDisplayName} añada una cuenta bancaria`,
adminCanceledRequest: ({manager, amount}: AdminCanceledRequestParams) => `${manager ? `${manager}: ` : ''}canceló el pago de ${amount}.`,
canceledRequest: ({amount, submitterDisplayName}: CanceledRequestParams) =>
@@ -854,6 +854,7 @@ export default {
expenseOnHold: 'Este gasto está bloqueado. Revisa los comentarios para saber como proceder.',
expensesOnHold: 'Todos los gastos quedaron bloqueados. Revisa los comentarios para saber como proceder.',
expenseDuplicate: 'Esta solicitud tiene los mismos detalles que otra. Revisa los duplicados para eliminar el bloqueo.',
+ someDuplicatesArePaid: 'Algunos de estos duplicados ya han sido aprobados o pagados.',
reviewDuplicates: 'Revisar duplicados',
keepAll: 'Mantener todos',
confirmApprove: 'Confirmar importe a aprobar',
@@ -1123,7 +1124,7 @@ export default {
whatIsTwoFactorAuth:
'La autenticación de dos factores (2FA) ayuda a mantener tu cuenta segura. Al iniciar sesión, deberás ingresar un código generado por tu aplicación de autenticación preferida.',
disableTwoFactorAuth: 'Deshabilitar la autenticación de dos factores',
- disableTwoFactorAuthConfirmation: 'La autenticación de dos factores mantiene tu cuenta más segura. ¿Estás seguro de que quieres desactivarla?',
+ explainProcessToRemove: 'Para deshabilitar la autenticación de dos factores (2FA), por favor introduce un código válido de tu aplicación de autenticación.',
disabled: 'La autenticación de dos factores está ahora deshabilitada',
noAuthenticatorApp: 'Ya no necesitarás una aplicación de autenticación para iniciar sesión en Expensify.',
stepCodes: 'Códigos de recuperación',
@@ -1356,6 +1357,11 @@ export default {
approverInMultipleWorkflows: 'Este miembro ya pertenece a otro flujo de aprobación. Cualquier actualización aquí se reflejará allí también.',
approverCircularReference: ({name1, name2}: ApprovalWorkflowErrorParams) =>
`${name1} ya aprueba informes a ${name2}. Por favor, elige un aprobador diferente para evitar un flujo de trabajo circular.`,
+ emptyContent: {
+ title: 'No hay miembros para mostrar',
+ expensesFromSubtitle: 'Todos los miembros del espacio de trabajo ya pertenecen a un flujo de aprobación existente.',
+ approverSubtitle: 'Todos los aprobadores pertenecen a un flujo de trabajo existente.',
+ },
},
workflowsDelayedSubmissionPage: {
autoReportingErrorMessage: 'El parámetro de envío retrasado no pudo ser cambiado. Por favor, inténtelo de nuevo o contacte al soporte.',
@@ -2149,9 +2155,9 @@ export default {
common: {
card: 'Tarjetas',
expensifyCard: 'Tarjeta Expensify',
+ companyCards: 'Tarjetas de empresa',
workflows: 'Flujos de trabajo',
workspace: 'Espacio de trabajo',
- companyCards: 'Tarjetas de empresa',
edit: 'Editar espacio de trabajo',
enabled: 'Activada',
disabled: 'Desactivada',
@@ -2798,6 +2804,10 @@ export default {
control: 'Control',
collect: 'Recolectar',
},
+ companyCards: {
+ addCompanyCards: 'Agregar tarjetas de empresa',
+ selectCardFeed: 'Seleccionar feed de tarjetas',
+ },
expensifyCard: {
issueAndManageCards: 'Emitir y gestionar Tarjetas Expensify',
getStartedIssuing: 'Empieza emitiendo tu primera tarjeta virtual o física.',
@@ -2932,9 +2942,38 @@ export default {
companyCards: {
title: 'Tarjetas de empresa',
subtitle: 'Importar gastos de las tarjetas de empresa existentes.',
+ feed: {
+ title: 'Importar tarjetas de empresa',
+ features: {
+ support: 'Compatibilidad con los principales proveedores de tarjetas',
+ assignCards: 'Asignar tarjetas a todo el equipo',
+ automaticImport: 'Importación automática de transacciones',
+ },
+ ctaTitle: 'Añadir tarjetas de empresa',
+ },
disableCardTitle: 'Deshabilitar tarjetas de empresa',
disableCardPrompt: 'No puedes deshabilitar las tarjetas de empresa porque esta función está en uso. Por favor, contacta a Concierge para los próximos pasos.',
disableCardButton: 'Chatear con Concierge',
+ assignCard: 'Asignar tarjeta',
+ cardFeedName: 'Nombre del feed de tarjeta',
+ cardFeedNameDescription: 'Dale al feed de tarjeta un nombre único para que puedas distinguirlo de los demás.',
+ cardFeedTransaction: 'Eliminar transacciones',
+ cardFeedTransactionDescription: 'Elige si los titulares de tarjetas pueden eliminar transacciones de tarjetas. Las nuevas transacciones seguirán estas reglas.',
+ cardFeedRestrictDeletingTransaction: 'Restringir eliminación de transacciones',
+ cardFeedAllowDeletingTransaction: 'Permitir eliminación de transacciones',
+ removeCardFeed: 'Quitar la alimentación de tarjetas',
+ removeCardFeedTitle: ({feedName}: CompanyCardFeedNameParams) => `Eliminar el feed de ${feedName}`,
+ removeCardFeedDescription: '¿Estás seguro de que deseas eliminar esta fuente de tarjetas? Esto anulará la asignación de todas las tarjetas.',
+ error: {
+ feedNameRequired: 'Se requiere el nombre de la fuente de la tarjeta.',
+ },
+ corporate: 'Restringir eliminación de transacciones',
+ personal: 'Permitir eliminación de transacciones',
+ setFeedNameDescription: 'Dale al feed de tarjeta un nombre único para que puedas distinguirlo de los demás.',
+ setTransactionLiabilityDescription:
+ 'Cuando está habilitada, los titulares de tarjetas pueden eliminar transacciones con tarjeta. Las transacciones nuevas seguirán esta regla.',
+ emptyAddedFeedTitle: 'Asignar tarjetas de empresa',
+ emptyAddedFeedDescription: 'Comienza asignando tu primera tarjeta a un miembro.',
},
distanceRates: {
title: 'Tasas de distancia',
@@ -3810,6 +3849,7 @@ export default {
search: {
resultsAreLimited: 'Los resultados de búsqueda están limitados.',
viewResults: 'Ver resultados',
+ resetFilters: 'Restablecer filtros',
searchResults: {
emptyResults: {
title: 'No hay nada que ver aquí',
@@ -3828,7 +3868,6 @@ export default {
unhold: 'Desbloquear',
noOptionsAvailable: 'No hay opciones disponibles para el grupo de gastos seleccionado.',
},
- offlinePrompt: 'No puedes realizar esta acción ahora mismo.',
filtersHeader: 'Filtros',
filters: {
date: {
@@ -3839,6 +3878,8 @@ export default {
keyword: 'Palabra clave',
hasKeywords: 'Tiene palabras clave',
currency: 'Divisa',
+ has: 'Tiene',
+ link: 'Enlace',
amount: {
lessThan: (amount?: string) => `Menos de ${amount ?? ''}`,
greaterThan: (amount?: string) => `Más que ${amount ?? ''}`,
@@ -5006,4 +5047,18 @@ export default {
updateRoomDescription: 'establece la descripción de la sala a:',
clearRoomDescription: 'la descripción de la habitación ha sido borrada',
},
+ delegate: {
+ switchAccount: 'Cambiar de cuenta:',
+ role: (role: DelegateRole): string => {
+ switch (role) {
+ case CONST.DELEGATE_ROLE.ALL:
+ return 'Completo';
+ case CONST.DELEGATE_ROLE.SUBMITTER:
+ return 'Limitado';
+ default:
+ return '';
+ }
+ },
+ genericError: '¡Ups! Ha ocurrido un error. Por favor, inténtalo de nuevo.',
+ },
} satisfies EnglishTranslation;
diff --git a/src/languages/types.ts b/src/languages/types.ts
index d1d44fdf9962..c4c6b25b06d0 100644
--- a/src/languages/types.ts
+++ b/src/languages/types.ts
@@ -103,7 +103,6 @@ type RequestCountParams = {
type SettleExpensifyCardParams = {
formattedAmount: string;
- available?: boolean;
};
type RequestAmountParams = {amount: string};
@@ -120,6 +119,8 @@ type PayerOwesAmountParams = {payer: string; amount: number | string; comment?:
type PayerOwesParams = {payer: string};
+type CompanyCardFeedNameParams = {feedName: string};
+
type PayerPaidAmountParams = {payer?: string; amount: number | string};
type ApprovedAmountParams = {amount: number | string};
@@ -372,6 +373,7 @@ export type {
CharacterLimitParams,
ConfirmHoldExpenseParams,
ConfirmThatParams,
+ CompanyCardFeedNameParams,
DateShouldBeAfterParams,
DateShouldBeBeforeParams,
DeleteActionParams,
diff --git a/src/libs/API/parameters/AddNewContactMethodParams.ts b/src/libs/API/parameters/AddNewContactMethodParams.ts
index f5cd7824c191..d362ca2db977 100644
--- a/src/libs/API/parameters/AddNewContactMethodParams.ts
+++ b/src/libs/API/parameters/AddNewContactMethodParams.ts
@@ -1,3 +1,3 @@
-type AddNewContactMethodParams = {partnerUserID: string};
+type AddNewContactMethodParams = {partnerUserID: string; validateCode: string};
export default AddNewContactMethodParams;
diff --git a/src/libs/API/parameters/CardDeactivateParams.ts b/src/libs/API/parameters/CardDeactivateParams.ts
new file mode 100644
index 000000000000..e329d02a0c9e
--- /dev/null
+++ b/src/libs/API/parameters/CardDeactivateParams.ts
@@ -0,0 +1,6 @@
+type CardDeactivateParams = {
+ authToken: string;
+ cardID: number;
+};
+
+export default CardDeactivateParams;
diff --git a/src/libs/API/parameters/ConnectAsDelegateParams.ts b/src/libs/API/parameters/ConnectAsDelegateParams.ts
new file mode 100644
index 000000000000..6bd2c666e1b0
--- /dev/null
+++ b/src/libs/API/parameters/ConnectAsDelegateParams.ts
@@ -0,0 +1,5 @@
+type ConnectAsDelegateParams = {
+ to: string;
+};
+
+export default ConnectAsDelegateParams;
diff --git a/src/libs/API/parameters/DeleteCompanyCardFeed.ts b/src/libs/API/parameters/DeleteCompanyCardFeed.ts
new file mode 100644
index 000000000000..aaf5a1c4ffc3
--- /dev/null
+++ b/src/libs/API/parameters/DeleteCompanyCardFeed.ts
@@ -0,0 +1,7 @@
+type DeleteCompanyCardFeed = {
+ authToken?: string | null;
+ policyID: string;
+ bankName: string;
+};
+
+export default DeleteCompanyCardFeed;
diff --git a/src/libs/API/parameters/DisableTwoFactorAuthParams.ts b/src/libs/API/parameters/DisableTwoFactorAuthParams.ts
new file mode 100644
index 000000000000..04fd378272fb
--- /dev/null
+++ b/src/libs/API/parameters/DisableTwoFactorAuthParams.ts
@@ -0,0 +1,5 @@
+type DisableTwoFactorAuthParams = {
+ twoFactorAuthCode: string;
+};
+
+export default DisableTwoFactorAuthParams;
diff --git a/src/libs/API/parameters/RequestExpensifyCardLimitIncreaseParams.ts b/src/libs/API/parameters/RequestExpensifyCardLimitIncreaseParams.ts
index 6e118f2a1c06..f50bf221ad09 100644
--- a/src/libs/API/parameters/RequestExpensifyCardLimitIncreaseParams.ts
+++ b/src/libs/API/parameters/RequestExpensifyCardLimitIncreaseParams.ts
@@ -1,6 +1,6 @@
type RequestExpensifyCardLimitIncreaseParams = {
authToken: string | null | undefined;
- settlementBankAccountID: string;
+ settlementBankAccountID: number;
};
export default RequestExpensifyCardLimitIncreaseParams;
diff --git a/src/libs/API/parameters/Search.ts b/src/libs/API/parameters/Search.ts
index 530388dc7f47..64bfc5baf5a1 100644
--- a/src/libs/API/parameters/Search.ts
+++ b/src/libs/API/parameters/Search.ts
@@ -3,8 +3,6 @@ import type {SearchQueryString} from '@components/Search/types';
type SearchParams = {
hash: number;
jsonQuery: SearchQueryString;
- // Tod this is temporary, remove top level policyIDs as part of: https://github.com/Expensify/App/issues/46592
- policyIDs?: string;
};
export default SearchParams;
diff --git a/src/libs/API/parameters/SetCompanyCardFeedName.ts b/src/libs/API/parameters/SetCompanyCardFeedName.ts
new file mode 100644
index 000000000000..3db87f52bff2
--- /dev/null
+++ b/src/libs/API/parameters/SetCompanyCardFeedName.ts
@@ -0,0 +1,8 @@
+type SetCompanyCardFeedName = {
+ authToken?: string | null;
+ policyID: string;
+ bankName: string;
+ userDefinedName: string;
+};
+
+export default SetCompanyCardFeedName;
diff --git a/src/libs/API/parameters/SetCompanyCardTransactionLiability.ts b/src/libs/API/parameters/SetCompanyCardTransactionLiability.ts
new file mode 100644
index 000000000000..277a9d9bbdbf
--- /dev/null
+++ b/src/libs/API/parameters/SetCompanyCardTransactionLiability.ts
@@ -0,0 +1,7 @@
+type SetCompanyCardTransactionLiability = {
+ authToken?: string | null;
+ bankName: string;
+ liabilityType: string;
+};
+
+export default SetCompanyCardTransactionLiability;
diff --git a/src/libs/API/parameters/UpdateCardSettlementAccountParams.ts b/src/libs/API/parameters/UpdateCardSettlementAccountParams.ts
new file mode 100644
index 000000000000..8af37bb9d4ce
--- /dev/null
+++ b/src/libs/API/parameters/UpdateCardSettlementAccountParams.ts
@@ -0,0 +1,6 @@
+type UpdateCardSettlementAccountParams = {
+ domainName: string;
+ settlementBankAccountID: number;
+};
+
+export default UpdateCardSettlementAccountParams;
diff --git a/src/libs/API/parameters/UpdateCardSettlementFrequencyParams.ts b/src/libs/API/parameters/UpdateCardSettlementFrequencyParams.ts
new file mode 100644
index 000000000000..6085713bac97
--- /dev/null
+++ b/src/libs/API/parameters/UpdateCardSettlementFrequencyParams.ts
@@ -0,0 +1,9 @@
+import type {ValueOf} from 'type-fest';
+import type CONST from '@src/CONST';
+
+type UpdateCardSettlementFrequencyParams = {
+ settlementFrequency: ValueOf;
+ workspaceAccountID: number;
+};
+
+export default UpdateCardSettlementFrequencyParams;
diff --git a/src/libs/API/parameters/index.ts b/src/libs/API/parameters/index.ts
index 7083823d871e..997ee7dc1fc9 100644
--- a/src/libs/API/parameters/index.ts
+++ b/src/libs/API/parameters/index.ts
@@ -84,6 +84,7 @@ export type {default as ValidateBankAccountWithTransactionsParams} from './Valid
export type {default as ValidateLoginParams} from './ValidateLoginParams';
export type {default as ValidateSecondaryLoginParams} from './ValidateSecondaryLoginParams';
export type {default as ValidateTwoFactorAuthParams} from './ValidateTwoFactorAuthParams';
+export type {default as DisableTwoFactorAuthParams} from './DisableTwoFactorAuthParams';
export type {default as VerifyIdentityForBankAccountParams} from './VerifyIdentityForBankAccountParams';
export type {default as AnswerQuestionsForWalletParams} from './AnswerQuestionsForWalletParams';
export type {default as AddCommentOrAttachementParams} from './AddCommentOrAttachementParams';
@@ -272,6 +273,7 @@ export type {default as ExportSearchItemsToCSVParams} from './ExportSearchItemsT
export type {default as UpdateExpensifyCardLimitParams} from './UpdateExpensifyCardLimitParams';
export type {CreateWorkspaceApprovalParams, UpdateWorkspaceApprovalParams, RemoveWorkspaceApprovalParams} from './WorkspaceApprovalParams';
export type {default as StartIssueNewCardFlowParams} from './StartIssueNewCardFlowParams';
+export type {default as ConnectAsDelegateParams} from './ConnectAsDelegateParams';
export type {default as SetPolicyRulesEnabledParams} from './SetPolicyRulesEnabledParams';
export type {default as SetPolicyExpenseMaxAmountNoReceipt} from './SetPolicyExpenseMaxAmountNoReceipt';
export type {default as SetPolicyExpenseMaxAmount} from './SetPolicyExpenseMaxAmount';
@@ -284,5 +286,11 @@ export type {default as UpdateExpensifyCardTitleParams} from './UpdateExpensifyC
export type {default as OpenCardDetailsPageParams} from './OpenCardDetailsPageParams';
export type {default as EnablePolicyCompanyCardsParams} from './EnablePolicyCompanyCardsParams';
export type {default as ToggleCardContinuousReconciliationParams} from './ToggleCardContinuousReconciliationParams';
+export type {default as CardDeactivateParams} from './CardDeactivateParams';
export type {default as UpdateExpensifyCardLimitTypeParams} from './UpdateExpensifyCardLimitTypeParams';
export type {default as UpdateXeroGenericTypeParams} from './UpdateXeroGenericTypeParams';
+export type {default as UpdateCardSettlementFrequencyParams} from './UpdateCardSettlementFrequencyParams';
+export type {default as UpdateCardSettlementAccountParams} from './UpdateCardSettlementAccountParams';
+export type {default as SetCompanyCardFeedName} from './SetCompanyCardFeedName';
+export type {default as DeleteCompanyCardFeed} from './DeleteCompanyCardFeed';
+export type {default as SetCompanyCardTransactionLiability} from './SetCompanyCardTransactionLiability';
diff --git a/src/libs/API/types.ts b/src/libs/API/types.ts
index 4e55de0640b3..b0a6256afd7b 100644
--- a/src/libs/API/types.ts
+++ b/src/libs/API/types.ts
@@ -34,6 +34,7 @@ const WRITE_COMMANDS = {
UPDATE_EXPENSIFY_CARD_LIMIT: 'UpdateExpensifyCardLimit',
UPDATE_EXPENSIFY_CARD_TITLE: 'UpdateExpensifyCardTitle',
UPDATE_EXPENSIFY_CARD_LIMIT_TYPE: 'UpdateExpensifyCardLimitType',
+ CARD_DEACTIVATE: 'Card_Deactivate',
CHRONOS_REMOVE_OOO_EVENT: 'Chronos_RemoveOOOEvent',
MAKE_DEFAULT_PAYMENT_METHOD: 'MakeDefaultPaymentMethod',
ADD_PAYMENT_CARD: 'AddPaymentCard',
@@ -89,6 +90,7 @@ const WRITE_COMMANDS = {
CONNECT_BANK_ACCOUNT_WITH_PLAID: 'ConnectBankAccountWithPlaid',
ADD_PERSONAL_BANK_ACCOUNT: 'AddPersonalBankAccount',
RESTART_BANK_ACCOUNT_SETUP: 'RestartBankAccountSetup',
+ RESEND_VALIDATE_CODE: 'ResendValidateCode',
OPT_IN_TO_PUSH_NOTIFICATIONS: 'OptInToPushNotifications',
OPT_OUT_OF_PUSH_NOTIFICATIONS: 'OptOutOfPushNotifications',
READ_NEWEST_ACTION: 'ReadNewestAction',
@@ -341,10 +343,15 @@ const WRITE_COMMANDS = {
CREATE_EXPENSIFY_CARD: 'CreateExpensifyCard',
CREATE_ADMIN_ISSUED_VIRTUAL_CARD: 'CreateAdminIssuedVirtualCard',
TOGGLE_CARD_CONTINUOUS_RECONCILIATION: 'ToggleCardContinuousReconciliation',
+ UPDATE_CARD_SETTLEMENT_FREQUENCY: 'UpdateCardSettlementFrequency',
+ UPDATE_CARD_SETTLEMENT_ACCOUNT: 'UpdateCardSettlementAccount',
UPDATE_XERO_IMPORT_TRACKING_CATEGORIES: 'UpdateXeroImportTrackingCategories',
UPDATE_XERO_IMPORT_TAX_RATES: 'UpdateXeroImportTaxRates',
UPDATE_XERO_TENANT_ID: 'UpdateXeroTenantID',
UPDATE_XERO_MAPPING: 'UpdateXeroMappings',
+ SET_COMPANY_CARD_FEED_NAME: 'SetFeedName',
+ DELETE_COMPANY_CARD_FEED: 'RemoveFeed',
+ SET_COMPANY_CARD_TRANSACTION_LIABILITY: 'SetFeedTransactionLiability',
} as const;
type WriteCommand = ValueOf;
@@ -370,6 +377,7 @@ type WriteCommandParameters = {
[WRITE_COMMANDS.UPDATE_EXPENSIFY_CARD_LIMIT]: Parameters.UpdateExpensifyCardLimitParams;
[WRITE_COMMANDS.UPDATE_EXPENSIFY_CARD_TITLE]: Parameters.UpdateExpensifyCardTitleParams;
[WRITE_COMMANDS.UPDATE_EXPENSIFY_CARD_LIMIT_TYPE]: Parameters.UpdateExpensifyCardLimitTypeParams;
+ [WRITE_COMMANDS.CARD_DEACTIVATE]: Parameters.CardDeactivateParams;
[WRITE_COMMANDS.MAKE_DEFAULT_PAYMENT_METHOD]: Parameters.MakeDefaultPaymentMethodParams;
[WRITE_COMMANDS.ADD_PAYMENT_CARD]: Parameters.AddPaymentCardParams;
[WRITE_COMMANDS.ADD_PAYMENT_CARD_GBP]: Parameters.AddPaymentCardParams;
@@ -401,6 +409,9 @@ type WriteCommandParameters = {
[WRITE_COMMANDS.UPDATE_STATUS]: Parameters.UpdateStatusParams;
[WRITE_COMMANDS.CLEAR_STATUS]: null;
[WRITE_COMMANDS.UPDATE_PERSONAL_DETAILS_FOR_WALLET]: Parameters.UpdatePersonalDetailsForWalletParams;
+ [WRITE_COMMANDS.SET_COMPANY_CARD_FEED_NAME]: Parameters.SetCompanyCardFeedName;
+ [WRITE_COMMANDS.DELETE_COMPANY_CARD_FEED]: Parameters.DeleteCompanyCardFeed;
+ [WRITE_COMMANDS.SET_COMPANY_CARD_TRANSACTION_LIABILITY]: Parameters.SetCompanyCardTransactionLiability;
[WRITE_COMMANDS.VERIFY_IDENTITY]: Parameters.VerifyIdentityParams;
[WRITE_COMMANDS.ACCEPT_WALLET_TERMS]: Parameters.AcceptWalletTermsParams;
[WRITE_COMMANDS.ANSWER_QUESTIONS_FOR_WALLET]: Parameters.AnswerQuestionsForWalletParams;
@@ -415,7 +426,7 @@ type WriteCommandParameters = {
[WRITE_COMMANDS.REQUEST_UNLINK_VALIDATION_LINK]: Parameters.RequestUnlinkValidationLinkParams;
[WRITE_COMMANDS.UNLINK_LOGIN]: Parameters.UnlinkLoginParams;
[WRITE_COMMANDS.ENABLE_TWO_FACTOR_AUTH]: null;
- [WRITE_COMMANDS.DISABLE_TWO_FACTOR_AUTH]: null;
+ [WRITE_COMMANDS.DISABLE_TWO_FACTOR_AUTH]: Parameters.DisableTwoFactorAuthParams;
[WRITE_COMMANDS.ADD_COMMENT]: Parameters.AddCommentOrAttachementParams;
[WRITE_COMMANDS.ADD_ATTACHMENT]: Parameters.AddCommentOrAttachementParams;
[WRITE_COMMANDS.ADD_TEXT_AND_ATTACHMENT]: Parameters.AddCommentOrAttachementParams;
@@ -423,6 +434,7 @@ type WriteCommandParameters = {
[WRITE_COMMANDS.ADD_PERSONAL_BANK_ACCOUNT]: Parameters.AddPersonalBankAccountParams;
[WRITE_COMMANDS.RESTART_BANK_ACCOUNT_SETUP]: Parameters.RestartBankAccountSetupParams;
[WRITE_COMMANDS.OPT_IN_TO_PUSH_NOTIFICATIONS]: Parameters.OptInOutToPushNotificationsParams;
+ [WRITE_COMMANDS.RESEND_VALIDATE_CODE]: null;
[WRITE_COMMANDS.OPT_OUT_OF_PUSH_NOTIFICATIONS]: Parameters.OptInOutToPushNotificationsParams;
[WRITE_COMMANDS.READ_NEWEST_ACTION]: Parameters.ReadNewestActionParams;
[WRITE_COMMANDS.MARK_AS_UNREAD]: Parameters.MarkAsUnreadParams;
@@ -692,6 +704,8 @@ type WriteCommandParameters = {
[WRITE_COMMANDS.CREATE_EXPENSIFY_CARD]: Parameters.CreateExpensifyCardParams;
[WRITE_COMMANDS.CREATE_ADMIN_ISSUED_VIRTUAL_CARD]: Omit;
[WRITE_COMMANDS.TOGGLE_CARD_CONTINUOUS_RECONCILIATION]: Parameters.ToggleCardContinuousReconciliationParams;
+ [WRITE_COMMANDS.UPDATE_CARD_SETTLEMENT_FREQUENCY]: Parameters.UpdateCardSettlementFrequencyParams;
+ [WRITE_COMMANDS.UPDATE_CARD_SETTLEMENT_ACCOUNT]: Parameters.UpdateCardSettlementAccountParams;
// Xero API
[WRITE_COMMANDS.UPDATE_XERO_TENANT_ID]: Parameters.UpdateXeroGenericTypeParams;
@@ -825,8 +839,9 @@ const SIDE_EFFECT_REQUEST_COMMANDS = {
RECONNECT_APP: 'ReconnectApp',
ADD_PAYMENT_CARD_GBP: 'AddPaymentCardGBP',
REVEAL_EXPENSIFY_CARD_DETAILS: 'RevealExpensifyCardDetails',
- SWITCH_TO_OLD_DOT: 'SwitchToOldDot',
TWO_FACTOR_AUTH_VALIDATE: 'TwoFactorAuth_Validate',
+ CONNECT_AS_DELEGATE: 'ConnectAsDelegate',
+ DISCONNECT_AS_DELEGATE: 'DisconnectAsDelegate',
} as const;
type SideEffectRequestCommand = ValueOf;
@@ -843,6 +858,8 @@ type SideEffectRequestCommandParameters = {
[SIDE_EFFECT_REQUEST_COMMANDS.ADD_PAYMENT_CARD_GBP]: Parameters.AddPaymentCardParams;
[SIDE_EFFECT_REQUEST_COMMANDS.ACCEPT_SPOTNANA_TERMS]: null;
[SIDE_EFFECT_REQUEST_COMMANDS.TWO_FACTOR_AUTH_VALIDATE]: Parameters.ValidateTwoFactorAuthParams;
+ [SIDE_EFFECT_REQUEST_COMMANDS.CONNECT_AS_DELEGATE]: Parameters.ConnectAsDelegateParams;
+ [SIDE_EFFECT_REQUEST_COMMANDS.DISCONNECT_AS_DELEGATE]: EmptyObject;
};
type ApiRequestCommandParameters = WriteCommandParameters & ReadCommandParameters & SideEffectRequestCommandParameters;
diff --git a/src/libs/CardUtils.ts b/src/libs/CardUtils.ts
index e0041dde9934..2398bc1e729a 100644
--- a/src/libs/CardUtils.ts
+++ b/src/libs/CardUtils.ts
@@ -2,12 +2,14 @@ import lodash from 'lodash';
import Onyx from 'react-native-onyx';
import type {OnyxEntry} from 'react-native-onyx';
import type {ValueOf} from 'type-fest';
+import * as Illustrations from '@src/components/Icon/Illustrations';
import CONST from '@src/CONST';
import type {TranslationPaths} from '@src/languages/types';
import type {OnyxValues} from '@src/ONYXKEYS';
import ONYXKEYS from '@src/ONYXKEYS';
import type {BankAccountList, Card, CardList, PersonalDetailsList, WorkspaceCardsList} from '@src/types/onyx';
import {isEmptyObject} from '@src/types/utils/EmptyObject';
+import type IconAsset from '@src/types/utils/IconAsset';
import localeCompare from './LocaleCompare';
import * as Localize from './Localize';
import * as PersonalDetailsUtils from './PersonalDetailsUtils';
@@ -180,6 +182,18 @@ function sortCardsByCardholderName(cardsList: OnyxEntry, per
});
}
+function getCardFeedIcon(cardFeed: string): IconAsset {
+ if (cardFeed.startsWith(CONST.COMPANY_CARD.FEED_BANK_NAME.MASTER_CARD)) {
+ return Illustrations.MasterCardCompanyCards;
+ }
+
+ if (cardFeed.startsWith(CONST.COMPANY_CARD.FEED_BANK_NAME.VISA)) {
+ return Illustrations.VisaCompanyCards;
+ }
+
+ return Illustrations.AmexCompanyCards;
+}
+
export {
isExpensifyCard,
isCorporateCard,
@@ -195,4 +209,5 @@ export {
getTranslationKeyForLimitType,
getEligibleBankAccountsForCard,
sortCardsByCardholderName,
+ getCardFeedIcon,
};
diff --git a/src/libs/DistanceRequestUtils.ts b/src/libs/DistanceRequestUtils.ts
index 2b2aad59d58d..b5af91dfacfb 100644
--- a/src/libs/DistanceRequestUtils.ts
+++ b/src/libs/DistanceRequestUtils.ts
@@ -128,6 +128,7 @@ function getRoundedDistanceInUnits(distanceInMeters: number, unit: Unit): string
* @param currency The currency associated with the rate
* @param translate Translate function
* @param toLocaleDigit Function to convert to localized digit
+ * @param useShortFormUnit If true, the unit will be returned in short form (e.g., "mi", "km").
* @returns A string that displays the rate used for expense calculation
*/
function getRateForDisplay(
@@ -137,6 +138,7 @@ function getRateForDisplay(
translate: LocaleContextProps['translate'],
toLocaleDigit: LocaleContextProps['toLocaleDigit'],
isOffline?: boolean,
+ useShortFormUnit?: boolean,
): string {
if (isOffline && !rate) {
return translate('iou.defaultRate');
@@ -146,11 +148,11 @@ function getRateForDisplay(
}
const singularDistanceUnit = unit === CONST.CUSTOM_UNITS.DISTANCE_UNIT_MILES ? translate('common.mile') : translate('common.kilometer');
- const formattedRate = PolicyUtils.getUnitRateValue(toLocaleDigit, {rate});
+ const formattedRate = PolicyUtils.getUnitRateValue(toLocaleDigit, {rate}, useShortFormUnit);
// eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
const currencySymbol = CurrencyUtils.getCurrencySymbol(currency) || `${currency} `;
- return `${currencySymbol}${formattedRate} / ${singularDistanceUnit}`;
+ return `${currencySymbol}${formattedRate} / ${useShortFormUnit ? unit : singularDistanceUnit}`;
}
/**
@@ -159,14 +161,26 @@ function getRateForDisplay(
* @param unit Unit that should be used to display the distance
* @param rate Expensable amount allowed per unit
* @param translate Translate function
+ * @param useShortFormUnit If true, the unit will be returned in short form (e.g., "mi", "km").
* @returns A string that describes the distance traveled
*/
-function getDistanceForDisplay(hasRoute: boolean, distanceInMeters: number, unit: Unit | undefined, rate: number | undefined, translate: LocaleContextProps['translate']): string {
+function getDistanceForDisplay(
+ hasRoute: boolean,
+ distanceInMeters: number,
+ unit: Unit | undefined,
+ rate: number | undefined,
+ translate: LocaleContextProps['translate'],
+ useShortFormUnit?: boolean,
+): string {
if (!hasRoute || !rate || !unit || !distanceInMeters) {
return translate('iou.fieldPending');
}
const distanceInUnits = getRoundedDistanceInUnits(distanceInMeters, unit);
+ if (useShortFormUnit) {
+ return `${distanceInUnits} ${unit}`;
+ }
+
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 unitString = distanceInUnits === '1' ? singularDistanceUnit : distanceUnit;
@@ -197,8 +211,8 @@ function getDistanceMerchant(
return translate('iou.fieldPending');
}
- const distanceInUnits = getDistanceForDisplay(hasRoute, distanceInMeters, unit, rate, translate);
- const ratePerUnit = getRateForDisplay(unit, rate, currency, translate, toLocaleDigit);
+ const distanceInUnits = getDistanceForDisplay(hasRoute, distanceInMeters, unit, rate, translate, true);
+ const ratePerUnit = getRateForDisplay(unit, rate, currency, translate, toLocaleDigit, undefined, true);
return `${distanceInUnits} @ ${ratePerUnit}`;
}
diff --git a/src/libs/ExportOnyxState/common.ts b/src/libs/ExportOnyxState/common.ts
index ac2adf010eca..564db322930c 100644
--- a/src/libs/ExportOnyxState/common.ts
+++ b/src/libs/ExportOnyxState/common.ts
@@ -1,5 +1,26 @@
import {Str} from 'expensify-common';
import ONYXKEYS from '@src/ONYXKEYS';
+import type {Session} from '@src/types/onyx';
+
+const MASKING_PATTERN = '***';
+
+const maskSessionDetails = (data: Record): Record => {
+ const session = data.session as Session;
+ const maskedData: Record = {};
+
+ Object.keys(session).forEach((key) => {
+ if (key !== 'authToken' && key !== 'encryptedAuthToken') {
+ maskedData[key] = session[key as keyof Session];
+ return;
+ }
+ maskedData[key] = MASKING_PATTERN;
+ });
+
+ return {
+ ...data,
+ session: maskedData,
+ };
+};
const maskFragileData = (data: Record, parentKey?: string): Record => {
const maskedData: Record = {};
@@ -15,10 +36,10 @@ const maskFragileData = (data: Record, parentKey?: string): Rec
const value = data[key];
- if (typeof value === 'string' && (Str.isValidEmail(value) || key === 'authToken' || key === 'encryptedAuthToken')) {
- maskedData[key] = '***';
+ if (typeof value === 'string' && Str.isValidEmail(value)) {
+ maskedData[key] = MASKING_PATTERN;
} else if (parentKey && parentKey.includes(ONYXKEYS.COLLECTION.REPORT_ACTIONS) && (key === 'text' || key === 'html')) {
- maskedData[key] = '***';
+ maskedData[key] = MASKING_PATTERN;
} else if (typeof value === 'object') {
maskedData[key] = maskFragileData(value as Record, key.includes(ONYXKEYS.COLLECTION.REPORT_ACTIONS) ? key : parentKey);
} else {
@@ -29,6 +50,16 @@ const maskFragileData = (data: Record, parentKey?: string): Rec
return maskedData;
};
-export default {
- maskFragileData,
+const maskOnyxState = (data: Record, isMaskingFragileDataEnabled?: boolean) => {
+ let onyxState = data;
+ // Mask session details by default
+ onyxState = maskSessionDetails(onyxState);
+ // Mask fragile data other than session details if the user has enabled the option
+ if (isMaskingFragileDataEnabled) {
+ onyxState = maskFragileData(onyxState);
+ }
+
+ return onyxState;
};
+
+export default {maskOnyxState};
diff --git a/src/libs/ExportOnyxState/index.native.ts b/src/libs/ExportOnyxState/index.native.ts
index bc32b29bc2ab..2ad9af0bf54c 100644
--- a/src/libs/ExportOnyxState/index.native.ts
+++ b/src/libs/ExportOnyxState/index.native.ts
@@ -2,7 +2,7 @@ import RNFS from 'react-native-fs';
import {open} from 'react-native-quick-sqlite';
import Share from 'react-native-share';
import CONST from '@src/CONST';
-import common from './common';
+import ExportOnyxState from './common';
const readFromOnyxDatabase = () =>
new Promise((resolve) => {
@@ -10,9 +10,12 @@ const readFromOnyxDatabase = () =>
const query = `SELECT * FROM ${CONST.DEFAULT_TABLE_NAME}`;
db.executeAsync(query, []).then(({rows}) => {
- // eslint-disable-next-line no-underscore-dangle, @typescript-eslint/no-unsafe-member-access
- const result = rows?._array.map((row) => ({[row?.record_key]: JSON.parse(row?.valueJSON as string) as unknown}));
-
+ // eslint-disable-next-line no-underscore-dangle
+ const result = rows?._array.reduce>((acc, row) => {
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
+ acc[row?.record_key] = JSON.parse(row?.valueJSON as string) as unknown;
+ return acc;
+ }, {});
resolve(result);
});
});
@@ -36,7 +39,7 @@ const shareAsFile = (value: string) => {
};
export default {
- maskFragileData: common.maskFragileData,
+ maskOnyxState: ExportOnyxState.maskOnyxState,
readFromOnyxDatabase,
shareAsFile,
};
diff --git a/src/libs/ExportOnyxState/index.ts b/src/libs/ExportOnyxState/index.ts
index 16a2e96bc25e..66fa6744ecdc 100644
--- a/src/libs/ExportOnyxState/index.ts
+++ b/src/libs/ExportOnyxState/index.ts
@@ -1,5 +1,5 @@
import CONST from '@src/CONST';
-import common from './common';
+import ExportOnyxState from './common';
const readFromOnyxDatabase = () =>
new Promise>((resolve) => {
@@ -44,7 +44,7 @@ const shareAsFile = (value: string) => {
};
export default {
- maskFragileData: common.maskFragileData,
+ maskOnyxState: ExportOnyxState.maskOnyxState,
readFromOnyxDatabase,
shareAsFile,
};
diff --git a/src/libs/IOUUtils.ts b/src/libs/IOUUtils.ts
index d1bf8fcd8c8c..55ac4ba2ac36 100644
--- a/src/libs/IOUUtils.ts
+++ b/src/libs/IOUUtils.ts
@@ -156,11 +156,16 @@ function isMovingTransactionFromTrackExpense(action?: IOUAction) {
return false;
}
+function shouldUseTransactionDraft(action: IOUAction | undefined) {
+ return action === CONST.IOU.ACTION.CREATE || isMovingTransactionFromTrackExpense(action);
+}
+
export {
calculateAmount,
insertTagIntoTransactionTagsString,
isIOUReportPendingCurrencyConversion,
isMovingTransactionFromTrackExpense,
+ shouldUseTransactionDraft,
isValidMoneyRequestType,
navigateToStartMoneyRequestStep,
updateIOUOwnerAndTotal,
diff --git a/src/libs/Navigation/AppNavigator/AuthScreens.tsx b/src/libs/Navigation/AppNavigator/AuthScreens.tsx
index 4bbdac7b17b1..99572c97a213 100644
--- a/src/libs/Navigation/AppNavigator/AuthScreens.tsx
+++ b/src/libs/Navigation/AppNavigator/AuthScreens.tsx
@@ -3,7 +3,9 @@ import {View} from 'react-native';
import type {OnyxEntry} from 'react-native-onyx';
import Onyx, {withOnyx} from 'react-native-onyx';
import type {ValueOf} from 'type-fest';
+import ComposeProviders from '@components/ComposeProviders';
import OptionsListContextProvider from '@components/OptionListContextProvider';
+import {SearchContextProvider} from '@components/Search/SearchContext';
import useActiveWorkspace from '@hooks/useActiveWorkspace';
import useOnboardingLayout from '@hooks/useOnboardingLayout';
import usePermissions from '@hooks/usePermissions';
@@ -19,6 +21,7 @@ import getOnboardingModalScreenOptions from '@libs/Navigation/getOnboardingModal
import Navigation from '@libs/Navigation/Navigation';
import type {AuthScreensParamList, CentralPaneName, CentralPaneScreensParamList} from '@libs/Navigation/types';
import NetworkConnection from '@libs/NetworkConnection';
+import onyxSubscribe from '@libs/onyxSubscribe';
import * as Pusher from '@libs/Pusher/pusher';
import PusherConnectionManager from '@libs/PusherConnectionManager';
import * as ReportUtils from '@libs/ReportUtils';
@@ -215,6 +218,7 @@ function AuthScreens({session, lastOpenedPublicRoomID, initialLastUpdateIDApplie
() => getOnboardingModalScreenOptions(isSmallScreenWidth, styles, StyleUtils, isMediumOrLargerScreenWidth),
[StyleUtils, isSmallScreenWidth, isMediumOrLargerScreenWidth, styles],
);
+ const modal = useRef({});
let initialReportID: string | undefined;
const isInitialRender = useRef(true);
@@ -283,6 +287,36 @@ function AuthScreens({session, lastOpenedPublicRoomID, initialLastUpdateIDApplie
Timing.end(CONST.TIMING.HOMEPAGE_INITIAL_RENDER);
+ const unsubscribeOnyxModal = onyxSubscribe({
+ key: ONYXKEYS.MODAL,
+ callback: (modalArg) => {
+ if (modalArg === null || typeof modalArg !== 'object') {
+ return;
+ }
+ modal.current = modalArg;
+ },
+ });
+
+ const shortcutConfig = CONST.KEYBOARD_SHORTCUTS.ESCAPE;
+ const unsubscribeEscapeKey = KeyboardShortcut.subscribe(
+ shortcutConfig.shortcutKey,
+ () => {
+ if (modal.current.willAlertModalBecomeVisible) {
+ return;
+ }
+
+ if (modal.current.disableDismissOnEscape) {
+ return;
+ }
+
+ Navigation.dismissModal();
+ },
+ shortcutConfig.descriptionKey,
+ shortcutConfig.modifiers,
+ true,
+ true,
+ );
+
// Listen to keyboard shortcuts for opening certain pages
const unsubscribeShortcutsOverviewShortcut = KeyboardShortcut.subscribe(
shortcutsOverviewShortcutConfig.shortcutKey,
@@ -305,7 +339,11 @@ function AuthScreens({session, lastOpenedPublicRoomID, initialLastUpdateIDApplie
const unsubscribeSearchShortcut = KeyboardShortcut.subscribe(
searchShortcutConfig.shortcutKey,
() => {
- Modal.close(Session.checkIfActionIsAllowed(() => Navigation.navigate(ROUTES.CHAT_FINDER)));
+ Modal.close(
+ Session.checkIfActionIsAllowed(() => Navigation.navigate(ROUTES.CHAT_FINDER)),
+ true,
+ true,
+ );
},
shortcutsOverviewShortcutConfig.descriptionKey,
shortcutsOverviewShortcutConfig.modifiers,
@@ -333,6 +371,8 @@ function AuthScreens({session, lastOpenedPublicRoomID, initialLastUpdateIDApplie
);
return () => {
+ unsubscribeEscapeKey();
+ unsubscribeOnyxModal();
unsubscribeShortcutsOverviewShortcut();
unsubscribeSearchShortcut();
unsubscribeChatShortcut();
@@ -353,7 +393,7 @@ function AuthScreens({session, lastOpenedPublicRoomID, initialLastUpdateIDApplie
};
return (
-
+
-
+
);
}
diff --git a/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx b/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx
index 40e8d5a106ef..e1f88ad36c76 100644
--- a/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx
+++ b/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx
@@ -191,6 +191,7 @@ const SettingsModalStackNavigator = createModalStackNavigator require('../../../../pages/settings/Profile/PersonalDetails/StateSelectionPage').default,
[SCREENS.SETTINGS.PROFILE.CONTACT_METHODS]: () => require('../../../../pages/settings/Profile/Contacts/ContactMethodsPage').default,
[SCREENS.SETTINGS.PROFILE.CONTACT_METHOD_DETAILS]: () => require('../../../../pages/settings/Profile/Contacts/ContactMethodDetailsPage').default,
+ [SCREENS.SETTINGS.PROFILE.CONTACT_METHOD_VALIDATE_ACTION]: () => require('../../../../pages/settings/Profile/Contacts/ValidateContactActionPage').default,
[SCREENS.SETTINGS.PROFILE.NEW_CONTACT_METHOD]: () => require('../../../../pages/settings/Profile/Contacts/NewContactMethodPage').default,
[SCREENS.SETTINGS.PREFERENCES.PRIORITY_MODE]: () => require('../../../../pages/settings/Preferences/PriorityModePage').default,
[SCREENS.WORKSPACE.ACCOUNTING.ROOT]: () => require('../../../../pages/workspace/accounting/PolicyAccountingPage').default,
@@ -421,6 +422,7 @@ const SettingsModalStackNavigator = createModalStackNavigator require('../../../../pages/workspace/taxes/WorkspaceTaxCodePage').default,
[SCREENS.WORKSPACE.INVOICES_COMPANY_NAME]: () => require('../../../../pages/workspace/invoices/WorkspaceInvoicingDetailsName').default,
[SCREENS.WORKSPACE.INVOICES_COMPANY_WEBSITE]: () => require('../../../../pages/workspace/invoices/WorkspaceInvoicingDetailsWebsite').default,
+ [SCREENS.WORKSPACE.COMPANY_CARDS_SELECT_FEED]: () => require('../../../../pages/workspace/companyCards/WorkspaceCompanyCardFeedSelectorPage').default,
[SCREENS.WORKSPACE.EXPENSIFY_CARD_ISSUE_NEW]: () => require('../../../../pages/workspace/expensifyCard/issueNew/IssueNewCardPage').default,
[SCREENS.WORKSPACE.EXPENSIFY_CARD_SETTINGS]: () => require('../../../../pages/workspace/expensifyCard/WorkspaceCardSettingsPage').default,
[SCREENS.WORKSPACE.EXPENSIFY_CARD_SETTINGS_ACCOUNT]: () => require('../../../../pages/workspace/expensifyCard/WorkspaceSettlementAccountPage').default,
@@ -430,6 +432,8 @@ const SettingsModalStackNavigator = createModalStackNavigator require('../../../../pages/workspace/expensifyCard/WorkspaceEditCardNamePage').default,
[SCREENS.WORKSPACE.EXPENSIFY_CARD_LIMIT]: () => require('../../../../pages/workspace/expensifyCard/WorkspaceEditCardLimitPage').default,
[SCREENS.WORKSPACE.EXPENSIFY_CARD_LIMIT_TYPE]: () => require('../../../../pages/workspace/expensifyCard/WorkspaceEditCardLimitTypePage').default,
+ [SCREENS.WORKSPACE.COMPANY_CARDS_SETTINGS]: () => require('../../../../pages/workspace/companyCards/WorkspaceCompanyCardsSettingsPage').default,
+ [SCREENS.WORKSPACE.COMPANY_CARDS_SETTINGS_FEED_NAME]: () => require('../../../../pages/workspace/companyCards/WorkspaceCompanyCardsSettingsFeedNamePage').default,
[SCREENS.SETTINGS.SAVE_THE_WORLD]: () => require('../../../../pages/TeachersUnite/SaveTheWorldPage').default,
[SCREENS.SETTINGS.SUBSCRIPTION.CHANGE_PAYMENT_CURRENCY]: () => require('../../../../pages/settings/PaymentCard/ChangeCurrency').default,
[SCREENS.SETTINGS.SUBSCRIPTION.CHANGE_BILLING_CURRENCY]: () => require('../../../../pages/settings/Subscription/PaymentCard/ChangeBillingCurrency').default,
@@ -532,6 +536,8 @@ const SearchAdvancedFiltersModalStackNavigator = createModalStackNavigator require('../../../../pages/Search/SearchAdvancedFiltersPage/SearchFiltersTagPage').default,
[SCREENS.SEARCH.ADVANCED_FILTERS_FROM_RHP]: () => require('@pages/Search/SearchAdvancedFiltersPage/SearchFiltersFromPage').default,
[SCREENS.SEARCH.ADVANCED_FILTERS_TO_RHP]: () => require('@pages/Search/SearchAdvancedFiltersPage/SearchFiltersToPage').default,
+ [SCREENS.SEARCH.ADVANCED_FILTERS_IN_RHP]: () => require('@pages/Search/SearchAdvancedFiltersPage/SearchFiltersInPage').default,
+ [SCREENS.SEARCH.ADVANCED_FILTERS_HAS_RHP]: () => require('@pages/Search/SearchAdvancedFiltersPage/SearchFiltersHasPage').default,
});
const RestrictedActionModalStackNavigator = createModalStackNavigator({
diff --git a/src/libs/Navigation/AppNavigator/createCustomBottomTabNavigator/BottomTabBar.tsx b/src/libs/Navigation/AppNavigator/createCustomBottomTabNavigator/BottomTabBar.tsx
index a9df43ad5b64..394a617278d4 100644
--- a/src/libs/Navigation/AppNavigator/createCustomBottomTabNavigator/BottomTabBar.tsx
+++ b/src/libs/Navigation/AppNavigator/createCustomBottomTabNavigator/BottomTabBar.tsx
@@ -5,6 +5,7 @@ import {useOnyx} from 'react-native-onyx';
import Icon from '@components/Icon';
import * as Expensicons from '@components/Icon/Expensicons';
import {PressableWithFeedback} from '@components/Pressable';
+import type {SearchQueryString} from '@components/Search/types';
import Tooltip from '@components/Tooltip';
import useActiveWorkspace from '@hooks/useActiveWorkspace';
import useLocalize from '@hooks/useLocalize';
@@ -36,6 +37,34 @@ type BottomTabBarProps = {
selectedTab: string | undefined;
};
+/**
+ * Returns SearchQueryString that has policyID correctly set.
+ *
+ * When we're coming back to Search Screen we might have pre-existing policyID inside SearchQuery.
+ * There are 2 cases when we might want to remove this `policyID`:
+ * - if Policy was removed in another screen
+ * - if WorkspaceSwitcher was used to globally unset a policyID
+ * Otherwise policyID will be inserted into query
+ */
+function handleQueryWithPolicyID(query: SearchQueryString, activePolicyID?: string): SearchQueryString {
+ const queryJSON = SearchUtils.buildSearchQueryJSON(query);
+ if (!queryJSON) {
+ return query;
+ }
+
+ const policyID = queryJSON.policyID ?? activePolicyID;
+ const policy = PolicyUtils.getPolicy(policyID);
+
+ // In case policy is missing or there is no policy currently selected via WorkspaceSwitcher we remove it
+ if (!activePolicyID || !policy) {
+ delete queryJSON.policyID;
+ } else {
+ queryJSON.policyID = policyID;
+ }
+
+ return SearchUtils.buildSearchQueryString(queryJSON);
+}
+
function BottomTabBar({selectedTab}: BottomTabBarProps) {
const theme = useTheme();
const styles = useThemeStyles();
@@ -91,13 +120,23 @@ function BottomTabBar({selectedTab}: BottomTabBarProps) {
const currentSearchParams = SearchUtils.getCurrentSearchParams();
if (currentSearchParams) {
const {q, ...rest} = currentSearchParams;
- const policy = PolicyUtils.getPolicy(currentSearchParams?.policyIDs);
- Navigation.navigate(ROUTES.SEARCH_CENTRAL_PANE.getRoute({query: q, ...rest, policyIDs: policy ? currentSearchParams?.policyIDs : undefined}));
+ const cleanedQuery = handleQueryWithPolicyID(q, activeWorkspaceID);
+
+ Navigation.navigate(
+ ROUTES.SEARCH_CENTRAL_PANE.getRoute({
+ query: cleanedQuery,
+ ...rest,
+ }),
+ );
return;
}
- Navigation.navigate(ROUTES.SEARCH_CENTRAL_PANE.getRoute({query: SearchUtils.buildCannedSearchQuery()}));
+
+ const defaultCannedQuery = SearchUtils.buildCannedSearchQuery();
+ // when navigating to search we might have an activePolicyID set from workspace switcher
+ const query = activeWorkspaceID ? `${defaultCannedQuery} ${CONST.SEARCH.SYNTAX_ROOT_KEYS.POLICY_ID}:${activeWorkspaceID}` : defaultCannedQuery;
+ Navigation.navigate(ROUTES.SEARCH_CENTRAL_PANE.getRoute({query}));
});
- }, [selectedTab]);
+ }, [activeWorkspaceID, selectedTab]);
return (
diff --git a/src/libs/Navigation/extractPolicyIDFromQuery.ts b/src/libs/Navigation/extractPolicyIDFromQuery.ts
new file mode 100644
index 000000000000..bd0464f4aab6
--- /dev/null
+++ b/src/libs/Navigation/extractPolicyIDFromQuery.ts
@@ -0,0 +1,22 @@
+import * as SearchUtils from '@libs/SearchUtils';
+import type {NavigationPartialRoute} from './types';
+
+function extractPolicyIDFromQuery(route?: NavigationPartialRoute) {
+ if (!route?.params) {
+ return undefined;
+ }
+
+ if (!('q' in route.params)) {
+ return undefined;
+ }
+
+ const queryString = route.params.q as string;
+ const queryJSON = SearchUtils.buildSearchQueryJSON(queryString);
+ if (!queryJSON) {
+ return undefined;
+ }
+
+ return SearchUtils.getPolicyIDFromSearchQuery(queryJSON);
+}
+
+export default extractPolicyIDFromQuery;
diff --git a/src/libs/Navigation/getPolicyIDFromState.ts b/src/libs/Navigation/getPolicyIDFromState.ts
index 00236fb0fce0..62690f29a98f 100644
--- a/src/libs/Navigation/getPolicyIDFromState.ts
+++ b/src/libs/Navigation/getPolicyIDFromState.ts
@@ -1,16 +1,23 @@
+import extractPolicyIDFromQuery from './extractPolicyIDFromQuery';
import getTopmostBottomTabRoute from './getTopmostBottomTabRoute';
import type {RootStackParamList, State} from './types';
+/**
+ * returns policyID value if one exists in navigation state
+ *
+ * PolicyID in this app can be stored in two ways:
+ * - on most screens but NOT Search as `policyID` param
+ * - on Search related screens as policyID filter inside `q` (SearchQuery) param
+ */
const getPolicyIDFromState = (state: State): string | undefined => {
const topmostBottomTabRoute = getTopmostBottomTabRoute(state);
- const shouldAddPolicyIDToUrl = !!topmostBottomTabRoute && !!topmostBottomTabRoute.params && 'policyID' in topmostBottomTabRoute.params && !!topmostBottomTabRoute.params?.policyID;
-
- if (!shouldAddPolicyIDToUrl) {
- return undefined;
+ const policyID = topmostBottomTabRoute && topmostBottomTabRoute.params && 'policyID' in topmostBottomTabRoute.params && topmostBottomTabRoute.params?.policyID;
+ if (policyID) {
+ return topmostBottomTabRoute.params?.policyID as string;
}
- return topmostBottomTabRoute.params?.policyID as string;
+ return extractPolicyIDFromQuery(topmostBottomTabRoute);
};
export default getPolicyIDFromState;
diff --git a/src/libs/Navigation/linkTo/index.ts b/src/libs/Navigation/linkTo/index.ts
index 45178ef1adaf..1fc99c771ca5 100644
--- a/src/libs/Navigation/linkTo/index.ts
+++ b/src/libs/Navigation/linkTo/index.ts
@@ -4,13 +4,13 @@ import {findFocusedRoute} from '@react-navigation/native';
import {omitBy} from 'lodash';
import getIsNarrowLayout from '@libs/getIsNarrowLayout';
import isReportOpenInRHP from '@libs/Navigation/isReportOpenInRHP';
-import extractPolicyIDsFromState from '@libs/Navigation/linkingConfig/extractPolicyIDsFromState';
import {isCentralPaneName} from '@libs/NavigationUtils';
import shallowCompare from '@libs/ObjectUtils';
import {extractPolicyIDFromPath, getPathWithoutPolicyID} from '@libs/PolicyUtils';
import getActionsFromPartialDiff from '@navigation/AppNavigator/getActionsFromPartialDiff';
import getPartialStateDiff from '@navigation/AppNavigator/getPartialStateDiff';
import dismissModal from '@navigation/dismissModal';
+import extractPolicyIDFromQuery from '@navigation/extractPolicyIDFromQuery';
import extrapolateStateFromParams from '@navigation/extrapolateStateFromParams';
import getPolicyIDFromState from '@navigation/getPolicyIDFromState';
import getStateFromPath from '@navigation/getStateFromPath';
@@ -49,18 +49,18 @@ export default function linkTo(navigation: NavigationContainerRef>;
// Creating path with /w/ included if necessary.
const topmostCentralPaneRoute = getTopmostCentralPaneRoute(rootState);
- const policyIDs = !!topmostCentralPaneRoute?.params && 'policyIDs' in topmostCentralPaneRoute.params ? (topmostCentralPaneRoute?.params?.policyIDs as string) : '';
+
const extractedPolicyID = extractPolicyIDFromPath(`/${path}`);
const policyIDFromState = getPolicyIDFromState(rootState);
- const policyID = extractedPolicyID ?? policyIDFromState ?? policyIDs;
+ const policyID = extractedPolicyID ?? policyIDFromState;
const lastRoute = rootState?.routes?.at(-1);
const isNarrowLayout = getIsNarrowLayout();
const isFullScreenOnTop = lastRoute?.name === NAVIGATORS.FULL_SCREEN_NAVIGATOR;
- // policyIDs is present only on SCREENS.SEARCH.CENTRAL_PANE and it's displayed in the url as a query param, on the other pages this parameter is called policyID and it's shown in the url in the format: /w/:policyID
- if (policyID && !isFullScreenOnTop && !policyIDs) {
+ // policyID on SCREENS.SEARCH.CENTRAL_PANE can be present only as part of SearchQuery, while on other pages it's stored in the url in the format: /w/:policyID/
+ if (policyID && !isFullScreenOnTop && !policyIDFromState) {
// The stateFromPath doesn't include proper path if there is a policy passed with /w/id.
// We need to replace the path in the state with the proper one.
// To avoid this hacky solution we may want to create custom getActionFromState function in the future.
@@ -95,8 +95,10 @@ export default function linkTo(navigation: NavigationContainerRef)?.policyID ?? '') !==
@@ -115,11 +117,6 @@ export default function linkTo(navigation: NavigationContainerRef).policyIDs = policyID;
- }
-
// If the type is UP, we deeplinked into one of the RHP flows and we want to replace the current screen with the previous one in the flow
// and at the same time we want the back button to go to the page we were before the deeplink
} else if (type === CONST.NAVIGATION.TYPE.UP) {
diff --git a/src/libs/Navigation/linkingConfig/CENTRAL_PANE_TO_RHP_MAPPING.ts b/src/libs/Navigation/linkingConfig/CENTRAL_PANE_TO_RHP_MAPPING.ts
index 8a4a53e0f705..ff3c60693cbc 100755
--- a/src/libs/Navigation/linkingConfig/CENTRAL_PANE_TO_RHP_MAPPING.ts
+++ b/src/libs/Navigation/linkingConfig/CENTRAL_PANE_TO_RHP_MAPPING.ts
@@ -6,6 +6,7 @@ const CENTRAL_PANE_TO_RHP_MAPPING: Partial> =
SCREENS.SETTINGS.PROFILE.DISPLAY_NAME,
SCREENS.SETTINGS.PROFILE.CONTACT_METHODS,
SCREENS.SETTINGS.PROFILE.CONTACT_METHOD_DETAILS,
+ SCREENS.SETTINGS.PROFILE.CONTACT_METHOD_VALIDATE_ACTION,
SCREENS.SETTINGS.PROFILE.NEW_CONTACT_METHOD,
SCREENS.SETTINGS.PROFILE.STATUS_CLEAR_AFTER,
SCREENS.SETTINGS.PROFILE.STATUS_CLEAR_AFTER_DATE,
@@ -58,6 +59,9 @@ const CENTRAL_PANE_TO_RHP_MAPPING: Partial> =
SCREENS.SEARCH.ADVANCED_FILTERS_TAG_RHP,
SCREENS.SEARCH.ADVANCED_FILTERS_FROM_RHP,
SCREENS.SEARCH.ADVANCED_FILTERS_TO_RHP,
+ SCREENS.SEARCH.ADVANCED_FILTERS_IN_RHP,
+ SCREENS.SEARCH.ADVANCED_FILTERS_CARD_RHP,
+ SCREENS.SEARCH.ADVANCED_FILTERS_HAS_RHP,
],
[SCREENS.SETTINGS.SUBSCRIPTION.ROOT]: [
SCREENS.SETTINGS.SUBSCRIPTION.ADD_PAYMENT_CARD,
diff --git a/src/libs/Navigation/linkingConfig/FULL_SCREEN_TO_RHP_MAPPING.ts b/src/libs/Navigation/linkingConfig/FULL_SCREEN_TO_RHP_MAPPING.ts
index c7939268fa12..790f1df56a24 100755
--- a/src/libs/Navigation/linkingConfig/FULL_SCREEN_TO_RHP_MAPPING.ts
+++ b/src/libs/Navigation/linkingConfig/FULL_SCREEN_TO_RHP_MAPPING.ts
@@ -160,7 +160,7 @@ const FULL_SCREEN_TO_RHP_MAPPING: Partial> = {
SCREENS.WORKSPACE.REPORT_FIELDS_EDIT_INITIAL_VALUE,
],
[SCREENS.WORKSPACE.INVOICES]: [SCREENS.WORKSPACE.INVOICES_COMPANY_NAME, SCREENS.WORKSPACE.INVOICES_COMPANY_WEBSITE],
- [SCREENS.WORKSPACE.COMPANY_CARDS]: [],
+ [SCREENS.WORKSPACE.COMPANY_CARDS]: [SCREENS.WORKSPACE.COMPANY_CARDS_SELECT_FEED, SCREENS.WORKSPACE.COMPANY_CARDS_SETTINGS, SCREENS.WORKSPACE.COMPANY_CARDS_SETTINGS_FEED_NAME],
[SCREENS.WORKSPACE.EXPENSIFY_CARD]: [
SCREENS.WORKSPACE.EXPENSIFY_CARD_ISSUE_NEW,
SCREENS.WORKSPACE.EXPENSIFY_CARD_BANK_ACCOUNT,
diff --git a/src/libs/Navigation/linkingConfig/config.ts b/src/libs/Navigation/linkingConfig/config.ts
index 43e7ece014eb..e7d02b3f65dc 100644
--- a/src/libs/Navigation/linkingConfig/config.ts
+++ b/src/libs/Navigation/linkingConfig/config.ts
@@ -251,6 +251,9 @@ const config: LinkingOptions['config'] = {
[SCREENS.SETTINGS.PROFILE.CONTACT_METHOD_DETAILS]: {
path: ROUTES.SETTINGS_CONTACT_METHOD_DETAILS.route,
},
+ [SCREENS.SETTINGS.PROFILE.CONTACT_METHOD_VALIDATE_ACTION]: {
+ path: ROUTES.SETINGS_CONTACT_METHOD_VALIDATE_ACTION,
+ },
[SCREENS.SETTINGS.PROFILE.NEW_CONTACT_METHOD]: {
path: ROUTES.SETTINGS_NEW_CONTACT_METHOD.route,
exact: true,
@@ -481,6 +484,9 @@ const config: LinkingOptions['config'] = {
[SCREENS.WORKSPACE.INVOICES_COMPANY_WEBSITE]: {
path: ROUTES.WORKSPACE_INVOICES_COMPANY_WEBSITE.route,
},
+ [SCREENS.WORKSPACE.COMPANY_CARDS_SELECT_FEED]: {
+ path: ROUTES.WORKSPACE_COMPANY_CARDS_SELECT_FEED.route,
+ },
[SCREENS.WORKSPACE.EXPENSIFY_CARD_LIMIT]: {
path: ROUTES.WORKSPACE_EXPENSIFY_CARD_LIMIT.route,
},
@@ -505,6 +511,12 @@ const config: LinkingOptions['config'] = {
[SCREENS.WORKSPACE.EXPENSIFY_CARD_SETTINGS_ACCOUNT]: {
path: ROUTES.WORKSPACE_EXPENSIFY_CARD_SETTINGS_ACCOUNT.route,
},
+ [SCREENS.WORKSPACE.COMPANY_CARDS_SETTINGS]: {
+ path: ROUTES.WORKSPACE_COMPANY_CARDS_SETTINGS.route,
+ },
+ [SCREENS.WORKSPACE.COMPANY_CARDS_SETTINGS_FEED_NAME]: {
+ path: ROUTES.WORKSPACE_COMPANY_CARDS_SETTINGS_FEED_NAME.route,
+ },
[SCREENS.WORKSPACE.EXPENSIFY_CARD_DETAILS]: {
path: ROUTES.WORKSPACE_EXPENSIFY_CARD_DETAILS.route,
},
@@ -1050,6 +1062,8 @@ const config: LinkingOptions['config'] = {
[SCREENS.SEARCH.ADVANCED_FILTERS_TAG_RHP]: ROUTES.SEARCH_ADVANCED_FILTERS_TAG,
[SCREENS.SEARCH.ADVANCED_FILTERS_FROM_RHP]: ROUTES.SEARCH_ADVANCED_FILTERS_FROM,
[SCREENS.SEARCH.ADVANCED_FILTERS_TO_RHP]: ROUTES.SEARCH_ADVANCED_FILTERS_TO,
+ [SCREENS.SEARCH.ADVANCED_FILTERS_IN_RHP]: ROUTES.SEARCH_ADVANCED_FILTERS_IN,
+ [SCREENS.SEARCH.ADVANCED_FILTERS_HAS_RHP]: ROUTES.SEARCH_ADVANCED_FILTERS_HAS,
},
},
[SCREENS.RIGHT_MODAL.RESTRICTED_ACTION]: {
diff --git a/src/libs/Navigation/linkingConfig/extractPolicyIDsFromState.ts b/src/libs/Navigation/linkingConfig/extractPolicyIDsFromState.ts
deleted file mode 100644
index fdaf7e6eb490..000000000000
--- a/src/libs/Navigation/linkingConfig/extractPolicyIDsFromState.ts
+++ /dev/null
@@ -1,13 +0,0 @@
-import type {InitialState} from '@react-navigation/native';
-import {findFocusedRoute} from '@react-navigation/native';
-import SCREENS from '@src/SCREENS';
-
-function extractPolicyIDsFromState(state: InitialState) {
- const focusedRoute = findFocusedRoute(state);
- if (focusedRoute && focusedRoute.name === SCREENS.SEARCH.CENTRAL_PANE && focusedRoute.params && 'policyIDs' in focusedRoute.params) {
- return focusedRoute.params.policyIDs as string;
- }
- return undefined;
-}
-
-export default extractPolicyIDsFromState;
diff --git a/src/libs/Navigation/linkingConfig/getAdaptedStateFromPath.ts b/src/libs/Navigation/linkingConfig/getAdaptedStateFromPath.ts
index 611831544bdc..10e68ad4a6a8 100644
--- a/src/libs/Navigation/linkingConfig/getAdaptedStateFromPath.ts
+++ b/src/libs/Navigation/linkingConfig/getAdaptedStateFromPath.ts
@@ -8,6 +8,7 @@ import type {BottomTabName, CentralPaneName, FullScreenName, NavigationPartialRo
import {isCentralPaneName} from '@libs/NavigationUtils';
import {extractPolicyIDFromPath, getPathWithoutPolicyID} from '@libs/PolicyUtils';
import * as ReportConnection from '@libs/ReportConnection';
+import extractPolicyIDFromQuery from '@navigation/extractPolicyIDFromQuery';
import CONST from '@src/CONST';
import NAVIGATORS from '@src/NAVIGATORS';
import ONYXKEYS from '@src/ONYXKEYS';
@@ -15,7 +16,6 @@ import type {Screen} from '@src/SCREENS';
import SCREENS from '@src/SCREENS';
import CENTRAL_PANE_TO_RHP_MAPPING from './CENTRAL_PANE_TO_RHP_MAPPING';
import config, {normalizedConfigs} from './config';
-import extractPolicyIDsFromState from './extractPolicyIDsFromState';
import FULL_SCREEN_TO_RHP_MAPPING from './FULL_SCREEN_TO_RHP_MAPPING';
import getMatchingBottomTabRouteForState from './getMatchingBottomTabRouteForState';
import getMatchingCentralPaneRouteForState from './getMatchingCentralPaneRouteForState';
@@ -379,10 +379,11 @@ const getAdaptedStateFromPath: GetAdaptedStateFromPath = (path, options) => {
throw new Error('Unable to parse path');
}
- // Only on SCREENS.SEARCH.CENTRAL_PANE policyID is stored differently as "policyIDs" param, so we're handling this case here
- const policyIDs = extractPolicyIDsFromState(state);
+ // On SCREENS.SEARCH.CENTRAL_PANE policyID is stored differently inside search query ("q" param), so we're handling this case
+ const focusedRoute = findFocusedRoute(state);
+ const policyIDFromQuery = extractPolicyIDFromQuery(focusedRoute);
- return getAdaptedState(state, policyID ?? policyIDs);
+ return getAdaptedState(state, policyID ?? policyIDFromQuery);
};
export default getAdaptedStateFromPath;
diff --git a/src/libs/Navigation/linkingConfig/getMatchingBottomTabRouteForState.ts b/src/libs/Navigation/linkingConfig/getMatchingBottomTabRouteForState.ts
index 67d76de4932d..7b213fdfeb6e 100644
--- a/src/libs/Navigation/linkingConfig/getMatchingBottomTabRouteForState.ts
+++ b/src/libs/Navigation/linkingConfig/getMatchingBottomTabRouteForState.ts
@@ -24,12 +24,9 @@ function getMatchingBottomTabRouteForState(state: State, pol
if (tabName === SCREENS.SEARCH.BOTTOM_TAB) {
const topmostCentralPaneRouteParams = {...topmostCentralPaneRoute.params} as Record;
- delete topmostCentralPaneRouteParams?.policyIDs;
- if (policyID) {
- topmostCentralPaneRouteParams.policyID = policyID;
- }
return {name: tabName, params: topmostCentralPaneRouteParams};
}
+
return {name: tabName, params: paramsWithPolicyID};
}
diff --git a/src/libs/Navigation/setupCustomAndroidBackHandler/index.android.ts b/src/libs/Navigation/setupCustomAndroidBackHandler/index.android.ts
index 107dae2bb74c..f7c2140b1117 100644
--- a/src/libs/Navigation/setupCustomAndroidBackHandler/index.android.ts
+++ b/src/libs/Navigation/setupCustomAndroidBackHandler/index.android.ts
@@ -17,7 +17,7 @@ function setupCustomAndroidBackHandler() {
const bottomTabRoutes = bottomTabRoute?.state?.routes;
const focusedRoute = findFocusedRoute(rootState);
- // Shoudn't happen but for type safety.
+ // Shouldn't happen but for type safety.
if (!bottomTabRoutes) {
return false;
}
@@ -38,15 +38,15 @@ function setupCustomAndroidBackHandler() {
const bottomTabRouteAfterPop = bottomTabRoutes.at(-2);
// It's possible that central pane search is desynchronized with the bottom tab search.
- // e.g. opening a tab different than search will wipe out central pane screens.
+ // e.g. opening a tab different from search will wipe out central pane screens.
// In that case we have to push the proper one.
if (
bottomTabRouteAfterPop &&
bottomTabRouteAfterPop.name === SCREENS.SEARCH.BOTTOM_TAB &&
(!centralPaneRouteAfterPop || centralPaneRouteAfterPop.name !== SCREENS.SEARCH.CENTRAL_PANE)
) {
- const {policyID, ...restParams} = bottomTabRoutes[bottomTabRoutes.length - 2].params as SearchPageProps['route']['params'];
- navigationRef.dispatch({...StackActions.push(SCREENS.SEARCH.CENTRAL_PANE, {...restParams, policyIDs: policyID})});
+ const searchParams = bottomTabRoutes[bottomTabRoutes.length - 2].params as SearchPageProps['route']['params'];
+ navigationRef.dispatch({...StackActions.push(SCREENS.SEARCH.CENTRAL_PANE, searchParams)});
}
return true;
@@ -54,11 +54,11 @@ function setupCustomAndroidBackHandler() {
// Handle back press to go back to the search page.
// It's possible that central pane search is desynchronized with the bottom tab search.
- // e.g. opening a tab different than search will wipe out central pane screens.
+ // e.g. opening a tab different from search will wipe out central pane screens.
// In that case we have to push the proper one.
if (bottomTabRoutes && bottomTabRoutes?.length >= 2 && bottomTabRoutes[bottomTabRoutes.length - 2].name === SCREENS.SEARCH.BOTTOM_TAB && rootState?.routes?.length === 1) {
- const {policyID, ...restParams} = bottomTabRoutes[bottomTabRoutes.length - 2].params as SearchPageProps['route']['params'];
- navigationRef.dispatch({...StackActions.push(SCREENS.SEARCH.CENTRAL_PANE, {...restParams, policyIDs: policyID})});
+ const searchParams = bottomTabRoutes[bottomTabRoutes.length - 2].params as SearchPageProps['route']['params'];
+ navigationRef.dispatch({...StackActions.push(SCREENS.SEARCH.CENTRAL_PANE, searchParams)});
navigationRef.dispatch({...StackActions.pop(), target: bottomTabRoute?.state?.key});
return true;
}
diff --git a/src/libs/Navigation/switchPolicyID.ts b/src/libs/Navigation/switchPolicyID.ts
index a37ccb0c2506..f65b32006c0e 100644
--- a/src/libs/Navigation/switchPolicyID.ts
+++ b/src/libs/Navigation/switchPolicyID.ts
@@ -20,7 +20,7 @@ type ActionPayloadParams = {
path?: string;
};
-type CentralPaneRouteParams = Record & {policyID?: string; policyIDs?: string; reportID?: string};
+type CentralPaneRouteParams = Record & {policyID?: string; q?: string; reportID?: string};
function checkIfActionPayloadNameIsEqual(action: Writable, screenName: string) {
return action?.payload && 'name' in action.payload && action?.payload?.name === screenName;
@@ -109,12 +109,19 @@ export default function switchPolicyID(navigation: NavigationContainerRef & {policyID: string};
+ [SCREENS.SEARCH.BOTTOM_TAB]: CentralPaneScreensParamList[typeof SCREENS.SEARCH.CENTRAL_PANE];
[SCREENS.SETTINGS.ROOT]: {policyID?: string};
};
diff --git a/src/libs/Network/SequentialQueue.ts b/src/libs/Network/SequentialQueue.ts
index fb09f7f216bd..0f68855eb362 100644
--- a/src/libs/Network/SequentialQueue.ts
+++ b/src/libs/Network/SequentialQueue.ts
@@ -161,7 +161,10 @@ function flush() {
resolveIsReadyPromise?.();
}
currentRequest = null;
- flushOnyxUpdatesQueue();
+ // The queue can be paused when we sync the data with backend so we should only update the Onyx data when the queue is empty
+ if (PersistedRequests.getAll().length === 0) {
+ flushOnyxUpdatesQueue();
+ }
});
},
});
@@ -179,7 +182,6 @@ function unpause() {
const numberOfPersistedRequests = PersistedRequests.getAll().length || 0;
console.debug(`[SequentialQueue] Unpausing the queue and flushing ${numberOfPersistedRequests} requests`);
isQueuePaused = false;
- flushOnyxUpdatesQueue();
flush();
}
diff --git a/src/libs/NumberFormatUtils/index.ts b/src/libs/NumberFormatUtils/index.ts
index 57ad6c63039b..204f0a80b5d9 100644
--- a/src/libs/NumberFormatUtils/index.ts
+++ b/src/libs/NumberFormatUtils/index.ts
@@ -5,7 +5,7 @@ import initPolyfill from './intlPolyfill';
initPolyfill();
-const MemoizedNumberFormat = memoize(Intl.NumberFormat, {maxSize: 10});
+const MemoizedNumberFormat = memoize(Intl.NumberFormat, {maxSize: 10, monitoringName: 'NumberFormatUtils'});
function format(locale: ValueOf, number: number, options?: Intl.NumberFormatOptions): string {
return new MemoizedNumberFormat(locale, options).format(number);
diff --git a/src/libs/OptionsListUtils.ts b/src/libs/OptionsListUtils.ts
index c3325237a7ab..acc9d4bdefc5 100644
--- a/src/libs/OptionsListUtils.ts
+++ b/src/libs/OptionsListUtils.ts
@@ -661,7 +661,9 @@ function getLastMessageTextForReport(report: OnyxEntry, lastActorDetails
} else if (lastReportAction?.actionName === CONST.REPORT.ACTIONS.TYPE.APPROVED) {
lastMessageTextFromReport = ReportUtils.getIOUApprovedMessage(lastReportAction);
} else if (lastReportAction?.actionName === CONST.REPORT.ACTIONS.TYPE.FORWARDED) {
- lastMessageTextFromReport = ReportUtils.getIOUForwardedMessage(lastReportAction);
+ lastMessageTextFromReport = ReportUtils.getIOUForwardedMessage(lastReportAction, report);
+ } else if (lastReportAction?.actionName === CONST.REPORT.ACTIONS.TYPE.REJECTED) {
+ lastMessageTextFromReport = ReportUtils.getRejectedReportMessage();
} else if (ReportActionUtils.isActionableAddPaymentCard(lastReportAction)) {
lastMessageTextFromReport = ReportActionUtils.getReportActionMessageText(lastReportAction);
} else if (lastReportAction?.actionName === 'EXPORTINTEGRATION') {
@@ -1965,6 +1967,7 @@ function getOptions(
}
reportOption.isSelected = isReportSelected(reportOption, selectedOptions);
+ reportOption.isBold = shouldUseBoldText(reportOption);
if (action === CONST.IOU.ACTION.CATEGORIZE) {
const policyCategories = allPolicyCategories?.[`${ONYXKEYS.COLLECTION.POLICY_CATEGORIES}${reportOption.policyID}`] ?? {};
@@ -1984,13 +1987,14 @@ function getOptions(
const personalDetailsOptionsToExclude = [...optionsToExclude, {login: currentUserLogin}];
// Next loop over all personal details removing any that are selectedUsers or recentChats
- allPersonalDetailsOptions.forEach((personalDetailOption) => {
+ for (const personalDetailOption of allPersonalDetailsOptions) {
if (personalDetailsOptionsToExclude.some((optionToExclude) => optionToExclude.login === personalDetailOption.login)) {
- return;
+ continue;
}
+ personalDetailOption.isBold = shouldUseBoldText(personalDetailOption);
personalDetailsOptions.push(personalDetailOption);
- });
+ }
const currentUserOption = allPersonalDetailsOptions.find((personalDetailsOption) => personalDetailsOption.login === currentUserLogin);
@@ -2540,6 +2544,7 @@ export {
getCurrentUserSearchTerms,
getEmptyOptions,
shouldUseBoldText,
+ getAlternateText,
};
export type {MemberForList, CategorySection, CategoryTreeSection, Options, OptionList, SearchOption, PayeePersonalDetails, Category, Tax, TaxRatesOption, Option, OptionTree};
diff --git a/src/libs/Permissions.ts b/src/libs/Permissions.ts
index 58bf57449362..466a0418910c 100644
--- a/src/libs/Permissions.ts
+++ b/src/libs/Permissions.ts
@@ -36,6 +36,10 @@ function canUseNetSuiteUSATax(betas: OnyxEntry): boolean {
return !!betas?.includes(CONST.BETAS.NETSUITE_USA_TAX) || canUseAllBetas(betas);
}
+function canUseNewDotCopilot(betas: OnyxEntry): boolean {
+ return !!betas?.includes(CONST.BETAS.NEW_DOT_COPILOT) || canUseAllBetas(betas);
+}
+
function canUseWorkspaceRules(betas: OnyxEntry): boolean {
return !!betas?.includes(CONST.BETAS.WORKSPACE_RULES) || canUseAllBetas(betas);
}
@@ -61,6 +65,7 @@ export default {
canUseSpotnanaTravel,
canUseWorkspaceFeeds,
canUseNetSuiteUSATax,
+ canUseNewDotCopilot,
canUseWorkspaceRules,
canUseCombinedTrackSubmit,
};
diff --git a/src/libs/PersonalDetailsUtils.ts b/src/libs/PersonalDetailsUtils.ts
index fe3ea7de3bac..af48fd793b43 100644
--- a/src/libs/PersonalDetailsUtils.ts
+++ b/src/libs/PersonalDetailsUtils.ts
@@ -338,6 +338,14 @@ function getPersonalDetailsLength() {
return personalDetails.length;
}
+function getUserNameByEmail(email: string, nameToDisplay: 'firstName' | 'displayName') {
+ const userDetails = getPersonalDetailByEmail(email);
+ if (userDetails) {
+ return userDetails[nameToDisplay] ? userDetails[nameToDisplay] : userDetails.login;
+ }
+ return email;
+}
+
export {
isPersonalDetailsEmpty,
getDisplayNameOrDefault,
@@ -355,4 +363,5 @@ export {
extractFirstAndLastNameFromAvailableDetails,
getNewAccountIDsAndLogins,
getPersonalDetailsLength,
+ getUserNameByEmail,
};
diff --git a/src/libs/PolicyUtils.ts b/src/libs/PolicyUtils.ts
index 3f3a2a96a1e1..07ce5b1539df 100644
--- a/src/libs/PolicyUtils.ts
+++ b/src/libs/PolicyUtils.ts
@@ -89,8 +89,8 @@ function hasPolicyCategoriesError(policyCategories: OnyxEntry)
/**
* Checks if the policy had a sync error.
*/
-function hasSyncError(policy: OnyxEntry): boolean {
- return (Object.keys(policy?.connections ?? {}) as ConnectionName[]).some((connection) => !!getSynchronizationErrorMessage(policy, connection, false));
+function hasSyncError(policy: OnyxEntry, isSyncInProgress: boolean): boolean {
+ return (Object.keys(policy?.connections ?? {}) as ConnectionName[]).some((connection) => !!getSynchronizationErrorMessage(policy, connection, isSyncInProgress));
}
/**
@@ -137,28 +137,39 @@ function getCustomUnitRate(policy: OnyxEntry, customUnitRateID: string):
return distanceUnit?.rates[customUnitRateID];
}
-function getRateDisplayValue(value: number, toLocaleDigit: (arg: string) => string): string {
+function getRateDisplayValue(value: number, toLocaleDigit: (arg: string) => string, withDecimals?: boolean): string {
const numValue = getNumericValue(value, toLocaleDigit);
if (Number.isNaN(numValue)) {
return '';
}
+
+ if (withDecimals) {
+ const decimalPart = numValue.toString().split('.')[1];
+ const fixedDecimalPoints = decimalPart.length > 2 && !decimalPart.endsWith('0') ? 3 : 2;
+ return Number(numValue).toFixed(fixedDecimalPoints).toString().replace('.', toLocaleDigit('.'));
+ }
+
return numValue.toString().replace('.', toLocaleDigit('.')).substring(0, value.toString().length);
}
-function getUnitRateValue(toLocaleDigit: (arg: string) => string, customUnitRate?: Rate) {
- return getRateDisplayValue((customUnitRate?.rate ?? 0) / CONST.POLICY.CUSTOM_UNIT_RATE_BASE_OFFSET, toLocaleDigit);
+function getUnitRateValue(toLocaleDigit: (arg: string) => string, customUnitRate?: Rate, withDecimals?: boolean) {
+ return getRateDisplayValue((customUnitRate?.rate ?? 0) / CONST.POLICY.CUSTOM_UNIT_RATE_BASE_OFFSET, toLocaleDigit, withDecimals);
}
/**
* Get the brick road indicator status for a policy. The policy has an error status if there is a policy member error, a custom unit error or a field error.
*/
-function getPolicyBrickRoadIndicatorStatus(policy: OnyxEntry): ValueOf | undefined {
- if (hasEmployeeListError(policy) || hasCustomUnitsError(policy) || hasPolicyErrorFields(policy) || hasSyncError(policy)) {
+function getPolicyBrickRoadIndicatorStatus(policy: OnyxEntry, isConnectionInProgress: boolean): ValueOf | undefined {
+ if (hasEmployeeListError(policy) || hasCustomUnitsError(policy) || hasPolicyErrorFields(policy) || hasSyncError(policy, isConnectionInProgress)) {
return CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR;
}
return undefined;
}
+function getPolicyRole(policy: OnyxInputOrEntry, currentUserLogin: string | undefined) {
+ return policy?.role ?? policy?.employeeList?.[currentUserLogin ?? '-1']?.role;
+}
+
/**
* Check if the policy can be displayed
* If offline, always show the policy pending deletion.
@@ -166,12 +177,12 @@ function getPolicyBrickRoadIndicatorStatus(policy: OnyxEntry): ValueOf, isOffline: boolean): boolean {
+function shouldShowPolicy(policy: OnyxEntry, isOffline: boolean, currentUserLogin: string | undefined): boolean {
return (
!!policy &&
(policy?.type !== CONST.POLICY.TYPE.PERSONAL || !!policy?.isJoinRequestPending) &&
(isOffline || policy?.pendingAction !== CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE || Object.keys(policy.errors ?? {}).length > 0) &&
- !!policy?.role
+ !!getPolicyRole(policy, currentUserLogin)
);
}
@@ -183,14 +194,12 @@ function isExpensifyTeam(email: string | undefined): boolean {
/**
* Checks if the current user is an admin of the policy.
*/
-const isPolicyAdmin = (policy: OnyxInputOrEntry, currentUserLogin?: string): boolean =>
- (policy?.role ?? (currentUserLogin && policy?.employeeList?.[currentUserLogin]?.role)) === CONST.POLICY.ROLE.ADMIN;
+const isPolicyAdmin = (policy: OnyxInputOrEntry, currentUserLogin?: string): boolean => getPolicyRole(policy, currentUserLogin) === CONST.POLICY.ROLE.ADMIN;
/**
* Checks if the current user is of the role "user" on the policy.
*/
-const isPolicyUser = (policy: OnyxInputOrEntry, currentUserLogin?: string): boolean =>
- (policy?.role ?? (currentUserLogin && policy?.employeeList?.[currentUserLogin]?.role)) === CONST.POLICY.ROLE.USER;
+const isPolicyUser = (policy: OnyxInputOrEntry, currentUserLogin?: string): boolean => getPolicyRole(policy, currentUserLogin) === CONST.POLICY.ROLE.USER;
/**
* Checks if the policy is a free group policy.
@@ -557,9 +566,9 @@ function getPolicy(policyID: string | undefined): OnyxEntry {
}
/** Return active policies where current user is an admin */
-function getActiveAdminWorkspaces(policies: OnyxCollection | null): Policy[] {
+function getActiveAdminWorkspaces(policies: OnyxCollection | null, currentUserLogin: string | undefined): Policy[] {
const activePolicies = getActivePolicies(policies);
- return activePolicies.filter((policy) => shouldShowPolicy(policy, NetworkStore.isOffline()) && isPolicyAdmin(policy));
+ return activePolicies.filter((policy) => shouldShowPolicy(policy, NetworkStore.isOffline(), currentUserLogin) && isPolicyAdmin(policy, currentUserLogin));
}
/** Whether the user can send invoice from the workspace */
@@ -569,8 +578,8 @@ function canSendInvoiceFromWorkspace(policyID: string | undefined): boolean {
}
/** Whether the user can send invoice */
-function canSendInvoice(policies: OnyxCollection | null): boolean {
- return getActiveAdminWorkspaces(policies).length > 0;
+function canSendInvoice(policies: OnyxCollection | null, currentUserLogin: string | undefined): boolean {
+ return getActiveAdminWorkspaces(policies, currentUserLogin).length > 0;
// TODO: Uncomment the following line when the invoices screen is ready - https://github.com/Expensify/App/issues/45175.
// return getActiveAdminWorkspaces(policies).some((policy) => canSendInvoiceFromWorkspace(policy.id));
}
@@ -969,6 +978,14 @@ function getWorkspaceAccountID(policyID: string) {
return policy.workspaceAccountID ?? 0;
}
+function getDomainNameForPolicy(policyID?: string): string {
+ if (!policyID) {
+ return '';
+ }
+
+ return `${CONST.EXPENSIFY_POLICY_DOMAIN}${policyID}${CONST.EXPENSIFY_POLICY_DOMAIN_EXTENSION}`;
+}
+
export {
canEditTaxRate,
extractPolicyIDFromPath,
@@ -993,6 +1010,7 @@ export {
getTagLists,
getTaxByID,
getUnitRateValue,
+ getRateDisplayValue,
goBackFromInvalidPolicy,
hasAccountingConnections,
hasSyncError,
@@ -1074,6 +1092,7 @@ export {
getWorkspaceAccountID,
getAllTaxRatesNamesAndKeys as getAllTaxRates,
getTagNamesFromTagsLists,
+ getDomainNameForPolicy,
};
export type {MemberEmailsToAccountIDs};
diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts
index d35be495c5c3..7b226b2e5c8e 100644
--- a/src/libs/ReportUtils.ts
+++ b/src/libs/ReportUtils.ts
@@ -9,7 +9,7 @@ import lodashMaxBy from 'lodash/maxBy';
import type {OnyxCollection, OnyxEntry, OnyxUpdate} from 'react-native-onyx';
import Onyx from 'react-native-onyx';
import type {SvgProps} from 'react-native-svg';
-import type {OriginalMessageModifiedExpense} from 'src/types/onyx/OriginalMessage';
+import type {OriginalMessageIOU, OriginalMessageModifiedExpense} from 'src/types/onyx/OriginalMessage';
import type {TupleToUnion, ValueOf} from 'type-fest';
import type {FileObject} from '@components/AttachmentModal';
import {FallbackAvatar, QBOCircle, XeroCircle} from '@components/Icon/Expensicons';
@@ -461,6 +461,7 @@ type OptionData = {
amountInputProps?: MoneyRequestAmountInputProps;
tabIndex?: 0 | -1;
isConciergeChat?: boolean;
+ isBold?: boolean;
} & Report;
type OnyxDataTaskAssigneeChat = {
@@ -1931,9 +1932,11 @@ function getWorkspaceIcon(report: OnyxInputOrEntry, policy?: OnyxInputOr
if (iconFromCache && (isSameAvatarURL || isDefaultWorkspaceAvatar) && !hasWorkSpaceNameChanged) {
return iconFromCache.icon;
}
- // `avatarURL` can be an empty string, so we have to use || operator here
+ // disabling to protect against empty strings
// eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
- const policyExpenseChatAvatarSource = avatarURL || getDefaultWorkspaceAvatar(workspaceName);
+ const policyAvatarURL = report?.policyAvatar || allPolicies?.[`${ONYXKEYS.COLLECTION.POLICY}${report?.policyID}`]?.avatarURL;
+ // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
+ const policyExpenseChatAvatarSource = policyAvatarURL || getDefaultWorkspaceAvatar(workspaceName);
const workspaceIcon: Icon = {
source: policyExpenseChatAvatarSource ?? '',
@@ -2521,17 +2524,24 @@ function getLastVisibleMessage(reportID: string | undefined, actionsToMerge: Rep
}
/**
- * Checks if a report is an open task report assigned to current user.
+ * Checks if a report is waiting for the manager to complete an action.
+ * Example: the assignee of an open task report or the manager of a processing expense report.
*
* @param [parentReportAction] - The parent report action of the report (Used to check if the task has been canceled)
*/
-function isWaitingForAssigneeToCompleteTask(report: OnyxEntry, parentReportAction: OnyxEntry): boolean {
+function isWaitingForAssigneeToCompleteAction(report: OnyxEntry, parentReportAction: OnyxEntry): boolean {
if (report?.hasOutstandingChildTask) {
return true;
}
- if (isOpenTaskReport(report, parentReportAction) && !report?.hasParentAccess && isReportManager(report)) {
- return true;
+ if (!report?.hasParentAccess && isReportManager(report)) {
+ if (isOpenTaskReport(report, parentReportAction)) {
+ return true;
+ }
+
+ if (isProcessingReport(report) && isExpenseReport(report)) {
+ return true;
+ }
}
return false;
@@ -2577,7 +2587,7 @@ function requiresAttentionFromCurrentUser(optionOrReport: OnyxEntry | Op
return true;
}
- if (isWaitingForAssigneeToCompleteTask(optionOrReport, parentReportAction)) {
+ if (isWaitingForAssigneeToCompleteAction(optionOrReport, parentReportAction)) {
return true;
}
@@ -3667,7 +3677,7 @@ function getReportName(
if (reportID) {
const reportNameFromCache = reportNameCache.get(cacheKey);
- if (reportNameFromCache && reportNameFromCache.reportName === report?.reportName) {
+ if (reportNameFromCache?.reportName && reportNameFromCache.reportName === report?.reportName) {
return reportNameFromCache.reportName;
}
}
@@ -3680,7 +3690,10 @@ function getReportName(
return getIOUSubmittedMessage(parentReportAction);
}
if (parentReportAction?.actionName === CONST.REPORT.ACTIONS.TYPE.FORWARDED) {
- return getIOUForwardedMessage(parentReportAction);
+ return getIOUForwardedMessage(parentReportAction, report);
+ }
+ if (parentReportAction?.actionName === CONST.REPORT.ACTIONS.TYPE.REJECTED) {
+ return getRejectedReportMessage();
}
if (parentReportAction?.actionName === CONST.REPORT.ACTIONS.TYPE.APPROVED) {
return getIOUApprovedMessage(parentReportAction);
@@ -4405,8 +4418,27 @@ function getIOUApprovedMessage(reportAction: ReportAction) {
return Localize.translateLocal('iou.approvedAmount', {amount: getFormattedAmount(reportAction)});
}
-function getIOUForwardedMessage(reportAction: ReportAction) {
- return Localize.translateLocal('iou.forwardedAmount', {amount: getFormattedAmount(reportAction)});
+/**
+ * We pass the reportID as older FORWARDED actions do not have the amount & currency stored in the message
+ * so we retrieve the amount from the report instead
+ */
+function getIOUForwardedMessage(reportAction: ReportAction, reportOrID: OnyxInputOrEntry | string) {
+ const expenseReport = typeof reportOrID === 'string' ? getReport(reportOrID) : reportOrID;
+ const originalMessage = ReportActionsUtils.getOriginalMessage(reportAction) as OriginalMessageIOU;
+ let formattedAmount;
+
+ // Older FORWARDED action might not have the amount stored in the original message, we'll fallback to getting the amount from the report instead.
+ if (originalMessage?.amount) {
+ formattedAmount = getFormattedAmount(reportAction);
+ } else {
+ formattedAmount = CurrencyUtils.convertToDisplayString(getMoneyRequestSpendBreakdown(expenseReport).totalDisplaySpend, expenseReport?.currency);
+ }
+
+ return Localize.translateLocal('iou.forwardedAmount', {amount: formattedAmount});
+}
+
+function getRejectedReportMessage() {
+ return Localize.translateLocal('iou.rejectedThisReport');
}
function getWorkspaceNameUpdatedMessage(action: ReportAction) {
@@ -4984,6 +5016,10 @@ function buildOptimisticTaskReportAction(
};
}
+function isWorkspaceChat(chatType: string) {
+ return chatType === CONST.REPORT.CHAT_TYPE.POLICY_ADMINS || chatType === CONST.REPORT.CHAT_TYPE.POLICY_ANNOUNCE || chatType === CONST.REPORT.CHAT_TYPE.POLICY_EXPENSE_CHAT;
+}
+
/**
* Builds an optimistic chat report with a randomly generated reportID and as much information as we currently have
*/
@@ -5005,10 +5041,11 @@ function buildOptimisticChatReport(
optimisticReportID = '',
shouldShowParticipants = true,
): OptimisticChatReport {
+ const isWorkspaceChatType = chatType && isWorkspaceChat(chatType);
const participants = participantList.reduce((reportParticipants: Participants, accountID: number) => {
const participant: ReportParticipant = {
hidden: !shouldShowParticipants,
- role: accountID === currentUserAccountID ? CONST.REPORT.ROLE.ADMIN : CONST.REPORT.ROLE.MEMBER,
+ ...(!isWorkspaceChatType && {role: accountID === currentUserAccountID ? CONST.REPORT.ROLE.ADMIN : CONST.REPORT.ROLE.MEMBER}),
};
// eslint-disable-next-line no-param-reassign
reportParticipants[accountID] = participant;
@@ -6319,6 +6356,11 @@ function canRequestMoney(report: OnyxEntry, policy: OnyxEntry, o
return false;
}
+ // Current user must be a manager or owner of this IOU
+ if (isIOUReport(report) && currentUserAccountID !== report?.managerID && currentUserAccountID !== report?.ownerAccountID) {
+ return false;
+ }
+
// User can submit expenses in any IOU report, unless paid, but the user can only submit expenses in an expense report
// which is tied to their workspace chat.
if (isMoneyRequestReport(report)) {
@@ -6386,9 +6428,7 @@ function getMoneyRequestOptions(report: OnyxEntry, policy: OnyxEntry currentUserPersonalDetails?.accountID !== accountID && !PolicyUtils.isExpensifyTeam(allPersonalDetails?.[accountID]?.login),
- );
+ const otherParticipants = reportParticipants.filter((accountID) => currentUserPersonalDetails?.accountID !== accountID);
const hasSingleParticipantInReport = otherParticipants.length === 1;
let options: IOUType[] = [];
@@ -7735,6 +7775,10 @@ function hasMissingInvoiceBankAccount(iouReportID: string): boolean {
return invoiceReport?.ownerAccountID === currentUserAccountID && isEmptyObject(getPolicy(invoiceReport?.policyID)?.invoice?.bankAccount ?? {}) && isSettled(iouReportID);
}
+function isSubmittedExpenseReportManagerWithoutParentAccess(report: OnyxEntry) {
+ return isExpenseReport(report) && report?.hasParentAccess === false && report?.managerID === currentUserAccountID && isProcessingReport(report);
+}
+
export {
addDomainToShortMention,
completeShortMention,
@@ -7831,6 +7875,7 @@ export {
getIOUReportActionMessage,
getIOUApprovedMessage,
getIOUForwardedMessage,
+ getRejectedReportMessage,
getWorkspaceNameUpdatedMessage,
getIOUSubmittedMessage,
getIcons,
@@ -7930,6 +7975,7 @@ export {
isEmptyReport,
isRootGroupChat,
isExpenseReport,
+ isSubmittedExpenseReportManagerWithoutParentAccess,
isExpenseRequest,
isExpensifyOnlyParticipantInReport,
isGroupChat,
@@ -7981,7 +8027,7 @@ export {
isUserCreatedPolicyRoom,
isValidReport,
isValidReportIDFromPath,
- isWaitingForAssigneeToCompleteTask,
+ isWaitingForAssigneeToCompleteAction,
isInvoiceRoom,
isInvoiceRoomWithID,
isInvoiceReport,
diff --git a/src/libs/SearchParser/searchParser.js b/src/libs/SearchParser/searchParser.js
index 64d5334aa265..a93e3fae8551 100644
--- a/src/libs/SearchParser/searchParser.js
+++ b/src/libs/SearchParser/searchParser.js
@@ -199,7 +199,9 @@ function peg$parse(input, options) {
var peg$c21 = "keyword";
var peg$c22 = "sortBy";
var peg$c23 = "sortOrder";
- var peg$c24 = "\"";
+ var peg$c24 = "policyID";
+ var peg$c25 = "has";
+ var peg$c26 = "\"";
var peg$r0 = /^[:=]/;
var peg$r1 = /^[^"\r\n]/;
@@ -231,13 +233,22 @@ function peg$parse(input, options) {
var peg$e22 = peg$literalExpectation("keyword", false);
var peg$e23 = peg$literalExpectation("sortBy", false);
var peg$e24 = peg$literalExpectation("sortOrder", false);
- var peg$e25 = peg$literalExpectation("\"", false);
- var peg$e26 = peg$classExpectation(["\"", "\r", "\n"], true, false);
- var peg$e27 = peg$classExpectation([["A", "Z"], ["a", "z"], ["0", "9"], "_", "@", ".", "/", "#", "&", "+", "-", "\\", "'", ",", ";"], false, false);
- var peg$e28 = peg$otherExpectation("whitespace");
- var peg$e29 = peg$classExpectation([" ", "\t", "\r", "\n"], false, false);
+ var peg$e25 = peg$literalExpectation("policyID", false);
+ var peg$e26 = peg$literalExpectation("has", false);
+ var peg$e27 = peg$literalExpectation("\"", false);
+ var peg$e28 = peg$classExpectation(["\"", "\r", "\n"], true, false);
+ var peg$e29 = peg$classExpectation([["A", "Z"], ["a", "z"], ["0", "9"], "_", "@", ".", "/", "#", "&", "+", "-", "\\", "'", ",", ";"], false, false);
+ var peg$e30 = peg$otherExpectation("whitespace");
+ var peg$e31 = peg$classExpectation([" ", "\t", "\r", "\n"], false, false);
+
+ var peg$f0 = function(filters) {
+ const withDefaults = applyDefaults(filters);
+ if (defaultValues.policyID) {
+ return applyPolicyID(withDefaults);
+ }
- var peg$f0 = function(filters) { return applyDefaults(filters); };
+ return withDefaults;
+ };
var peg$f1 = function(head, tail) {
const allFilters = [head, ...tail.map(([_, filter]) => filter)].filter(filter => filter !== null);
if (!allFilters.length) {
@@ -251,10 +262,9 @@ function peg$parse(input, options) {
if(!keywords.length){
return nonKeywords.reduce((result, filter) => buildFilter("and", result, filter))
}
-
+
return buildFilter("and", keywords.reduce((result, filter) => buildFilter("or", result, filter)), nonKeywords.reduce((result, filter) => buildFilter("and", result, filter)))
-
-
+
return allFilters.reduce((result, filter) => buildFilter("and", result, filter));
};
var peg$f2 = function(field, op, value) {
@@ -263,6 +273,11 @@ function peg$parse(input, options) {
return null;
}
+ if (isPolicyID(field)) {
+ updateDefaultValues(field, value.trim());
+ return null;
+ }
+
if (!field && !op) {
return buildFilter('eq', 'keyword', value.trim());
}
@@ -297,10 +312,12 @@ function peg$parse(input, options) {
var peg$f25 = function() { return "keyword"; };
var peg$f26 = function() { return "sortBy"; };
var peg$f27 = function() { return "sortOrder"; };
- var peg$f28 = function(parts) { return parts.join(''); };
- var peg$f29 = function(chars) { return chars.join(''); };
- var peg$f30 = function(chars) { return chars.join(''); };
- var peg$f31 = function() { return "and"; };
+ var peg$f28 = function() { return "policyID"; };
+ var peg$f29 = function() { return "has"; };
+ var peg$f30 = function(parts) { return parts.join(''); };
+ var peg$f31 = function(chars) { return chars.join(''); };
+ var peg$f32 = function(chars) { return chars.join(''); };
+ var peg$f33 = function() { return "and"; };
var peg$currPos = options.peg$currPos | 0;
var peg$savedPos = peg$currPos;
var peg$posDetailsCache = [{ line: 1, column: 1 }];
@@ -909,6 +926,36 @@ function peg$parse(input, options) {
s1 = peg$f27();
}
s0 = s1;
+ if (s0 === peg$FAILED) {
+ s0 = peg$currPos;
+ if (input.substr(peg$currPos, 8) === peg$c24) {
+ s1 = peg$c24;
+ peg$currPos += 8;
+ } else {
+ s1 = peg$FAILED;
+ if (peg$silentFails === 0) { peg$fail(peg$e25); }
+ }
+ if (s1 !== peg$FAILED) {
+ peg$savedPos = s0;
+ s1 = peg$f28();
+ }
+ s0 = s1;
+ if (s0 === peg$FAILED) {
+ s0 = peg$currPos;
+ if (input.substr(peg$currPos, 3) === peg$c25) {
+ s1 = peg$c25;
+ peg$currPos += 3;
+ } else {
+ s1 = peg$FAILED;
+ if (peg$silentFails === 0) { peg$fail(peg$e26); }
+ }
+ if (s1 !== peg$FAILED) {
+ peg$savedPos = s0;
+ s1 = peg$f29();
+ }
+ s0 = s1;
+ }
+ }
}
}
}
@@ -953,7 +1000,7 @@ function peg$parse(input, options) {
}
if (s1 !== peg$FAILED) {
peg$savedPos = s0;
- s1 = peg$f28(s1);
+ s1 = peg$f30(s1);
}
s0 = s1;
@@ -965,11 +1012,11 @@ function peg$parse(input, options) {
s0 = peg$currPos;
if (input.charCodeAt(peg$currPos) === 34) {
- s1 = peg$c24;
+ s1 = peg$c26;
peg$currPos++;
} else {
s1 = peg$FAILED;
- if (peg$silentFails === 0) { peg$fail(peg$e25); }
+ if (peg$silentFails === 0) { peg$fail(peg$e27); }
}
if (s1 !== peg$FAILED) {
s2 = [];
@@ -978,7 +1025,7 @@ function peg$parse(input, options) {
peg$currPos++;
} else {
s3 = peg$FAILED;
- if (peg$silentFails === 0) { peg$fail(peg$e26); }
+ if (peg$silentFails === 0) { peg$fail(peg$e28); }
}
while (s3 !== peg$FAILED) {
s2.push(s3);
@@ -987,19 +1034,19 @@ function peg$parse(input, options) {
peg$currPos++;
} else {
s3 = peg$FAILED;
- if (peg$silentFails === 0) { peg$fail(peg$e26); }
+ if (peg$silentFails === 0) { peg$fail(peg$e28); }
}
}
if (input.charCodeAt(peg$currPos) === 34) {
- s3 = peg$c24;
+ s3 = peg$c26;
peg$currPos++;
} else {
s3 = peg$FAILED;
- if (peg$silentFails === 0) { peg$fail(peg$e25); }
+ if (peg$silentFails === 0) { peg$fail(peg$e27); }
}
if (s3 !== peg$FAILED) {
peg$savedPos = s0;
- s0 = peg$f29(s2);
+ s0 = peg$f31(s2);
} else {
peg$currPos = s0;
s0 = peg$FAILED;
@@ -1022,7 +1069,7 @@ function peg$parse(input, options) {
peg$currPos++;
} else {
s2 = peg$FAILED;
- if (peg$silentFails === 0) { peg$fail(peg$e27); }
+ if (peg$silentFails === 0) { peg$fail(peg$e29); }
}
if (s2 !== peg$FAILED) {
while (s2 !== peg$FAILED) {
@@ -1032,7 +1079,7 @@ function peg$parse(input, options) {
peg$currPos++;
} else {
s2 = peg$FAILED;
- if (peg$silentFails === 0) { peg$fail(peg$e27); }
+ if (peg$silentFails === 0) { peg$fail(peg$e29); }
}
}
} else {
@@ -1040,7 +1087,7 @@ function peg$parse(input, options) {
}
if (s1 !== peg$FAILED) {
peg$savedPos = s0;
- s1 = peg$f30(s1);
+ s1 = peg$f32(s1);
}
s0 = s1;
@@ -1053,7 +1100,7 @@ function peg$parse(input, options) {
s0 = peg$currPos;
s1 = peg$parse_();
peg$savedPos = s0;
- s1 = peg$f31();
+ s1 = peg$f33();
s0 = s1;
return s0;
@@ -1069,7 +1116,7 @@ function peg$parse(input, options) {
peg$currPos++;
} else {
s1 = peg$FAILED;
- if (peg$silentFails === 0) { peg$fail(peg$e29); }
+ if (peg$silentFails === 0) { peg$fail(peg$e31); }
}
while (s1 !== peg$FAILED) {
s0.push(s1);
@@ -1078,12 +1125,12 @@ function peg$parse(input, options) {
peg$currPos++;
} else {
s1 = peg$FAILED;
- if (peg$silentFails === 0) { peg$fail(peg$e29); }
+ if (peg$silentFails === 0) { peg$fail(peg$e31); }
}
}
peg$silentFails--;
s1 = peg$FAILED;
- if (peg$silentFails === 0) { peg$fail(peg$e28); }
+ if (peg$silentFails === 0) { peg$fail(peg$e30); }
return s0;
}
@@ -1106,7 +1153,14 @@ function peg$parse(input, options) {
filters
};
}
-
+
+ function applyPolicyID(filtersWithDefaults) {
+ return {
+ ...filtersWithDefaults,
+ policyID: filtersWithDefaults.policyID
+ };
+ }
+
function updateDefaultValues(field, value) {
defaultValues[field] = value;
}
@@ -1115,6 +1169,10 @@ function peg$parse(input, options) {
return defaultValues.hasOwnProperty(field);
}
+ function isPolicyID(field) {
+ return field === 'policyID';
+ }
+
peg$result = peg$startRuleFunction();
if (options.peg$library) {
diff --git a/src/libs/SearchParser/searchParser.peggy b/src/libs/SearchParser/searchParser.peggy
index d9ede101f7f8..105a8a62bc39 100644
--- a/src/libs/SearchParser/searchParser.peggy
+++ b/src/libs/SearchParser/searchParser.peggy
@@ -32,7 +32,14 @@
filters
};
}
-
+
+ function applyPolicyID(filtersWithDefaults) {
+ return {
+ ...filtersWithDefaults,
+ policyID: filtersWithDefaults.policyID
+ };
+ }
+
function updateDefaultValues(field, value) {
defaultValues[field] = value;
}
@@ -40,10 +47,21 @@
function isDefaultField(field) {
return defaultValues.hasOwnProperty(field);
}
+
+ function isPolicyID(field) {
+ return field === 'policyID';
+ }
}
query
- = _ filters:filterList? _ { return applyDefaults(filters); }
+ = _ filters:filterList? _ {
+ const withDefaults = applyDefaults(filters);
+ if (defaultValues.policyID) {
+ return applyPolicyID(withDefaults);
+ }
+
+ return withDefaults;
+ }
filterList
= head:filter tail:(logicalAnd filter)* {
@@ -59,10 +77,9 @@ filterList
if(!keywords.length){
return nonKeywords.reduce((result, filter) => buildFilter("and", result, filter))
}
-
+
return buildFilter("and", keywords.reduce((result, filter) => buildFilter("or", result, filter)), nonKeywords.reduce((result, filter) => buildFilter("and", result, filter)))
-
-
+
return allFilters.reduce((result, filter) => buildFilter("and", result, filter));
}
@@ -73,6 +90,11 @@ filter
return null;
}
+ if (isPolicyID(field)) {
+ updateDefaultValues(field, value.trim());
+ return null;
+ }
+
if (!field && !op) {
return buildFilter('eq', 'keyword', value.trim());
}
@@ -111,6 +133,8 @@ key
/ "keyword" { return "keyword"; }
/ "sortBy" { return "sortBy"; }
/ "sortOrder" { return "sortOrder"; }
+ / "policyID" { return "policyID"; }
+ / "has" { return "has"; }
identifier
= parts:(quotedString / alphanumeric)+ { return parts.join(''); }
diff --git a/src/libs/SearchUtils.ts b/src/libs/SearchUtils.ts
index 8d4a59f3987c..b30ff55ef5ae 100644
--- a/src/libs/SearchUtils.ts
+++ b/src/libs/SearchUtils.ts
@@ -245,11 +245,6 @@ function getSortedSections(status: SearchStatus, data: ListItemDataType): TranslationPaths {
+ // eslint-disable-next-line default-case
+ switch (has) {
+ case CONST.SEARCH.CHAT_TYPES.LINK:
+ return 'search.filters.link';
+ case CONST.SEARCH.CHAT_TYPES.ATTACHMENT:
+ return 'common.attachment';
+ }
+}
+
/**
* Given object with chosen search filters builds correct query string from them
*/
function buildQueryStringFromFilters(filterValues: Partial) {
const filtersString = Object.entries(filterValues).map(([filterKey, filterValue]) => {
- if ((filterKey === FILTER_KEYS.MERCHANT || filterKey === FILTER_KEYS.DESCRIPTION || filterKey === FILTER_KEYS.REPORT_ID || filterKey === FILTER_KEYS.KEYWORD) && filterValue) {
+ if ((filterKey === FILTER_KEYS.MERCHANT || filterKey === FILTER_KEYS.DESCRIPTION || filterKey === FILTER_KEYS.REPORT_ID) && filterValue) {
+ const keyInCorrectForm = (Object.keys(CONST.SEARCH.SYNTAX_FILTER_KEYS) as KeysOfFilterKeysObject[]).find((key) => CONST.SEARCH.SYNTAX_FILTER_KEYS[key] === filterKey);
+ if (keyInCorrectForm) {
+ return `${CONST.SEARCH.SYNTAX_FILTER_KEYS[keyInCorrectForm]}:${sanitizeString(filterValue as string)}`;
+ }
+ }
+ if (filterKey === FILTER_KEYS.KEYWORD && filterValue) {
const keyInCorrectForm = (Object.keys(CONST.SEARCH.SYNTAX_FILTER_KEYS) as KeysOfFilterKeysObject[]).find((key) => CONST.SEARCH.SYNTAX_FILTER_KEYS[key] === filterKey);
if (keyInCorrectForm) {
return `${CONST.SEARCH.SYNTAX_FILTER_KEYS[keyInCorrectForm]}:${filterValue as string}`;
@@ -456,7 +463,9 @@ function buildQueryStringFromFilters(filterValues: Partial 0
) {
@@ -520,6 +529,26 @@ function getFilters(queryJSON: SearchQueryJSON) {
return filters;
}
+/**
+ * Given a SearchQueryJSON this function will try to find the value of policyID filter saved in query
+ * and return just the first policyID value from the filter.
+ *
+ * Note: `policyID` property can store multiple policy ids (just like many other search filters) as a comma separated value;
+ * however there are several places in the app (related to WorkspaceSwitcher) that will accept only a single policyID.
+ */
+function getPolicyIDFromSearchQuery(queryJSON: SearchQueryJSON) {
+ const policyIDFilter = queryJSON.policyID;
+
+ if (!policyIDFilter) {
+ return;
+ }
+
+ // policyID is a comma-separated value
+ const [policyID] = policyIDFilter.split(',');
+
+ return policyID;
+}
+
function buildFilterString(filterName: string, queryFilters: QueryFilter[]) {
let filterValueString = '';
queryFilters.forEach((queryFilter, index) => {
@@ -558,8 +587,8 @@ export {
buildSearchQueryString,
getCurrentSearchParams,
getFilters,
+ getPolicyIDFromSearchQuery,
getListItem,
- getQueryHash,
getSearchHeaderTitle,
getSections,
getShouldShowMerchant,
@@ -571,4 +600,5 @@ export {
shouldShowYear,
buildCannedSearchQuery,
getExpenseTypeTranslationKey,
+ getChatFiltersTranslationKey,
};
diff --git a/src/libs/SidebarUtils.ts b/src/libs/SidebarUtils.ts
index dedd608ce77d..0e8447635098 100644
--- a/src/libs/SidebarUtils.ts
+++ b/src/libs/SidebarUtils.ts
@@ -129,7 +129,10 @@ function getOrderedReportIDs(
return;
}
const isSystemChat = ReportUtils.isSystemChat(report);
- const shouldOverrideHidden = hasValidDraftComment(report.reportID) || hasErrorsOtherThanFailedReceipt || isFocused || isSystemChat || report.isPinned;
+ const isSubmittedExpenseReportManagerWithoutParentAccess = ReportUtils.isSubmittedExpenseReportManagerWithoutParentAccess(report);
+ const shouldOverrideHidden =
+ // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
+ hasValidDraftComment(report.reportID) || hasErrorsOtherThanFailedReceipt || isFocused || isSystemChat || report.isPinned || isSubmittedExpenseReportManagerWithoutParentAccess;
if (isHidden && !shouldOverrideHidden) {
return;
}
@@ -435,6 +438,8 @@ function getOptionData({
result.alternateText = ReportActionsUtils.getPolicyChangeLogChangeRoleMessage(lastAction);
} else if (lastAction?.actionName === CONST.REPORT.ACTIONS.TYPE.POLICY_CHANGE_LOG.DELETE_EMPLOYEE) {
result.alternateText = ReportActionsUtils.getPolicyChangeLogDeleteMemberMessage(lastAction);
+ } else if (lastAction?.actionName === CONST.REPORT.ACTIONS.TYPE.POLICY_CHANGE_LOG.DELETE_CUSTOM_UNIT_RATE) {
+ result.alternateText = ReportActionsUtils.getReportActionMessageText(lastAction) ?? '';
} else {
result.alternateText =
lastMessageTextFromReport.length > 0
diff --git a/src/libs/TransactionUtils/index.ts b/src/libs/TransactionUtils/index.ts
index e5bd5d9b0753..339f57c8e04a 100644
--- a/src/libs/TransactionUtils/index.ts
+++ b/src/libs/TransactionUtils/index.ts
@@ -16,6 +16,7 @@ import * as PolicyUtils from '@libs/PolicyUtils';
// eslint-disable-next-line import/no-cycle
import * as ReportActionsUtils from '@libs/ReportActionsUtils';
import * as ReportConnection from '@libs/ReportConnection';
+import * as ReportUtils from '@libs/ReportUtils';
import type {IOURequestType} from '@userActions/IOU';
import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
@@ -904,6 +905,14 @@ type FieldsToChange = {
reimbursable?: Array;
};
+function removeSettledAndApprovedTransactions(transactionIDs: string[]) {
+ return transactionIDs.filter(
+ (transactionID) =>
+ !ReportUtils.isSettled(allTransactions?.[`${ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`]?.reportID) &&
+ !ReportUtils.isReportApproved(allTransactions?.[`${ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`]?.reportID),
+ );
+}
+
/**
* This function compares fields of duplicate transactions and determines which fields should be kept and which should be changed.
*
@@ -925,7 +934,7 @@ type FieldsToChange = {
function compareDuplicateTransactionFields(transactionID: string): {keep: Partial; change: FieldsToChange} {
const transactionViolations = allTransactionViolations?.[`${ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS}${transactionID}`];
const duplicates = transactionViolations?.find((violation) => violation.name === CONST.VIOLATIONS.DUPLICATED_TRANSACTION)?.data?.duplicates ?? [];
- const transactions = [transactionID, ...duplicates].map((item) => getTransaction(item));
+ const transactions = removeSettledAndApprovedTransactions([transactionID, ...duplicates]).map((item) => getTransaction(item));
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const keep: Record = {};
// eslint-disable-next-line @typescript-eslint/no-explicit-any
@@ -1039,7 +1048,7 @@ function buildTransactionsMergeParams(reviewDuplicates: OnyxEntry),
created: getFormattedCreated(originalTransaction as OnyxEntry),
transactionID: reviewDuplicates?.transactionID ?? '',
- transactionIDList: reviewDuplicates?.duplicates ?? [],
+ transactionIDList: removeSettledAndApprovedTransactions(reviewDuplicates?.duplicates ?? []),
billable: reviewDuplicates?.billable ?? false,
reimbursable: reviewDuplicates?.reimbursable ?? false,
category: reviewDuplicates?.category ?? '',
@@ -1125,6 +1134,7 @@ export {
buildTransactionsMergeParams,
getReimbursable,
isPayAtEndExpense,
+ removeSettledAndApprovedTransactions,
getCardName,
};
diff --git a/src/libs/WorkspacesSettingsUtils.ts b/src/libs/WorkspacesSettingsUtils.ts
index 9b96c8404bf6..ed46b0b5f5ec 100644
--- a/src/libs/WorkspacesSettingsUtils.ts
+++ b/src/libs/WorkspacesSettingsUtils.ts
@@ -5,7 +5,8 @@ import CONST from '@src/CONST';
import type {TranslationPaths} from '@src/languages/types';
import ONYXKEYS from '@src/ONYXKEYS';
import type {Policy, ReimbursementAccount, Report, ReportAction, ReportActions, TransactionViolations} from '@src/types/onyx';
-import type {Unit} from '@src/types/onyx/Policy';
+import type {PolicyConnectionSyncProgress, Unit} from '@src/types/onyx/Policy';
+import {isConnectionInProgress} from './actions/connections';
import * as CurrencyUtils from './CurrencyUtils';
import type {Phrase, PhraseParameters} from './Localize';
import * as OptionsListUtils from './OptionsListUtils';
@@ -18,14 +19,6 @@ type CheckingMethod = () => boolean;
type BrickRoad = ValueOf | undefined;
-let allPolicies: OnyxCollection;
-
-Onyx.connect({
- key: ONYXKEYS.COLLECTION.POLICY,
- waitForCollectionCallback: true,
- callback: (value) => (allPolicies = value),
-});
-
let reimbursementAccount: OnyxEntry;
Onyx.connect({
@@ -100,7 +93,7 @@ const getBrickRoadForPolicy = (report: Report, altReportActions?: OnyxCollection
return shouldShowGreenDotIndicator ? CONST.BRICK_ROAD_INDICATOR_STATUS.INFO : undefined;
};
-function hasGlobalWorkspaceSettingsRBR(policies: OnyxCollection) {
+function hasGlobalWorkspaceSettingsRBR(policies: OnyxCollection, allConnectionProgresses: OnyxCollection) {
// When attempting to open a policy with an invalid policyID, the policy collection is updated to include policy objects with error information.
// Only policies displayed on the policy list page should be verified. Otherwise, the user will encounter an RBR unrelated to any policies on the list.
const cleanPolicies = Object.fromEntries(Object.entries(policies ?? {}).filter(([, policy]) => policy?.id));
@@ -110,7 +103,10 @@ function hasGlobalWorkspaceSettingsRBR(policies: OnyxCollection) {
() => Object.values(cleanPolicies).some(hasCustomUnitsError),
() => Object.values(cleanPolicies).some(hasTaxRateError),
() => Object.values(cleanPolicies).some(hasEmployeeListError),
- () => Object.values(cleanPolicies).some(hasSyncError),
+ () =>
+ Object.values(cleanPolicies).some((cleanPolicy) =>
+ hasSyncError(cleanPolicy, isConnectionInProgress(allConnectionProgresses?.[`${ONYXKEYS.COLLECTION.POLICY_CONNECTION_SYNC_PROGRESS}${cleanPolicy?.id}`], cleanPolicy)),
+ ),
() => Object.keys(reimbursementAccount?.errors ?? {}).length > 0,
];
@@ -154,19 +150,6 @@ function getChatTabBrickRoad(policyID?: string): BrickRoad | undefined {
return undefined;
}
-function checkIfWorkspaceSettingsTabHasRBR(policyID?: string) {
- if (!policyID) {
- return hasGlobalWorkspaceSettingsRBR(allPolicies);
- }
- const policy = allPolicies ? allPolicies[`${ONYXKEYS.COLLECTION.POLICY}${policyID}`] : null;
-
- if (!policy) {
- return false;
- }
-
- return hasWorkspaceSettingsRBR(policy);
-}
-
/**
* @returns a map where the keys are policyIDs and the values are BrickRoads for each policy
*/
@@ -318,7 +301,6 @@ export {
getWorkspacesBrickRoads,
getWorkspacesUnreadStatuses,
hasGlobalWorkspaceSettingsRBR,
- checkIfWorkspaceSettingsTabHasRBR,
hasWorkspaceSettingsRBR,
getChatTabBrickRoad,
getUnitTranslationKey,
diff --git a/src/libs/actions/App.ts b/src/libs/actions/App.ts
index fcbfc73b3dbd..a0f60752913e 100644
--- a/src/libs/actions/App.ts
+++ b/src/libs/actions/App.ts
@@ -362,6 +362,7 @@ function createWorkspaceWithPolicyDraftAndNavigateToIt(policyOwnerEmail = '', po
// We must call goBack() to remove the /transition route from history
Navigation.goBack();
}
+ savePolicyDraftByNewWorkspace(policyID, policyName, policyOwnerEmail, makeMeAdmin);
Navigation.navigate(ROUTES.WORKSPACE_INITIAL.getRoute(policyID, backTo));
})
.then(endSignOnTransition);
diff --git a/src/libs/actions/BankAccounts.ts b/src/libs/actions/BankAccounts.ts
index 6e718b1bde34..a7b4f6703f8f 100644
--- a/src/libs/actions/BankAccounts.ts
+++ b/src/libs/actions/BankAccounts.ts
@@ -22,7 +22,7 @@ import type {Route} from '@src/ROUTES';
import type {PersonalBankAccountForm} from '@src/types/form';
import type {ACHContractStepProps, BeneficialOwnersStepProps, CompanyStepProps, RequestorStepProps} from '@src/types/form/ReimbursementAccountForm';
import type PlaidBankAccount from '@src/types/onyx/PlaidBankAccount';
-import type {BankAccountStep, BankAccountSubStep} from '@src/types/onyx/ReimbursementAccount';
+import type {BankAccountStep, ReimbursementAccountStep, ReimbursementAccountSubStep} from '@src/types/onyx/ReimbursementAccount';
import type {OnyxData} from '@src/types/onyx/Request';
import * as ReimbursementAccount from './ReimbursementAccount';
@@ -40,10 +40,6 @@ export {
export {openPlaidBankAccountSelector, openPlaidBankLogin} from './Plaid';
export {openOnfidoFlow, answerQuestionsForWallet, verifyIdentity, acceptWalletTerms} from './Wallet';
-type ReimbursementAccountStep = BankAccountStep | '';
-
-type ReimbursementAccountSubStep = BankAccountSubStep | '';
-
type AccountFormValues = typeof ONYXKEYS.FORMS.PERSONAL_BANK_ACCOUNT_FORM | typeof ONYXKEYS.FORMS.REIMBURSEMENT_ACCOUNT_FORM;
type BusinessAddress = {
diff --git a/src/libs/actions/Card.ts b/src/libs/actions/Card.ts
index 747df5d3998e..a09baf7109b7 100644
--- a/src/libs/actions/Card.ts
+++ b/src/libs/actions/Card.ts
@@ -4,6 +4,7 @@ import type {ValueOf} from 'type-fest';
import * as API from '@libs/API';
import type {
ActivatePhysicalExpensifyCardParams,
+ CardDeactivateParams,
OpenCardDetailsPageParams,
ReportVirtualExpensifyCardFraudParams,
RequestReplacementExpensifyCardParams,
@@ -16,8 +17,10 @@ import type {
import {READ_COMMANDS, SIDE_EFFECT_REQUEST_COMMANDS, WRITE_COMMANDS} from '@libs/API/types';
import * as ErrorUtils from '@libs/ErrorUtils';
import * as NetworkStore from '@libs/Network/NetworkStore';
+import * as PolicyUtils from '@libs/PolicyUtils';
import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
+import type {Card} from '@src/types/onyx';
import type {CardLimitType, ExpensifyCardDetails, IssueNewCardData, IssueNewCardStep} from '@src/types/onyx/Card';
import type {ConnectionName} from '@src/types/onyx/Policy';
@@ -210,100 +213,96 @@ function revealVirtualCardDetails(cardID: number): Promise
});
}
-function updateSettlementFrequency(workspaceAccountID: number, frequency: ValueOf) {
- // TODO: remove this code when the API is ready
- if (frequency === CONST.EXPENSIFY_CARD.FREQUENCY_SETTING.DAILY) {
- Onyx.merge(`${ONYXKEYS.COLLECTION.PRIVATE_EXPENSIFY_CARD_SETTINGS}${workspaceAccountID}`, {
- monthlySettlementDate: null,
- });
- } else {
- Onyx.merge(`${ONYXKEYS.COLLECTION.PRIVATE_EXPENSIFY_CARD_SETTINGS}${workspaceAccountID}`, {
- monthlySettlementDate: new Date(),
- });
- }
+function updateSettlementFrequency(workspaceAccountID: number, settlementFrequency: ValueOf, currentFrequency?: Date) {
+ const monthlySettlementDate = settlementFrequency === CONST.EXPENSIFY_CARD.FREQUENCY_SETTING.DAILY ? null : new Date();
+
+ const optimisticData: OnyxUpdate[] = [
+ {
+ onyxMethod: Onyx.METHOD.MERGE,
+ key: `${ONYXKEYS.COLLECTION.PRIVATE_EXPENSIFY_CARD_SETTINGS}${workspaceAccountID}`,
+ value: {
+ monthlySettlementDate,
+ },
+ },
+ ];
- // TODO: uncomment this code when the API is ready
- // const optimisticData: OnyxUpdate[] = [
- // {
- // onyxMethod: Onyx.METHOD.MERGE,
- // key: `${ONYXKEYS.COLLECTION.PRIVATE_EXPENSIFY_CARD_SETTINGS}${workspaceAccountID}`,
- // value: {
- // monthlySettlementDate: '',
- // },
- // },
- // ];
- //
- // const successData: OnyxUpdate[] = [
- // {
- // onyxMethod: Onyx.METHOD.MERGE,
- // key: `${ONYXKEYS.COLLECTION.PRIVATE_EXPENSIFY_CARD_SETTINGS}${workspaceAccountID}`,
- // value: {
- // monthlySettlementDate: '',
- // },
- // },
- // ];
- //
- // const failureData: OnyxUpdate[] = [
- // {
- // onyxMethod: Onyx.METHOD.MERGE,
- // key: `${ONYXKEYS.COLLECTION.PRIVATE_EXPENSIFY_CARD_SETTINGS}${workspaceAccountID}`,
- // value: {
- // monthlySettlementDate: null,
- // },
- // },
- // ];
- //
- // const parameters = {
- // workspaceAccountID,
- // settlementFrequency: frequency,
- // };
- //
- // API.write(WRITE_COMMANDS.UPDATE_CARD_SETTLEMENT_FREQUENCY, parameters, {optimisticData, successData, failureData});
+ const successData: OnyxUpdate[] = [
+ {
+ onyxMethod: Onyx.METHOD.MERGE,
+ key: `${ONYXKEYS.COLLECTION.PRIVATE_EXPENSIFY_CARD_SETTINGS}${workspaceAccountID}`,
+ value: {
+ monthlySettlementDate,
+ },
+ },
+ ];
+
+ const failureData: OnyxUpdate[] = [
+ {
+ onyxMethod: Onyx.METHOD.MERGE,
+ key: `${ONYXKEYS.COLLECTION.PRIVATE_EXPENSIFY_CARD_SETTINGS}${workspaceAccountID}`,
+ value: {
+ monthlySettlementDate: currentFrequency,
+ },
+ },
+ ];
+
+ const parameters = {
+ workspaceAccountID,
+ settlementFrequency,
+ };
+
+ API.write(WRITE_COMMANDS.UPDATE_CARD_SETTLEMENT_FREQUENCY, parameters, {optimisticData, successData, failureData});
}
-function updateSettlementAccount(workspaceAccountID: number, accountID: number) {
- // TODO: remove this code when the API is ready
- Onyx.merge(`${ONYXKEYS.COLLECTION.PRIVATE_EXPENSIFY_CARD_SETTINGS}${workspaceAccountID}`, {
- paymentBankAccountID: accountID,
- });
+function updateSettlementAccount(workspaceAccountID: number, policyID: string, settlementBankAccountID?: number, currentSettlementBankAccountID?: number) {
+ if (!settlementBankAccountID) {
+ return;
+ }
+ const domainName = PolicyUtils.getDomainNameForPolicy(policyID);
+
+ const optimisticData: OnyxUpdate[] = [
+ {
+ onyxMethod: Onyx.METHOD.MERGE,
+ key: `${ONYXKEYS.COLLECTION.PRIVATE_EXPENSIFY_CARD_SETTINGS}${workspaceAccountID}`,
+ value: {
+ paymentBankAccountID: settlementBankAccountID,
+ },
+ },
+ ];
+
+ const successData: OnyxUpdate[] = [
+ {
+ onyxMethod: Onyx.METHOD.MERGE,
+ key: `${ONYXKEYS.COLLECTION.PRIVATE_EXPENSIFY_CARD_SETTINGS}${workspaceAccountID}`,
+ value: {
+ paymentBankAccountID: settlementBankAccountID,
+ },
+ },
+ ];
- // TODO: uncomment this code when the API is ready
- // const optimisticData: OnyxUpdate[] = [
- // {
- // onyxMethod: Onyx.METHOD.MERGE,
- // key: `${ONYXKEYS.COLLECTION.PRIVATE_EXPENSIFY_CARD_SETTINGS}${workspaceAccountID}`,
- // value: {
- // paymentBankAccountID: accountID,
- // },
- // },
- // ];
- //
- // const successData: OnyxUpdate[] = [
- // {
- // onyxMethod: Onyx.METHOD.MERGE,
- // key: `${ONYXKEYS.COLLECTION.PRIVATE_EXPENSIFY_CARD_SETTINGS}${workspaceAccountID}`,
- // value: {
- // paymentBankAccountID: accountID,
- // },
- // },
- // ];
- //
- // const failureData: OnyxUpdate[] = [
- // {
- // onyxMethod: Onyx.METHOD.MERGE,
- // key: `${ONYXKEYS.COLLECTION.PRIVATE_EXPENSIFY_CARD_SETTINGS}${workspaceAccountID}`,
- // value: {
- // paymentBankAccountID: null,
- // },
- // },
- // ];
- //
- // const parameters = {
- // workspaceAccountID,
- // settlementBankAccountID: accountID,
- // };
- //
- // API.write(WRITE_COMMANDS.UPDATE_CARD_SETTLEMENT_ACCOUNT, parameters, {optimisticData, successData, failureData});
+ const failureData: OnyxUpdate[] = [
+ {
+ onyxMethod: Onyx.METHOD.MERGE,
+ key: `${ONYXKEYS.COLLECTION.PRIVATE_EXPENSIFY_CARD_SETTINGS}${workspaceAccountID}`,
+ value: {
+ paymentBankAccountID: currentSettlementBankAccountID,
+ },
+ },
+ ];
+
+ const parameters = {
+ domainName,
+ settlementBankAccountID,
+ };
+
+ API.write(WRITE_COMMANDS.UPDATE_CARD_SETTLEMENT_ACCOUNT, parameters, {optimisticData, successData, failureData});
+}
+
+function getCardDefaultName(userName?: string) {
+ if (!userName) {
+ return '';
+ }
+ return `${userName}'s Card`;
}
function setIssueNewCardStepAndData({data, isEditing, step}: IssueNewCardFlowData) {
@@ -333,7 +332,10 @@ function updateExpensifyCardLimit(workspaceAccountID: number, cardID: number, ne
availableSpend: newAvailableSpend,
nameValuePairs: {
unapprovedExpenseLimit: newLimit,
+ pendingFields: {unapprovedExpenseLimit: CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE},
},
+ pendingAction: CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE,
+ pendingFields: {availableSpend: CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE},
isLoading: true,
errors: null,
},
@@ -347,6 +349,11 @@ function updateExpensifyCardLimit(workspaceAccountID: number, cardID: number, ne
key: `${ONYXKEYS.COLLECTION.WORKSPACE_CARDS_LIST}${workspaceAccountID}_${CONST.EXPENSIFY_CARD.BANK}`,
value: {
[cardID]: {
+ nameValuePairs: {
+ pendingFields: {unapprovedExpenseLimit: null},
+ },
+ pendingAction: null,
+ pendingFields: {availableSpend: null},
isLoading: false,
},
},
@@ -362,7 +369,10 @@ function updateExpensifyCardLimit(workspaceAccountID: number, cardID: number, ne
availableSpend: oldAvailableSpend,
nameValuePairs: {
unapprovedExpenseLimit: oldLimit,
+ pendingFields: {unapprovedExpenseLimit: null},
},
+ pendingAction: null,
+ pendingFields: {availableSpend: null},
isLoading: false,
errors: ErrorUtils.getMicroSecondOnyxErrorWithTranslationKey('common.genericErrorMessage'),
},
@@ -394,7 +404,9 @@ function updateExpensifyCardTitle(workspaceAccountID: number, cardID: number, ne
[cardID]: {
nameValuePairs: {
cardTitle: newCardTitle,
+ pendingFields: {cardTitle: CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE},
},
+ pendingAction: CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE,
isLoading: true,
errors: null,
},
@@ -408,6 +420,10 @@ function updateExpensifyCardTitle(workspaceAccountID: number, cardID: number, ne
key: `${ONYXKEYS.COLLECTION.WORKSPACE_CARDS_LIST}${workspaceAccountID}_${CONST.EXPENSIFY_CARD.BANK}`,
value: {
[cardID]: {
+ nameValuePairs: {
+ pendingFields: {cardTitle: null},
+ },
+ pendingAction: null,
isLoading: false,
},
},
@@ -422,7 +438,9 @@ function updateExpensifyCardTitle(workspaceAccountID: number, cardID: number, ne
[cardID]: {
nameValuePairs: {
cardTitle: oldCardTitle,
+ pendingFields: {cardTitle: null},
},
+ pendingAction: null,
isLoading: false,
errors: ErrorUtils.getMicroSecondOnyxErrorWithTranslationKey('common.genericErrorMessage'),
},
@@ -454,7 +472,10 @@ function updateExpensifyCardLimitType(workspaceAccountID: number, cardID: number
[cardID]: {
nameValuePairs: {
limitType: newLimitType,
+ pendingFields: {limitType: CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE},
},
+ pendingAction: CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE,
+ pendingFields: {availableSpend: CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE},
isLoading: true,
errors: null,
},
@@ -469,6 +490,11 @@ function updateExpensifyCardLimitType(workspaceAccountID: number, cardID: number
value: {
[cardID]: {
isLoading: false,
+ nameValuePairs: {
+ pendingFields: {limitType: null},
+ },
+ pendingAction: null,
+ pendingFields: {availableSpend: null},
},
},
},
@@ -482,7 +508,10 @@ function updateExpensifyCardLimitType(workspaceAccountID: number, cardID: number
[cardID]: {
nameValuePairs: {
limitType: oldLimitType,
+ pendingFields: {limitType: null},
},
+ pendingFields: {availableSpend: null},
+ pendingAction: null,
isLoading: false,
errors: ErrorUtils.getMicroSecondOnyxErrorWithTranslationKey('common.genericErrorMessage'),
},
@@ -499,6 +528,45 @@ function updateExpensifyCardLimitType(workspaceAccountID: number, cardID: number
API.write(WRITE_COMMANDS.UPDATE_EXPENSIFY_CARD_LIMIT_TYPE, parameters, {optimisticData, successData, failureData});
}
+function deactivateCard(workspaceAccountID: number, card?: Card) {
+ const authToken = NetworkStore.getAuthToken();
+ const cardID = card?.cardID ?? -1;
+
+ if (!authToken) {
+ return;
+ }
+
+ const optimisticData: OnyxUpdate[] = [
+ {
+ onyxMethod: Onyx.METHOD.MERGE,
+ key: `${ONYXKEYS.COLLECTION.WORKSPACE_CARDS_LIST}${workspaceAccountID}_${CONST.EXPENSIFY_CARD.BANK}`,
+ value: {
+ [cardID]: null,
+ },
+ },
+ ];
+
+ const failureData: OnyxUpdate[] = [
+ {
+ onyxMethod: Onyx.METHOD.MERGE,
+ key: `${ONYXKEYS.COLLECTION.WORKSPACE_CARDS_LIST}${workspaceAccountID}_${CONST.EXPENSIFY_CARD.BANK}`,
+ value: {
+ [cardID]: {
+ ...card,
+ errors: ErrorUtils.getMicroSecondOnyxErrorWithTranslationKey('common.genericErrorMessage'),
+ },
+ },
+ },
+ ];
+
+ const parameters: CardDeactivateParams = {
+ authToken,
+ cardID,
+ };
+
+ API.write(WRITE_COMMANDS.CARD_DEACTIVATE, parameters, {optimisticData, failureData});
+}
+
function startIssueNewCardFlow(policyID: string) {
const parameters: StartIssueNewCardFlowParams = {
policyID,
@@ -617,6 +685,16 @@ function toggleContinuousReconciliation(workspaceAccountID: number, shouldUseCon
});
}
+function updateSelectedFeed(feed: string, policyID: string) {
+ Onyx.update([
+ {
+ onyxMethod: Onyx.METHOD.MERGE,
+ key: `${ONYXKEYS.COLLECTION.LAST_SELECTED_FEED}${policyID}`,
+ value: feed,
+ },
+ ]);
+}
+
export {
requestReplacementExpensifyCard,
activatePhysicalExpensifyCard,
@@ -635,5 +713,8 @@ export {
openCardDetailsPage,
toggleContinuousReconciliation,
updateExpensifyCardLimitType,
+ updateSelectedFeed,
+ deactivateCard,
+ getCardDefaultName,
};
export type {ReplacementReason};
diff --git a/src/libs/actions/Delegate.ts b/src/libs/actions/Delegate.ts
new file mode 100644
index 000000000000..4797506d1a3c
--- /dev/null
+++ b/src/libs/actions/Delegate.ts
@@ -0,0 +1,159 @@
+import Onyx from 'react-native-onyx';
+import type {OnyxUpdate} from 'react-native-onyx';
+import * as API from '@libs/API';
+import {SIDE_EFFECT_REQUEST_COMMANDS} from '@libs/API/types';
+import Log from '@libs/Log';
+import * as NetworkStore from '@libs/Network/NetworkStore';
+import * as SequentialQueue from '@libs/Network/SequentialQueue';
+import ONYXKEYS from '@src/ONYXKEYS';
+import type {DelegatedAccess} from '@src/types/onyx/Account';
+import {confirmReadyToOpenApp, openApp} from './App';
+import updateSessionAuthTokens from './Session/updateSessionAuthTokens';
+
+let delegatedAccess: DelegatedAccess;
+Onyx.connect({
+ key: ONYXKEYS.ACCOUNT,
+ callback: (val) => {
+ delegatedAccess = val?.delegatedAccess ?? {};
+ },
+});
+
+const KEYS_TO_PRESERVE_DELEGATE_ACCESS = [ONYXKEYS.NVP_TRY_FOCUS_MODE, ONYXKEYS.PREFERRED_THEME, ONYXKEYS.NVP_PREFERRED_LOCALE, ONYXKEYS.SESSION];
+
+function connect(email: string) {
+ if (!delegatedAccess?.delegators) {
+ return;
+ }
+
+ const optimisticData: OnyxUpdate[] = [
+ {
+ onyxMethod: Onyx.METHOD.MERGE,
+ key: ONYXKEYS.ACCOUNT,
+ value: {
+ delegatedAccess: {
+ delegators: delegatedAccess.delegators.map((delegator) => (delegator.email === email ? {...delegator, error: undefined} : delegator)),
+ },
+ },
+ },
+ ];
+
+ const successData: OnyxUpdate[] = [
+ {
+ onyxMethod: Onyx.METHOD.MERGE,
+ key: ONYXKEYS.ACCOUNT,
+ value: {
+ delegatedAccess: {
+ delegators: delegatedAccess.delegators.map((delegator) => (delegator.email === email ? {...delegator, error: undefined} : delegator)),
+ },
+ },
+ },
+ ];
+
+ const failureData: OnyxUpdate[] = [
+ {
+ onyxMethod: Onyx.METHOD.MERGE,
+ key: ONYXKEYS.ACCOUNT,
+ value: {
+ delegatedAccess: {
+ delegators: delegatedAccess.delegators.map((delegator) => (delegator.email === email ? {...delegator, error: 'delegate.genericError'} : delegator)),
+ },
+ },
+ },
+ ];
+
+ // We need to access the authToken directly from the response to update the session
+ // eslint-disable-next-line rulesdir/no-api-side-effects-method
+ API.makeRequestWithSideEffects(SIDE_EFFECT_REQUEST_COMMANDS.CONNECT_AS_DELEGATE, {to: email}, {optimisticData, successData, failureData})
+ .then((response) => {
+ if (!response?.restrictedToken || !response?.encryptedAuthToken) {
+ Log.alert('[Delegate] No auth token returned while connecting as a delegate');
+ Onyx.update(failureData);
+ return;
+ }
+ return SequentialQueue.waitForIdle()
+ .then(() => Onyx.clear(KEYS_TO_PRESERVE_DELEGATE_ACCESS))
+ .then(() => {
+ // Update authToken in Onyx and in our local variables so that API requests will use the new authToken
+ updateSessionAuthTokens(response?.restrictedToken, response?.encryptedAuthToken);
+
+ NetworkStore.setAuthToken(response?.restrictedToken ?? null);
+ confirmReadyToOpenApp();
+ openApp();
+ });
+ })
+ .catch((error) => {
+ Log.alert('[Delegate] Error connecting as delegate', {error});
+ Onyx.update(failureData);
+ });
+}
+
+function disconnect() {
+ const optimisticData: OnyxUpdate[] = [
+ {
+ onyxMethod: Onyx.METHOD.MERGE,
+ key: ONYXKEYS.ACCOUNT,
+ value: {
+ delegatedAccess: {
+ error: null,
+ },
+ },
+ },
+ ];
+
+ const successData: OnyxUpdate[] = [
+ {
+ onyxMethod: Onyx.METHOD.MERGE,
+ key: ONYXKEYS.ACCOUNT,
+ value: {
+ delegatedAccess: {
+ error: null,
+ },
+ },
+ },
+ ];
+
+ const failureData: OnyxUpdate[] = [
+ {
+ onyxMethod: Onyx.METHOD.MERGE,
+ key: ONYXKEYS.ACCOUNT,
+ value: {
+ delegatedAccess: {
+ error: 'delegate.genericError',
+ },
+ },
+ },
+ ];
+
+ // We need to access the authToken directly from the response to update the session
+ // eslint-disable-next-line rulesdir/no-api-side-effects-method
+ API.makeRequestWithSideEffects(SIDE_EFFECT_REQUEST_COMMANDS.DISCONNECT_AS_DELEGATE, {}, {optimisticData, successData, failureData})
+ .then((response) => {
+ if (!response?.authToken || !response?.encryptedAuthToken) {
+ Log.alert('[Delegate] No auth token returned while disconnecting as a delegate');
+ return;
+ }
+
+ return SequentialQueue.waitForIdle()
+ .then(() => Onyx.clear(KEYS_TO_PRESERVE_DELEGATE_ACCESS))
+ .then(() => {
+ // Update authToken in Onyx and in our local variables so that API requests will use the new authToken
+ updateSessionAuthTokens(response?.authToken, response?.encryptedAuthToken);
+
+ NetworkStore.setAuthToken(response?.authToken ?? null);
+ confirmReadyToOpenApp();
+ openApp();
+ });
+ })
+ .catch((error) => {
+ Log.alert('[Delegate] Error disconnecting as a delegate', {error});
+ });
+}
+
+function clearDelegatorErrors() {
+ if (!delegatedAccess?.delegators) {
+ return;
+ }
+ Onyx.merge(ONYXKEYS.ACCOUNT, {delegatedAccess: {delegators: delegatedAccess.delegators.map((delegator) => ({...delegator, error: undefined}))}});
+}
+
+export {connect, clearDelegatorErrors, disconnect};
diff --git a/src/libs/actions/ExitSurvey.ts b/src/libs/actions/ExitSurvey.ts
index 12dc57f1dfde..43f73f4a2764 100644
--- a/src/libs/actions/ExitSurvey.ts
+++ b/src/libs/actions/ExitSurvey.ts
@@ -1,7 +1,7 @@
import type {OnyxUpdate} from 'react-native-onyx';
import Onyx from 'react-native-onyx';
import * as API from '@libs/API';
-import {SIDE_EFFECT_REQUEST_COMMANDS} from '@libs/API/types';
+import {WRITE_COMMANDS} from '@libs/API/types';
import ONYXKEYS from '@src/ONYXKEYS';
import REASON_INPUT_IDS from '@src/types/form/ExitSurveyReasonForm';
import type {ExitReason} from '@src/types/form/ExitSurveyReasonForm';
@@ -31,19 +31,6 @@ function saveResponse(response: string) {
*/
function switchToOldDot() {
const optimisticData: OnyxUpdate[] = [
- {
- onyxMethod: Onyx.METHOD.SET,
- key: ONYXKEYS.IS_SWITCHING_TO_OLD_DOT,
- value: true,
- },
- ];
-
- const finallyData: OnyxUpdate[] = [
- {
- onyxMethod: Onyx.METHOD.SET,
- key: ONYXKEYS.IS_SWITCHING_TO_OLD_DOT,
- value: false,
- },
{
onyxMethod: Onyx.METHOD.SET,
key: ONYXKEYS.FORMS.EXIT_SURVEY_REASON_FORM,
@@ -67,13 +54,13 @@ function switchToOldDot() {
];
// eslint-disable-next-line rulesdir/no-api-side-effects-method
- return API.makeRequestWithSideEffects(
- SIDE_EFFECT_REQUEST_COMMANDS.SWITCH_TO_OLD_DOT,
+ API.write(
+ WRITE_COMMANDS.SWITCH_TO_OLD_DOT,
{
reason: exitReason,
surveyResponse: exitSurveyResponse,
},
- {optimisticData, finallyData},
+ {optimisticData},
);
}
diff --git a/src/libs/actions/IOU.ts b/src/libs/actions/IOU.ts
index f935b813cebd..29d481737790 100644
--- a/src/libs/actions/IOU.ts
+++ b/src/libs/actions/IOU.ts
@@ -6719,6 +6719,31 @@ function getPayMoneyRequestParams(
});
}
+ // Optimistically unhold all transactions if we pay all requests
+ if (full) {
+ const reportTransactions = TransactionUtils.getAllReportTransactions(iouReport.reportID);
+ for (const transaction of reportTransactions) {
+ optimisticData.push({
+ onyxMethod: Onyx.METHOD.MERGE,
+ key: `${ONYXKEYS.COLLECTION.TRANSACTION}${transaction.transactionID}`,
+ value: {
+ comment: {
+ hold: null,
+ },
+ },
+ });
+ failureData.push({
+ onyxMethod: Onyx.METHOD.MERGE,
+ key: `${ONYXKEYS.COLLECTION.TRANSACTION}${transaction.transactionID}`,
+ value: {
+ comment: {
+ hold: transaction.comment?.hold,
+ },
+ },
+ });
+ }
+ }
+
let optimisticHoldReportID;
let optimisticHoldActionID;
let optimisticHoldReportExpenseActionIDs;
@@ -6776,15 +6801,9 @@ function sendMoneyWithWallet(report: OnyxEntry, amount: number
Report.notifyNewAction(params.chatReportID, managerID);
}
-function canApproveIOU(
- iouReport: OnyxTypes.OnyxInputOrEntry,
- chatReport: OnyxTypes.OnyxInputOrEntry,
- policy: OnyxTypes.OnyxInputOrEntry,
-) {
- if (isEmptyObject(chatReport)) {
- return false;
- }
- const isPaidGroupPolicy = ReportUtils.isPaidGroupPolicyExpenseChat(chatReport);
+function canApproveIOU(iouReport: OnyxTypes.OnyxInputOrEntry, policy: OnyxTypes.OnyxInputOrEntry) {
+ // Only expense reports can be approved
+ const isPaidGroupPolicy = policy && PolicyUtils.isPaidGroupPolicy(policy);
if (!isPaidGroupPolicy) {
return false;
}
@@ -6796,9 +6815,7 @@ function canApproveIOU(
const managerID = iouReport?.managerID ?? -1;
const isCurrentUserManager = managerID === userAccountID;
- const isPolicyExpenseChat = ReportUtils.isPolicyExpenseChat(chatReport);
-
- const isOpenExpenseReport = isPolicyExpenseChat && ReportUtils.isOpenExpenseReport(iouReport);
+ const isOpenExpenseReport = ReportUtils.isOpenExpenseReport(iouReport);
const isApproved = ReportUtils.isReportApproved(iouReport);
const iouSettled = ReportUtils.isSettled(iouReport?.reportID);
const reportNameValuePairs = ReportUtils.getReportNameValuePairs(iouReport?.reportID);
@@ -6848,7 +6865,7 @@ function canIOUBePaid(
const {reimbursableSpend} = ReportUtils.getMoneyRequestSpendBreakdown(iouReport);
const isAutoReimbursable = policy?.reimbursementChoice === CONST.POLICY.REIMBURSEMENT_CHOICES.REIMBURSEMENT_YES ? false : ReportUtils.canBeAutoReimbursed(iouReport, policy);
- const shouldBeApproved = canApproveIOU(iouReport, chatReport, policy);
+ const shouldBeApproved = canApproveIOU(iouReport, policy);
const isPayAtEndExpenseReport = ReportUtils.isPayAtEndExpenseReport(iouReport?.reportID, transactions);
@@ -6871,7 +6888,7 @@ function hasIOUToApproveOrPay(chatReport: OnyxEntry, excludedI
return Object.values(chatReportActions).some((action) => {
const iouReport = ReportUtils.getReportOrDraftReport(action.childReportID ?? '-1');
const policy = PolicyUtils.getPolicy(iouReport?.policyID);
- const shouldShowSettlementButton = canIOUBePaid(iouReport, chatReport, policy) || canApproveIOU(iouReport, chatReport, policy);
+ const shouldShowSettlementButton = canIOUBePaid(iouReport, chatReport, policy) || canApproveIOU(iouReport, policy);
return action.childReportID?.toString() !== excludedIOUReportID && action.actionName === CONST.REPORT.ACTIONS.TYPE.REPORT_PREVIEW && shouldShowSettlementButton;
});
}
diff --git a/src/libs/actions/Modal.ts b/src/libs/actions/Modal.ts
index 9cba7a359537..01ac832336ab 100644
--- a/src/libs/actions/Modal.ts
+++ b/src/libs/actions/Modal.ts
@@ -5,6 +5,7 @@ const closeModals: Array<(isNavigating?: boolean) => void> = [];
let onModalClose: null | (() => void);
let isNavigate: undefined | boolean;
+let shouldCloseAll: boolean | undefined;
/**
* Allows other parts of the app to call modal close function
@@ -39,12 +40,13 @@ function closeTop() {
/**
* Close modal in other parts of the app
*/
-function close(onModalCloseCallback: () => void, isNavigating = true) {
+function close(onModalCloseCallback: () => void, isNavigating = true, shouldCloseAllModals = false) {
if (closeModals.length === 0) {
onModalCloseCallback();
return;
}
onModalClose = onModalCloseCallback;
+ shouldCloseAll = shouldCloseAllModals;
isNavigate = isNavigating;
closeTop();
}
@@ -53,7 +55,7 @@ function onModalDidClose() {
if (!onModalClose) {
return;
}
- if (closeModals.length) {
+ if (closeModals.length && shouldCloseAll) {
closeTop();
return;
}
diff --git a/src/libs/actions/Policy/Policy.ts b/src/libs/actions/Policy/Policy.ts
index 1299129d808e..abf906d66a4e 100644
--- a/src/libs/actions/Policy/Policy.ts
+++ b/src/libs/actions/Policy/Policy.ts
@@ -208,8 +208,8 @@ function getPolicy(policyID: string | undefined): OnyxEntry {
* Returns a primary policy for the user
*/
// TODO: Use getInvoicePrimaryWorkspace when the invoices screen is ready - https://github.com/Expensify/App/issues/45175.
-function getPrimaryPolicy(activePolicyID?: OnyxEntry): Policy | undefined {
- const activeAdminWorkspaces = PolicyUtils.getActiveAdminWorkspaces(allPolicies);
+function getPrimaryPolicy(activePolicyID: OnyxEntry, currentUserLogin: string | undefined): Policy | undefined {
+ const activeAdminWorkspaces = PolicyUtils.getActiveAdminWorkspaces(allPolicies, currentUserLogin);
const primaryPolicy: Policy | null | undefined = activeAdminWorkspaces.find((policy) => policy.id === activePolicyID);
return primaryPolicy ?? activeAdminWorkspaces[0];
}
@@ -225,11 +225,11 @@ function hasInvoicingDetails(policy: OnyxEntry): boolean {
/**
* Returns a primary invoice workspace for the user
*/
-function getInvoicePrimaryWorkspace(activePolicyID?: OnyxEntry): Policy | undefined {
+function getInvoicePrimaryWorkspace(activePolicyID: OnyxEntry, currentUserLogin: string | undefined): Policy | undefined {
if (PolicyUtils.canSendInvoiceFromWorkspace(activePolicyID)) {
return allPolicies?.[`${ONYXKEYS.COLLECTION.POLICY}${activePolicyID ?? '-1'}`];
}
- const activeAdminWorkspaces = PolicyUtils.getActiveAdminWorkspaces(allPolicies);
+ const activeAdminWorkspaces = PolicyUtils.getActiveAdminWorkspaces(allPolicies, currentUserLogin);
return activeAdminWorkspaces.find((policy) => PolicyUtils.canSendInvoiceFromWorkspace(policy.id));
}
@@ -2141,12 +2141,21 @@ function openWorkspaceInvitePage(policyID: string, clientMemberEmails: string[])
}
function openDraftWorkspaceRequest(policyID: string) {
+ if (policyID === '-1' || policyID === CONST.POLICY.ID_FAKE) {
+ Log.warn('openDraftWorkspaceRequest invalid params', {policyID});
+ return;
+ }
+
const params: OpenDraftWorkspaceRequestParams = {policyID};
API.read(READ_COMMANDS.OPEN_DRAFT_WORKSPACE_REQUEST, params);
}
-function requestExpensifyCardLimitIncrease(settlementBankAccountID: string) {
+function requestExpensifyCardLimitIncrease(settlementBankAccountID?: number) {
+ if (!settlementBankAccountID) {
+ return;
+ }
+
const authToken = NetworkStore.getAuthToken();
const params: RequestExpensifyCardLimitIncreaseParams = {
@@ -2592,6 +2601,26 @@ function createWorkspaceFromIOUPayment(iouReport: OnyxEntry): WorkspaceF
value: {[movedReportAction.reportActionID]: null},
});
+ // We know that this new workspace has no BankAccount yet, so we can set
+ // the reimbursement account to be immediately in the setup state for a new bank account:
+ optimisticData.push({
+ onyxMethod: Onyx.METHOD.MERGE,
+ key: `${ONYXKEYS.REIMBURSEMENT_ACCOUNT}`,
+ value: {
+ isLoading: false,
+ achData: {
+ currentStep: CONST.BANK_ACCOUNT.STEP.BANK_ACCOUNT,
+ policyID,
+ subStep: '',
+ },
+ },
+ });
+ failureData.push({
+ onyxMethod: Onyx.METHOD.SET,
+ key: `${ONYXKEYS.REIMBURSEMENT_ACCOUNT}`,
+ value: CONST.REIMBURSEMENT_ACCOUNT.DEFAULT_DATA,
+ });
+
const params: CreateWorkspaceFromIOUPaymentParams = {
policyID,
announceChatReportID,
@@ -3715,6 +3744,85 @@ function getAdminPoliciesConnectedToNetSuite(): Policy[] {
return Object.values(allPolicies ?? {}).filter((policy): policy is Policy => !!policy && policy.role === CONST.POLICY.ROLE.ADMIN && !!policy?.connections?.netsuite);
}
+function setWorkspaceCompanyCardFeedName(policyID: string, workspaceAccountID: number, bankName: string, userDefinedName: string) {
+ const authToken = NetworkStore.getAuthToken();
+ const onyxData: OnyxData = {
+ optimisticData: [
+ {
+ onyxMethod: Onyx.METHOD.MERGE,
+ key: `${ONYXKEYS.COLLECTION.SHARED_NVP_PRIVATE_DOMAIN_MEMBER}${workspaceAccountID}`,
+ value: {
+ companyCardNicknames: {
+ [bankName]: userDefinedName,
+ },
+ },
+ },
+ ],
+ };
+
+ const parameters = {
+ authToken,
+ policyID,
+ bankName,
+ userDefinedName,
+ };
+
+ API.write(WRITE_COMMANDS.SET_COMPANY_CARD_FEED_NAME, parameters, onyxData);
+}
+
+function setWorkspaceCompanyCardTransactionLiability(workspaceAccountID: number, bankName: string, liabilityType: string) {
+ const authToken = NetworkStore.getAuthToken();
+ const onyxData: OnyxData = {
+ optimisticData: [
+ {
+ onyxMethod: Onyx.METHOD.MERGE,
+ key: `${ONYXKEYS.COLLECTION.SHARED_NVP_PRIVATE_DOMAIN_MEMBER}${workspaceAccountID}`,
+ value: {
+ companyCards: {
+ [bankName]: {liabilityType},
+ },
+ },
+ },
+ ],
+ };
+
+ const parameters = {
+ authToken,
+ bankName,
+ liabilityType,
+ };
+
+ API.write(WRITE_COMMANDS.SET_COMPANY_CARD_TRANSACTION_LIABILITY, parameters, onyxData);
+}
+function deleteWorkspaceCompanyCardFeed(policyID: string, workspaceAccountID: number, bankName: string) {
+ const authToken = NetworkStore.getAuthToken();
+
+ const onyxData: OnyxData = {
+ optimisticData: [
+ {
+ onyxMethod: Onyx.METHOD.MERGE,
+ key: `${ONYXKEYS.COLLECTION.SHARED_NVP_PRIVATE_DOMAIN_MEMBER}${workspaceAccountID}`,
+ value: {
+ companyCards: {
+ [bankName]: null,
+ },
+ companyCardNicknames: {
+ [bankName]: null,
+ },
+ },
+ },
+ ],
+ };
+
+ const parameters = {
+ authToken,
+ policyID,
+ bankName,
+ };
+
+ API.write(WRITE_COMMANDS.DELETE_COMPANY_CARD_FEED, parameters, onyxData);
+}
+
function clearAllPolicies() {
if (!allPolicies) {
return;
@@ -3806,6 +3914,9 @@ export {
setPolicyMaxExpenseAge,
setPolicyBillableMode,
setWorkspaceEReceiptsEnabled,
+ setWorkspaceCompanyCardFeedName,
+ deleteWorkspaceCompanyCardFeed,
+ setWorkspaceCompanyCardTransactionLiability,
};
export type {NewCustomUnit};
diff --git a/src/libs/actions/Report.ts b/src/libs/actions/Report.ts
index ed984080ff6c..edded7a4c4c6 100644
--- a/src/libs/actions/Report.ts
+++ b/src/libs/actions/Report.ts
@@ -2612,6 +2612,7 @@ function toggleEmojiReaction(
reactionObject: Emoji,
existingReactions: OnyxEntry,
paramSkinTone: number = preferredSkinTone,
+ ignoreSkinToneOnCompare = false,
) {
const originalReportID = ReportUtils.getOriginalReportID(reportID, reportAction);
@@ -2633,7 +2634,7 @@ function toggleEmojiReaction(
// Only use skin tone if emoji supports it
const skinTone = emoji.types === undefined ? -1 : paramSkinTone;
- if (existingReactionObject && EmojiUtils.hasAccountIDEmojiReacted(currentUserAccountID, existingReactionObject.users, skinTone)) {
+ if (existingReactionObject && EmojiUtils.hasAccountIDEmojiReacted(currentUserAccountID, existingReactionObject.users, ignoreSkinToneOnCompare ? undefined : skinTone)) {
removeEmojiReaction(originalReportID, reportAction.reportActionID, emoji);
return;
}
@@ -2668,9 +2669,15 @@ function openReportFromDeepLink(url: string) {
// Navigate to the report after sign-in/sign-up.
InteractionManager.runAfterInteractions(() => {
Session.waitForUserSignIn().then(() => {
- Onyx.connect({
+ const connection = Onyx.connect({
key: ONYXKEYS.NVP_ONBOARDING,
callback: (onboarding) => {
+ if (onboarding) {
+ // Once the onboarding data is available, we want to disconnect the connection
+ // so it won't trigger the deeplink again every time the data is changed, for example, when relogin.
+ Onyx.disconnect(connection);
+ }
+
Navigation.waitForProtectedRoutes().then(() => {
if (route && Session.isAnonymousUser() && !Session.canAnonymousUserAccessRoute(route)) {
Session.signOutAndRedirectToSignIn(true);
diff --git a/src/libs/actions/ReportActions.ts b/src/libs/actions/ReportActions.ts
index b3718079441f..ed8d4886659c 100644
--- a/src/libs/actions/ReportActions.ts
+++ b/src/libs/actions/ReportActions.ts
@@ -32,7 +32,8 @@ function clearReportActionErrors(reportID: string, reportAction: ReportAction, k
});
// If there's a linked transaction, delete that too
- const linkedTransactionID = ReportActionUtils.getLinkedTransactionID(reportAction.reportActionID, originalReportID ?? '-1');
+ // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
+ const linkedTransactionID = ReportActionUtils.getLinkedTransactionID(reportAction.reportActionID, originalReportID || '-1');
if (linkedTransactionID) {
Onyx.set(`${ONYXKEYS.COLLECTION.TRANSACTION}${linkedTransactionID}`, null);
}
diff --git a/src/libs/actions/Search.ts b/src/libs/actions/Search.ts
index b51955c9cc59..020fc1bd2c30 100644
--- a/src/libs/actions/Search.ts
+++ b/src/libs/actions/Search.ts
@@ -49,7 +49,7 @@ function getOnyxLoadingData(hash: number): {optimisticData: OnyxUpdate[]; finall
return {optimisticData, finallyData};
}
-function search({queryJSON, offset, policyIDs}: {queryJSON: SearchQueryJSON; offset?: number; policyIDs?: string}) {
+function search({queryJSON, offset}: {queryJSON: SearchQueryJSON; offset?: number}) {
const {optimisticData, finallyData} = getOnyxLoadingData(queryJSON.hash);
const queryWithOffset = {
@@ -58,7 +58,7 @@ function search({queryJSON, offset, policyIDs}: {queryJSON: SearchQueryJSON; off
};
const jsonQuery = JSON.stringify(queryWithOffset);
- API.read(READ_COMMANDS.SEARCH, {hash: queryJSON.hash, jsonQuery, policyIDs}, {optimisticData, finallyData});
+ API.read(READ_COMMANDS.SEARCH, {hash: queryJSON.hash, jsonQuery}, {optimisticData, finallyData});
}
/**
diff --git a/src/libs/actions/Session/index.ts b/src/libs/actions/Session/index.ts
index b687abd61bb9..e905464e551f 100644
--- a/src/libs/actions/Session/index.ts
+++ b/src/libs/actions/Session/index.ts
@@ -12,6 +12,7 @@ import type {
BeginAppleSignInParams,
BeginGoogleSignInParams,
BeginSignInParams,
+ DisableTwoFactorAuthParams,
RequestAccountValidationLinkParams,
RequestNewValidateCodeParams,
RequestUnlinkValidationLinkParams,
@@ -206,7 +207,7 @@ function signOutAndRedirectToSignIn(shouldResetToHome?: boolean, shouldStashSess
if (!isAnonymousUser()) {
// In the HybridApp, we want the Old Dot to handle the sign out process
if (NativeModules.HybridAppModule && killHybridApp) {
- NativeModules.HybridAppModule.closeReactNativeApp(true);
+ NativeModules.HybridAppModule.closeReactNativeApp(true, false);
return;
}
// We'll only call signOut if we're not stashing the session and this is not a supportal session,
@@ -877,7 +878,7 @@ function unlinkLogin(accountID: number, validateCode: string) {
/**
* Toggles two-factor authentication based on the `enable` parameter
*/
-function toggleTwoFactorAuth(enable: boolean) {
+function toggleTwoFactorAuth(enable: boolean, twoFactorAuthCode = '') {
const optimisticData: OnyxUpdate[] = [
{
onyxMethod: Onyx.METHOD.MERGE,
@@ -894,6 +895,9 @@ function toggleTwoFactorAuth(enable: boolean) {
key: ONYXKEYS.ACCOUNT,
value: {
isLoading: false,
+
+ // When disabling 2FA, the user needs to end up on the step that confirms the setting was disabled
+ twoFactorAuthStep: enable ? undefined : CONST.TWO_FACTOR_AUTH_STEPS.DISABLED,
},
},
];
@@ -908,7 +912,16 @@ function toggleTwoFactorAuth(enable: boolean) {
},
];
- API.write(enable ? WRITE_COMMANDS.ENABLE_TWO_FACTOR_AUTH : WRITE_COMMANDS.DISABLE_TWO_FACTOR_AUTH, null, {optimisticData, successData, failureData});
+ if (enable) {
+ API.write(WRITE_COMMANDS.ENABLE_TWO_FACTOR_AUTH, null, {optimisticData, successData, failureData});
+ return;
+ }
+
+ // A 2FA code is required to disable 2FA
+ const params: DisableTwoFactorAuthParams = {twoFactorAuthCode};
+
+ // eslint-disable-next-line rulesdir/no-multiple-api-calls
+ API.write(WRITE_COMMANDS.DISABLE_TWO_FACTOR_AUTH, params, {optimisticData, successData, failureData});
}
function updateAuthTokenAndOpenApp(authToken?: string, encryptedAuthToken?: string) {
diff --git a/src/libs/actions/TaxRate.ts b/src/libs/actions/TaxRate.ts
index 53f29473d1e3..514b73915633 100644
--- a/src/libs/actions/TaxRate.ts
+++ b/src/libs/actions/TaxRate.ts
@@ -21,6 +21,7 @@ import INPUT_IDS from '@src/types/form/WorkspaceNewTaxForm';
import {default as INPUT_IDS_TAX_CODE} from '@src/types/form/WorkspaceTaxCodeForm';
import type {Policy, TaxRate, TaxRates} from '@src/types/onyx';
import type * as OnyxCommon from '@src/types/onyx/OnyxCommon';
+import type {CustomUnit, Rate} from '@src/types/onyx/Policy';
import type {OnyxData} from '@src/types/onyx/Request';
let allPolicies: OnyxCollection;
@@ -486,6 +487,35 @@ function renamePolicyTax(policyID: string, taxID: string, newName: string) {
function setPolicyTaxCode(policyID: string, oldTaxCode: string, newTaxCode: string) {
const policy = allPolicies?.[`${ONYXKEYS.COLLECTION.POLICY}${policyID}`];
const originalTaxRate = {...policy?.taxRates?.taxes[oldTaxCode]};
+ const customUnits = Object.values(policy?.customUnits ?? {});
+ const optimisticCustomUnit = {
+ customUnits: {
+ ...customUnits.reduce((units, customUnit) => {
+ // eslint-disable-next-line no-param-reassign
+ units[customUnit.customUnitID] = {
+ rates: {
+ ...Object.keys(customUnit.rates).reduce((rates, rateID) => {
+ if (customUnit.rates[rateID].attributes?.taxRateExternalID === oldTaxCode) {
+ // eslint-disable-next-line no-param-reassign
+ rates[rateID] = {
+ attributes: {
+ taxRateExternalID: newTaxCode,
+ },
+ };
+ }
+ return rates;
+ }, {} as Record),
+ },
+ };
+ return units;
+ }, {} as Record>),
+ },
+ };
+ const failureCustomUnit = {
+ customUnits: policy?.customUnits,
+ };
+ const oldDefaultExternalID = policy?.taxRates?.defaultExternalID;
+ const oldForeignTaxDefault = policy?.taxRates?.foreignTaxDefault;
const onyxData: OnyxData = {
optimisticData: [
{
@@ -493,18 +523,20 @@ function setPolicyTaxCode(policyID: string, oldTaxCode: string, newTaxCode: stri
key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`,
value: {
taxRates: {
- defaultExternalID: oldTaxCode === policy?.taxRates?.defaultExternalID ? newTaxCode : policy?.taxRates?.defaultExternalID,
+ defaultExternalID: oldTaxCode === oldDefaultExternalID ? newTaxCode : oldDefaultExternalID,
+ foreignTaxDefault: oldTaxCode === oldForeignTaxDefault ? newTaxCode : oldForeignTaxDefault,
taxes: {
[oldTaxCode]: null,
[newTaxCode]: {
...originalTaxRate,
- pendingFields: {code: CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE},
+ pendingFields: {...originalTaxRate.pendingFields, code: CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE},
pendingAction: CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE,
errorFields: {code: null},
previousTaxCode: oldTaxCode,
},
},
},
+ ...(!!customUnits && optimisticCustomUnit),
},
},
],
@@ -514,13 +546,14 @@ function setPolicyTaxCode(policyID: string, oldTaxCode: string, newTaxCode: stri
key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`,
value: {
taxRates: {
- defaultExternalID: oldTaxCode === policy?.taxRates?.defaultExternalID ? newTaxCode : policy?.taxRates?.defaultExternalID,
+ defaultExternalID: oldTaxCode === oldDefaultExternalID ? newTaxCode : oldDefaultExternalID,
+ foreignTaxDefault: oldTaxCode === oldForeignTaxDefault ? newTaxCode : oldForeignTaxDefault,
taxes: {
[oldTaxCode]: null,
[newTaxCode]: {
...originalTaxRate,
code: newTaxCode,
- pendingFields: {code: null},
+ pendingFields: {...originalTaxRate.pendingFields, code: null},
pendingAction: null,
errorFields: {code: null},
},
@@ -535,18 +568,20 @@ function setPolicyTaxCode(policyID: string, oldTaxCode: string, newTaxCode: stri
key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`,
value: {
taxRates: {
- defaultExternalID: policy?.taxRates?.defaultExternalID,
+ defaultExternalID: oldDefaultExternalID,
+ foreignTaxDefault: oldForeignTaxDefault,
taxes: {
[newTaxCode]: null,
[oldTaxCode]: {
...originalTaxRate,
code: originalTaxRate.code,
- pendingFields: {code: null},
+ pendingFields: {...originalTaxRate.pendingFields, code: null},
pendingAction: null,
errorFields: {code: ErrorUtils.getMicroSecondOnyxErrorWithTranslationKey('workspace.taxes.error.updateFailureMessage')},
},
},
},
+ ...(!!customUnits && failureCustomUnit),
},
},
],
diff --git a/src/libs/actions/TransactionEdit.ts b/src/libs/actions/TransactionEdit.ts
index 2ce2f775586e..a76cb8f25b75 100644
--- a/src/libs/actions/TransactionEdit.ts
+++ b/src/libs/actions/TransactionEdit.ts
@@ -1,8 +1,10 @@
import Onyx from 'react-native-onyx';
-import type {OnyxEntry} from 'react-native-onyx';
+import type {Connection, OnyxEntry} from 'react-native-onyx';
import ONYXKEYS from '@src/ONYXKEYS';
import type {Transaction} from '@src/types/onyx';
+let connection: Connection;
+
/**
* Makes a backup copy of a transaction object that can be restored when the user cancels editing a transaction.
*/
@@ -11,6 +13,10 @@ function createBackupTransaction(transaction: OnyxEntry) {
return;
}
+ // In Strict Mode, the backup logic useEffect is triggered twice on mount. The restore logic is delayed because we need to connect to the onyx first,
+ // so it's possible that the restore logic is executed after creating the backup for the 2nd time which will completely clear the backup.
+ // To avoid that, we need to cancel the pending connection.
+ Onyx.disconnect(connection);
const newTransaction = {
...transaction,
};
@@ -27,7 +33,7 @@ function removeBackupTransaction(transactionID: string) {
}
function restoreOriginalTransactionFromBackup(transactionID: string, isDraft: boolean) {
- const connection = Onyx.connect({
+ connection = Onyx.connect({
key: `${ONYXKEYS.COLLECTION.TRANSACTION_BACKUP}${transactionID}`,
callback: (backupTransaction) => {
Onyx.disconnect(connection);
diff --git a/src/libs/actions/Travel.ts b/src/libs/actions/Travel.ts
index 57201df771be..f63d73fc27eb 100644
--- a/src/libs/actions/Travel.ts
+++ b/src/libs/actions/Travel.ts
@@ -15,15 +15,27 @@ function acceptSpotnanaTerms() {
key: ONYXKEYS.NVP_TRAVEL_SETTINGS,
value: {
hasAcceptedTerms: true,
+ isLoading: true,
},
},
];
+
+ const finallyData: OnyxUpdate[] = [
+ {
+ onyxMethod: 'merge',
+ key: ONYXKEYS.NVP_TRAVEL_SETTINGS,
+ value: {
+ isLoading: false,
+ },
+ },
+ ];
+
const error = new Error('Failed to generate spotnana token.');
- return new Promise((_, reject) => {
+ return new Promise((resolve, reject) => {
asyncOpenURL(
// eslint-disable-next-line rulesdir/no-api-side-effects-method
- API.makeRequestWithSideEffects(SIDE_EFFECT_REQUEST_COMMANDS.ACCEPT_SPOTNANA_TERMS, null, {optimisticData})
+ API.makeRequestWithSideEffects(SIDE_EFFECT_REQUEST_COMMANDS.ACCEPT_SPOTNANA_TERMS, null, {optimisticData, finallyData})
.then((response) => {
if (!response?.spotnanaToken) {
reject(error);
@@ -35,7 +47,10 @@ function acceptSpotnanaTerms() {
reject(error);
throw error;
}),
- (travelDotURL) => travelDotURL ?? '',
+ (travelDotURL) => {
+ resolve(travelDotURL);
+ return travelDotURL ?? '';
+ },
);
});
}
diff --git a/src/libs/actions/User.ts b/src/libs/actions/User.ts
index 74e5e5d869f9..1ac44512b8c5 100644
--- a/src/libs/actions/User.ts
+++ b/src/libs/actions/User.ts
@@ -208,7 +208,7 @@ function updateNewsletterSubscription(isSubscribed: boolean) {
* @param contactMethod - the contact method being deleted
* @param loginList
*/
-function deleteContactMethod(contactMethod: string, loginList: Record) {
+function deleteContactMethod(contactMethod: string, loginList: Record, backTo?: string) {
const oldLoginData = loginList[contactMethod];
const optimisticData: OnyxUpdate[] = [
@@ -257,7 +257,17 @@ function deleteContactMethod(contactMethod: string, loginList: Record) {
@@ -1123,7 +1246,8 @@ export {
updateNewsletterSubscription,
deleteContactMethod,
clearContactMethodErrors,
- addNewContactMethodAndNavigate,
+ clearContactMethod,
+ addNewContactMethod,
validateLogin,
validateSecondaryLogin,
isBlockedFromConcierge,
@@ -1144,4 +1268,6 @@ export {
updateDraftCustomStatus,
clearDraftCustomStatus,
requestRefund,
+ saveNewContactMethodAndRequestValidationCode,
+ clearUnvalidatedNewContactMethodAction,
};
diff --git a/src/libs/actions/connections/index.ts b/src/libs/actions/connections/index.ts
index fa2e274204a2..4c7ec69e6134 100644
--- a/src/libs/actions/connections/index.ts
+++ b/src/libs/actions/connections/index.ts
@@ -1,3 +1,4 @@
+import {differenceInMinutes, isValid, parseISO} from 'date-fns';
import isObject from 'lodash/isObject';
import type {OnyxEntry, OnyxUpdate} from 'react-native-onyx';
import Onyx from 'react-native-onyx';
@@ -9,7 +10,7 @@ import * as Localize from '@libs/Localize';
import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
import type * as OnyxCommon from '@src/types/onyx/OnyxCommon';
-import type {ConnectionName, Connections, PolicyConnectionName} from '@src/types/onyx/Policy';
+import type {ConnectionName, Connections, PolicyConnectionName, PolicyConnectionSyncProgress} from '@src/types/onyx/Policy';
import type Policy from '@src/types/onyx/Policy';
import {isEmptyObject} from '@src/types/utils/EmptyObject';
@@ -383,7 +384,8 @@ function getSynchronizationErrorMessage(policy: OnyxEntry, connectionNam
}
const connection = policy?.connections?.[connectionName];
- if (isSyncInProgress || isEmptyObject(connection?.lastSync) || connection?.lastSync?.isSuccessful) {
+
+ if (isSyncInProgress || isEmptyObject(connection?.lastSync) || connection?.lastSync?.isSuccessful !== false || !connection?.lastSync?.errorDate) {
return;
}
return `${syncError} ("${connection?.lastSync?.errorMessage}")`;
@@ -448,6 +450,20 @@ function copyExistingPolicyConnection(connectedPolicyID: string, targetPolicyID:
);
}
+function isConnectionInProgress(connectionSyncProgress: OnyxEntry, policy?: OnyxEntry): boolean {
+ if (!policy || !connectionSyncProgress) {
+ return false;
+ }
+
+ const lastSyncProgressDate = parseISO(connectionSyncProgress?.timestamp ?? '');
+ return (
+ !!connectionSyncProgress?.stageInProgress &&
+ (connectionSyncProgress.stageInProgress !== CONST.POLICY.CONNECTIONS.SYNC_STAGE_NAME.JOB_DONE || !policy?.connections?.[connectionSyncProgress.connectionName]) &&
+ isValid(lastSyncProgressDate) &&
+ differenceInMinutes(new Date(), lastSyncProgressDate) < CONST.POLICY.CONNECTIONS.SYNC_STAGE_TIMEOUT_MINUTES
+ );
+}
+
export {
removePolicyConnection,
updatePolicyConnectionConfig,
@@ -458,4 +474,5 @@ export {
syncConnection,
copyExistingPolicyConnection,
isConnectionUnverified,
+ isConnectionInProgress,
};
diff --git a/src/libs/fileDownload/getPhotoSource/index.android.ts b/src/libs/fileDownload/getPhotoSource/index.android.ts
new file mode 100644
index 000000000000..2d44d7fbf2db
--- /dev/null
+++ b/src/libs/fileDownload/getPhotoSource/index.android.ts
@@ -0,0 +1,5 @@
+function getPhotoSource(filePath: string): string {
+ return `file://${filePath}`;
+}
+
+export default getPhotoSource;
diff --git a/src/libs/fileDownload/getPhotoSource/index.ts b/src/libs/fileDownload/getPhotoSource/index.ts
new file mode 100644
index 000000000000..efbdeb54a063
--- /dev/null
+++ b/src/libs/fileDownload/getPhotoSource/index.ts
@@ -0,0 +1,5 @@
+function getPhotoSource(filePath: string): string {
+ return filePath;
+}
+
+export default getPhotoSource;
diff --git a/src/libs/freezeScreenWithLazyLoading.tsx b/src/libs/freezeScreenWithLazyLoading.tsx
index 21a2681b08c1..eb3c8fa8bc63 100644
--- a/src/libs/freezeScreenWithLazyLoading.tsx
+++ b/src/libs/freezeScreenWithLazyLoading.tsx
@@ -14,8 +14,11 @@ function FrozenScreen(WrappedCompo
}
export default function freezeScreenWithLazyLoading(lazyComponent: () => React.ComponentType) {
- return memoize(() => {
- const Component = lazyComponent();
- return FrozenScreen(Component);
- });
+ return memoize(
+ () => {
+ const Component = lazyComponent();
+ return FrozenScreen(Component);
+ },
+ {monitoringName: 'freezeScreenWithLazyLoading'},
+ );
}
diff --git a/src/libs/memoize/index.ts b/src/libs/memoize/index.ts
index e14606618953..f02b1adbf5ba 100644
--- a/src/libs/memoize/index.ts
+++ b/src/libs/memoize/index.ts
@@ -54,6 +54,9 @@ function memoize): IsomorphicReturnType {
+ const statsEntry = stats.createEntry();
+ const retrievalTimeStart = performance.now();
+
// Detect if memoized function was called with `new` keyword. If so we need to call the original function as constructor.
const constructable = !!new.target;
@@ -61,22 +64,22 @@ function memoize {
const fnTimeStart = performance.now();
-
const result = (constructable ? new (fn as Constructable)(...args) : (fn as Callable)(...args)) as IsomorphicReturnType;
- statsEntry.trackTime('fnTime', fnTimeStart);
+ // Track processing time
+ statsEntry.trackTime('processingTime', fnTimeStart);
statsEntry.track('didHit', false);
return result;
});
- // Subtract the time it took to run the function from the total retrieval time
- statsEntry.trackTime('cacheRetrievalTime', retrievalTimeStart + (statsEntry.get('fnTime') ?? 0));
+
+ // If processing time was not tracked inside getSet callback, track it as a cache retrieval
+ if (statsEntry.get('processingTime') === undefined) {
+ statsEntry.trackTime('processingTime', retrievalTimeStart);
+ statsEntry.track('didHit', true);
+ }
statsEntry.track('cacheSize', cache.size);
statsEntry.save();
diff --git a/src/libs/memoize/stats.ts b/src/libs/memoize/stats.ts
index 043ecc92b216..59d563100ec4 100644
--- a/src/libs/memoize/stats.ts
+++ b/src/libs/memoize/stats.ts
@@ -2,15 +2,14 @@ import Log from '@libs/Log';
type MemoizeStatsEntry = {
didHit: boolean;
- cacheRetrievalTime: number;
- fnTime?: number;
+ processingTime: number;
cacheSize: number;
};
// eslint-disable-next-line @typescript-eslint/no-explicit-any
function isMemoizeStatsEntry(entry: any): entry is MemoizeStatsEntry {
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
- return entry.didHit !== undefined && entry.cacheRetrievalTime !== undefined;
+ return entry.didHit !== undefined && entry.processingTime !== undefined;
}
class MemoizeStats {
@@ -18,7 +17,7 @@ class MemoizeStats {
private hits = 0;
- private avgCacheRetrievalTime = 0;
+ private avgCacheTime = 0;
private avgFnTime = 0;
@@ -39,14 +38,13 @@ class MemoizeStats {
private cumulateEntry(entry: MemoizeStatsEntry) {
this.calls++;
- this.hits += entry.didHit ? 1 : 0;
-
this.cacheSize = entry.cacheSize;
- this.avgCacheRetrievalTime = this.calculateCumulativeAvg(this.avgCacheRetrievalTime, this.hits, entry.cacheRetrievalTime);
-
- if (entry.fnTime !== undefined) {
- this.avgFnTime = this.calculateCumulativeAvg(this.avgFnTime, this.calls - this.hits, entry.fnTime);
+ if (entry.didHit) {
+ this.hits++;
+ this.avgCacheTime = this.calculateCumulativeAvg(this.avgCacheTime, this.hits, entry.processingTime);
+ } else {
+ this.avgFnTime = this.calculateCumulativeAvg(this.avgFnTime, this.calls - this.hits, entry.processingTime);
}
}
@@ -80,7 +78,7 @@ class MemoizeStats {
track: